// Package execution defines provider-neutral contracts for one selected upstream attempt. package execution import ( "context" "encoding/json" "fmt" "net/http" "net/url" "time" "gpt-load/internal/protocol" "gpt-load/internal/reasoning" "gpt-load/internal/usage" ) // ValidationError identifies one invalid contract field without embedding secret values. type ValidationError struct { Field string Reason string } // Error implements error. func (e *ValidationError) Error() string { return fmt.Sprintf("%s: %s", e.Field, e.Reason) } // Operation identifies one logical upstream operation. type Operation string const ( OperationChatCompletion Operation = "chat_completion" OperationResponsesCreate Operation = "responses_create" OperationResponsesRetrieve Operation = "responses_retrieve" OperationResponsesDelete Operation = "responses_delete" OperationResponsesCancel Operation = "responses_cancel" OperationResponsesInputItems Operation = "responses_input_items" OperationResponsesCompact Operation = "responses_compact" OperationResponsesInputTokens Operation = "responses_input_tokens" OperationResponsesPassthrough Operation = "responses_passthrough" OperationListModels Operation = "list_models" OperationProbe Operation = "probe" ) // Valid reports whether the operation is supported by the execution contract. func (o Operation) Valid() bool { switch o { case OperationChatCompletion, OperationResponsesCreate, OperationResponsesRetrieve, OperationResponsesDelete, OperationResponsesCancel, OperationResponsesInputItems, OperationResponsesCompact, OperationResponsesInputTokens, OperationResponsesPassthrough, OperationListModels, OperationProbe: return true default: return false } } // RouteMode records the already-selected wire strategy for one attempt. // Executors must execute this decision or reject it; they must not reroute it. type RouteMode string const ( RouteNative RouteMode = "native" RouteConverted RouteMode = "converted" ) // UpstreamAPI identifies the actual upstream wire API selected for an attempt. // Values are stable product vocabulary and intentionally exclude SDK names. type UpstreamAPI string const ( UpstreamAPIOpenAIChatCompletions UpstreamAPI = "openai-chat-completions" UpstreamAPIOpenAIResponses UpstreamAPI = "openai-responses" UpstreamAPIAnthropicMessages UpstreamAPI = "anthropic-messages" UpstreamAPIGeminiGenerateContent UpstreamAPI = "gemini-generate-content" UpstreamAPIOpenAIModels UpstreamAPI = "openai-models" UpstreamAPIAnthropicModels UpstreamAPI = "anthropic-models" UpstreamAPIGeminiModels UpstreamAPI = "gemini-models" UpstreamAPIAzureOpenAI UpstreamAPI = "azure-openai" UpstreamAPIAWSBedrock UpstreamAPI = "aws-bedrock" UpstreamAPIGoogleVertex UpstreamAPI = "google-vertex" ) // Valid reports whether the upstream API value is recognized. func (api UpstreamAPI) Valid() bool { switch api { case UpstreamAPIOpenAIChatCompletions, UpstreamAPIOpenAIResponses, UpstreamAPIAnthropicMessages, UpstreamAPIGeminiGenerateContent, UpstreamAPIOpenAIModels, UpstreamAPIAnthropicModels, UpstreamAPIGeminiModels, UpstreamAPIAzureOpenAI, UpstreamAPIAWSBedrock, UpstreamAPIGoogleVertex: return true default: return false } } // Valid reports whether the route mode is recognized. func (m RouteMode) Valid() bool { return m == RouteNative || m == RouteConverted } // RouteRequirement describes whether a request may use a converted route. // It protects operation and provider-resource semantics, not optional model // capabilities such as tools, reasoning, or multimodal input. type RouteRequirement string const ( RouteRequirementAny RouteRequirement = "any" RouteRequirementNative RouteRequirement = "native" ) // Normalize maps the internal zero value to the default best-effort policy. func (r RouteRequirement) Normalize() RouteRequirement { if r == "" { return RouteRequirementAny } return r } // Valid reports whether the normalized requirement is recognized. func (r RouteRequirement) Valid() bool { switch r.Normalize() { case RouteRequirementAny, RouteRequirementNative: return true default: return false } } // Allows reports whether the selected wire route mode satisfies the native // requirement. Target operation coverage is validated separately. func (r RouteRequirement) Allows(mode RouteMode) bool { if !mode.Valid() || !r.Valid() { return false } return r.Normalize() == RouteRequirementAny || mode == RouteNative } // CredentialSnapshot is the exact logical credential selected for an attempt. // Secret data is private and is never included in JSON. type CredentialSnapshot struct { ID uint `json:"id"` Version uint64 `json:"version"` IdentityGeneration uint64 `json:"identity_generation"` data []byte } // NewCredentialSnapshot copies the credential data into a snapshot. func NewCredentialSnapshot(id uint, version, identityGeneration uint64, data []byte) CredentialSnapshot { return CredentialSnapshot{ ID: id, Version: version, IdentityGeneration: identityGeneration, data: cloneBytes(data), } } // Data returns an independent copy of the secret credential data. func (c CredentialSnapshot) Data() []byte { return cloneBytes(c.data) } // Clone returns an independent credential snapshot. func (c CredentialSnapshot) Clone() CredentialSnapshot { clone := c clone.data = cloneBytes(c.data) return clone } // MarshalJSON exposes only non-secret credential identity metadata. func (c CredentialSnapshot) MarshalJSON() ([]byte, error) { return json.Marshal(struct { ID uint `json:"id"` Version uint64 `json:"version"` IdentityGeneration uint64 `json:"identity_generation"` }{ ID: c.ID, Version: c.Version, IdentityGeneration: c.IdentityGeneration, }) } // AttemptTimeouts contains per-attempt timeout overrides. Zero inherits the caller default. type AttemptTimeouts struct { FirstByte time.Duration `json:"first_byte,omitempty"` Request time.Duration `json:"request,omitempty"` StreamIdle time.Duration `json:"stream_idle,omitempty"` } // AttemptSpec is a fully selected, provider-neutral upstream attempt. // NewAttemptSpec or Clone must be used at ownership boundaries because Query, // Header, Body, TargetConfig, and Credential contain reference-backed values. type AttemptSpec struct { RequestID string `json:"request_id"` AttemptID string `json:"attempt_id"` Sequence uint32 `json:"sequence"` ChannelID string `json:"channel_id"` TargetKind string `json:"target_kind"` RouteMode RouteMode `json:"route_mode"` ClientProtocol protocol.Protocol `json:"client_protocol"` Operation Operation `json:"operation"` RouteRequirement RouteRequirement `json:"route_requirement"` ClientModel string `json:"client_model,omitempty"` UpstreamModel string `json:"upstream_model,omitempty"` Method string `json:"method"` Path string `json:"path"` Query url.Values `json:"query,omitempty"` // RawQuery preserves the original query bytes when exact forwarding matters. // It is mutually exclusive with Query and intentionally is not URL-decoded. RawQuery string `json:"raw_query,omitempty"` Header http.Header `json:"header,omitempty"` Body []byte `json:"body,omitempty"` // IncludeUsage asks the executor to request provider usage details when the // selected operation supports an explicit wire option. IncludeUsage bool `json:"include_usage,omitempty"` // TargetConfig is non-secret configuration resolved by the channel registry. TargetConfig json.RawMessage `json:"target_config,omitempty"` Timeouts AttemptTimeouts `json:"timeouts"` Credential CredentialSnapshot `json:"credential"` } // NewAttemptSpec takes ownership of an independent clone of spec. func NewAttemptSpec(spec AttemptSpec) AttemptSpec { spec.RouteRequirement = spec.RouteRequirement.Normalize() return spec.Clone() } // Clone returns an independent attempt specification. func (s AttemptSpec) Clone() AttemptSpec { clone := s clone.Query = cloneValues(s.Query) clone.Header = cloneHeader(s.Header) clone.Body = cloneBytes(s.Body) clone.TargetConfig = cloneRawMessage(s.TargetConfig) clone.Credential = s.Credential.Clone() return clone } // DispatchState describes whether an attempt may have reached the upstream. type DispatchState string const ( DispatchNotSent DispatchState = "not_sent" DispatchMaybeSent DispatchState = "maybe_sent" ) // Valid reports whether the dispatch state is recognized. func (s DispatchState) Valid() bool { return s == DispatchNotSent || s == DispatchMaybeSent } // MaxErrorSummaryLength bounds sanitized error messages retained by the contract. const MaxErrorSummaryLength = 4096 // ErrorKind is the stable category used for retry and health decisions. type ErrorKind string const ( ErrorKindTransport ErrorKind = "transport" ErrorKindTimeout ErrorKind = "timeout" ErrorKindCanceled ErrorKind = "canceled" ErrorKindHTTP ErrorKind = "http" ErrorKindProvider ErrorKind = "provider" ErrorKindInvalidRequest ErrorKind = "invalid_request" ErrorKindConversionUnsupported ErrorKind = "conversion_unsupported" ErrorKindInternal ErrorKind = "internal" ) const ( ErrorCodeCriticalSemanticLoss = "critical_semantic_loss" ErrorCodeTargetConversionNotSupported = "target_conversion_not_supported" ErrorCodeTargetSerializationFailed = "target_serialization_failed" ) // Valid reports whether the error kind is recognized. func (k ErrorKind) Valid() bool { switch k { case ErrorKindTransport, ErrorKindTimeout, ErrorKindCanceled, ErrorKindHTTP, ErrorKindProvider, ErrorKindInvalidRequest, ErrorKindConversionUnsupported, ErrorKindInternal: return true default: return false } } // FailureHint is a provider-neutral classification hint. GPT-Load remains the // owner of health and retry decisions. type FailureHint string const ( FailureHintInvalidCredential FailureHint = "invalid_credential" FailureHintRateLimited FailureHint = "rate_limited" FailureHintModelUnavailable FailureHint = "model_unavailable" FailureHintHostError FailureHint = "host_error" ) // Valid reports whether the optional hint is recognized. func (h FailureHint) Valid() bool { switch h { case "", FailureHintInvalidCredential, FailureHintRateLimited, FailureHintModelUnavailable, FailureHintHostError: return true default: return false } } // ErrorEvidence contains provider-neutral evidence for an unsuccessful attempt. // Summary may retain a bounded, sanitized upstream error message; raw error // bodies are never retained. type ErrorEvidence struct { Kind ErrorKind `json:"kind"` Hint FailureHint `json:"hint,omitempty"` StatusCode int `json:"status_code,omitempty"` Type string `json:"type,omitempty"` Code string `json:"code,omitempty"` Summary string `json:"summary,omitempty"` RequestID string `json:"request_id,omitempty"` RetryAfter time.Duration `json:"retry_after,omitempty"` Header http.Header `json:"-"` } // Clone returns an independent error evidence value. func (e ErrorEvidence) Clone() ErrorEvidence { clone := e clone.Header = cloneHeader(e.Header) return clone } // UsageEvidence contains normalized usage and the raw provider evidence used to derive it. type UsageEvidence struct { Normalized usage.Result `json:"normalized"` Raw []byte `json:"raw,omitempty"` } // Clone returns an independent usage evidence value. func (e UsageEvidence) Clone() UsageEvidence { clone := e clone.Raw = cloneBytes(e.Raw) return clone } // AttemptResult is the terminal result of a non-streaming attempt. // The result owns Header, Body, Usage, and Error after return from Executor. type AttemptResult struct { DispatchState DispatchState `json:"dispatch_state"` ResponseStarted bool `json:"response_started"` UpstreamAPI UpstreamAPI `json:"upstream_api,omitempty"` AppliedReasoning *reasoning.Config `json:"applied_reasoning,omitempty"` StatusCode int `json:"status_code,omitempty"` Header http.Header `json:"header,omitempty"` Body []byte `json:"body,omitempty"` Model string `json:"model,omitempty"` UpstreamRequestID string `json:"upstream_request_id,omitempty"` Usage *UsageEvidence `json:"usage,omitempty"` Error *ErrorEvidence `json:"error,omitempty"` } // Clone returns an independent attempt result. func (r AttemptResult) Clone() AttemptResult { clone := r clone.Header = cloneHeader(r.Header) clone.Body = cloneBytes(r.Body) if r.Usage != nil { usage := r.Usage.Clone() clone.Usage = &usage } if r.Error != nil { evidence := r.Error.Clone() clone.Error = &evidence } if r.AppliedReasoning != nil { reasoningConfig := r.AppliedReasoning.Clone() clone.AppliedReasoning = &reasoningConfig } return clone } // StreamEventKind identifies one streaming event category. type StreamEventKind string const ( StreamEventReady StreamEventKind = "ready" StreamEventData StreamEventKind = "data" StreamEventUsage StreamEventKind = "usage" ) // Valid reports whether the streaming event kind is recognized. func (k StreamEventKind) Valid() bool { return k == StreamEventReady || k == StreamEventData || k == StreamEventUsage } // StreamEvent is one ordered item emitted by a streaming attempt. // Ownership of Header, Data, and Usage transfers to the stream sink. Executors // must not mutate or reuse reference-backed event values after the sink call. type StreamEvent struct { Sequence uint64 `json:"sequence"` Kind StreamEventKind `json:"kind"` StatusCode int `json:"status_code,omitempty"` Header http.Header `json:"header,omitempty"` UpstreamRequestID string `json:"upstream_request_id,omitempty"` Data []byte `json:"data,omitempty"` Usage *UsageEvidence `json:"usage,omitempty"` } // Clone returns an independent stream event. func (e StreamEvent) Clone() StreamEvent { clone := e clone.Header = cloneHeader(e.Header) clone.Data = cloneBytes(e.Data) if e.Usage != nil { usage := e.Usage.Clone() clone.Usage = &usage } return clone } // StreamResult is the terminal metadata returned after streaming ends. // The result owns Header, Usage, and Error after return from Executor. type StreamResult struct { DispatchState DispatchState `json:"dispatch_state"` ResponseStarted bool `json:"response_started"` UpstreamAPI UpstreamAPI `json:"upstream_api,omitempty"` AppliedReasoning *reasoning.Config `json:"applied_reasoning,omitempty"` StatusCode int `json:"status_code,omitempty"` Header http.Header `json:"header,omitempty"` Model string `json:"model,omitempty"` UpstreamRequestID string `json:"upstream_request_id,omitempty"` Usage *UsageEvidence `json:"usage,omitempty"` Error *ErrorEvidence `json:"error,omitempty"` } // Clone returns an independent streaming result. func (r StreamResult) Clone() StreamResult { clone := r clone.Header = cloneHeader(r.Header) if r.Usage != nil { usage := r.Usage.Clone() clone.Usage = &usage } if r.Error != nil { evidence := r.Error.Clone() clone.Error = &evidence } if r.AppliedReasoning != nil { reasoningConfig := r.AppliedReasoning.Clone() clone.AppliedReasoning = &reasoningConfig } return clone } // StreamSink consumes events synchronously. Returning an error stops the stream. type StreamSink func(StreamEvent) error // Executor executes exactly the selected attempt described by AttemptSpec. // Implementations must not mutate spec or retain reference-backed values from it. type Executor interface { Execute(context.Context, AttemptSpec) AttemptResult ExecuteStream(context.Context, AttemptSpec, StreamSink) StreamResult } var _ json.Marshaler = CredentialSnapshot{} func cloneBytes(value []byte) []byte { if value == nil { return nil } return append([]byte(nil), value...) } func cloneRawMessage(value json.RawMessage) json.RawMessage { if value == nil { return nil } return json.RawMessage(cloneBytes(value)) } func cloneHeader(value http.Header) http.Header { if value == nil { return nil } return value.Clone() } func cloneValues(value url.Values) url.Values { if value == nil { return nil } clone := make(url.Values, len(value)) for key, values := range value { clone[key] = append([]string(nil), values...) } return clone }