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 "/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 ("/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 "/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-. 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 }