package gateway import ( "context" "fmt" "strings" "github.com/enterpilot/gomodel/internal/core" ) // BatchExecutionSelection captures the provider and workflow selector for a native batch. type BatchExecutionSelection struct { ProviderType string Selector core.WorkflowSelector } // BatchInputFileProviderResolver resolves provider ownership for an uploaded // batch input file. type BatchInputFileProviderResolver interface { ResolveBatchInputFileProvider(ctx context.Context, fileID string) (providerType string, ok bool, err error) } // DetermineBatchExecutionSelectionWithAuthorizerAndInputFileResolver resolves // and authorizes native batch items, using file ownership metadata for // file-backed batches when no explicit provider hint is supplied. func DetermineBatchExecutionSelectionWithAuthorizerAndInputFileResolver( ctx context.Context, provider core.RoutableProvider, resolver ModelResolver, authorizer ModelAuthorizer, inputFileProviderResolver BatchInputFileProviderResolver, req *core.BatchRequest, ) (BatchExecutionSelection, error) { if req == nil { return BatchExecutionSelection{}, core.NewInvalidRequestError("batch request is required", nil) } if provider == nil { return BatchExecutionSelection{}, core.NewInvalidRequestError("provider is not configured", nil) } if strings.TrimSpace(req.InputFileID) != "" { providerType := "" if req.Metadata != nil { providerType = strings.TrimSpace(req.Metadata["provider"]) } if providerType == "" && inputFileProviderResolver != nil { resolved, ok, err := inputFileProviderResolver.ResolveBatchInputFileProvider(ctx, req.InputFileID) if err != nil { return BatchExecutionSelection{}, err } if ok { providerType = strings.TrimSpace(resolved) } } if providerType == "" { return BatchExecutionSelection{}, core.NewInvalidRequestError("unable to resolve provider for input_file_id batch", nil) } return BatchExecutionSelection{ ProviderType: providerType, Selector: core.NewWorkflowSelector(WorkflowProviderNameForType(provider, providerType), ""), }, nil } if len(req.Requests) == 0 { return BatchExecutionSelection{}, core.NewInvalidRequestError("requests is required and must not be empty", nil) } var ( providerType string providerName string commonModel string hasCommonModel = true ) for i, item := range req.Requests { requested, err := core.BatchItemRequestedModelSelector(req.Endpoint, item) if err != nil { return BatchExecutionSelection{}, core.NewInvalidRequestError(fmt.Sprintf("batch item %d: %s", i, err.Error()), err) } resolvedSelector, _, err := ResolveExecutionSelector(ctx, provider, resolver, requested) if err != nil { return BatchExecutionSelection{}, core.NewInvalidRequestError(fmt.Sprintf("batch item %d: %s", i, err.Error()), err) } model := resolvedSelector.QualifiedModel() if model == "" { return BatchExecutionSelection{}, core.NewInvalidRequestError(fmt.Sprintf("batch item %d: model is required", i), nil) } if !provider.Supports(model) { return BatchExecutionSelection{}, core.NewModelNotFoundError(model) } if authorizer != nil { if err := authorizer.ValidateModelAccess(ctx, resolvedSelector); err != nil { return BatchExecutionSelection{}, err } } itemProvider := provider.GetProviderType(model) if providerType == "" { providerType = itemProvider } else if providerType != itemProvider { return BatchExecutionSelection{}, core.NewInvalidRequestError("native batch supports a single provider per batch; split mixed-provider requests", nil) } itemProviderName := ResolvedProviderName(provider, resolvedSelector, resolvedSelector.Provider) if providerName == "" { providerName = itemProviderName } else if itemProviderName != "" && providerName != itemProviderName { return BatchExecutionSelection{}, core.NewInvalidRequestError("native batch supports a single configured provider per batch; split mixed-provider requests", nil) } if !hasCommonModel { continue } resolvedModel := strings.TrimSpace(resolvedSelector.Model) if resolvedModel == "" { hasCommonModel = false continue } if commonModel == "" { commonModel = resolvedModel continue } if commonModel != resolvedModel { hasCommonModel = false } } if providerType == "" { return BatchExecutionSelection{}, core.NewInvalidRequestError("unable to resolve provider for batch", nil) } if providerName == "" { providerName = WorkflowProviderNameForType(provider, providerType) } if !hasCommonModel { commonModel = "" } return BatchExecutionSelection{ ProviderType: providerType, Selector: core.NewWorkflowSelector(providerName, commonModel), }, nil }