Documentation
¶
Overview ¶
Package router implements the model-router sidecar logic. It exposes an OpenAI-compatible HTTP API, authenticates requests via Kubernetes JWT TokenReview, routes to the best available LLM provider, checkpoints conversation state, and dispatches tool calls to the appropriate executor backend.
Index ¶
- Variables
- func ExtractBearerToken(r *http.Request) string
- func RunHTTPInput(ctx context.Context, cfg HTTPInputConfig, store state.Store) error
- type AuthHeaderFile
- type Authenticator
- type ChatCompletionRequest
- type ChatCompletionResponse
- type Choice
- type Config
- type EnvFileMapping
- type EpisodicMemoryConfig
- type Filter
- type FilterConfig
- type FilterResult
- type FunctionCall
- type GuardrailConfig
- type GuardrailPipeline
- type HTTPInputConfig
- type HindsightConfig
- type KnowledgeBaseConfig
- type LongTermMemoryConfig
- type MCPServerConfig
- type Message
- type MetaRouter
- type Metrics
- type PatternConfig
- type ProviderConfig
- type RouteResult
- type Router
- func (r *Router) Cancel()
- func (r *Router) ClaimRun(input WarmRunInput)
- func (r *Router) CloseMCPClient()
- func (r *Router) CloseTokenBroadcaster()
- func (r *Router) Finalize()
- func (r *Router) HandleChatCompletions(w http.ResponseWriter, req *http.Request)
- func (r *Router) HandleGemini(w http.ResponseWriter, req *http.Request)
- func (r *Router) HandleInternalStream(w http.ResponseWriter, req *http.Request)
- func (r *Router) HandleModels(w http.ResponseWriter, req *http.Request)
- func (r *Router) HandleState(w http.ResponseWriter, req *http.Request)
- func (r *Router) InitMCPServers()
- func (r *Router) SetExecutor(exec *executor.Executor)
- func (r *Router) SetMetrics(m *Metrics)
- func (r *Router) SpendUSD() float64
- type RouterSafeguards
- type RuleRouter
- type StreamOptions
- type TokenBroadcaster
- type TokenUsage
- type ToolCall
- type ToolDefinition
- type ToolSecretRef
- type WarmRunInput
Constants ¶
This section is empty.
Variables ¶
var ErrUnauthenticated = errors.New("unauthenticated")
ErrUnauthenticated is returned when a request carries an invalid or missing token.
Functions ¶
func ExtractBearerToken ¶
ExtractBearerToken returns the token from an "Authorization: Bearer <token>" header.
func RunHTTPInput ¶
RunHTTPInput implements http input mode: waits for the agent HTTP server to become available, POSTs the run input, reads the response, and saves it to the state store for the controller to pick up.
If the HTTP call fails (e.g. timeout because the agent's handler is hanging), the error message is saved as the run's output so the user can see what went wrong in the UI, rather than the run silently timing out.
Types ¶
type AuthHeaderFile ¶
type AuthHeaderFile struct {
// HeaderName is the HTTP header name (e.g., "Authorization").
HeaderName string `json:"headerName"`
// FilePath is the absolute path to the mounted secret file.
FilePath string `json:"filePath"`
// Prefix is prepended to the file contents (e.g., "Bearer " for bearer tokens).
Prefix string `json:"prefix,omitempty"`
}
AuthHeaderFile maps an HTTP header to a file containing its secret value.
type Authenticator ¶
type Authenticator struct {
// contains filtered or unexported fields
}
Authenticator validates Kubernetes ServiceAccount JWT tokens via the TokenReview API. The model-router sidecar uses this to authenticate every incoming request.
Token flow:
- Agent pod has a projected SA token at /var/run/secrets/agentorca/token
- Agent framework sends it as "Authorization: Bearer <token>" (via OPENAI_API_KEY env var)
- Authenticator calls k8s TokenReview API to validate the token
- Validates the SA name matches the expected Agent SA
func NewAuthenticator ¶
func NewAuthenticator(kubeAPIURL, namespace, saName string) *Authenticator
NewAuthenticator creates an Authenticator configured for the given run. saName is "agentorca-agent-<agent-name>" in the run's namespace.
func (*Authenticator) Authenticate ¶
func (a *Authenticator) Authenticate(ctx context.Context, bearerToken string) error
Authenticate validates the bearer token and returns an error if invalid. Results are cached to avoid a Kubernetes API round-trip on every LLM call.
type ChatCompletionRequest ¶
type ChatCompletionRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Tools []map[string]any `json:"tools,omitempty"`
Stream bool `json:"stream,omitempty"`
StreamOptions *StreamOptions `json:"stream_options,omitempty"`
}
ChatCompletionRequest mirrors the OpenAI chat completion request body.
type ChatCompletionResponse ¶
type ChatCompletionResponse struct {
ID string `json:"id"`
Choices []Choice `json:"choices"`
Usage TokenUsage `json:"usage"`
}
ChatCompletionResponse mirrors the OpenAI chat completion response body.
type Config ¶
type Config struct {
// RunName is the AgentRun name — used as checkpoint key prefix and for TokenReview validation.
RunName string
// RunNamespace is the namespace of the AgentRun.
RunNamespace string
// AgentSAName is the expected ServiceAccount name; validated during JWT authentication.
AgentSAName string
// WorkflowName is set when this run is part of an AgentWorkflow.
// Used to scope workflow-shared state keys.
WorkflowName string
// DeploymentName is set when this run is part of an AgentDeployment.
// Used to scope deployment-shared state keys.
DeploymentName string
// DisableClarify prevents the auto-trigger clarify safety net from activating.
// Set from Agent.Spec.DisableClarify for autonomous agents.
DisableClarify bool
// Providers is the ordered list of LLM providers resolved from the ModelSelector.
Providers []ProviderConfig
// Strategy is the routing strategy: "rule-based", "llm-meta", or "hybrid".
Strategy string
// CapabilityRouting maps capability tags to provider names.
CapabilityRouting map[string]string
// FallbackChain is the ordered list of provider names used on 429/5xx.
FallbackChain []string
// MetaRouterProviderName is the provider to use for LLM meta-routing decisions.
MetaRouterProviderName string
// MetaRouterThreshold is the minimum rule-router confidence below which meta-router is invoked.
MetaRouterThreshold float64
// BudgetPerRunUSD is the maximum USD spend for this run (0 = unlimited).
BudgetPerRunUSD float64
// ToolDefinitions are the OpenAI function definitions to inject into every chat completion.
// Built by the operator from Tool CRDs, MCP tool lists, and agent-as-tool schemas.
ToolDefinitions []ToolDefinition
// MCPServers are the MCP servers to connect to at startup.
// Populated by the operator from Tool CRDs with type=mcp.
MCPServers []MCPServerConfig
// CheckpointEvery is how many LLM turns between checkpoints.
CheckpointEvery int
// CheckpointKey is the storage key for conversation state.
CheckpointKey string
// ResumeCheckpointKey is the checkpoint key to load on startup (empty = fresh start).
ResumeCheckpointKey string
// StateConfig is the connection config for the conversation state store.
StateConfig state.Config
// KubeAPIURL is the Kubernetes API server URL used for TokenReview.
// Defaults to https://kubernetes.default.svc.
KubeAPIURL string
// KnowledgeBases are the resolved KnowledgeBase configurations available to this agent.
KnowledgeBases []KnowledgeBaseConfig
// Safeguards configures behavioral guardrails for loop/cycle detection.
Safeguards RouterSafeguards
// EpisodicMemory configures periodic conversation summarization.
EpisodicMemory EpisodicMemoryConfig
// LongTermMemory configures a KnowledgeBase as persistent agent memory.
LongTermMemory LongTermMemoryConfig
// ContextWindowReserve is the fraction of a provider's context window to reserve
// for the response and system overhead when performing pre-send truncation.
// Range 0.0–1.0. Default: 0.20 (80% usable for conversation history).
ContextWindowReserve float64 `json:"contextWindowReserve,omitempty"`
// MaxToolResultTokens caps the estimated token count of any single tool result
// before it enters conversation history. Results exceeding this are truncated
// with a marker. Default: 4000 (~16K chars). 0 after defaulting = unlimited.
MaxToolResultTokens int `json:"maxToolResultTokens,omitempty"`
// Guardrails configures content filtering and validation for agent inputs/outputs.
Guardrails *GuardrailConfig `json:"guardrails,omitempty"`
// ConfirmRequiredKBs lists KnowledgeBase names that require user confirmation
// before ingesting fixes. For these KBs, _rag_ingest is replaced with
// _propose_fix and _confirm_fix built-in tools.
ConfirmRequiredKBs []string `json:"confirmRequiredKBs,omitempty"`
// HTTPInput configures http input mode delivery of run input to the agent.
HTTPInput HTTPInputConfig `json:"httpInput,omitempty"`
// ChatMode is true when the agent is running in interactive chat mode (inputMode: http or chat).
// Controls the tone of built-in system hints — chat agents get conversational guidance
// rather than the automation-pipeline framing used for batch/workflow agents.
ChatMode bool
// WarmMode is true when the model-router starts in pre-warmed idle mode.
// In warm mode the router initializes fully (MCP servers, state store) but does NOT
// start processing immediately. Instead it waits for POST /v1/claim-run to receive
// the run-specific fields (RunName, input) before beginning.
WarmMode bool
// WarmLocalCacheDir is the on-disk directory used as an L1 cache that
// supplements the Redis state store on warm pods. The model-router
// write-throughs each checkpoint to this directory AND to Redis, then reads
// from the local directory first (falling back to Redis on a miss). Non-empty
// only on warm pods (set by the operator from AgentDeployment.spec.warmLocalCache).
// Empty means the local cache is disabled.
WarmLocalCacheDir string `json:"warmLocalCacheDir,omitempty"`
// WarmLocalCacheSizeMi is the SizeLimit for the warm-pod local cache emptyDir,
// mirrored here for observability/debugging. 0 = unlimited.
WarmLocalCacheSizeMi int `json:"warmLocalCacheSizeMi,omitempty"`
// SystemPrompt is the agent's system-level instruction, prepended to every conversation.
// Injected by the operator from Agent.spec.systemPrompt.
SystemPrompt string
// OperatorAPIURL is the base URL of the operator's internal API server.
// Injected by the operator via OPERATOR_API_URL env var (e.g. http://agent-orca-internal-api.svc:8082).
OperatorAPIURL string
// SATokenFile is the path to the projected ServiceAccount token used to authenticate
// requests to the operator's internal API.
// Defaults to /var/run/secrets/agentorca/token.
SATokenFile string
// LLMRequestTimeout is the maximum time the model-router waits for a single LLM
// provider response (chat completion). Decoupled from the agent's inbound request
// context so a short agent-side timeout doesn't cancel an in-flight LLM call.
// Defaults to 1h. Set by the operator from the LLM_REQUEST_TIMEOUT env var
// (chart value modelRouter.llmRequestTimeout), configurable per deployment.
LLMRequestTimeout time.Duration `json:"llmRequestTimeout,omitempty"`
// ProactiveTruncationThreshold is the fraction of the live buffer budget at which
// truncation is triggered proactively (before the 80% budget is reached). This
// prevents large spikes in latency when the buffer is near full capacity.
// Range 0.0–1.0. Default: 0.6 (trigger at 60% of budget).
// Set to 0.0 to disable proactive truncation (only truncate when over budget).
ProactiveTruncationThreshold float64 `json:"proactiveTruncationThreshold,omitempty"`
// ContextManagementStrategy controls how aggressively the router manages context:
// - "balanced": proactive truncation at 60% budget, episodic summaries if enabled
// - "aggressive": proactive truncation at 40% budget, more frequent compaction
// - "max-fidelity": minimal truncation, only when hard limit is reached
// Default: "balanced"
ContextManagementStrategy string `json:"contextManagementStrategy,omitempty"`
// ContextCompactionRatio is the target fraction of the context window to
// compact the in-memory buffer down to when truncation fires. The checkpoint
// budget (80%) remains the safety ceiling — buffers exceeding it trigger
// compaction, which then reduces to this target rather than to the ceiling.
// Lower values retain less recent history in memory (reducing per-turn
// token-counting cost and checkpoint size) while the compaction summary
// carries the dropped context. Default 0.5; set to 0.1 for aggressive
// compaction down to ~10% of the window (issue #54).
ContextCompactionRatio float64 `json:"contextCompactionRatio,omitempty"`
// Hindsight configures the hindsight memory system integration.
// When enabled, every LLM turn will recall relevant memories before the call
// and retain the conversation after the call.
Hindsight HindsightConfig `json:"hindsight,omitempty"`
}
Config holds all configuration injected into the model-router sidecar via environment variables or a mounted ConfigMap. The operator populates this at pod creation time.
func ConfigFromEnv ¶
ConfigFromEnv builds a Config from environment variables and the mounted config file. The operator writes a JSON config file to /etc/agentorca/router-config.json at pod creation.
type EnvFileMapping ¶
type EnvFileMapping struct {
// Name is the environment variable name.
Name string `json:"name"`
// FilePath is the absolute path to the mounted secret file.
FilePath string `json:"filePath"`
}
EnvFileMapping maps an environment variable name to a file containing its secret value.
type EpisodicMemoryConfig ¶
type EpisodicMemoryConfig struct {
// SummaryEvery is the number of LLM turns between summarization calls. 0 = disabled.
SummaryEvery int `json:"summaryEvery,omitempty"`
// SummaryProviderName is the ModelProvider name to use for the summarization call.
SummaryProviderName string `json:"summaryProviderName,omitempty"`
// SummaryModel is the LiteLLM model identifier for summarization.
SummaryModel string `json:"summaryModel,omitempty"`
}
EpisodicMemoryConfig enables periodic summarization of the conversation to keep the active context window compact across long-running runs.
type Filter ¶
type Filter interface {
// Apply processes the text and returns the result.
Apply(text string) FilterResult
// Name returns the filter's human-readable name.
Name() string
}
Filter is the interface all filter implementations must satisfy.
type FilterConfig ¶
type FilterConfig struct {
Name string `json:"name"`
Type string `json:"type"` // "regex", "keyword-blocklist", "topic-validation"
Action string `json:"action"` // "redact", "block", "warn"
Patterns []PatternConfig `json:"patterns,omitempty"`
Keywords []string `json:"keywords,omitempty"`
AllowedTopics []string `json:"allowedTopics,omitempty"`
BlockMessage string `json:"blockMessage,omitempty"`
Replacement string `json:"replacement,omitempty"`
}
FilterConfig is a single filter rule in the serialized config.
type FilterResult ¶
type FilterResult struct {
// Blocked is true if the filter blocked the content entirely.
Blocked bool
// BlockMessage is the message to return when blocked.
BlockMessage string
// FilteredText is the text after redaction (if any).
FilteredText string
// Warnings are warning messages for "warn" action filters.
Warnings []string
}
FilterResult describes what happened when a filter was applied.
type FunctionCall ¶
type FunctionCall struct {
Name string `json:"name"`
Arguments string `json:"arguments"` // JSON-encoded arguments
}
FunctionCall holds the name and arguments of a tool call.
type GuardrailConfig ¶
type GuardrailConfig struct {
// OutputFilters are applied to LLM responses before returning to the agent.
OutputFilters []FilterConfig `json:"outputFilters,omitempty"`
// InputFilters are applied to user messages before sending to the LLM.
InputFilters []FilterConfig `json:"inputFilters,omitempty"`
// MaxOutputTokens limits output length. 0 = no limit.
MaxOutputTokens int `json:"maxOutputTokens,omitempty"`
}
GuardrailConfig is the serialized guardrail policy injected into the router config.
type GuardrailPipeline ¶
type GuardrailPipeline struct {
// contains filtered or unexported fields
}
GuardrailPipeline evaluates content against a set of filters.
func NewGuardrailPipeline ¶
func NewGuardrailPipeline(cfg *GuardrailConfig) *GuardrailPipeline
NewGuardrailPipeline builds a pipeline from the serialized config. Returns nil if the config has no filters.
func (*GuardrailPipeline) ApplyInput ¶
func (p *GuardrailPipeline) ApplyInput(text string) FilterResult
ApplyInput runs input filters on user messages.
func (*GuardrailPipeline) ApplyOutput ¶
func (p *GuardrailPipeline) ApplyOutput(text string) FilterResult
ApplyOutput runs output filters on LLM responses.
type HTTPInputConfig ¶
type HTTPInputConfig struct {
Enabled bool `json:"enabled"`
Input string `json:"input"`
Port int `json:"port"`
Path string `json:"path"`
RunName string `json:"runName"`
// TimeoutSeconds is the maximum time the model-router waits for the agent's
// HTTP handler to respond to a single run input. Defaults to 1800 (30m) when 0.
// This must comfortably exceed a single agent turn — which may perform many
// sequential LLM/tool calls — so that long-trajectory tasks are not killed
// mid-flight. The old hard-coded 60s cap routinely terminated in-progress
// sessions and then (before cleanup) deleted the warm pod.
TimeoutSeconds int `json:"timeoutSeconds,omitempty"`
}
HTTPInputConfig describes how the model-router delivers run input to an HTTP agent.
type HindsightConfig ¶
type HindsightConfig struct {
// Enabled is true when hindsight is active.
Enabled bool `json:"enabled,omitempty"`
// URL is the hindsight API endpoint (e.g. http://hindsight.hindsight.svc.cluster.local:8888).
URL string `json:"url,omitempty"`
// BankIDTemplate is the template for the hindsight bank ID. Can use placeholders like
// {namespace}, {runName}, {agentName} which will be replaced at runtime.
BankIDTemplate string `json:"bankIdTemplate,omitempty"`
// RecallBudget is the maximum number of memories to retrieve per turn.
RecallBudget int `json:"recallBudget,omitempty"`
// RetainOnEveryTurn is true to retain conversation after every LLM turn.
RetainOnEveryTurn bool `json:"retainOnEveryTurn,omitempty"`
}
HindsightConfig configures the hindsight memory system integration.
type KnowledgeBaseConfig ¶
type KnowledgeBaseConfig struct {
// Name is the KnowledgeBase CR name.
Name string
// VectorStoreURL is the Qdrant gRPC endpoint.
VectorStoreURL string
// CollectionName is the Qdrant collection to search.
CollectionName string
// EmbeddingProviderName is the ModelProvider to use for embedding queries.
EmbeddingProviderName string
// EmbeddingModel is the LiteLLM model identifier for embeddings.
EmbeddingModel string
// Dimensions is the embedding vector size.
Dimensions int
}
KnowledgeBaseConfig is a resolved KnowledgeBase ready for RAG queries.
type LongTermMemoryConfig ¶
type LongTermMemoryConfig struct {
// Enabled is true when long-term memory is active.
Enabled bool `json:"enabled,omitempty"`
// KBName is the KnowledgeBase CR name used for long-term memory.
KBName string `json:"kbName,omitempty"`
// VectorStoreURL is the Qdrant gRPC endpoint.
VectorStoreURL string `json:"vectorStoreURL,omitempty"`
// CollectionName is the Qdrant collection.
CollectionName string `json:"collectionName,omitempty"`
// EmbeddingProviderName is the ModelProvider for embedding queries.
EmbeddingProviderName string `json:"embeddingProviderName,omitempty"`
// EmbeddingModel is the LiteLLM model identifier for embeddings.
EmbeddingModel string `json:"embeddingModel,omitempty"`
// Dimensions is the embedding vector size.
Dimensions int `json:"dimensions,omitempty"`
// TopK is how many memories to retrieve per turn. Defaults to 5.
TopK int `json:"topK,omitempty"`
}
LongTermMemoryConfig wires a KnowledgeBase as the agent's persistent long-term memory.
type MCPServerConfig ¶
type MCPServerConfig struct {
// Name is the Tool CRD name this server corresponds to.
Name string `json:"name"`
// Transport is the MCP transport: "stdio", "http", or "sse".
Transport string `json:"transport"`
// URL is the server URL for http/sse transports.
URL string `json:"url,omitempty"`
// Cmd is the executable for stdio transport.
Cmd string `json:"cmd,omitempty"`
// Args are the command arguments for stdio transport.
Args []string `json:"args,omitempty"`
// Env is a list of "KEY=VALUE" env vars for stdio transport.
Env []string `json:"env,omitempty"`
// EnvFiles maps env var names to file paths containing secret values.
// The model-router reads these files at MCP server startup and injects the values.
EnvFiles []EnvFileMapping `json:"envFiles,omitempty"`
// AuthHeaderFiles maps HTTP header names to file paths containing secret values.
// Used for HTTP/SSE transport authentication.
AuthHeaderFiles []AuthHeaderFile `json:"authHeaderFiles,omitempty"`
// OAuth, when set, makes the model-router perform the OAuth 2.0 exchange itself
// (in-memory bearer) instead of a static authHeaderFile. Used for OAuth-only MCP
// servers (e.g. Slack); no access-token Secret is mounted — only the credentials
// Secret (see MCPOAuthConfig on the MCPServer CRD).
OAuth *mcp.OAuthConfig `json:"oauth,omitempty"`
// AllowApps enables MCP App iframe rendering for tools from this server.
AllowApps bool `json:"allowApps,omitempty"`
// IncludePatterns is a list of glob patterns used to filter the tools the
// model-router discovers at runtime (mirrors MCPServer.spec.includePatterns).
// When non-empty, only discovered tools whose name matches at least one pattern
// are exposed to the LLM. Empty means "all discovered tools" (no filtering).
// +optional
IncludePatterns []string `json:"includePatterns,omitempty"`
// ExcludePatterns hides discovered tools matching any glob, even if they
// matched IncludePatterns (mirrors MCPServer.spec.excludePatterns).
// +optional
ExcludePatterns []string `json:"excludePatterns,omitempty"`
}
MCPServerConfig describes an MCP server the model-router should connect to at startup.
type Message ¶
type Message struct {
Role string `json:"role"`
Content any `json:"content"` // string or []ContentPart
Name string `json:"name,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
}
Message represents a single message in an LLM conversation.
type MetaRouter ¶
type MetaRouter struct {
// contains filtered or unexported fields
}
MetaRouter uses a cheap/fast LLM to decide which model to use for ambiguous requests. It is only invoked when the rule-router's confidence falls below the configured threshold.
func NewMetaRouter ¶
func NewMetaRouter(cfg *Config) *MetaRouter
NewMetaRouter creates a MetaRouter from the given configuration.
func (*MetaRouter) Route ¶
func (m *MetaRouter) Route(ctx context.Context, messages []Message) (string, TokenUsage, error)
Route asks the meta-router LLM to select the best provider for the given messages. Returns the provider name or an error; callers fall back to rule-based routing on error.
type Metrics ¶
type Metrics struct {
// contains filtered or unexported fields
}
Metrics holds the Prometheus counters/gauges/histograms that the model-router exposes on its :9091/metrics endpoint. They are registered into the router's own registry (created in cmd/model-router/main.go), NOT the process default registry, so they are scoped to this model-router pod.
func NewMetrics ¶
func NewMetrics(reg prometheus.Registerer) *Metrics
NewMetrics builds a Metrics set and registers it into reg. If reg is nil the counters are still allocated (no-ops at the registry level) so call sites can never nil-deref; they simply aren't exposed.
func (*Metrics) IncToolCall ¶
func (m *Metrics) IncToolCall()
IncToolCall records one dispatched tool call. Nil-safe.
func (*Metrics) ObserveStreamDuration ¶
ObserveStreamDuration records a streaming response duration (seconds). Nil-safe.
type PatternConfig ¶
type PatternConfig struct {
Name string `json:"name"`
Pattern string `json:"pattern"`
Replacement string `json:"replacement,omitempty"`
}
PatternConfig is a named regex pattern.
type ProviderConfig ¶
type ProviderConfig struct {
// Name is the ModelProvider CRD name.
Name string
// LiteLLMModel is the LiteLLM model identifier (e.g. "anthropic/claude-sonnet-4-6").
LiteLLMModel string
// APIKeyFile is the path to the mounted secret file containing the API key.
APIKeyFile string
// Weight is used for weighted random selection.
Weight int
// Capabilities tags for this model.
Capabilities []string
// ContextWindow in tokens.
ContextWindow int
// MaxRequestTokens is the effective per-request token limit.
// When > 0, the router skips this provider for requests exceeding this size.
MaxRequestTokens int
// LatencyProfile: "fast", "medium", or "slow".
LatencyProfile string
// CostPerInputToken is the USD cost per single input token (converted from per-million at config load).
CostPerInputToken float64
// CostPerOutputToken is the USD cost per single output token (converted from per-million at config load).
CostPerOutputToken float64
// BaseURL is an optional API endpoint override for self-hosted models (e.g. "http://ollama.svc:11434").
BaseURL string
// RoutingHint is passed to the LLM meta-router to help it decide when to select this provider.
RoutingHint string
// TokenEncoding is the tiktoken encoding name for this provider (e.g. "cl100k_base", "o200k_base").
// Defaults to "cl100k_base" when empty.
TokenEncoding string
}
ProviderConfig is a fully-resolved LLM provider ready for dispatch.
type RouteResult ¶
type RouteResult struct {
// ProviderName is the selected ModelProvider CRD name.
ProviderName string
// Confidence is a 0.0–1.0 score indicating how certain the rule-based router is.
// Low confidence triggers the LLM meta-router in hybrid mode.
Confidence float64
// Reason is a human-readable explanation for the routing decision.
Reason string
}
RouteResult is the output of the rule-based router.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router is the core model-router sidecar. It is created once at startup and handles all LLM proxy requests for the lifetime of the agent pod.
func New ¶
func New(cfg *Config, store state.Store, initialMessages []json.RawMessage, exec *executor.Executor) (*Router, error)
New creates a Router with the given configuration. initialMessages is the conversation history loaded from a checkpoint (may be nil). exec may be nil if in-cluster Kubernetes is unavailable; tool dispatch for regular/agent backends is then disabled.
func (*Router) Cancel ¶
func (r *Router) Cancel()
Cancel terminates all in-flight LLM requests and marks the router as failed. It is safe to call multiple times.
func (*Router) ClaimRun ¶
func (r *Router) ClaimRun(input WarmRunInput)
ClaimRun assigns a run to this warm-mode router instance. It sets the mutable per-run fields on the config and resets all per-run state so the router is ready to process the run as if it just started. Must only be called when r.cfg.WarmMode is true.
func (*Router) CloseMCPClient ¶
func (r *Router) CloseMCPClient()
CloseMCPClient disconnects from all MCP servers and terminates any stdio subprocesses.
func (*Router) CloseTokenBroadcaster ¶
func (r *Router) CloseTokenBroadcaster()
CloseTokenBroadcaster shuts down the token broadcaster, closing all subscriber channels.
func (*Router) Finalize ¶
func (r *Router) Finalize()
Finalize emits a terminal trace-event release decision if no explicit terminal was reached. Call this from the shutdown path before HTTP servers are closed so the operator POST can still reach the apiserver.
Heuristic: if the router served at least one LLM turn, the run completed without _done — the run completed normally. If zero turns were served, the pod was killed before any work happened — emit a terminal event and exit.
func (*Router) HandleChatCompletions ¶
func (r *Router) HandleChatCompletions(w http.ResponseWriter, req *http.Request)
HandleChatCompletions handles POST /v1/chat/completions. This is the primary endpoint called by all OpenAI-compatible agent frameworks.
func (*Router) HandleGemini ¶
func (r *Router) HandleGemini(w http.ResponseWriter, req *http.Request)
HandleGemini handles Gemini API format requests on :8082. Used by native Google ADK agents that target generativelanguage.googleapis.com. Translates to OpenAI format and proxies through the same routing logic.
func (*Router) HandleInternalStream ¶
func (r *Router) HandleInternalStream(w http.ResponseWriter, req *http.Request)
HandleInternalStream serves GET /internal/stream — an SSE endpoint that streams LLM tokens in real-time to the operator. This bypasses Kubernetes pod log buffering by reading directly from the model-router's token broadcaster.
func (*Router) HandleModels ¶
func (r *Router) HandleModels(w http.ResponseWriter, req *http.Request)
HandleModels handles GET /v1/models — returns the list of available providers.
func (*Router) HandleState ¶
func (r *Router) HandleState(w http.ResponseWriter, req *http.Request)
HandleState handles the REST API for agent-code state access:
PUT /v1/state/{scope}/{key} — write a value
GET /v1/state/{scope}/{key} — read a value
DELETE /v1/state/{scope}/{key} — delete a value
GET /v1/state/{scope} — list keys
func (*Router) InitMCPServers ¶
func (r *Router) InitMCPServers()
InitMCPServers connects to all configured MCP servers and merges their tool schemas into the router's tool definitions. It is designed to be called in a goroutine after the HTTP server is already listening so MCP subprocess startup does not delay the startup probe.
func (*Router) SetExecutor ¶
SetExecutor binds the in-process tool executor (warm mode: call after ClaimRun when run name is known).
func (*Router) SetMetrics ¶
SetMetrics attaches a Prometheus metrics set so the streaming hot path can record token/tool/latency counters exposed on :9091. Safe to call (or skip) after New; metrics is nil-safe for all increments.
type RouterSafeguards ¶
type RouterSafeguards struct {
// MaxConsecutiveNoopTurns halts the run if the LLM produces this many consecutive
// turns with no tool calls and fewer than MinSubstantiveTokens output tokens.
// 0 disables the check.
MaxConsecutiveNoopTurns int `json:"maxConsecutiveNoopTurns,omitempty"`
// MinSubstantiveTokens is the minimum output token count for a turn to count as substantive.
// Defaults to 20 when MaxConsecutiveNoopTurns is set.
MinSubstantiveTokens int `json:"minSubstantiveTokens,omitempty"`
// MaxRepeatedToolCalls halts the run if the same tool+args hash is seen this many times.
// 0 disables the check.
MaxRepeatedToolCalls int `json:"maxRepeatedToolCalls,omitempty"`
// ToolFrequencyCap halts the run if any single tool is called this many times total.
// 0 disables the check.
ToolFrequencyCap int `json:"toolFrequencyCap,omitempty"`
// ToolExecutionTimeoutSec is the maximum seconds a single tool call may take
// before being cancelled and returning a structured error to the LLM.
// 0 means use the default (60s).
ToolExecutionTimeoutSec int `json:"toolExecutionTimeoutSec,omitempty"`
}
RouterSafeguards configures behavioral guardrails applied by the model-router per run.
type RuleRouter ¶
type RuleRouter struct {
// contains filtered or unexported fields
}
RuleRouter selects a model provider using deterministic rules. It is the fast path in both "rule-based" and "hybrid" strategies.
func NewRuleRouter ¶
func NewRuleRouter(cfg *Config) *RuleRouter
NewRuleRouter creates a RuleRouter for a given configuration.
func (*RuleRouter) IncrementTurn ¶
func (r *RuleRouter) IncrementTurn()
IncrementTurn increments the turn counter.
func (*RuleRouter) Route ¶
func (r *RuleRouter) Route(messages []Message, estimatedTokens int, remainingTimeoutSec float64) RouteResult
Route selects a provider for the given chat completion request. Decision tree (evaluated in priority order):
- Token limit filter: exclude providers whose MaxRequestTokens < estimated tokens
- Vision capability: request contains image attachments → vision-capable model
- Context length: estimated tokens > 80% of model context window → long-context model
- Budget: remaining budget < 20% → cheapest model
- Timeout proximity: elapsed > 70% of run timeout → fastest model
- Capability routing map: check configured tag hints
- Default: weighted random selection
func (*RuleRouter) TurnCount ¶
func (r *RuleRouter) TurnCount() int
TurnCount returns the number of LLM turns completed.
func (*RuleRouter) UpdateSpend ¶
func (r *RuleRouter) UpdateSpend(usd float64)
UpdateSpend records additional spend so budget rules stay accurate.
type StreamOptions ¶
type StreamOptions struct {
IncludeUsage bool `json:"include_usage"`
}
StreamOptions controls streaming behavior.
type TokenBroadcaster ¶
type TokenBroadcaster struct {
// contains filtered or unexported fields
}
TokenBroadcaster fans out streaming LLM tokens to multiple SSE subscribers. It maintains a replay buffer so that late subscribers (e.g. the operator connecting after streaming has already started) receive all prior tokens.
func NewTokenBroadcaster ¶
func NewTokenBroadcaster() *TokenBroadcaster
NewTokenBroadcaster creates a broadcaster with no initial subscribers.
func (*TokenBroadcaster) Close ¶
func (b *TokenBroadcaster) Close()
Close closes all subscriber channels and prevents further sends.
func (*TokenBroadcaster) Send ¶
func (b *TokenBroadcaster) Send(token string)
Send broadcasts a token to all current subscribers and appends it to the replay buffer for future subscribers.
func (*TokenBroadcaster) Subscribe ¶
func (b *TokenBroadcaster) Subscribe() (<-chan string, func())
Subscribe returns a channel that receives all tokens (past and future) and an unsubscribe function. Past tokens are replayed from the buffer immediately. The channel is closed when the broadcaster is closed.
This is called under the lock so no tokens are missed between the replay and the subscription becoming active.
type TokenUsage ¶
type TokenUsage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
}
TokenUsage holds token count metadata.
type ToolCall ¶
type ToolCall struct {
ID string `json:"id"`
Type string `json:"type"` // always "function"
Index int `json:"index,omitempty"` // used in streaming deltas to identify which call a chunk belongs to
Function FunctionCall `json:"function"`
}
ToolCall represents an LLM-requested function invocation.
type ToolDefinition ¶
type ToolDefinition struct {
// Name is the function name.
Name string
// Description is the human-readable description shown to the LLM.
Description string
// Parameters is the JSON Schema for the function parameters.
Parameters json.RawMessage
// BackendType indicates how to execute this tool: "regular", "agent", "mcp", "builtin".
BackendType string
// BackendRef is the Tool/Agent/MCP name to call.
BackendRef string
// SecretRefs are Kubernetes Secrets to mount into tool pods.
// Used by the executor when spawning regular (pod) tool invocations.
SecretRefs []ToolSecretRef `json:"secretRefs,omitempty"`
// Command overrides the container entrypoint for pod-type tools.
Command []string `json:"command,omitempty"`
// Args overrides the container command arguments for pod-type tools.
Args []string `json:"args,omitempty"`
// AppResourceURI is the ui:// resource URI from _meta.ui.resourceUri, if present.
// Only set when AllowApps is true on the owning MCPServer.
AppResourceURI string `json:"appResourceUri,omitempty"`
// AllowApps enables MCP App iframe rendering for this tool.
AllowApps bool `json:"allowApps,omitempty"`
}
ToolDefinition is an OpenAI function definition injected into every chat completion.
func ConfirmFixToolDefinition ¶
func ConfirmFixToolDefinition() ToolDefinition
ConfirmFixToolDefinition returns the OpenAI function schema for _confirm_fix.
func ProposedFixToolDefinition ¶
func ProposedFixToolDefinition() ToolDefinition
ProposedFixToolDefinition returns the OpenAI function schema for _propose_fix.
type ToolSecretRef ¶
type ToolSecretRef struct {
// SecretName is the Kubernetes Secret name.
SecretName string `json:"secretName"`
// MountPath is where to mount the Secret. Empty means inject as env vars.
MountPath string `json:"mountPath,omitempty"`
}
ToolSecretRef describes a Secret to inject into a tool pod.
type WarmRunInput ¶
type WarmRunInput struct {
// RunName is the AgentRun name to assign to this warm pod.
RunName string `json:"runName"`
// Input is the initial message to deliver to the agent.
Input string `json:"input"`
// PriorRunRef is the previous AgentRun name whose checkpoint should be loaded (empty = fresh).
PriorRunRef string `json:"priorRunRef,omitempty"`
}
WarmRunInput is the payload sent to POST /v1/claim-run when a warm pod is assigned to a run.