Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions horizon/internal/externalcall/gcs_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type GCSClientInterface interface {
GetFolderInfo(bucket, folderPrefix string) (*GCSFolderInfo, error)
ListFoldersWithTimestamp(bucket, prefix string) ([]GCSFolderInfo, error)
FindFileWithSuffix(bucket, folderPath, suffix string) (bool, string, error)
ListFilesWithSuffix(bucket, folderPath, suffix string) ([]string, error)
}

const (
Expand Down Expand Up @@ -771,6 +772,39 @@ func (g *GCSClient) FindFileWithSuffix(bucket, folderPath, suffix string) (bool,
return true, foundFile, nil
}

// ListFilesWithSuffix returns all object paths (full GCS object keys) ending with the given
// suffix under folderPath. Unlike FindFileWithSuffix which returns only the first match,
// this method collects every matching file path. Directory markers are skipped.
func (g *GCSClient) ListFilesWithSuffix(bucket, folderPath, suffix string) ([]string, error) {
if g.client == nil {
return nil, fmt.Errorf("GCS client not initialized properly")
}

if !strings.HasSuffix(folderPath, "/") {
folderPath += "/"
}

log.Info().Msgf("Listing files with suffix '%s' in GCS bucket %s with prefix %s", suffix, bucket, folderPath)

var files []string
err := g.forEachObject(bucket, folderPath, func(attrs *storage.ObjectAttrs) error {
// Skip directory markers
if strings.HasSuffix(attrs.Name, "/") {
return nil
}
if strings.HasSuffix(attrs.Name, suffix) {
files = append(files, attrs.Name)
}
return nil
})
if err != nil {
return nil, fmt.Errorf("failed to list files with suffix '%s': %w", suffix, err)
}

log.Info().Msgf("Found %d files with suffix '%s' under %s/%s", len(files), suffix, bucket, folderPath)
return files, nil
}

// forEachObject iterates over all objects with the given prefix and calls the visitor for each.
func (g *GCSClient) forEachObject(bucket, prefix string, visitor ObjectVisitor) error {
if g.client == nil {
Expand Down
16 changes: 15 additions & 1 deletion horizon/internal/predator/handler/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type IOField struct {
}

type ConfigMapping struct {
ServiceDeployableID uint `json:"service_deployable_id"`
ServiceDeployableID uint `json:"service_deployable_id"`
SourceModelName string `json:"source_model_name,omitempty"`
}

Expand Down Expand Up @@ -270,3 +270,17 @@ type TritonInputTensor struct {
type TritonOutputTensor struct {
Name string `json:"name"`
}

// fileViolationInfo groups all detected violations for a single Python file.
// Used by validateNoLoggerOrPrintStatements and buildBalancedViolationSummary
// to produce balanced, capped error messages.
type fileViolationInfo struct {
fileName string
details []string
}

// funcScopeEntry represents a Python function scope on the tracking stack.
type funcScopeEntry struct {
name string
indent int
}
13 changes: 11 additions & 2 deletions horizon/internal/predator/handler/predator_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const (
successDeleteRequestMsg = "Model deletion request raised successfully"
fieldModelSourcePath = "model_source_path"
fieldMetaData = "meta_data"
fieldDiscoveryConfigID = "discovery_config_id"
fieldDiscoveryConfigID = "discovery_config_id"
fieldConfigMapping = "config_mapping"
errReadConfigFileFormat = "failed to read config.pbtxt: %v"
errUnmarshalProtoFormat = "failed to unmarshal proto text: %v"
Expand All @@ -63,7 +63,7 @@ const (
errMaxBatchSizeMissing = "max_batch_size is missing or zero in config"
errBackendMissing = "backend is missing in config"
errNoInputDefinitions = "no input definitions found in config"
errNoOutputDefinitions = "no output definitions found in config"
errNoOutputDefinitions = "no output definitions found in config"
errInstanceGroupMissing = "instance group is missing in config"
errInvalidRequestIDFormat = "invalid group ID format"
errFailedToFetchRequest = "failed to fetch request for group id %s"
Expand Down Expand Up @@ -112,4 +112,13 @@ const (
predatorInferMethod = "inference.GRPCInferenceService/ModelInfer"
deployableTagDelimiter = "_"
scaleupTag = "scaleup"
errWarmupConfigMissing = "model_warmup configuration is missing in config.pbtxt for non-ensemble model; warmup is required to pre-load models and avoid cold-start latency"
errLoggerOrPrintStatementsFound = "model validation rejected: Python model files contain logger/print statements outside of initialize() and finalize() functions; these must be removed or commented out before deployment to prevent excessive logging in production"
pythonBackend = "python"
ensembleBackend = "ensemble"
ensemblePlatform = "ensemble"
pyFileSuffix = ".py"
allowedFuncInitialize = "initialize"
allowedFuncFinalize = "finalize"
maxDisplayedViolationsForPythonModel = 5
)
Loading