Documentation
¶
Overview ¶
Package router provides model routing and fallback capabilities for the Starport gateway.
Package router provides model routing and fallback capabilities for the Starport gateway. It implements OpenRouter-compatible routing with provider preferences, health tracking, latency-based routing, cost optimization, and sticky sessions for conversation continuity.
Index ¶
- Constants
- Variables
- type APIKeyConfig
- type Config
- type EMALatencyTracker
- type EmbeddingRequest
- type EmbeddingResponse
- type ImagesRequest
- type ImagesResponse
- type LatencyTracker
- type MediaRequest
- type MediaResponse
- type Metadata
- type ModelAttempt
- type ModelRouter
- type ModerationRequest
- type ModerationResponse
- type OperationRequest
- type OperationResponse
- type OperatorCredentialGate
- type Option
- func WithAvailability(tracker *availability.Tracker) Option
- func WithCatalog(catalogPlane *runtimecatalog.ControlPlane) Option
- func WithOperatorCredentialGate(gate OperatorCredentialGate) Option
- func WithOutcomePublisher(publisher execution.OutcomePublisher) Option
- func WithSharedHealthStore(store availability.KVStore) Option
- func WithStoredCredentials(resolver StoredCredentialResolver) Option
- type ProviderPreferences
- type RecognitionRequest
- type RecognitionResponse
- type Request
- type RequestMetadata
- type RerankRequest
- type RerankResponse
- type Response
- type SharedLatencyTracker
- type SpeechRequest
- type SpeechResponse
- type StickyProviderSessionManager
- type StoredCredentialResolver
- type StreamEvidence
- type TranscriptionRequest
- type TranscriptionResponse
- type VideoAssetReference
- type VideoAssetRequest
- type VideoAssetResponse
- type VideoJobReference
- type VideoJobRequest
- type VideoJobResponse
- type VideoSubmitRequest
Constants ¶
const (
// AutoModelID is the special model ID for automatic routing
AutoModelID = "openrouter/auto"
)
Variables ¶
var ( // ErrNoModelsAvailable is returned when no models can be used ErrNoModelsAvailable = errors.New("no models available for routing") // ErrAllModelsFailed is returned when all models in the chain failed ErrAllModelsFailed = errors.New("all models failed") )
Functions ¶
This section is empty.
Types ¶
type APIKeyConfig ¶
type APIKeyConfig struct {
// Allowed providers for this API key
AllowedProviders []string
// Allowed models for this API key
AllowedModels []string
// Model-specific overrides
ModelOverrides map[string]string
// Rate limit tier
RateLimitTier string
// CredentialStrategy is the effective credential order for this request,
// already resolved against the account's governing strategy by the caller.
CredentialStrategy keyring.Strategy
// Access carries the account's paired provider and model grants into
// planning. Nil grants every provider and model.
Access []routing.ProviderAccess
// BYOKProviders gates which providers the BYOK credential source may
// serve for this request. Nil allows every provider, an empty list
// allows none, and a non-empty list allows only its members. The caller
// resolves it from the account's BYOK policy so this package never
// learns the account vocabulary.
BYOKProviders *[]string
}
APIKeyConfig contains API key specific routing configuration
type Config ¶
type Config struct {
// Latency tracking configuration
LatencyAlpha float64 // EMA smoothing factor (0-1)
LatencyWindowSize int // Samples before EMA kicks in
// Cost optimization
EnableCostOptimization bool
// Sticky sessions
EnableStickySessions bool
SessionTTL time.Duration
// Execution owns the one total retry and fallback budget.
Execution execution.Config
// Availability owns exact offering circuit state.
Availability availability.Config
}
Config contains configuration for the router
type EMALatencyTracker ¶
type EMALatencyTracker struct {
// contains filtered or unexported fields
}
EMALatencyTracker implements LatencyTracker using exponential moving average
func (*EMALatencyTracker) GetAllLatencies ¶
func (t *EMALatencyTracker) GetAllLatencies() map[string]time.Duration
GetAllLatencies returns latencies for all tracked providers
func (*EMALatencyTracker) GetLatency ¶
func (t *EMALatencyTracker) GetLatency(provider string) time.Duration
GetLatency returns the current EMA latency for a provider
func (*EMALatencyTracker) RecordLatency ¶
func (t *EMALatencyTracker) RecordLatency(provider string, latency time.Duration)
RecordLatency records a latency measurement for a provider
type EmbeddingRequest ¶ added in v1.0.2
type EmbeddingRequest struct {
*connectors.EmbeddingsRequest
APIKeyConfig *APIKeyConfig
AccountID string
}
EmbeddingRequest contains one provider-neutral embedding request plus account routing and credential policy.
type EmbeddingResponse ¶ added in v1.0.2
type EmbeddingResponse struct {
Response inference.EmbeddingResponse
ModelUsed string
ProviderUsed string
CredentialSource string
Attempts int
Metadata *Metadata
CatalogSnapshot *runtimecatalog.RoutableSnapshot
}
EmbeddingResponse wraps one embedding result with route evidence.
type ImagesRequest ¶ added in v1.1.0
type ImagesRequest = MediaRequest[inference.ImagesRequest]
ImagesRequest routes one image generation or image edit.
type ImagesResponse ¶ added in v1.1.0
type ImagesResponse = MediaResponse[inference.ImagesResponse]
ImagesResponse is one image result with route evidence.
type LatencyTracker ¶
type LatencyTracker interface {
// RecordLatency records a latency measurement for a provider
RecordLatency(provider string, latency time.Duration)
// GetLatency returns the current EMA latency for a provider
GetLatency(provider string) time.Duration
// GetAllLatencies returns latencies for all tracked providers
GetAllLatencies() map[string]time.Duration
// Reset clears all latency data
Reset()
}
LatencyTracker tracks provider latencies using exponential moving average (EMA)
func NewLatencyTracker ¶
func NewLatencyTracker(alpha float64, windowSize int) LatencyTracker
NewLatencyTracker creates a new EMA-based latency tracker
type MediaRequest ¶ added in v1.1.0
type MediaRequest[Request any] = OperationRequest[Request]
MediaRequest routes one canonical media request. The shared carrier is OperationRequest.
type MediaResponse ¶ added in v1.1.0
type MediaResponse[Response any] = OperationResponse[Response]
MediaResponse is one media result. The shared carrier is OperationResponse: reranking and document recognition are not media, and a media name on the answer they return would say they were.
type Metadata ¶
type Metadata struct {
// Models that were tried
ModelsAttempted []ModelAttempt `json:"models_attempted"`
// Total routing time
RoutingDuration time.Duration `json:"routing_duration_ms"`
// Reason for final model selection
SelectionReason string `json:"selection_reason"`
}
Metadata contains detailed routing information
type ModelAttempt ¶
type ModelAttempt struct {
Model string `json:"model"`
Provider string `json:"provider"`
Error string `json:"error,omitempty"`
Duration time.Duration `json:"duration_ms"`
Status string `json:"status"` // "success", "failed", "skipped"
}
ModelAttempt records an attempt to use a specific model
type ModelRouter ¶
type ModelRouter interface {
// SelectModel chooses the best model based on the request and routing strategy
// Returns the selected model ID and the connector to use
SelectModel(ctx context.Context, req *Request) (modelID string, connector connectors.Connector, err error)
// RouteWithFallback attempts to route a request through multiple models with fallback logic
// Returns the response and which model was actually used
RouteWithFallback(ctx context.Context, req *Request) (*Response, error)
// RouteStream executes the same immutable route plan and budget for streaming.
RouteStream(ctx context.Context, req *Request) (execution.ManagedStream, error)
// RouteEmbeddings executes one embedding request through the same route,
// credential, availability, and total-attempt policies as chat requests.
RouteEmbeddings(ctx context.Context, req *EmbeddingRequest) (*EmbeddingResponse, error)
// RouteRerank scores one document list against a query through the same
// route, credential, availability, and total-attempt policies as chat.
RouteRerank(ctx context.Context, req *RerankRequest) (*RerankResponse, error)
// RouteModerations classifies one input list against a provider's harm
// categories through the same policies as chat.
RouteModerations(ctx context.Context, req *ModerationRequest) (*ModerationResponse, error)
// RouteImages executes one image generation or image edit request.
RouteImages(ctx context.Context, req *ImagesRequest) (*ImagesResponse, error)
// RouteSpeech executes one text-to-speech request.
RouteSpeech(ctx context.Context, req *SpeechRequest) (*SpeechResponse, error)
// RouteTranscription executes one speech-to-text request, in the spoken
// language or translated into English.
RouteTranscription(ctx context.Context, req *TranscriptionRequest) (*TranscriptionResponse, error)
// RouteVideoSubmit starts one video generation at a provider that serves
// it, and answers with the provider's own job identifier.
RouteVideoSubmit(ctx context.Context, req *VideoSubmitRequest) (*VideoJobResponse, error)
// RouteVideoPoll asks the provider that accepted a job where it got to.
RouteVideoPoll(ctx context.Context, req *VideoJobRequest) (*VideoJobResponse, error)
// RouteVideoCancel asks the provider that accepted a job to stop it.
RouteVideoCancel(ctx context.Context, req *VideoJobRequest) (*VideoJobResponse, error)
// RouteVideoContent reads the finished output of one accepted job from the
// provider that produced it.
RouteVideoContent(ctx context.Context, req *VideoAssetRequest) (*VideoAssetResponse, error)
// RouteDocumentRecognition reads the text off a document whose pages carry
// none. The gateway orders this read on a caller's behalf inside a chat
// turn, so it is the one route no HTTP path reaches.
RouteDocumentRecognition(ctx context.Context, req *RecognitionRequest) (*RecognitionResponse, error)
}
ModelRouter handles model selection, fallback logic, and provider routing
func New ¶
func New(registry connectors.Registry, opts ...Option) ModelRouter
New creates a new model router with all features enabled by default
type ModerationRequest ¶ added in v1.2.0
type ModerationRequest = OperationRequest[inference.ModerationRequest]
ModerationRequest routes one moderation call.
type ModerationResponse ¶ added in v1.2.0
type ModerationResponse = OperationResponse[inference.ModerationResponse]
ModerationResponse is one classified input list with route evidence.
type OperationRequest ¶ added in v1.1.0
type OperationRequest[Request any] struct { Request Request APIKeyConfig *APIKeyConfig AccountID string }
OperationRequest carries one canonical request plus the account routing and credential policy every operation reads.
type OperationResponse ¶ added in v1.1.0
type OperationResponse[Response any] struct { Response Response ModelUsed string ProviderUsed string CredentialSource string Attempts int Metadata *Metadata CatalogSnapshot *runtimecatalog.RoutableSnapshot }
OperationResponse is one operation result with the same route evidence a chat or an embedding answer carries.
type OperatorCredentialGate ¶ added in v1.0.3
type OperatorCredentialGate interface {
OperatorMaterialReady(providerID string, materialVersion string) bool
}
OperatorCredentialGate admits one exact resolved operator material version.
type Option ¶
type Option func(*modelRouter)
Option configures the transitional router composition.
func WithAvailability ¶
func WithAvailability(tracker *availability.Tracker) Option
WithAvailability supplies the one runtime offering availability owner.
func WithCatalog ¶
func WithCatalog(catalogPlane *runtimecatalog.ControlPlane) Option
WithCatalog supplies the shared generation-consistent routable snapshot.
func WithOperatorCredentialGate ¶ added in v1.0.3
func WithOperatorCredentialGate(gate OperatorCredentialGate) Option
WithOperatorCredentialGate prevents a provider-proved bad material version from being retried before its lifecycle owner supplies a replacement.
func WithOutcomePublisher ¶ added in v1.0.3
func WithOutcomePublisher(publisher execution.OutcomePublisher) Option
WithOutcomePublisher supplies the safe provider invocation outcome sink.
func WithSharedHealthStore ¶ added in v1.2.0
func WithSharedHealthStore(store availability.KVStore) Option
WithSharedHealthStore supplies the distributed store that replicas share. The latency tracker publishes its snapshots there and reads peer measurements back. Without it every measurement stays process-local.
func WithStoredCredentials ¶ added in v1.1.0
func WithStoredCredentials(resolver StoredCredentialResolver) Option
WithStoredCredentials supplies the stored inference credential planes: the operator's shared credentials and every account's own.
type ProviderPreferences ¶
type ProviderPreferences struct {
// Try providers in this order
Order []string `json:"order,omitempty"`
// Only use these providers
Only []string `json:"only,omitempty"`
// Never use these providers
Ignore []string `json:"ignore,omitempty"`
// Allow fallback to other providers not in order list
AllowFallbacks bool `json:"allow_fallbacks,omitempty"`
// Sort selects the route ordering: "price", "latency", "throughput",
// or "spread". Starport measures latency, not throughput, so
// "throughput" routes by measured latency. "spread" balances traffic
// across the leading ranking band. Empty keeps the server default.
Sort string `json:"sort,omitempty"`
// MaxPromptPricePer1M and MaxCompletionPricePer1M cap the accepted route
// price in USD per million tokens. Zero means no cap.
MaxPromptPricePer1M float64 `json:"max_prompt_price_per_1m,omitempty"`
MaxCompletionPricePer1M float64 `json:"max_completion_price_per_1m,omitempty"`
}
ProviderPreferences controls which providers can be used
type RecognitionRequest ¶ added in v1.1.0
type RecognitionRequest = MediaRequest[inference.RecognitionRequest]
RecognitionRequest routes one document read.
type RecognitionResponse ¶ added in v1.1.0
type RecognitionResponse = MediaResponse[inference.RecognitionResponse]
RecognitionResponse is one document's recognized pages with route evidence.
type Request ¶
type Request struct {
// Original chat request
*connectors.ChatRequest
// Models to try in order (fallback chain)
Models []string
// Provider routing preferences
ProviderPreferences *ProviderPreferences
// API key configuration (for provider restrictions)
APIKeyConfig *APIKeyConfig
// AccountID selects the account whose BYOK credential record the request
// may read. It is never a gateway API key ID.
AccountID string
// Request metadata for routing decisions
Metadata *RequestMetadata
// PrepareAttempt optionally adjusts the provider request for the selected
// model immediately before invoking the connector.
PrepareAttempt func(route routing.Route, req *connectors.ChatRequest) *connectors.ChatRequest
}
Request contains the original request plus routing preferences
type RequestMetadata ¶
type RequestMetadata struct {
// Estimated tokens in the request
EstimatedTokens int
// Required features (e.g., "vision", "function_calling")
RequiredFeatures []string
// RequiredModalities names the media the request carries, such as
// "audio". A model that does not read one of them cannot answer the
// request, so the planner drops it rather than letting the provider
// refuse the call.
RequiredModalities []string
// Conversation ID for sticky routing
ConversationID string
// User preferences
UserPreferences map[string]any
}
RequestMetadata contains information for routing decisions
type RerankRequest ¶ added in v1.1.0
type RerankRequest = OperationRequest[inference.RerankRequest]
RerankRequest routes one rerank call.
type RerankResponse ¶ added in v1.1.0
type RerankResponse = OperationResponse[inference.RerankResponse]
RerankResponse is one ranked document list with route evidence.
type Response ¶
type Response struct {
// The actual response from the model
*connectors.ChatResponse
// Which model was actually used
ModelUsed string `json:"model_used"`
// Provider that handled the request
ProviderUsed string `json:"provider_used"`
// CredentialSource names which credential plane paid for the attempt
// that answered: the operator's environment, the operator's shared
// credential, the account's own BYOK, or no credential at all.
CredentialSource string `json:"credential_source,omitempty"`
// Number of attempts made
Attempts int `json:"attempts"`
// Routing metadata
Metadata *Metadata `json:"metadata,omitempty"`
// CatalogSnapshot is the exact leased runtime generation that produced the
// response.
CatalogSnapshot *runtimecatalog.RoutableSnapshot `json:"-"`
}
Response wraps the chat response with routing metadata
type SharedLatencyTracker ¶ added in v1.2.0
type SharedLatencyTracker struct {
// contains filtered or unexported fields
}
SharedLatencyTracker layers peer latency snapshots from the distributed store over a process-local tracker. A local measurement wins; peers fill in the providers this replica has not called yet, so a fresh replica starts with the fleet's view.
func NewSharedLatencyTracker ¶ added in v1.2.0
func NewSharedLatencyTracker(local LatencyTracker, store availability.KVStore) *SharedLatencyTracker
NewSharedLatencyTracker wraps a local tracker with distributed publication.
func (*SharedLatencyTracker) GetAllLatencies ¶ added in v1.2.0
func (t *SharedLatencyTracker) GetAllLatencies() map[string]time.Duration
GetAllLatencies merges peer snapshots under the local measurements.
func (*SharedLatencyTracker) GetLatency ¶ added in v1.2.0
func (t *SharedLatencyTracker) GetLatency(provider string) time.Duration
GetLatency returns the local measurement, or the freshest peer measurement when this replica holds none.
func (*SharedLatencyTracker) RecordLatency ¶ added in v1.2.0
func (t *SharedLatencyTracker) RecordLatency(provider string, latency time.Duration)
RecordLatency records the measurement locally and publishes the replica snapshot at most once per refresh interval.
func (*SharedLatencyTracker) Reset ¶ added in v1.2.0
func (t *SharedLatencyTracker) Reset()
Reset clears the local measurements and the merged peer view.
type SpeechRequest ¶ added in v1.1.0
type SpeechRequest = MediaRequest[inference.SpeechRequest]
SpeechRequest routes one text-to-speech call.
type SpeechResponse ¶ added in v1.1.0
type SpeechResponse = MediaResponse[inference.SpeechResponse]
SpeechResponse is one speech result with route evidence.
type StickyProviderSessionManager ¶
type StickyProviderSessionManager interface {
// GetProvider returns the provider for a conversation, if any
GetProvider(conversationID string) (string, bool)
// SetProvider sets the provider for a conversation
SetProvider(conversationID string, provider string)
// RemoveSession removes a conversation's sticky session
RemoveSession(conversationID string)
// CleanupExpired removes expired sessions
CleanupExpired()
}
StickyProviderSessionManager manages conversation-to-provider mappings
func NewStickyProviderSessionManager ¶
func NewStickyProviderSessionManager(ttl time.Duration) StickyProviderSessionManager
NewStickyProviderSessionManager creates a new sticky session manager
type StoredCredentialResolver ¶ added in v1.1.0
type StoredCredentialResolver interface {
ResolveStoredMaterial(context.Context, string, catalogs.Provider) (credentials.Material, error)
}
StoredCredentialResolver resolves stored credentials against the provider contract retained by the request's runtime generation. The two stored planes have different shapes, so each has its own method: the account plane reads one exact scoped record, and the shared plane picks the first shared credential the account may spend.
type StreamEvidence ¶ added in v1.1.0
type StreamEvidence interface {
ProviderUsed() string
CredentialSourceUsed() string
AttemptCount() int
RoutingDuration() time.Duration
CatalogSnapshot() *runtimecatalog.RoutableSnapshot
}
StreamEvidence exposes route evidence from a managed stream for usage accounting.
type TranscriptionRequest ¶ added in v1.1.0
type TranscriptionRequest = MediaRequest[inference.TranscriptionRequest]
TranscriptionRequest routes one speech-to-text call.
type TranscriptionResponse ¶ added in v1.1.0
type TranscriptionResponse = MediaResponse[inference.TranscriptionResponse]
TranscriptionResponse is one transcript with route evidence.
type VideoAssetReference ¶ added in v1.1.0
type VideoAssetReference struct {
VideoJobReference
// MaxBytes is the largest asset the caller will store. It travels with the
// request rather than living here, because the half that stores the bytes
// is the half that decides what it is willing to store.
MaxBytes int64
}
VideoAssetReference names the finished output of one accepted job and states the bound the record store is willing to hold.
type VideoAssetRequest ¶ added in v1.1.0
type VideoAssetRequest = MediaRequest[VideoAssetReference]
VideoAssetRequest routes one read of a finished job's asset.
type VideoAssetResponse ¶ added in v1.1.0
type VideoAssetResponse = MediaResponse[connectors.JobAsset]
VideoAssetResponse is one finished asset with route evidence.
type VideoJobReference ¶ added in v1.1.0
type VideoJobReference struct {
// Provider is who accepted the job. Planning is pinned to it.
Provider string
// Model is the catalog model the job runs. The plan needs it to find the
// offering that names the endpoint.
Model string
// ProviderJobID is the provider's own identifier. It reaches this package
// from the job record and travels no further than the request body.
ProviderJobID string
}
VideoJobReference names one job a provider already accepted.
type VideoJobRequest ¶ added in v1.1.0
type VideoJobRequest = MediaRequest[VideoJobReference]
VideoJobRequest routes one poll or one cancel of an accepted job.
type VideoJobResponse ¶ added in v1.1.0
type VideoJobResponse = MediaResponse[connectors.ProviderJob]
VideoJobResponse is one provider job answer with route evidence.
type VideoSubmitRequest ¶ added in v1.1.0
type VideoSubmitRequest = MediaRequest[inference.VideoJobRequest]
VideoSubmitRequest routes one video generation submission.