internal/config/config_types.go
Rama main · réplica de lectura del repositorio original.
package config
import (
"fmt"
"math"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/router-for-me/CLIProxyAPI/v8/internal/registry"
sdkpluginstore "github.com/router-for-me/CLIProxyAPI/v8/sdk/pluginstore"
"gopkg.in/yaml.v3"
)
// RequestScopedErrorRule configures custom classification and handling for upstream errors.
type RequestScopedErrorRule struct {
// Status matches the HTTP status code of the upstream response (e.g. 400).
Status int `yaml:"status,omitempty" json:"status,omitempty"`
// Match matches substrings in the upstream error body.
Match []string `yaml:"match,omitempty" json:"match,omitempty"`
// MatchRegexr matches regular expressions in the upstream error body.
MatchRegexr []string `yaml:"match-regexr,omitempty" json:"match-regexr,omitempty"`
// Action specifies the handling behavior: "stop", "stop-and-cooldown", "continue", "continue-and-cooldown".
Action string `yaml:"action,omitempty" json:"action,omitempty"`
}
// PluginsConfig holds dynamic plugin system settings.
type PluginsConfig struct {
// Enabled toggles dynamic plugin loading.
Enabled bool `yaml:"enabled" json:"enabled"`
// Dir is the plugin discovery directory.
Dir string `yaml:"dir" json:"dir"`
// StoreSources appends third-party plugin store registries to the built-in official source.
StoreSources []string `yaml:"store-sources,omitempty" json:"store-sources,omitempty"`
// StoreAuth defines optional auth rules for plugin store registry, metadata, and artifact requests.
StoreAuth []sdkpluginstore.AuthConfig `yaml:"store-auth,omitempty" json:"store-auth,omitempty"`
// AuthRevision changes when Home-managed plugin credentials change.
AuthRevision int64 `yaml:"auth-revision,omitempty" json:"auth-revision,omitempty"`
// Configs stores per-plugin instance configuration by plugin ID.
Configs map[string]PluginInstanceConfig `yaml:"configs" json:"configs"`
}
// PluginInstanceConfig stores host-owned plugin settings and the original plugin YAML subtree.
type PluginInstanceConfig struct {
// Enabled toggles this plugin instance. Nil is normalized to false during YAML parsing.
Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
// Priority controls plugin startup and routing order.
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
// Raw preserves the full original plugin configuration YAML subtree.
Raw yaml.Node `yaml:"-" json:"-"`
}
// UnmarshalYAML extracts host-owned fields while preserving the full original YAML node.
func (c *PluginInstanceConfig) UnmarshalYAML(value *yaml.Node) error {
if c == nil {
return nil
}
c.Priority = 0
defaultEnabled := false
c.Enabled = &defaultEnabled
if value == nil || value.Kind == 0 {
c.Raw = *defaultPluginInstanceConfigNode()
return nil
}
c.Raw = *deepCopyNode(value)
if value.Kind != yaml.MappingNode {
return nil
}
for i := 0; i+1 < len(value.Content); i += 2 {
key := value.Content[i]
node := value.Content[i+1]
if key == nil {
continue
}
switch key.Value {
case "enabled":
var enabled bool
if errDecodeEnabled := node.Decode(&enabled); errDecodeEnabled != nil {
return fmt.Errorf("parse plugin enabled: %w", errDecodeEnabled)
}
c.Enabled = &enabled
case "priority":
var priority int
if errDecodePriority := node.Decode(&priority); errDecodePriority != nil {
return fmt.Errorf("parse plugin priority: %w", errDecodePriority)
}
c.Priority = priority
}
}
return nil
}
// MarshalYAML returns the preserved raw plugin YAML subtree for lossless config output.
func (c PluginInstanceConfig) MarshalYAML() (any, error) {
if c.Raw.Kind == 0 {
return defaultPluginInstanceConfigNode(), nil
}
return deepCopyNode(&c.Raw), nil
}
func defaultPluginInstanceConfigNode() *yaml.Node {
return &yaml.Node{
Kind: yaml.MappingNode,
Tag: "!!map",
Content: []*yaml.Node{},
}
}
// ClaudeConfig configures provider-wide Claude request behavior.
type ClaudeConfig struct {
// ModelLevelCooling scopes Claude quota cooldowns to the requested model
// rather than cooling down the entire credential across all sibling models.
ModelLevelCooling bool `yaml:"model-level-cooling" json:"model-level-cooling"`
// ToolSearch enables Codex client tool_search on Claude Responses routes:
// Anthropic native tool search where the credential supports it, a pre-tool_search
// translation elsewhere, and per-credential search advertising in the Codex
// catalog. Default false keeps the translation that predates tool_search support.
ToolSearch bool `yaml:"tool-search" json:"tool-search"`
// ServerSideFallback opts cloaked Claude requests into Anthropic server-side
// refusal fallback injection (claude-opus-5-5 -> claude-opus-4-8 and
// claude-fable-5-1 -> claude-opus-5) when the request body does not already
// specify fallbacks. Default false leaves cross-model fallbacks omitted unless
// explicitly configured by the caller or payload rules.
ServerSideFallback bool `yaml:"server-side-fallback" json:"server-side-fallback"`
// SessionDatePinDays retains the cloaked currentDate pin per session for
// this many days in in-memory mode (server.kv-store.disabled: true without
// Home), independently of continuity expiry and credential switches. A nil
// value (unset) defaults to 7 days; an explicit 0 keeps the
// continuity-scoped pin (1h lifetime). When a KV store (local bbolt or Home
// KV) is active, session date pins are persisted permanently and
// session-date-pin-days is ignored.
SessionDatePinDays *int `yaml:"session-date-pin-days" json:"session-date-pin-days"`
}
// DefaultClaudeSessionDatePinDays is the session date pin retention applied
// when session-date-pin-days is not configured.
const DefaultClaudeSessionDatePinDays = 7
// SessionDatePinRetention returns the configured session date pin retention.
// An unset value defaults to 7 days; an explicit zero keeps the legacy
// continuity-scoped behavior. Days are clamped so the duration cannot
// overflow.
func (c ClaudeConfig) SessionDatePinRetention() time.Duration {
days := DefaultClaudeSessionDatePinDays
if c.SessionDatePinDays != nil {
days = *c.SessionDatePinDays
}
if days <= 0 {
return 0
}
maxDays := int((time.Duration(math.MaxInt64) / (24 * time.Hour)))
if days > maxDays {
days = maxDays
}
return time.Duration(days) * 24 * time.Hour
}
// ClaudeHeaderDefaults configures the measured Claude Code software baseline.
// Verified native requests preserve their entrypoint and software shape only when their
// Claude Code, package, and runtime versions exactly match this baseline; unmeasured
// versions use the configured values. Timeout remains a fallback. Stabilized profiles
// also pin OS and Arch and never learn newer software versions automatically.
// NativeEntrypoints extends the built-in native Claude Code entrypoint allowlist.
// AnthropicBetaStrip and AnthropicBetaAppend are exact-name operator overrides applied
// to the final Anthropic-Beta set; InjectDiagnostics and ClientRequestID tune the
// first-party Claude Code CLI request shape.
type ClaudeHeaderDefaults struct {
UserAgent string `yaml:"user-agent" json:"user-agent"`
PackageVersion string `yaml:"package-version" json:"package-version"`
RuntimeVersion string `yaml:"runtime-version" json:"runtime-version"`
OS string `yaml:"os" json:"os"`
Arch string `yaml:"arch" json:"arch"`
Timeout string `yaml:"timeout" json:"timeout"`
Timezone string `yaml:"timezone" json:"timezone"`
StabilizeDeviceProfile *bool `yaml:"stabilize-device-profile,omitempty" json:"stabilize-device-profile,omitempty"`
NativeEntrypoints []string `yaml:"native-entrypoints,omitempty" json:"native-entrypoints,omitempty"`
AnthropicBetaStrip []string `yaml:"anthropic-beta-strip,omitempty" json:"anthropic-beta-strip,omitempty"`
AnthropicBetaAppend []string `yaml:"anthropic-beta-append,omitempty" json:"anthropic-beta-append,omitempty"`
InjectDiagnostics *bool `yaml:"inject-diagnostics,omitempty" json:"inject-diagnostics,omitempty"`
ClientRequestID string `yaml:"client-request-id,omitempty" json:"client-request-id,omitempty"`
}
// Supported ClaudeHeaderDefaults.ClientRequestID values. An empty value means
// ClaudeClientRequestIDOn.
const (
ClaudeClientRequestIDOn = "on"
ClaudeClientRequestIDOff = "off"
ClaudeClientRequestIDPassthrough = "passthrough"
)
// CodexHeaderDefaults configures fallback header values injected into Codex
// model requests for OAuth/file-backed auth when the client omits them.
// UserAgent applies to HTTP and websocket requests; BetaFeatures only applies to websockets.
type CodexHeaderDefaults struct {
UserAgent string `yaml:"user-agent" json:"user-agent"`
BetaFeatures string `yaml:"beta-features" json:"beta-features"`
}
// XAIConfig configures provider-wide xAI request behavior.
type XAIConfig struct {
// InjectXSearch injects xAI's native x_search tool when the request does not declare it.
InjectXSearch bool `yaml:"inject-x-search" json:"inject-x-search"`
}
// DevinConfig configures provider-wide Devin request behavior.
type DevinConfig struct {
// SensitiveWords is a list of words to obfuscate with zero-width characters in system prompts and messages.
SensitiveWords []string `yaml:"sensitive-words,omitempty" json:"sensitive-words,omitempty"`
// SensitiveWordsReplace maps sensitive words or phrases to literal replacements in system prompts.
SensitiveWordsReplace map[string]string `yaml:"sensitive-words-replace,omitempty" json:"sensitive-words-replace,omitempty"`
}
// ProxyConnectionPoolConfig configures upstream connection reuse for one api-key
// credential (group level or per key).
//
// Pools are per credential: two API keys never share a connection, and OAuth
// credentials are not poolable at all (Antigravity keeps its own switch). The
// transport cache is bounded, so a deployment with very many keys degrades to the
// historical one-transport-per-request behavior instead of growing without limit.
type ProxyConnectionPoolConfig struct {
// Enabled controls whether connection reuse is active for this credential.
// Writing the connection-pool block is the opt-in, so an omitted or true
// value enables pooling; an explicit false opts a key out of its group pool.
Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
// IdleConnTimeout is how long an idle pooled connection survives before it
// is closed. Defaults to "300s". Measured against ollama.com, an idle
// connection was still reused after 600s, so 300s sits inside the upstream
// window while covering the multi-minute gaps between interactive requests.
// Zero or a negative value disables keep-alives, so every request dials
// afresh.
IdleConnTimeout string `yaml:"idle-conn-timeout,omitempty" json:"idle-conn-timeout,omitempty"`
// MaxIdleConnsPerHost caps idle connections kept per upstream host.
// Defaults to 2, matching net/http's DefaultMaxIdleConnsPerHost. Zero or a
// negative value disables keep-alives as well.
MaxIdleConnsPerHost *int `yaml:"max-idle-conns-per-host,omitempty" json:"max-idle-conns-per-host,omitempty"`
}
// AntigravityConfig configures provider-wide Antigravity request behavior.
type AntigravityConfig struct {
// SensitiveWords is a list of words to obfuscate with invisible combining grapheme joiners in system instructions.
SensitiveWords []string `yaml:"sensitive-words,omitempty" json:"sensitive-words,omitempty"`
// SensitiveWordsReplace maps sensitive words or phrases to literal replacements in system instructions.
SensitiveWordsReplace map[string]string `yaml:"sensitive-words-replace,omitempty" json:"sensitive-words-replace,omitempty"`
// ConnectionPool configures upstream HTTP connection pooling behavior for Antigravity.
ConnectionPool AntigravityConnectionPoolConfig `yaml:"connection-pool,omitempty" json:"connection-pool,omitempty"`
}
// AntigravityConnectionPoolConfig controls upstream HTTP/1.1 connection pooling behavior for Antigravity.
type AntigravityConnectionPoolConfig struct {
// Enabled controls whether upstream connection pooling is enabled.
// Defaults to false (short-lived connection mode). Set to true to enable connection pooling.
Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
// IdleConnTimeout specifies how long an idle connection stays in the pool before expiring.
// Defaults to "30s". Capped at 210s to prevent exceeding Google Frontend (GFE) 240s cutoff.
IdleConnTimeout string `yaml:"idle-conn-timeout,omitempty" json:"idle-conn-timeout,omitempty"`
// MaxIdleConnsPerHost specifies the maximum number of idle connections to retain per host per credential.
// Defaults to 2.
MaxIdleConnsPerHost *int `yaml:"max-idle-conns-per-host,omitempty" json:"max-idle-conns-per-host,omitempty"`
}
// ResponseCompressionConfig controls downstream HTTP response compression.
type ResponseCompressionConfig struct {
// Enabled defaults to true when unset.
Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
// MinBytes defaults to 1024 when zero or negative.
MinBytes int `yaml:"min-bytes" json:"min-bytes"`
}
// IsEnabled reports whether negotiated response compression is enabled.
func (c ResponseCompressionConfig) IsEnabled() bool {
return c.Enabled == nil || *c.Enabled
}
// EffectiveMinBytes returns the minimum non-streaming response size to compress.
func (c ResponseCompressionConfig) EffectiveMinBytes() int {
if c.MinBytes <= 0 {
return 1024
}
return c.MinBytes
}
// CodexConfig configures provider-wide Codex request behavior.
type CodexConfig struct {
// WebsocketCompression controls upstream and downstream permessage-deflate; unset means enabled.
WebsocketCompression *bool `yaml:"websocket-compression,omitempty" json:"websocket-compression,omitempty"`
// UpstreamRequestCompression enables zstd request bodies for the ChatGPT Codex backend.
UpstreamRequestCompression bool `yaml:"upstream-request-compression" json:"upstream-request-compression"`
// UpstreamRequestCompressionMinBytes defaults to 1024 when zero or negative.
UpstreamRequestCompressionMinBytes int `yaml:"upstream-request-compression-min-bytes,omitempty" json:"upstream-request-compression-min-bytes,omitempty"`
// DisableCodexCloaking disables forcing the official Codex identity headers on HTTP/SSE and WebSocket requests.
DisableCodexCloaking bool `yaml:"disable-codex-cloaking" json:"disable-codex-cloaking"`
// StreamBootstrapBuffering holds back the frames that arrive before generation starts, none of
// which the client has seen anything from - the handshake (response.created, response.in_progress,
// the websocket metadata frames), keepalive heartbeats, and the *.added announcements of an item
// or part that is still empty - until the first generated event arrives. The upstream delivers
// server_is_overloaded rejections inside an HTTP 200 stream right after those frames instead of
// returning 503 on the wire, so buffering them keeps the downstream response headers uncommitted
// long enough to retry on another credential. Trade-off: the response headers are delayed until
// the upstream starts generating, which on a slow reasoning turn now means several heartbeat
// intervals rather than one, and can trip client or reverse-proxy read timeouts. The hold is
// bounded by a frame and a byte budget, not by wall-clock time, and neither budget is advanced
// by a websocket peer that sends only control frames or by an upstream that never terminates an
// SSE line. Only overload and rate-limit rejections fail over deliberately. A stream that ends
// while the bootstrap is still holding ends the attempt rather than reaching the client, and
// what follows is pre-existing but now far more likely, since the hold can span the whole
// reasoning phase instead of ending at the first keepalive: a clean end with no terminal event
// is request-scoped on SSE and stops there, while a websocket close or a transport error on
// either transport is not, so the request may be retried on another credential.
// Default is false.
StreamBootstrapBuffering bool `yaml:"stream-bootstrap-buffering" json:"stream-bootstrap-buffering"`
// StreamBootstrapTimeout specifies an optional maximum duration to hold back uncommitted response
// headers during bootstrap buffering before releasing the stream to the client.
// Defaults to "0" (unlimited time, relying purely on the 48-frame and 1MB byte bounds).
// When set (e.g. "20s"), the stream is released once the time ceiling is reached, avoiding
// reverse-proxy timeouts (e.g. Nginx 60s proxy_read_timeout).
StreamBootstrapTimeout string `yaml:"stream-bootstrap-timeout,omitempty" json:"stream-bootstrap-timeout,omitempty"`
// OrphanDelegationCompatibility enables opt-in compatibility for orphan Codex delegation outputs.
OrphanDelegationCompatibility bool `yaml:"orphan-delegation-compatibility" json:"orphan-delegation-compatibility"`
// ModelLevelCooling scopes Codex usage_limit_reached quota cooldowns to the requested model
// rather than cooling down the entire credential across all sibling models.
ModelLevelCooling bool `yaml:"model-level-cooling" json:"model-level-cooling"`
// LiveMediaRelay terminates and relays Codex Live WebRTC media in this process.
LiveMediaRelay CodexLiveMediaRelayConfig `yaml:"live-media-relay" json:"live-media-relay"`
// ResponseSteering enables full-duplex Codex WebSockets, bound to one
// upstream model/account/socket for their entire lifetime. Default is false.
ResponseSteering bool `yaml:"response-steering" json:"response-steering"`
// SignInWithChatGPTIdentifier configures the identifier (agent_name_hint and User-Agent prefix)
// for Codex Sign in with ChatGPT (SIWC) OAuth authorization and API requests.
// Defaults to "CLIProxyAPI".
SignInWithChatGPTIdentifier string `yaml:"signin-with-chatgpt-identifier,omitempty" json:"signin-with-chatgpt-identifier,omitempty"`
}
// WebsocketCompressionEnabled reports whether WebSocket compression is enabled.
func (c *CodexConfig) WebsocketCompressionEnabled() bool {
return c == nil || c.WebsocketCompression == nil || *c.WebsocketCompression
}
// RequestCompressionMinBytes returns the minimum request size eligible for compression.
func (c *CodexConfig) RequestCompressionMinBytes() int {
if c == nil || c.UpstreamRequestCompressionMinBytes <= 0 {
return 1024
}
return c.UpstreamRequestCompressionMinBytes
}
// DefaultSignInWithChatGPTIdentifier is the default identifier (agent_name_hint and User-Agent prefix)
// used for Codex Sign in with ChatGPT (SIWC) OAuth authorization and API requests.
const DefaultSignInWithChatGPTIdentifier = "CLIProxyAPI"
// EffectiveSignInWithChatGPTIdentifier returns the configured Sign in with ChatGPT
// identifier, or DefaultSignInWithChatGPTIdentifier when unset or blank.
func (c CodexConfig) EffectiveSignInWithChatGPTIdentifier() string {
if v := strings.TrimSpace(c.SignInWithChatGPTIdentifier); v != "" {
return v
}
return DefaultSignInWithChatGPTIdentifier
}
// DefaultCodexStreamBootstrapTimeout is the default maximum duration to buffer bootstrap events.
// By default, it is 0 (unlimited time, relying purely on the 48-frame and 1MB byte bounds).
const DefaultCodexStreamBootstrapTimeout = 0
const maxBootstrapTimeoutSeconds = int64(math.MaxInt64 / time.Second)
// StreamBootstrapTimeoutDuration returns the maximum duration to buffer bootstrap events.
// Defaults to 0 (unlimited time, relying purely on the 48-frame and 1MB byte bounds).
// If explicitly set to a positive duration (e.g. "10s", "500ms", "15"), returns that duration.
// If set to "0", "0s", "none", "unlimited", "disabled", "off", "never", or invalid strings, returns 0.
func (c *CodexConfig) StreamBootstrapTimeoutDuration() time.Duration {
if c == nil {
return DefaultCodexStreamBootstrapTimeout
}
raw := strings.TrimSpace(c.StreamBootstrapTimeout)
if raw == "" || raw == "0" || strings.EqualFold(raw, "none") || strings.EqualFold(raw, "unlimited") || strings.EqualFold(raw, "disabled") || strings.EqualFold(raw, "off") || strings.EqualFold(raw, "never") {
return 0
}
if d, err := time.ParseDuration(raw); err == nil && d >= 0 {
return d
}
if secs, err := strconv.Atoi(raw); err == nil && secs >= 0 && int64(secs) <= maxBootstrapTimeoutSeconds {
d := time.Duration(secs) * time.Second
if d >= 0 {
return d
}
}
return DefaultCodexStreamBootstrapTimeout
}
// CodexLiveMediaRelayConfig configures the in-process Codex Live WebRTC gateway.
type CodexLiveMediaRelayConfig struct {
Enabled bool `yaml:"enabled" json:"enabled"`
MaxSessions int `yaml:"max-sessions" json:"max-sessions"`
DisablePrivateRemoteIPs bool `yaml:"disable-private-remote-ips" json:"disable-private-remote-ips"`
PublicIP string `yaml:"public-ip" json:"public-ip"`
UDPPortMin uint16 `yaml:"udp-port-min" json:"udp-port-min"`
UDPPortMax uint16 `yaml:"udp-port-max" json:"udp-port-max"`
ICEServers []CodexLiveICEServer `yaml:"ice-servers" json:"ice-servers"`
}
// CodexLiveICEServer configures a STUN or TURN server for the media relay.
type CodexLiveICEServer struct {
URLs []string `yaml:"urls" json:"urls"`
Username string `yaml:"username" json:"-"`
Credential string `yaml:"credential" json:"-"`
}
// TLSConfig holds HTTPS server settings.
type TLSConfig struct {
// Enable toggles HTTPS server mode.
Enable bool `yaml:"enable" json:"enable"`
// Cert is the path to the TLS certificate file.
Cert string `yaml:"cert" json:"cert"`
// Key is the path to the TLS private key file.
Key string `yaml:"key" json:"key"`
}
// PprofConfig holds pprof HTTP server settings.
type PprofConfig struct {
// Enable toggles the pprof HTTP debug server.
Enable bool `yaml:"enable" json:"enable"`
// Addr is the host:port address for the pprof HTTP server.
Addr string `yaml:"addr" json:"addr"`
}
// DiscoveryInterfacesConfig specifies interface inclusion and exclusion rules.
type DiscoveryInterfacesConfig struct {
Include []string `yaml:"include" json:"include"`
Exclude []string `yaml:"exclude" json:"exclude"`
}
// KVStoreConfig holds configuration for the local persistent bbolt KV store.
type KVStoreConfig struct {
// Disabled turns off the local bbolt KV store when true (fallback to in-memory).
// Defaults to false (bbolt enabled by default in non-Home mode).
Disabled bool `yaml:"disabled" json:"disabled"`
// Path is the file system path to the bbolt database file.
// When empty, it defaults to "<auth-dir>/kv.db".
Path string `yaml:"path,omitempty" json:"path,omitempty"`
}
// EffectivePath returns the trimmed Path or DefaultKVStorePath ("kv.db") when empty.
func (c KVStoreConfig) EffectivePath() string {
if trimmed := strings.TrimSpace(c.Path); trimmed != "" {
return trimmed
}
return DefaultKVStorePath
}
// EffectivePathForAuthDir returns the trimmed Path when explicitly configured,
// or joins the given authDir (falling back to DefaultAuthDir when authDir is empty)
// with DefaultKVStorePath ("<auth-dir>/kv.db").
func (c KVStoreConfig) EffectivePathForAuthDir(authDir string) string {
if trimmed := strings.TrimSpace(c.Path); trimmed != "" {
return trimmed
}
baseDir := strings.TrimSpace(authDir)
if baseDir == "" {
baseDir = DefaultAuthDir
}
return filepath.Join(baseDir, DefaultKVStorePath)
}
// EffectiveKVStorePath returns the configured KVStore.Path or "<auth-dir>/kv.db" when empty.
func (cfg *Config) EffectiveKVStorePath() string {
if cfg == nil {
return filepath.Join(DefaultAuthDir, DefaultKVStorePath)
}
return cfg.KVStore.EffectivePathForAuthDir(cfg.AuthDir)
}
// DiscoveryConfig controls local network mDNS / DNS-SD service advertising.
type DiscoveryConfig struct {
// Enabled toggles mDNS service advertising on the local network (default: false).
Enabled bool `yaml:"enabled" json:"enabled"`
// ServiceName is the optional custom instance name. When empty, defaults to CPA-<ShortID>.
ServiceName string `yaml:"service-name" json:"service-name"`
// ServiceType is the DNS-SD service type (default: _ai-gateway._tcp).
ServiceType string `yaml:"service-type" json:"service-type"`
// Subtypes specifies DNS-SD API protocol subtypes to advertise (e.g. _responses, _messages, _generate-content).
Subtypes []string `yaml:"subtypes" json:"subtypes"`
// Interfaces specifies network interface filtering rules.
Interfaces DiscoveryInterfacesConfig `yaml:"interfaces" json:"interfaces"`
// AuthRequired indicates whether authentication is required for client calls (default: true).
AuthRequired *bool `yaml:"auth-required" json:"auth-required"`
// AdvertiseManagement is deprecated and ignored. Management endpoints are never
// advertised over DNS-SD; the field remains so existing config files still load.
AdvertiseManagement bool `yaml:"advertise-management" json:"advertise-management"`
}
// RemoteManagement holds management API configuration under 'remote-management'.
type RemoteManagement struct {
// AllowRemote toggles remote (non-localhost) access to management API.
AllowRemote bool `yaml:"allow-remote"`
// SecretKey is the management key (plaintext or bcrypt hashed). YAML key intentionally 'secret-key'.
SecretKey string `yaml:"secret-key"`
// DisableControlPanel skips serving and syncing the bundled management UI when true.
DisableControlPanel bool `yaml:"disable-control-panel"`
// DisableAutoUpdatePanel disables automatic periodic background updates of the management panel asset from GitHub.
// When false (the default), the background updater remains enabled; when true, the panel is only downloaded on first access if missing.
DisableAutoUpdatePanel bool `yaml:"disable-auto-update-panel"`
// PanelGitHubRepository overrides the GitHub repository used to fetch the management panel asset.
// Accepts either a repository URL (https://github.com/org/repo) or an API releases endpoint.
PanelGitHubRepository string `yaml:"panel-github-repository"`
// BaseURL specifies the base URL of the remote management API for TUI client mode (e.g. "https://proxy.example.com").
BaseURL string `yaml:"base-url,omitempty" json:"base-url,omitempty"`
}
// QuotaExceeded defines the behavior when API quota limits are exceeded.
// It provides configuration options for automatic failover mechanisms.
type QuotaExceeded struct {
// SwitchProject indicates whether to automatically switch to another project when a quota is exceeded.
SwitchProject bool `yaml:"switch-project" json:"switch-project"`
// SwitchPreviewModel indicates whether to automatically switch to a preview model when a quota is exceeded.
SwitchPreviewModel bool `yaml:"switch-preview-model" json:"switch-preview-model"`
// AntigravityCredits enables credits-based last-resort fallback for Claude models.
// When all free-tier auths are exhausted (429/503), the conductor retries with
// an auth that has available Google One AI credits.
AntigravityCredits bool `yaml:"antigravity-credits" json:"antigravity-credits"`
}
// RoutingConfig configures how credentials are selected for requests.
type RoutingConfig struct {
// Strategy selects the credential selection strategy.
// Supported values: "round-robin" (default), "weighted-round-robin", "fill-first".
Strategy string `yaml:"strategy,omitempty" json:"strategy,omitempty"`
// SessionAffinity enables universal session-sticky routing for all clients.
// Explicit Claude Code, Codex, OpenCode, pi, and generic (X-Session-ID) session headers
// are preferred, followed by prompt_cache_key, Responses conversation IDs, legacy body IDs,
// execution session identity, Merkle LCP prefix/compaction matching, derived session
// identity, and the existing message-content hash fallback.
// LCP compaction matching requires at least 2 shared trailing turns; clients whose
// compaction replaces all earlier turns with a summary should send X-Session-ID (or
// another explicit session ID), or affinity starts a new binding at every compaction.
// Automatic failover is always enabled when bound auth becomes unavailable.
SessionAffinity bool `yaml:"session-affinity,omitempty" json:"session-affinity,omitempty"`
// SessionAffinityTTL specifies how long session-to-auth bindings are retained.
// Default: 1h. Accepts duration strings like "30m", "1h", "2h30m".
SessionAffinityTTL string `yaml:"session-affinity-ttl,omitempty" json:"session-affinity-ttl,omitempty"`
// SessionAffinitySubagents controls whether subagents (child sessions with parent references)
// inherit and bind to the parent's upstream credential across all providers (Claude, Codex,
// Antigravity, Gemini), maximizing prompt and KV cache reuse.
// When false, subagents are distributed across the credential pool via the fallback selector.
// Default: true. Ignored when SessionAffinity is false.
SessionAffinitySubagents *bool `yaml:"session-affinity-subagents,omitempty" json:"session-affinity-subagents,omitempty"`
// SessionAffinityRebindOn controls which credential failures rebind an established session
// to the failover credential.
// Supported values:
// - "any" (default): any credential-attributed failure unbinds and rebinds on failover.
// - "quota": transient errors (408, 5xx, empty stream, buffer limit) fail over for that
// request only while keeping the session bound to its original credential; quota and
// auth errors (429, 401/402/403, 404/model support, invalid_grant) rebind as normal.
// - "never": never move or fail over an established session to another credential;
// keep the binding and return the error (or retry the same credential) instead.
// Ignored when SessionAffinity is false.
SessionAffinityRebindOn string `yaml:"session-affinity-rebind-on,omitempty" json:"session-affinity-rebind-on,omitempty"`
}
// OAuthModelAlias defines a model ID alias for a specific channel.
// It maps the upstream model name (Name) to the client-visible alias (Alias).
// When Fork is true, the alias is added as an additional model in listings while
// keeping the original model ID available.
type OAuthModelAlias struct {
// OpenAPI overrides Vertex endpoint selection for this model.
OpenAPI *bool `yaml:"openapi,omitempty" json:"openapi,omitempty"`
Name string `yaml:"name" json:"name"`
Alias string `yaml:"alias" json:"alias"`
Fork bool `yaml:"fork,omitempty" json:"fork,omitempty"`
// DisplayName is the optional human-readable name shown in model catalogs.
DisplayName string `yaml:"display-name,omitempty" json:"display-name,omitempty"`
ForceMapping bool `yaml:"force-mapping,omitempty" json:"force-mapping,omitempty"`
}
// OAuthModelSetting defines provider/channel model settings (such as context window overrides) for OAuth credentials.
type OAuthModelSetting struct {
// OpenAPI overrides Vertex endpoint selection for this model.
OpenAPI *bool `yaml:"openapi,omitempty" json:"openapi,omitempty"`
Name string `yaml:"name" json:"name"`
Alias string `yaml:"alias,omitempty" json:"alias,omitempty"`
// MaxContextLength overrides the context window advertised to Codex clients.
MaxContextLength int `yaml:"max-context-length,omitempty" json:"max-context-length,omitempty"`
}
// GetMaxContextLength returns the configured maximum context length override.
func (s OAuthModelSetting) GetMaxContextLength() int { return s.MaxContextLength }
// ResolveOAuthModelSetting finds the best matching OAuthModelSetting for a given model.
// An exact Alias match on the model ID takes precedence over a general Name match.
// Within the same match specificity, later entries in the slice override earlier ones.
func ResolveOAuthModelSetting(settings []OAuthModelSetting, modelID, metadataModelID, modelName string) *OAuthModelSetting {
if len(settings) == 0 {
return nil
}
id := strings.ToLower(strings.TrimSpace(modelID))
metaID := strings.ToLower(strings.TrimSpace(metadataModelID))
name := strings.ToLower(strings.TrimSpace(modelName))
var aliasMatch *OAuthModelSetting
var nameMatch *OAuthModelSetting
for i := range settings {
entry := &settings[i]
entryName := strings.ToLower(strings.TrimSpace(entry.Name))
if entryName == "" {
continue
}
entryAlias := strings.ToLower(strings.TrimSpace(entry.Alias))
if entryAlias != "" && id != "" && id == entryAlias {
aliasMatch = entry
} else if (entryAlias == "" || entryAlias == id) && (id == entryName || (metaID != "" && metaID == entryName) || (name != "" && name == entryName)) {
nameMatch = entry
}
}
if aliasMatch != nil {
return aliasMatch
}
return nameMatch
}
// PayloadConfig defines default and override parameter rules applied to provider payloads.
type PayloadConfig struct {
// Default defines rules that only set parameters when they are missing in the payload.
Default []PayloadRule `yaml:"default" json:"default"`
// DefaultRaw defines rules that set raw JSON values only when they are missing.
DefaultRaw []PayloadRule `yaml:"default-raw" json:"default-raw"`
// Override defines rules that always set parameters, overwriting any existing values.
Override []PayloadRule `yaml:"override" json:"override"`
// OverrideRaw defines rules that always set raw JSON values, overwriting any existing values.
OverrideRaw []PayloadRule `yaml:"override-raw" json:"override-raw"`
// Filter defines rules that remove parameters from the payload by JSON path.
Filter []PayloadFilterRule `yaml:"filter" json:"filter"`
}
// PayloadFilterRule describes a rule to remove specific JSON paths from matching model payloads.
type PayloadFilterRule struct {
// Models lists model entries with name pattern and protocol constraint.
Models []PayloadModelRule `yaml:"models" json:"models"`
// Params lists JSON paths (gjson/sjson syntax) to remove from the payload.
Params []string `yaml:"params" json:"params"`
}
// PayloadRule describes a single rule targeting a list of models with parameter updates.
type PayloadRule struct {
// Models lists model entries with name pattern and protocol constraint.
Models []PayloadModelRule `yaml:"models" json:"models"`
// Params maps JSON paths (gjson/sjson syntax) to values written into the payload.
// For *-raw rules, values are treated as raw JSON fragments (strings are used as-is).
Params map[string]any `yaml:"params" json:"params"`
}
// PayloadModelRule ties a model name pattern to a specific translator protocol.
type PayloadModelRule struct {
// Name is the model name or wildcard pattern (e.g., "gpt-*", "*-5", "gemini-*-pro").
Name string `yaml:"name" json:"name"`
// Protocol restricts the rule to a specific translator format (e.g., "gemini", "responses").
Protocol string `yaml:"protocol" json:"protocol"`
// Headers restricts the rule to requests whose headers match all configured wildcard patterns.
Headers map[string]string `yaml:"headers" json:"headers"`
// FromProtocol restricts the rule to a specific source protocol (e.g., "gemini", "responses").
FromProtocol string `yaml:"from-protocol" json:"from-protocol"`
// Match requires payload JSON paths to equal the configured values.
Match []map[string]any `yaml:"match" json:"match"`
// NotMatch requires payload JSON paths to not equal the configured values.
NotMatch []map[string]any `yaml:"not-match" json:"not-match"`
// Exist requires payload JSON paths to exist and not be null.
Exist []string `yaml:"exist" json:"exist"`
// NotExist requires payload JSON paths to be missing or null.
NotExist []string `yaml:"not-exist" json:"not-exist"`
}
// CloakConfig configures request cloaking for non-Claude-Code clients.
// Cloaking disguises API requests to appear as originating from the official Claude Code CLI.
type CloakConfig struct {
// Mode controls cloaking behavior: "auto" (default), "always", "never", or "uncloak".
// Supplying this CloakConfig explicitly enables cloaking for an unprofiled API key.
// - "auto": cloak unless strong request signals identify a verified native entrypoint
// - "always": cloak every unconfirmed client; confirmed native Claude Code remains passthrough
// - "never" / "uncloak": never apply cloaking; on non-CLI API keys, also strip incoming Claude Code identity and billing attribution
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
// StrictMode controls how caller system prompts are handled when cloaking.
// - false (default): legacy-model whitelist uses a user reminder; all other models use a mid-conversation system message
// - true: strip caller system prompts and keep only the Claude Code billing and identity blocks
StrictMode bool `yaml:"strict-mode,omitempty" json:"strict-mode,omitempty"`
// SensitiveWords is a list of words to obfuscate with zero-width characters.
// This can help bypass certain content filters.
SensitiveWords []string `yaml:"sensitive-words,omitempty" json:"sensitive-words,omitempty"`
// SensitiveWordsReplace maps sensitive words or phrases to literal replacements.
SensitiveWordsReplace map[string]string `yaml:"sensitive-words-replace,omitempty" json:"sensitive-words-replace,omitempty"`
// CacheUserID controls whether Claude user_id values are cached per API key.
// When false, a fresh random user_id is generated for every request.
CacheUserID *bool `yaml:"cache-user-id,omitempty" json:"cache-user-id,omitempty"`
}
// ClaudeKey represents the configuration for a Claude API key,
// including the API key itself and an optional base URL for the API endpoint.
type ClaudeKey struct {
// APIKey is the authentication key for accessing Claude API services.
APIKey string `yaml:"api-key" json:"api-key"`
// Priority controls selection preference when multiple credentials match.
// Higher values are preferred; defaults to 0.
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
// Weight controls proportional selection under weighted-round-robin.
// An omitted value defaults to 1; non-positive values exclude this credential; maximum 1,000,000.
Weight *int `yaml:"weight,omitempty" json:"weight,omitempty"`
// Prefix optionally namespaces models for this credential (e.g., "teamA/claude-sonnet-4").
Prefix string `yaml:"prefix,omitempty" json:"prefix,omitempty"`
// BaseURL is the base URL for the Claude API endpoint.
// If empty, the default Claude API URL will be used.
BaseURL string `yaml:"base-url" json:"base-url"`
// ProxyURL overrides the global proxy setting for this API key if provided.
ProxyURL string `yaml:"proxy-url" json:"proxy-url"`
// ConnectionPool configures upstream connection reuse for this credential.
// Omitted disables pooling (preserving the fresh-transport-per-request default);
// pools stay per credential, so opting in never shares connections with another key.
ConnectionPool *ProxyConnectionPoolConfig `yaml:"connection-pool,omitempty" json:"connection-pool,omitempty"`
// Models defines upstream model names and aliases for request routing.
Models []ClaudeModel `yaml:"models" json:"models"`
// Headers optionally adds extra HTTP headers for requests sent with this key.
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
// ExcludedModels lists model IDs that should be excluded for this provider.
ExcludedModels []string `yaml:"excluded-models,omitempty" json:"excluded-models,omitempty"`
// RebuildMidSystemMessage moves Claude messages with role "system" into the top-level system field.
RebuildMidSystemMessage bool `yaml:"rebuild-mid-system-message,omitempty" json:"rebuild-mid-system-message,omitempty"`
// NativeToolSearch trusts a custom base URL with Anthropic native tool search
// (defer_loading tools and tool_reference results) for Codex tool_search.
// Official api.anthropic.com credentials use native tool search without it.
NativeToolSearch bool `yaml:"native-tool-search,omitempty" json:"native-tool-search,omitempty"`
// DisableCooling overrides the global cooling policy for this credential when set.
// True disables auth/model cooldowns; false explicitly enables them.
DisableCooling *bool `yaml:"disable-cooling,omitempty" json:"disable-cooling,omitempty"`
// RequestRetry optionally overrides the global request-retry for this credential.
// Nil or a negative value means "use the global request-retry". 0 disables additional retry rounds.
RequestRetry *int `yaml:"request-retry,omitempty" json:"request-retry,omitempty"`
// RequestScopedErrors configures custom classification rules for upstream errors.
RequestScopedErrors []RequestScopedErrorRule `yaml:"request-scoped-errors,omitempty" json:"request-scoped-errors,omitempty"`
// Cloak configures request cloaking for non-Claude-Code clients.
Cloak *CloakConfig `yaml:"cloak,omitempty" json:"cloak,omitempty"`
// FingerprintProfile selects the Claude Code request fingerprint for this
// credential on Anthropic Messages. Empty/default keeps the caller request
// fingerprint and headers, including first-party api.anthropic.com API keys.
// "claude-code-cli" opts official Anthropic API keys, custom gateways, and
// delegated providers such as Kimi into the Claude Code OAuth CLI Messages
// shape (OAuth betas, CCH signing, stable CLI identity) without treating the
// credential as a real OAuth token for refresh/profile/runtime semantics.
// CCH is a per-request hash and follows the native gate: it is emitted only on
// api.anthropic.com and Vertex, so an opt-in on any other gateway sends the
// billing block unsigned and cannot bust that gateway's prompt cache. Kimi
// strips the attribution entirely by default and keeps it, unsigned, after an
// explicit opt-in. count_tokens keeps the native model/messages/tools shape.
// Recognized values are defined by NormalizeClaudeFingerprintProfile.
FingerprintProfile string `yaml:"fingerprint-profile,omitempty" json:"fingerprint-profile,omitempty"`
// ExperimentalCCHSigning is retained for configuration compatibility.
// CCH signing is automatic for Claude OAuth and supported direct upstreams.
ExperimentalCCHSigning bool `yaml:"experimental-cch-signing,omitempty" json:"experimental-cch-signing,omitempty"`
}
func (k ClaudeKey) GetAPIKey() string { return k.APIKey }
func (k ClaudeKey) GetBaseURL() string { return k.BaseURL }
func (k ClaudeKey) GetPrefix() string { return k.Prefix }
func (k ClaudeKey) GetProxyURL() string { return k.ProxyURL }
// ClaudeModel describes a mapping between an alias and the actual upstream model name.
type ClaudeModel struct {
// Name is the upstream model identifier used when issuing requests.
Name string `yaml:"name" json:"name"`
// Alias is the client-facing model name that maps to Name.
Alias string `yaml:"alias" json:"alias"`
// DisplayName is the optional human-readable name shown in model catalogs.
DisplayName string `yaml:"display-name,omitempty" json:"display-name,omitempty"`
// MaxContextLength overrides the context window advertised to Codex clients.
MaxContextLength int `yaml:"max-context-length,omitempty" json:"max-context-length,omitempty"`
// ForceMapping rewrites upstream response model fields back to Alias.
ForceMapping bool `yaml:"force-mapping,omitempty" json:"force-mapping,omitempty"`
// IsCompat preserves thinking blocks with empty signatures for compatible upstreams
// and enables provider-aware signed-thinking replay for Claude-compatible API-key models.
// Default false keeps the normal signature validation behavior.
IsCompat bool `yaml:"is-compat,omitempty" json:"is-compat,omitempty"`
// Thinking configures the thinking/reasoning capability for this model.
Thinking *registry.ThinkingSupport `yaml:"thinking,omitempty" json:"thinking,omitempty"`
}
func (m ClaudeModel) GetName() string { return m.Name }
func (m ClaudeModel) GetAlias() string { return m.Alias }
func (m ClaudeModel) GetDisplayName() string { return m.DisplayName }
func (m ClaudeModel) GetMaxContextLength() int { return m.MaxContextLength }
func (m ClaudeModel) GetForceMapping() bool { return m.ForceMapping }
func (m ClaudeModel) GetIsCompat() bool { return m.IsCompat }
func (m ClaudeModel) GetThinking() *registry.ThinkingSupport { return m.Thinking }
// CodexKey represents the configuration for a Codex API key,
// including the API key itself and an optional base URL for the API endpoint.
type CodexKey struct {
// APIKey is the authentication key for accessing Codex API services.
APIKey string `yaml:"api-key" json:"api-key"`
// Priority controls selection preference when multiple credentials match.
// Higher values are preferred; defaults to 0.
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
// Weight controls proportional selection under weighted-round-robin.
// An omitted value defaults to 1; non-positive values exclude this credential; maximum 1,000,000.
Weight *int `yaml:"weight,omitempty" json:"weight,omitempty"`
// Prefix optionally namespaces models for this credential (e.g., "teamA/gpt-5-codex").
Prefix string `yaml:"prefix,omitempty" json:"prefix,omitempty"`
// BaseURL is the base URL for the Codex API endpoint.
// If empty, the default Codex API URL will be used.
BaseURL string `yaml:"base-url" json:"base-url"`
// Websockets enables the Responses API websocket transport for this credential.
Websockets bool `yaml:"websockets,omitempty" json:"websockets,omitempty"`
// AlphaSearch allows this Codex API key to serve the Alpha Search endpoint.
AlphaSearch bool `yaml:"alpha-search,omitempty" json:"alpha-search,omitempty"`
// ProxyURL overrides the global proxy setting for this API key if provided.
ProxyURL string `yaml:"proxy-url" json:"proxy-url"`
// ConnectionPool configures upstream connection reuse for this credential.
// Omitted disables pooling (preserving the fresh-transport-per-request default);
// pools stay per credential, so opting in never shares connections with another key.
ConnectionPool *ProxyConnectionPoolConfig `yaml:"connection-pool,omitempty" json:"connection-pool,omitempty"`
// Models defines upstream model names and aliases for request routing.
Models []CodexModel `yaml:"models" json:"models"`
// Headers optionally adds extra HTTP headers for requests sent with this key.
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
// ExcludedModels lists model IDs that should be excluded for this provider.
ExcludedModels []string `yaml:"excluded-models,omitempty" json:"excluded-models,omitempty"`
// DisableCooling overrides the global cooling policy for this credential when set.
// True disables auth/model cooldowns; false explicitly enables them.
DisableCooling *bool `yaml:"disable-cooling,omitempty" json:"disable-cooling,omitempty"`
// DisableCodexCloaking optionally overrides the global codex.disable-codex-cloaking for this credential.
// True disables cloaking; false explicitly enables cloaking; omitted inherits global codex.disable-codex-cloaking.
DisableCodexCloaking *bool `yaml:"disable-codex-cloaking,omitempty" json:"disable-codex-cloaking,omitempty"`
// RequestRetry optionally overrides the global request-retry for this credential.
// Nil or a negative value means "use the global request-retry". 0 disables additional retry rounds.
RequestRetry *int `yaml:"request-retry,omitempty" json:"request-retry,omitempty"`
// RequestScopedErrors configures custom classification rules for upstream errors.
RequestScopedErrors []RequestScopedErrorRule `yaml:"request-scoped-errors,omitempty" json:"request-scoped-errors,omitempty"`
}
func (k CodexKey) GetAPIKey() string { return k.APIKey }
func (k CodexKey) GetBaseURL() string { return k.BaseURL }
func (k CodexKey) GetPrefix() string { return k.Prefix }
func (k CodexKey) GetProxyURL() string { return k.ProxyURL }
// CodexModel describes a mapping between an alias and the actual upstream model name.
type CodexModel struct {
// Name is the upstream model identifier used when issuing requests.
Name string `yaml:"name" json:"name"`
// Alias is the client-facing model name that maps to Name.
Alias string `yaml:"alias" json:"alias"`
// DisplayName is the optional human-readable name shown in model catalogs.
DisplayName string `yaml:"display-name,omitempty" json:"display-name,omitempty"`
// MaxContextLength overrides the context window advertised to Codex clients.
MaxContextLength int `yaml:"max-context-length,omitempty" json:"max-context-length,omitempty"`
// ForceMapping rewrites upstream response model fields back to Alias.
ForceMapping bool `yaml:"force-mapping,omitempty" json:"force-mapping,omitempty"`
// IsCompat converts Codex MultiAgentV2 agent_message items into portable
// Responses message/user input when client.codex.optimize-multi-agent-v2 is also true.
// Use this for third-party Responses-compatible endpoints that do not accept
// native agent_message items or empty-signature thinking blocks. Default false
// keeps the native behavior unchanged.
// When enabled, compaction_trigger items also use local summary compaction.
IsCompat bool `yaml:"is-compat,omitempty" json:"is-compat,omitempty"`
// SupportConfigurationUpdate enables configuration_update for this API-key model.
// It defaults to false, independently of the built-in OAuth model catalog.
SupportConfigurationUpdate bool `yaml:"support-configuration-update,omitempty" json:"support-configuration-update,omitempty"`
// Thinking configures the thinking/reasoning capability for this model.
Thinking *registry.ThinkingSupport `yaml:"thinking,omitempty" json:"thinking,omitempty"`
}
func (m CodexModel) GetName() string { return m.Name }
func (m CodexModel) GetAlias() string { return m.Alias }
func (m CodexModel) GetDisplayName() string { return m.DisplayName }
func (m CodexModel) GetMaxContextLength() int { return m.MaxContextLength }
func (m CodexModel) GetForceMapping() bool { return m.ForceMapping }
func (m CodexModel) GetIsCompat() bool { return m.IsCompat }
func (m CodexModel) GetThinking() *registry.ThinkingSupport { return m.Thinking }
// XAIKey uses the Codex API key structure for native xAI execution.
type XAIKey = CodexKey
// XAIModel uses the Codex model mapping structure for xAI models.
type XAIModel = CodexModel
// MetaKey uses the Codex API key structure for native Meta Muse execution.
type MetaKey = CodexKey
// MetaModel uses the Codex model mapping structure for Meta Muse models.
type MetaModel = CodexModel
// GeminiKey represents the configuration for a Gemini API key,
// including optional overrides for upstream base URL, proxy routing, and headers.
type GeminiKey struct {
// APIKey is the authentication key for accessing Gemini API services.
APIKey string `yaml:"api-key" json:"api-key"`
// Priority controls selection preference when multiple credentials match.
// Higher values are preferred; defaults to 0.
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
// Weight controls proportional selection under weighted-round-robin.
// An omitted value defaults to 1; non-positive values exclude this credential; maximum 1,000,000.
Weight *int `yaml:"weight,omitempty" json:"weight,omitempty"`
// Prefix optionally namespaces models for this credential (e.g., "teamA/gemini-3-pro-preview").
Prefix string `yaml:"prefix,omitempty" json:"prefix,omitempty"`
// BaseURL optionally overrides the Gemini API endpoint.
BaseURL string `yaml:"base-url,omitempty" json:"base-url,omitempty"`
// ProxyURL optionally overrides the global proxy for this API key.
ProxyURL string `yaml:"proxy-url,omitempty" json:"proxy-url,omitempty"`
// ConnectionPool configures upstream connection reuse for this credential.
// Omitted disables pooling (preserving the fresh-transport-per-request default);
// pools stay per credential, so opting in never shares connections with another key.
ConnectionPool *ProxyConnectionPoolConfig `yaml:"connection-pool,omitempty" json:"connection-pool,omitempty"`
// Models defines upstream model names and aliases for request routing.
Models []GeminiModel `yaml:"models,omitempty" json:"models,omitempty"`
// Headers optionally adds extra HTTP headers for requests sent with this key.
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
// ExcludedModels lists model IDs that should be excluded for this provider.
ExcludedModels []string `yaml:"excluded-models,omitempty" json:"excluded-models,omitempty"`
// DisableCooling overrides the global cooling policy for this credential when set.
// True disables auth/model cooldowns; false explicitly enables them.
DisableCooling *bool `yaml:"disable-cooling,omitempty" json:"disable-cooling,omitempty"`
// RequestRetry optionally overrides the global request-retry for this credential.
// Nil or a negative value means "use the global request-retry". 0 disables additional retry rounds.
RequestRetry *int `yaml:"request-retry,omitempty" json:"request-retry,omitempty"`
// RequestScopedErrors configures custom classification rules for upstream errors.
RequestScopedErrors []RequestScopedErrorRule `yaml:"request-scoped-errors,omitempty" json:"request-scoped-errors,omitempty"`
}
func (k GeminiKey) GetAPIKey() string { return k.APIKey }
func (k GeminiKey) GetBaseURL() string { return k.BaseURL }
func (k GeminiKey) GetPrefix() string { return k.Prefix }
func (k GeminiKey) GetProxyURL() string { return k.ProxyURL }
// GeminiModel describes a mapping between an alias and the actual upstream model name.
type GeminiModel struct {
// Name is the upstream model identifier used when issuing requests.
Name string `yaml:"name" json:"name"`
// Alias is the client-facing model name that maps to Name.
Alias string `yaml:"alias" json:"alias"`
// DisplayName is the optional human-readable name shown in model catalogs.
DisplayName string `yaml:"display-name,omitempty" json:"display-name,omitempty"`
// MaxContextLength overrides the context window advertised to Codex clients.
MaxContextLength int `yaml:"max-context-length,omitempty" json:"max-context-length,omitempty"`
// ForceMapping rewrites upstream response model fields back to Alias.
ForceMapping bool `yaml:"force-mapping,omitempty" json:"force-mapping,omitempty"`
// IsCompat preserves thinking blocks with empty signatures for compatible upstreams.
// Default false keeps the normal signature validation behavior.
IsCompat bool `yaml:"is-compat,omitempty" json:"is-compat,omitempty"`
// Thinking configures the thinking/reasoning capability for this model.
Thinking *registry.ThinkingSupport `yaml:"thinking,omitempty" json:"thinking,omitempty"`
}
func (m GeminiModel) GetName() string { return m.Name }
func (m GeminiModel) GetAlias() string { return m.Alias }
func (m GeminiModel) GetDisplayName() string { return m.DisplayName }
func (m GeminiModel) GetMaxContextLength() int { return m.MaxContextLength }
func (m GeminiModel) GetForceMapping() bool { return m.ForceMapping }
func (m GeminiModel) GetIsCompat() bool { return m.IsCompat }
func (m GeminiModel) GetThinking() *registry.ThinkingSupport { return m.Thinking }
// OpenAICompatibility represents the configuration for OpenAI API compatibility
// with external providers, allowing model aliases to be routed through OpenAI API format.
type OpenAICompatibility struct {
// Name is the identifier for this OpenAI compatibility configuration.
Name string `yaml:"name" json:"name"`
// Priority controls selection preference when multiple providers or credentials match.
// Higher values are preferred; defaults to 0.
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
// Disabled prevents this provider from being used for routing.
Disabled bool `yaml:"disabled,omitempty" json:"disabled,omitempty"`
// Prefix optionally namespaces model aliases for this provider (e.g., "teamA/kimi-k2").
Prefix string `yaml:"prefix,omitempty" json:"prefix,omitempty"`
// BaseURL is the base URL for the external OpenAI-compatible API endpoint.
BaseURL string `yaml:"base-url" json:"base-url"`
// APIKeyEntries defines API keys with optional per-key proxy configuration.
APIKeyEntries []OpenAICompatibilityAPIKey `yaml:"api-key-entries,omitempty" json:"api-key-entries,omitempty"`
// ConnectionPool is the provider-group default for upstream connection reuse.
// A per-key connection-pool entry overrides it field by field; omitted disables pooling.
ConnectionPool *ProxyConnectionPoolConfig `yaml:"connection-pool,omitempty" json:"connection-pool,omitempty"`
// Models defines the model configurations including aliases for routing.
Models []OpenAICompatibilityModel `yaml:"models" json:"models"`
// Headers optionally adds extra HTTP headers for requests sent to this provider.
Headers map[string]string `yaml:"headers,omitempty" json:"headers,omitempty"`
// SupportPromptCacheKey enables derived prompt_cache_key injection for supported requests.
SupportPromptCacheKey bool `yaml:"support-prompt-cache-key,omitempty" json:"support-prompt-cache-key,omitempty"`
// DisableCooling overrides the global cooling policy for this provider when set.
// True disables auth/model cooldowns; false explicitly enables them.
DisableCooling *bool `yaml:"disable-cooling,omitempty" json:"disable-cooling,omitempty"`
// RequestRetry optionally overrides the global request-retry for this provider.
// Nil or a negative value means "use the global request-retry". 0 disables additional retry rounds.
RequestRetry *int `yaml:"request-retry,omitempty" json:"request-retry,omitempty"`
// RequestScopedErrors configures custom classification rules for upstream errors.
RequestScopedErrors []RequestScopedErrorRule `yaml:"request-scoped-errors,omitempty" json:"request-scoped-errors,omitempty"`
}
// OpenAICompatibilityAPIKey represents an API key configuration with optional proxy setting.
type OpenAICompatibilityAPIKey struct {
// APIKey is the authentication key for accessing the external API services.
APIKey string `yaml:"api-key" json:"api-key"`
// Weight controls proportional selection under weighted-round-robin.
// An omitted value defaults to 1; non-positive values exclude this credential; maximum 1,000,000.
Weight *int `yaml:"weight,omitempty" json:"weight,omitempty"`
// ProxyURL overrides the global proxy setting for this API key if provided.
ProxyURL string `yaml:"proxy-url,omitempty" json:"proxy-url,omitempty"`
// ConnectionPool configures upstream connection reuse for this key, overriding
// the provider-group default field by field. Pools stay per credential, so
// opting in never shares connections with another key.
ConnectionPool *ProxyConnectionPoolConfig `yaml:"connection-pool,omitempty" json:"connection-pool,omitempty"`
}
// OpenAICompatibilityModel represents a model configuration for OpenAI compatibility,
// including the actual model name and its alias for API routing.
type OpenAICompatibilityModel struct {
// Name is the actual model name used by the external provider.
Name string `yaml:"name" json:"name"`
// Alias is the model name alias that clients will use to reference this model.
Alias string `yaml:"alias" json:"alias"`
// DisplayName is the optional human-readable name shown in model catalogs.
DisplayName string `yaml:"display-name,omitempty" json:"display-name,omitempty"`
// MaxContextLength overrides the context window advertised to Codex clients.
MaxContextLength int `yaml:"max-context-length,omitempty" json:"max-context-length,omitempty"`
// ForceMapping rewrites upstream response model fields back to Alias.
ForceMapping bool `yaml:"force-mapping,omitempty" json:"force-mapping,omitempty"`
// Image marks this model as callable through /v1/images/generations and /v1/images/edits.
Image bool `yaml:"image,omitempty" json:"image,omitempty"`
// InputModalities declares chat/responses input capabilities (e.g. text, image) for Codex and other clients.
// This is separate from Image, which only enables /v1/images/* endpoints.
InputModalities []string `yaml:"input-modalities,omitempty" json:"input-modalities,omitempty"`
// OutputModalities declares supported output modalities when known (e.g. text, image).
OutputModalities []string `yaml:"output-modalities,omitempty" json:"output-modalities,omitempty"`
// IsCompat preserves Claude thinking blocks for compatible upstreams.
// Default false keeps the normal signature validation behavior.
IsCompat bool `yaml:"is-compat,omitempty" json:"is-compat,omitempty"`
// UseMaxCompletionTokens emits max_completion_tokens instead of legacy max_tokens for this model.
// Default false preserves max_tokens for older compatible upstreams.
UseMaxCompletionTokens bool `yaml:"use-max-completion-tokens,omitempty" json:"use-max-completion-tokens,omitempty"`
// Thinking configures the thinking/reasoning capability for this model.
// If nil, the model defaults to level-based reasoning with levels ["low", "medium", "high"].
Thinking *registry.ThinkingSupport `yaml:"thinking,omitempty" json:"thinking,omitempty"`
}
func (m OpenAICompatibilityModel) GetName() string { return m.Name }
func (m OpenAICompatibilityModel) GetAlias() string { return m.Alias }
func (m OpenAICompatibilityModel) GetDisplayName() string { return m.DisplayName }
func (m OpenAICompatibilityModel) GetMaxContextLength() int { return m.MaxContextLength }
func (m OpenAICompatibilityModel) GetForceMapping() bool { return m.ForceMapping }
func (m OpenAICompatibilityModel) GetIsCompat() bool { return m.IsCompat }
func (m OpenAICompatibilityModel) GetUseMaxCompletionTokens() bool { return m.UseMaxCompletionTokens }
func (m OpenAICompatibilityModel) GetThinking() *registry.ThinkingSupport { return m.Thinking }