Documentation
¶
Overview ¶
Package config loads kram-gateway's YAML configuration: which upstream providers exist, how they're grouped into fallback combos, and which routing strategy each combo uses.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Save ¶
Save writes cfg as YAML to path, creating parent directories as needed. Writes to a temporary file first and renames it into place — on POSIX this rename is atomic; os.Rename also fails if path already exists on Windows, so the pre-existing file is removed first (there's a brief window where neither the old nor new file exists, but that's strictly better than a reader ever observing a half-written file, which the temp-file-then-rename alone already prevents on POSIX).
Types ¶
type ComboConfig ¶
type ComboConfig struct {
ID string `yaml:"id"`
Strategy string `yaml:"strategy"` // "round-robin" in v0; see internal/router for the full set
Providers []string `yaml:"providers"`
StrategyOptions StrategyOptions `yaml:"strategy_options,omitempty"`
Response ResponseGateConfig `yaml:"response,omitempty"`
}
ComboConfig is a named, ordered fallback chain of providers plus the strategy used to pick among the healthy ones.
type Config ¶
type Config struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Providers []ProviderConfig `yaml:"providers"`
Combos []ComboConfig `yaml:"combos"`
// DefaultCombo is used when a request's "model" doesn't match a combo ID.
DefaultCombo string `yaml:"default_combo"`
}
Config is the top-level gateway configuration.
type ProviderConfig ¶
type ProviderConfig struct {
ID string `yaml:"id"`
// Kind selects the adapter: "anthropic", "gemini", "openai-compat", or
// "openai-responses" (the ChatGPT-login-only Codex backend — see
// internal/provider/openai_responses.go).
Kind string `yaml:"kind"`
// BaseURL is the upstream API root. Optional for kinds with a
// well-known default (anthropic, gemini, openai-responses).
BaseURL string `yaml:"base_url,omitempty"`
// APIKeyEnv names the environment variable holding the credential —
// for AuthMode "oauth" it's used purely as the lookup key into the
// credentials store's OAuth token map, not as a real env var.
APIKeyEnv string `yaml:"api_key_env"`
// AuthMode selects how the gateway resolves this provider's
// credential. Empty (the default) is today's behavior: read
// APIKeyEnv from the process environment once at startup and use it
// for the provider's lifetime. "oauth" means the credential is a
// refreshable token in internal/credentials' OAuth store, resolved
// live on every request (see internal/gateway.Run) since it can
// expire mid-session — see internal/credentials.Store.Resolve.
AuthMode string `yaml:"auth_mode,omitempty"`
// KeyOptional means APIKey should return an empty string instead of an
// error when APIKeyEnv isn't set — true only for
// internal/customprovider entries, most of which point at a local/LAN
// server with no auth at all. Every other provider keeps the strict
// default: an unset env var is a real misconfiguration worth failing
// loudly on (a clear startup error beats a confusing 401 later).
KeyOptional bool `yaml:"key_optional,omitempty"`
// Model is the upstream model ID to request, if it should be pinned
// regardless of what the client asked for. Empty means passthrough.
Model string `yaml:"model,omitempty"`
// SupportsImages declares whether this provider's model accepts image
// input. Callers must check this (via /admin/status) before attaching
// images — the gateway never guesses.
SupportsImages bool `yaml:"supports_images,omitempty"`
// SupportsTools declares whether this provider's model can be sent
// tool/function definitions. Kram's agent loop skips providers that
// don't when a request requires tool calling.
SupportsTools bool `yaml:"supports_tools,omitempty"`
// QualityHint is an optional, explicitly user-configured 0..1 signal
// feeding the "quality" scoring factor (see internal/router) —
// deliberately never inferred or fabricated by Kram itself, since
// there is no real per-provider quality measurement to derive it
// from. Zero means "no opinion", which the scorer treats as neutral
// (0.5), not "worst possible" — see DECISIONS.md.
QualityHint float64 `yaml:"quality_hint,omitempty"`
}
ProviderConfig describes one upstream LLM backend.
func (ProviderConfig) APIKey ¶
func (p ProviderConfig) APIKey() (string, error)
APIKey resolves the provider's credential from its configured env var.
type ResponseGateConfig ¶
type ResponseGateConfig struct {
// RejectEmpty rejects a response with neither text content nor tool
// calls.
RejectEmpty bool `yaml:"reject_empty,omitempty"`
// RequireTerminal rejects a stream that never produced a proper
// finish signal (e.g. a connection that was cut mid-stream) even if
// it carried some content.
RequireTerminal bool `yaml:"require_terminal,omitempty"`
// MinContentLength rejects a text response shorter than this many
// characters (tool-call-only responses are exempt — a short "ok,
// calling the tool now" isn't what this guards against).
MinContentLength int `yaml:"min_content_length,omitempty"`
// ForbiddenSubstrings rejects a response whose content contains any
// of these — for masked upstream errors that come back as HTTP 200
// with an error message as the body, not for filtering legitimate
// model output.
ForbiddenSubstrings []string `yaml:"forbidden_substrings,omitempty"`
}
ResponseGateConfig configures deterministic, technical validation of an otherwise-successful response before it's allowed to end the fallback chain — see router.ResponseGate. Every field is opt-in; an absent response block disables gating entirely (the v0 behavior: any technically-successful response is accepted).
type StrategyOptions ¶
type StrategyOptions struct {
// Sticky pins a run (see router.RunKey) to its winning provider
// across tool round-trips, only used by the weighted (smart/quality/
// fast/cheap/reliable/custom) strategy family. Defaults to true for
// that family if unset — see DECISIONS.md, "Sticky by default".
Sticky *bool `yaml:"sticky,omitempty"`
// LKGPBoost is the additive score bonus (0..1) a candidate gets when
// it's the combo's current last-known-good provider, on top of its
// own weighted score. 0 (the zero value) if unset would disable the
// boost entirely, so an unset field falls back to a small nonzero
// default instead — see router.defaultLKGPBoost.
LKGPBoost *float64 `yaml:"lkgp_boost,omitempty"`
// Exploration is the probability (0..1) of picking a uniformly random
// eligible candidate as the leader instead of the top-ranked one, so
// non-winning candidates still occasionally get real telemetry — see
// DECISIONS.md, "Exploration". Small and conservative by default.
Exploration *float64 `yaml:"exploration,omitempty"`
// Weights overrides the weighted strategy's per-factor weights
// (health, reliability, latency, quality, cache_affinity, priority).
// Any factor not mentioned keeps its preset's weight; values are
// normalized automatically, so they don't need to sum to any
// particular total.
Weights map[string]float64 `yaml:"weights,omitempty"`
}
StrategyOptions tunes a combo's routing strategy — every field is optional, and an absent strategy_options block (the whole existing v0 config shape) means "use the strategy's own defaults", never a required section. Pointers distinguish "not set, use the default" from "set to the zero value", which matters for Sticky in particular (explicitly disabling stickiness is a real, different choice from never mentioning it).