From d764c7fdec37b0f560971df55461324295033c61 Mon Sep 17 00:00:00 2001 From: "Jakub A. W" Date: Fri, 17 Jul 2026 14:25:57 +0200 Subject: [PATCH] docs(config): teach canonical GOMODEL_-prefixed env vars everywhere Rename every GoModel-defined environment variable to its canonical GOMODEL_ spelling across the documentation surface: the docs site, README, .env.template, config.example.yaml, helm chart, docker-compose, and Makefile. Exempt names (PORT, REDIS_URL, the provider family) and dated material under docs/dev, docs/adr, and the benchmark directories are untouched. Adds a naming note to the configuration reference page. Co-Authored-By: Claude Fable 5 --- .env.template | 256 ++++++++++---------- CLAUDE.md | 53 ++-- Makefile | 8 +- README.md | 8 +- config/config.example.yaml | 48 ++-- docker-compose.yaml | 12 +- docs/advanced/admin-endpoints.mdx | 14 +- docs/advanced/api-endpoints.mdx | 8 +- docs/advanced/audio-api.mdx | 6 +- docs/advanced/cli.mdx | 2 +- docs/advanced/config-yaml.mdx | 16 +- docs/advanced/configuration.mdx | 138 ++++++----- docs/advanced/guardrails.mdx | 2 +- docs/advanced/resilience.mdx | 24 +- docs/advanced/usage-api.mdx | 2 +- docs/dev/2026-07-17_env-prefix-migration.md | 19 +- docs/features/budgets.mdx | 16 +- docs/features/cache.mdx | 4 +- docs/features/cost-tracking.mdx | 2 +- docs/features/failover.mdx | 4 +- docs/features/labelling.mdx | 10 +- docs/features/mcp-gateway.mdx | 12 +- docs/features/passthrough-api.mdx | 10 +- docs/features/rate-limits.mdx | 18 +- docs/features/user-path.mdx | 2 +- docs/features/virtual-models.mdx | 6 +- docs/getting-started/quickstart.mdx | 10 +- docs/guides/prometheus-metrics.mdx | 36 +-- docs/providers/anthropic.mdx | 4 +- docs/providers/bailian.mdx | 2 +- docs/providers/bedrock.mdx | 2 +- docs/providers/gemini.mdx | 2 +- docs/providers/opencode-go.mdx | 2 +- docs/providers/oracle.mdx | 2 +- docs/providers/overview.mdx | 6 +- helm/templates/_helpers.tpl | 2 +- helm/templates/configmap.yaml | 22 +- helm/templates/deployment.yaml | 40 +-- 38 files changed, 422 insertions(+), 408 deletions(-) diff --git a/.env.template b/.env.template index 7aea454f9..7e65b012d 100644 --- a/.env.template +++ b/.env.template @@ -1,8 +1,8 @@ # GoModel-defined variables are canonically spelled GOMODEL_ (e.g. -# GOMODEL_SQLITE_PATH). The unprefixed spellings shown below still work but are -# deprecated: each one logs a warning at startup naming its replacement, and -# they will be removed in a future major release. Setting both spellings -# resolves to the GOMODEL_ one. +# GOMODEL_SQLITE_PATH), as shown below. The unprefixed spellings (SQLITE_PATH, +# ...) still work but are deprecated: each one logs a warning at startup naming +# its replacement, and they will be removed in a future major release. Setting +# both spellings resolves to the GOMODEL_ one. # # Two groups keep their bare names permanently and must not be prefixed: # - PORT and REDIS_URL, which PaaS platforms inject. @@ -15,17 +15,17 @@ # Server Configuration # PORT=8080 # Mount the whole gateway under a path prefix, e.g. https://example.com/g/ -# BASE_PATH=/g +# GOMODEL_BASE_PATH=/g # Header used to read/write request user_path values (default: X-GoModel-User-Path) -# USER_PATH_HEADER=X-GoModel-User-Path +# GOMODEL_USER_PATH_HEADER=X-GoModel-User-Path # Reject unknown keys in config.yaml and in the JSON env vars that declare the same -# structures (VIRTUAL_MODELS, SET_RATE_LIMIT_*, SET_BUDGET_*). Default: true, so a +# structures (GOMODEL_VIRTUAL_MODELS, GOMODEL_SET_RATE_LIMIT_*, GOMODEL_SET_BUDGET_*). Default: true, so a # typo or a misindented section fails startup instead of silently dropping providers, # rate limits, budgets, or guardrails. Set false to downgrade unknown keys to # warnings — intended for rolling a binary back under a newer config file. Malformed # values stay fatal in either mode. -# CONFIG_STRICT=true +# GOMODEL_CONFIG_STRICT=true # Tagging based on headers: label every request from the listed headers (numbered # from 1). Labels are recorded in usage tracking and audit logs. A header value can @@ -35,35 +35,35 @@ # with the same header name (leaving _PREFIX/_DONOTPASS/_DELIMITER unset resets those # fields to their defaults, it does not inherit the YAML values); other env entries # are appended. Both are read-only in the dashboard. -# TAGGING_HEADER_1=X-My-Tags -# TAGGING_HEADER_1_PREFIX=tag- -# TAGGING_HEADER_1_DONOTPASS=false -# TAGGING_HEADER_1_DELIMITER=, -# TAGGING_HEADER_2=X-Internal-Routing -# TAGGING_HEADER_2_DONOTPASS=true +# GOMODEL_TAGGING_HEADER_1=X-My-Tags +# GOMODEL_TAGGING_HEADER_1_PREFIX=tag- +# GOMODEL_TAGGING_HEADER_1_DONOTPASS=false +# GOMODEL_TAGGING_HEADER_1_DELIMITER=, +# GOMODEL_TAGGING_HEADER_2=X-Internal-Routing +# GOMODEL_TAGGING_HEADER_2_DONOTPASS=true # Log output format: leave unset to auto-detect, or set to "json" / "text" -# LOG_FORMAT=text +# GOMODEL_LOG_FORMAT=text # Log verbosity: "debug", "info" (default), "warn", or "error" -# LOG_LEVEL=info +# GOMODEL_LOG_LEVEL=info # Maximum request body size (prevents DoS attacks) # Accepts values like "10M", "1G", "500K" (default: 10M) -# BODY_SIZE_LIMIT=10M +# GOMODEL_BODY_SIZE_LIMIT=10M # Enable/disable Swagger UI at /swagger/index.html (default: true) -# SWAGGER_ENABLED=true +# GOMODEL_SWAGGER_ENABLED=true # Enable/disable pprof profiling routes at /debug/pprof/* (default: false) -# PPROF_ENABLED=false +# GOMODEL_PPROF_ENABLED=false # Enable/disable provider-native passthrough routes under /p/{provider}/{endpoint} (default: true) -# ENABLE_PASSTHROUGH_ROUTES=true +# GOMODEL_ENABLE_PASSTHROUGH_ROUTES=true # Allow optional /p/{provider}/v1/... passthrough aliases while keeping /p/{provider}/... canonical (default: true) -# ALLOW_PASSTHROUGH_V1_ALIAS=true +# GOMODEL_ALLOW_PASSTHROUGH_V1_ALIAS=true # Comma-separated list of provider types enabled for /p/{provider}/... passthrough (default: openai,anthropic,openrouter,zai,vllm) -# ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,openrouter,zai,vllm +# GOMODEL_ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,openrouter,zai,vllm # Enable the realtime (speech-to-speech) endpoints (default: true): the /v1/realtime # websocket (and /p/{provider}/v1/realtime passthrough upgrade), the WebRTC SDP @@ -74,26 +74,26 @@ # between client and provider; the gateway proxies the signaling and records usage # via a sideband observer on each call it creates. WebRTC providers: OpenAI, xAI # (calls are team-gated by xAI), and Azure OpenAI (GA v1 surface). -# REALTIME_ENABLED=true +# GOMODEL_REALTIME_ENABLED=true # MCP gateway: aggregate upstream MCP (Model Context Protocol) servers behind the # authenticated /mcp endpoint (default: true; a no-op until servers are declared here, # in config.yaml under `mcp.servers`, or in the dashboard). Tools are namespaced as # {server}_{tool}; /mcp/{server} exposes one upstream with original tool names. -# MCP_ENABLED=true -# MCP_SERVERS is a JSON object mapping server names to definitions; entries replace +# GOMODEL_MCP_ENABLED=true +# GOMODEL_MCP_SERVERS is a JSON object mapping server names to definitions; entries replace # config.yaml `mcp.servers` entries with the same name and are read-only in the # dashboard. Transports: "http" (streamable HTTP, default), "sse" (legacy), "stdio" # (spawns a local subprocess — declarable only here or in config.yaml, never via the # admin API, because runtime-registered subprocesses would be a code execution vector). -# MCP_SERVERS={"github":{"url":"https://api.githubcopilot.com/mcp","headers":{"Authorization":"Bearer ${GITHUB_PAT}"}}} +# GOMODEL_MCP_SERVERS={"github":{"url":"https://api.githubcopilot.com/mcp","headers":{"Authorization":"Bearer ${GITHUB_PAT}"}}} # HTTP Client Configuration (for upstream API requests) # Values in seconds (or Go duration format like "10m", "1h30m") # Overall request timeout (default: 600 = 10 minutes, matches OpenAI/Anthropic SDKs) -# HTTP_TIMEOUT=600 +# GOMODEL_HTTP_TIMEOUT=600 # Time to wait for response headers (default: 600) -# HTTP_RESPONSE_HEADER_TIMEOUT=600 +# GOMODEL_HTTP_RESPONSE_HEADER_TIMEOUT=600 # Security Configuration # CRITICAL: Set this to secure your gateway from unauthorized access @@ -102,9 +102,9 @@ # Metrics Configuration (Prometheus) # Enable/disable Prometheus metrics collection and /metrics endpoint -# METRICS_ENABLED=false +# GOMODEL_METRICS_ENABLED=false # Custom metrics endpoint path (default: /metrics) -# METRICS_ENDPOINT=/metrics +# GOMODEL_METRICS_ENDPOINT=/metrics # Cache Configuration # Model cache uses the local filesystem by default. @@ -112,72 +112,72 @@ # Redis Configuration # REDIS_URL=redis://localhost:6379 -# REDIS_KEY_MODELS=gomodel:models -# REDIS_TTL_MODELS=86400 +# GOMODEL_REDIS_KEY_MODELS=gomodel:models +# GOMODEL_REDIS_TTL_MODELS=86400 # How often to refresh the model registry cache in seconds (default: 3600). # Also sets how often provider health ("Last checked" in the dashboard) is # re-checked; lower it to detect provider outages/recoveries faster. -# CACHE_REFRESH_INTERVAL=3600 +# GOMODEL_CACHE_REFRESH_INTERVAL=3600 # How often to re-probe only the providers whose last refresh failed, in # seconds (default: 60, 0 disables). Detects provider recovery without # waiting for the next full refresh. -# PROVIDER_RECHECK_INTERVAL=60 -# REDIS_KEY_RESPONSES=gomodel:response: -# REDIS_TTL_RESPONSES=3600 +# GOMODEL_PROVIDER_RECHECK_INTERVAL=60 +# GOMODEL_REDIS_KEY_RESPONSES=gomodel:response: +# GOMODEL_REDIS_TTL_RESPONSES=3600 # Opt-in when config.yaml has no cache.response.simple block (e.g. env-only deploys). Omit otherwise. -# RESPONSE_CACHE_SIMPLE_ENABLED=true +# GOMODEL_RESPONSE_CACHE_SIMPLE_ENABLED=true # Opt-in when config.yaml has no cache.response.semantic block (e.g. env-only deploys). Omit otherwise. -# SEMANTIC_CACHE_ENABLED=true +# GOMODEL_SEMANTIC_CACHE_ENABLED=true # Similarity threshold between 0 and 1 (default: 0.92) -# SEMANTIC_CACHE_THRESHOLD=0.92 +# GOMODEL_SEMANTIC_CACHE_THRESHOLD=0.92 # Semantic cache entry TTL in seconds (default: 3600) -# SEMANTIC_CACHE_TTL=3600 +# GOMODEL_SEMANTIC_CACHE_TTL=3600 # Number of recent conversation messages to embed (default: 3) -# SEMANTIC_CACHE_MAX_CONV_MESSAGES=3 +# GOMODEL_SEMANTIC_CACHE_MAX_CONV_MESSAGES=3 # Exclude the system prompt from semantic cache keys (default: false) -# SEMANTIC_CACHE_EXCLUDE_SYSTEM_PROMPT=false +# GOMODEL_SEMANTIC_CACHE_EXCLUDE_SYSTEM_PROMPT=false # Embedding provider name used for semantic cache -# SEMANTIC_CACHE_EMBEDDER_PROVIDER=openai +# GOMODEL_SEMANTIC_CACHE_EMBEDDER_PROVIDER=openai # Optional embedding model override -# SEMANTIC_CACHE_EMBEDDER_MODEL=text-embedding-3-small +# GOMODEL_SEMANTIC_CACHE_EMBEDDER_MODEL=text-embedding-3-small # Vector store backend: qdrant, pgvector, pinecone, or weaviate -# SEMANTIC_CACHE_VECTOR_STORE_TYPE=qdrant +# GOMODEL_SEMANTIC_CACHE_VECTOR_STORE_TYPE=qdrant # Qdrant -# SEMANTIC_CACHE_QDRANT_URL=http://localhost:6333 -# SEMANTIC_CACHE_QDRANT_COLLECTION=gomodel_semantic -# SEMANTIC_CACHE_QDRANT_API_KEY= +# GOMODEL_SEMANTIC_CACHE_QDRANT_URL=http://localhost:6333 +# GOMODEL_SEMANTIC_CACHE_QDRANT_COLLECTION=gomodel_semantic +# GOMODEL_SEMANTIC_CACHE_QDRANT_API_KEY= # pgvector -# SEMANTIC_CACHE_PGVECTOR_URL=postgres://user:pass@localhost:5432/gomodel -# SEMANTIC_CACHE_PGVECTOR_TABLE=gomodel_semantic_cache -# SEMANTIC_CACHE_PGVECTOR_DIMENSION=1536 +# GOMODEL_SEMANTIC_CACHE_PGVECTOR_URL=postgres://user:pass@localhost:5432/gomodel +# GOMODEL_SEMANTIC_CACHE_PGVECTOR_TABLE=gomodel_semantic_cache +# GOMODEL_SEMANTIC_CACHE_PGVECTOR_DIMENSION=1536 # Pinecone -# SEMANTIC_CACHE_PINECONE_HOST=https://your-index.svc.region.pinecone.io -# SEMANTIC_CACHE_PINECONE_API_KEY= -# SEMANTIC_CACHE_PINECONE_NAMESPACE= -# SEMANTIC_CACHE_PINECONE_DIMENSION=1536 +# GOMODEL_SEMANTIC_CACHE_PINECONE_HOST=https://your-index.svc.region.pinecone.io +# GOMODEL_SEMANTIC_CACHE_PINECONE_API_KEY= +# GOMODEL_SEMANTIC_CACHE_PINECONE_NAMESPACE= +# GOMODEL_SEMANTIC_CACHE_PINECONE_DIMENSION=1536 # Weaviate -# SEMANTIC_CACHE_WEAVIATE_URL=http://localhost:8080 -# SEMANTIC_CACHE_WEAVIATE_CLASS=GomodelSemanticCache -# SEMANTIC_CACHE_WEAVIATE_API_KEY= +# GOMODEL_SEMANTIC_CACHE_WEAVIATE_URL=http://localhost:8080 +# GOMODEL_SEMANTIC_CACHE_WEAVIATE_CLASS=GomodelSemanticCache +# GOMODEL_SEMANTIC_CACHE_WEAVIATE_API_KEY= # Optional: Custom cache directory for local file cache # GOMODEL_CACHE_DIR=.cache # External model metadata registry (provides pricing, capabilities, context window, etc.) # Set to empty string to disable (default: ENTERPILOT/ai-model-list on GitHub) -# MODEL_LIST_URL=https://raw.githubusercontent.com/ENTERPILOT/ai-model-list/refs/heads/main/models.min.json +# GOMODEL_MODEL_LIST_URL=https://raw.githubusercontent.com/ENTERPILOT/ai-model-list/refs/heads/main/models.min.json # Model Access Configuration # Process-wide default for provider models when no persisted override exists (default: true) # Set to false to keep models unavailable until a model override allows one or more user paths. -# MODELS_ENABLED_BY_DEFAULT=true +# GOMODEL_MODELS_ENABLED_BY_DEFAULT=true # Hide provider models from GET /v1/models and expose only enabled aliases (default: false). -# KEEP_ONLY_ALIASES_AT_MODELS_ENDPOINT=false +# GOMODEL_KEEP_ONLY_ALIASES_AT_MODELS_ENDPOINT=false # How providers..models and [_SUFFIX]_MODELS affect provider inventory. # fallback (default): use configured models only when upstream /models fails, is nil, or is empty. # allowlist: expose only the configured models for providers that define a list, and skip their upstream /models calls. -# CONFIGURED_PROVIDER_MODELS_MODE=fallback +# GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE=fallback # Examples: OPENROUTER_MODELS=..., OPENROUTER_EU_MODELS=..., AZURE_MODELS=..., VLLM_MODELS=... # Virtual models as infrastructure-as-code (JSON array). Declares redirects, load @@ -186,37 +186,37 @@ # `virtual_models:` in config.yaml. Each entry: source, target (single) or # targets[] (load balanced), strategy (round_robin | cost), user_paths, # description, enabled. A target has model and optional provider + weight. -# VIRTUAL_MODELS=[{"source":"smart","strategy":"round_robin","targets":[{"model":"openai/gpt-4o","weight":2},{"model":"anthropic/claude-sonnet-4-6"}]}] +# GOMODEL_VIRTUAL_MODELS=[{"source":"smart","strategy":"round_robin","targets":[{"model":"openai/gpt-4o","weight":2},{"model":"anthropic/claude-sonnet-4-6"}]}] # Failover & Workflow Configuration # Failover is manual-only at runtime and enabled by default. -# FAILOVER_ENABLED=true +# GOMODEL_FAILOVER_ENABLED=true # JSON file mapping primary model selectors to ordered failover target lists -# FAILOVER_MANUAL_RULES_PATH=config/failover.example.json +# GOMODEL_FAILOVER_MANUAL_RULES_PATH=config/failover.example.json # Inline JSON object mapping primary model selectors to ordered failover target arrays -# FAILOVER_RULES_JSON={"gpt-4o":["azure/gpt-4o","gemini/gemini-2.5-pro"]} +# GOMODEL_FAILOVER_RULES_JSON={"gpt-4o":["azure/gpt-4o","gemini/gemini-2.5-pro"]} # JSON array or boolean object of primary model selectors where failover is disabled -# FAILOVER_DISABLED_MODELS_JSON=["claude-sonnet-4"] +# GOMODEL_FAILOVER_DISABLED_MODELS_JSON=["claude-sonnet-4"] # How often to refresh persisted workflows from storage (default: 1m) -# WORKFLOW_REFRESH_INTERVAL=1m +# GOMODEL_WORKFLOW_REFRESH_INTERVAL=1m # LLM Client Resilience Configuration # Retry attempts for upstream provider calls (default: 3) -# RETRY_MAX_RETRIES=3 +# GOMODEL_RETRY_MAX_RETRIES=3 # Initial retry backoff duration (default: 1s) -# RETRY_INITIAL_BACKOFF=1s +# GOMODEL_RETRY_INITIAL_BACKOFF=1s # Maximum retry backoff duration (default: 30s) -# RETRY_MAX_BACKOFF=30s +# GOMODEL_RETRY_MAX_BACKOFF=30s # Exponential backoff factor (default: 2.0) -# RETRY_BACKOFF_FACTOR=2.0 +# GOMODEL_RETRY_BACKOFF_FACTOR=2.0 # Random jitter factor applied to retry delays (default: 0.1) -# RETRY_JITTER_FACTOR=0.1 +# GOMODEL_RETRY_JITTER_FACTOR=0.1 # Consecutive failures before opening the circuit breaker (default: 5) -# CIRCUIT_BREAKER_FAILURE_THRESHOLD=5 +# GOMODEL_CIRCUIT_BREAKER_FAILURE_THRESHOLD=5 # Consecutive successes required to close the circuit breaker (default: 2) -# CIRCUIT_BREAKER_SUCCESS_THRESHOLD=2 +# GOMODEL_CIRCUIT_BREAKER_SUCCESS_THRESHOLD=2 # Circuit breaker open-state timeout duration (default: 30s) -# CIRCUIT_BREAKER_TIMEOUT=30s +# GOMODEL_CIRCUIT_BREAKER_TIMEOUT=30s # ============================================================================= # Admin API & Dashboard Configuration @@ -224,37 +224,37 @@ # Enable/disable admin REST API endpoints (default: true) # When enabled, provides /admin/* REST endpoints -# ADMIN_ENDPOINTS_ENABLED=true +# GOMODEL_ADMIN_ENDPOINTS_ENABLED=true # Enable/disable admin dashboard UI (default: true) # When enabled, provides /admin/dashboard UI -# Requires ADMIN_ENDPOINTS_ENABLED=true — if endpoints are disabled +# Requires GOMODEL_ADMIN_ENDPOINTS_ENABLED=true — if endpoints are disabled # and UI is enabled, a warning is logged and UI is forced to disabled -# ADMIN_UI_ENABLED=true +# GOMODEL_ADMIN_UI_ENABLED=true # Enable/disable realtime dashboard log previews (default: true) # When enabled, the dashboard opens /admin/live/logs and streams compact # audit/usage lifecycle updates before the async DB flush completes. -# Requires ADMIN_ENDPOINTS_ENABLED=true and ADMIN_UI_ENABLED=true for the UI. -# DASHBOARD_LIVE_LOGS_ENABLED=true +# Requires GOMODEL_ADMIN_ENDPOINTS_ENABLED=true and GOMODEL_ADMIN_UI_ENABLED=true for the UI. +# GOMODEL_DASHBOARD_LIVE_LOGS_ENABLED=true # In-memory live log replay buffer in events (default: 10000) -# The effective buffer is capped at DASHBOARD_LIVE_LOGS_REPLAY_LIMIT + 1, since +# The effective buffer is capped at GOMODEL_DASHBOARD_LIVE_LOGS_REPLAY_LIMIT + 1, since # events older than the replay window can never be served; lower this below the # replay limit only to shrink memory further at the cost of more replay resets. # Buffered events hold compact previews only — request/response bodies are # never retained (connected dashboards receive them live and hydrate history # from the persisted audit entries). -# DASHBOARD_LIVE_LOGS_BUFFER_SIZE=10000 +# GOMODEL_DASHBOARD_LIVE_LOGS_BUFFER_SIZE=10000 # Maximum live log events replayed to one reconnecting dashboard client (default: 1000) # Also bounds the replay buffer above. If a cursor is older than the replay # window, the dashboard resets from REST APIs. -# DASHBOARD_LIVE_LOGS_REPLAY_LIMIT=1000 +# GOMODEL_DASHBOARD_LIVE_LOGS_REPLAY_LIMIT=1000 # Heartbeat interval in seconds for idle live log streams (default: 15) # Keeps proxies and poor connections from closing an otherwise quiet stream. -# DASHBOARD_LIVE_LOGS_HEARTBEAT_SECONDS=15 +# GOMODEL_DASHBOARD_LIVE_LOGS_HEARTBEAT_SECONDS=15 # ============================================================================= # Storage Configuration (used by audit logging, usage tracking, future IAM, etc.) @@ -262,19 +262,19 @@ # Storage type: "sqlite" (default), "postgresql", or "mongodb" # This determines where both audit logs and usage data are stored -# STORAGE_TYPE=sqlite +# GOMODEL_STORAGE_TYPE=sqlite # SQLite Configuration (default, good for single instance) -# SQLITE_PATH=data/gomodel.db +# GOMODEL_SQLITE_PATH=data/gomodel.db # PostgreSQL Configuration (for multi-instance deployments) -# POSTGRES_URL=postgres://user:password@localhost:5432/gomodel -# POSTGRES_MAX_CONNS=10 +# GOMODEL_POSTGRES_URL=postgres://user:password@localhost:5432/gomodel +# GOMODEL_POSTGRES_MAX_CONNS=10 # MongoDB Configuration (recommended for high-volume logging) -# MONGODB_URL=mongodb://localhost:27017/gomodel -# MONGODB_DATABASE overrides the database named in MONGODB_URL (default: gomodel) -# MONGODB_DATABASE=gomodel +# GOMODEL_MONGODB_URL=mongodb://localhost:27017/gomodel +# GOMODEL_MONGODB_DATABASE overrides the database named in GOMODEL_MONGODB_URL (default: gomodel) +# GOMODEL_MONGODB_DATABASE=gomodel # ============================================================================= # Audit Logging Configuration @@ -282,40 +282,40 @@ # Enable/disable audit logging (default: false) # When enabled, all requests and responses are logged to the configured storage -# LOGGING_ENABLED=false +# GOMODEL_LOGGING_ENABLED=false # Log full request/response bodies (default: false) # WARNING: May contain PII, API keys in prompts, or sensitive data -# LOGGING_LOG_BODIES=false +# GOMODEL_LOGGING_LOG_BODIES=false # Log audio endpoint inputs/outputs: /v1/audio/speech text input + binary audio # output (stored as base64 so the dashboard can play it back) and -# /v1/audio/transcriptions upload metadata. Requires LOGGING_LOG_BODIES=true (the +# /v1/audio/transcriptions upload metadata. Requires GOMODEL_LOGGING_LOG_BODIES=true (the # master body-logging switch); when bodies are logged but this is off, audio # responses are recorded as a lightweight placeholder instead of the full bytes. # WARNING: stores full audio in the audit log and grows storage quickly (default: false) -# LOGGING_LOG_AUDIO_BODIES=false +# GOMODEL_LOGGING_LOG_AUDIO_BODIES=false # Log request/response headers (default: false) # Sensitive headers (Authorization, Cookie, etc.) are automatically redacted -# LOGGING_LOG_HEADERS=false +# GOMODEL_LOGGING_LOG_HEADERS=false # Log only model interactions, skip /health, /metrics, /admin endpoints (default: true) -# LOGGING_ONLY_MODEL_INTERACTIONS=true +# GOMODEL_LOGGING_ONLY_MODEL_INTERACTIONS=true # In-memory audit log queue capacity in entries/rows, not bytes (default: 1000) # If the queue is full, new audit log entries are dropped with a warning -# LOGGING_BUFFER_SIZE=1000 +# GOMODEL_LOGGING_BUFFER_SIZE=1000 # How often to flush buffered logs in seconds (default: 5) # Logs are written at least every 5 seconds by default, and earlier when batches fill up -# LOGGING_FLUSH_INTERVAL=5 +# GOMODEL_LOGGING_FLUSH_INTERVAL=5 -# When LOGGING_LOG_BODIES=true, captured request/response bodies are capped at 1MB each +# When GOMODEL_LOGGING_LOG_BODIES=true, captured request/response bodies are capped at 1MB each # Streaming response content is also capped at 1MB of accumulated captured content # Auto-delete logs older than N days, 0 = keep forever (default: 30) -# LOGGING_RETENTION_DAYS=30 +# GOMODEL_LOGGING_RETENTION_DAYS=30 # ============================================================================= # Token Usage Tracking Configuration @@ -323,34 +323,34 @@ # Enable/disable token usage tracking (default: true) # When enabled, token usage is tracked separately from audit logs -# USAGE_ENABLED=true +# GOMODEL_USAGE_ENABLED=true # Enable/disable the admin usage pricing recalculation action (default: true) -# Requires USAGE_ENABLED=true and supported storage; false always hides/blocks it -# USAGE_PRICING_RECALCULATION_ENABLED=true +# Requires GOMODEL_USAGE_ENABLED=true and supported storage; false always hides/blocks it +# GOMODEL_USAGE_PRICING_RECALCULATION_ENABLED=true # Enforce returning usage data in streaming responses (default: true) # When true, stream_options: {"include_usage": true} is automatically added # to streaming requests for OpenAI-compatible providers -# ENFORCE_RETURNING_USAGE_DATA=true +# GOMODEL_ENFORCE_RETURNING_USAGE_DATA=true # Enable/disable guardrails globally (default: false) # When enabled, configured guardrails can run for workflows that reference them -# GUARDRAILS_ENABLED=false +# GOMODEL_GUARDRAILS_ENABLED=false # Guardrails for inline batch processing (default: false) # When true, guardrails are applied to inline /v1/batches request items # (e.g. /v1/chat/completions and /v1/responses items). -# ENABLE_GUARDRAILS_FOR_BATCH_PROCESSING=false +# GOMODEL_ENABLE_GUARDRAILS_FOR_BATCH_PROCESSING=false # In-memory buffer size before flushing to storage (default: 1000) -# USAGE_BUFFER_SIZE=1000 +# GOMODEL_USAGE_BUFFER_SIZE=1000 # How often to flush buffered usage entries in seconds (default: 5) -# USAGE_FLUSH_INTERVAL=5 +# GOMODEL_USAGE_FLUSH_INTERVAL=5 # Auto-delete usage data older than N days, 0 = keep forever (default: 90) -# USAGE_RETENTION_DAYS=90 +# GOMODEL_USAGE_RETENTION_DAYS=90 # ============================================================================= # Rate Limits @@ -358,25 +358,25 @@ # Cap requests, tokens, and in-flight concurrency per user_path subtree. # Breaches return 429 with Retry-After and x-ratelimit-* headers. Counters are # in-memory per gateway instance and reset on restart; token limits (tpm/tph/ -# tpd) additionally require USAGE_ENABLED=true. Enabled by default; with no +# tpd) additionally require GOMODEL_USAGE_ENABLED=true. Enabled by default; with no # configured rules the check is a no-op. -# RATE_LIMITS_ENABLED=true +# GOMODEL_RATE_LIMITS_ENABLED=true -# Declare rules per user path with SET_RATE_LIMIT_ (double underscores -# separate path segments, like SET_BUDGET_*). Names: rpm/tpm (per minute), +# Declare rules per user path with GOMODEL_SET_RATE_LIMIT_ (double underscores +# separate path segments, like GOMODEL_SET_BUDGET_*). Names: rpm/tpm (per minute), # rph/tph (per hour), rpd/tpd (per day), concurrent (max in-flight requests). # A JSON array of {period|period_seconds, max_requests, max_tokens} objects is # also accepted for custom windows. -# SET_RATE_LIMIT_TEAM__ALPHA="rpm=100,tpm=50000,rpd=10000,concurrent=10" -# SET_RATE_LIMIT_="rpm=1000" +# GOMODEL_SET_RATE_LIMIT_TEAM__ALPHA="rpm=100,tpm=50000,rpd=10000,concurrent=10" +# GOMODEL_SET_RATE_LIMIT_="rpm=1000" # Cap one provider across all consumers and models with -# SET_PROVIDER_RATE_LIMIT_ (same rpm/tpm/... syntax; underscores in the +# GOMODEL_SET_PROVIDER_RATE_LIMIT_ (same rpm/tpm/... syntax; underscores in the # suffix become hyphens like provider-instance env vars). Load balancing and # failover skip a saturated provider while another target has capacity. # Per-model rules have no env form; declare them under rate_limits.models in # config.yaml or via the admin API/dashboard. -# SET_PROVIDER_RATE_LIMIT_OPENAI="rpm=500,tpm=200000,concurrent=50" +# GOMODEL_SET_PROVIDER_RATE_LIMIT_OPENAI="rpm=500,tpm=200000,concurrent=50" # ============================================================================= # Provider API Keys (uncomment and set the ones you need) @@ -402,7 +402,7 @@ # Anthropic /v1/messages requires max_tokens. When the caller omits it, GoModel # injects this value. Default: 4096. Raise it for newer models that produce # longer outputs (e.g. Sonnet 4.6 / Opus 4.7). -# ANTHROPIC_DEFAULT_MAX_TOKENS=4096 +# GOMODEL_ANTHROPIC_DEFAULT_MAX_TOKENS=4096 # Google Gemini # GEMINI_API_KEY=... @@ -411,7 +411,7 @@ # Native mode supports inline image data via data: URLs, but GoModel does not # fetch remote image URLs or upload them through Gemini Files API yet. Set this # to false when you need OpenAI-compatible image_url pass-through behavior. -# USE_GOOGLE_GEMINI_NATIVE_API=true +# GOMODEL_USE_GOOGLE_GEMINI_NATIVE_API=true # Gemini base URL. The official defaults are: # - native chat/models: https://generativelanguage.googleapis.com/v1beta # - OpenAI-compatible API: https://generativelanguage.googleapis.com/v1beta/openai @@ -430,28 +430,28 @@ # Model IDs are account-scoped paths, e.g. accounts/fireworks/models/gpt-oss-120b # FIREWORKS_API_KEY=fw_... # FIREWORKS_BASE_URL=https://api.fireworks.ai/inference/v1 -# Optional configured model list; see CONFIGURED_PROVIDER_MODELS_MODE below +# Optional configured model list; see GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE below # FIREWORKS_MODELS=accounts/fireworks/models/gpt-oss-120b,accounts/fireworks/models/deepseek-v3p2 # Meta Model API (Muse Spark, default base URL: https://api.meta.ai/v1) # META_API_KEY=... # META_BASE_URL=https://api.meta.ai/v1 -# Optional configured model list; see CONFIGURED_PROVIDER_MODELS_MODE below +# Optional configured model list; see GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE below # META_MODELS=muse-spark-1.1 # OpenRouter (default base URL: https://openrouter.ai/api/v1) # OPENROUTER_API_KEY=sk-or-... # OPENROUTER_BASE_URL=https://openrouter.ai/api/v1 -# Optional configured model list; see CONFIGURED_PROVIDER_MODELS_MODE below +# Optional configured model list; see GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE below # OPENROUTER_MODELS=openai/gpt-oss-120b,anthropic/claude-sonnet-4 -# OPENROUTER_SITE_URL=https://gomodel.enterpilot.io -# OPENROUTER_APP_NAME=GoModel +# GOMODEL_OPENROUTER_SITE_URL=https://gomodel.enterpilot.io +# GOMODEL_OPENROUTER_APP_NAME=GoModel # Kilo AI Gateway (default base URL: https://api.kilo.ai/api/gateway) # Model IDs use provider/model and pass through unchanged. # KILO_API_KEY=... # KILO_BASE_URL=https://api.kilo.ai/api/gateway -# Optional configured model list; see CONFIGURED_PROVIDER_MODELS_MODE below +# Optional configured model list; see GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE below # KILO_MODELS=anthropic/claude-sonnet-4.5,openai/gpt-5.5 # Z.ai (default base URL: https://api.z.ai/api/paas/v4) @@ -477,7 +477,7 @@ # Models that must be routed to the Anthropic-native /messages endpoint instead # of /chat/completions (default: qwen3.7-max). Temporary manual split until # upstream exposes per-model endpoint metadata. -# OPENCODE_GO_MESSAGES_MODELS=qwen3.7-max +# GOMODEL_OPENCODE_GO_MESSAGES_MODELS=qwen3.7-max # Azure OpenAI # AZURE_API_KEY=... diff --git a/CLAUDE.md b/CLAUDE.md index ef8a215b8..2821446eb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -105,9 +105,10 @@ If this repository is not the official GoModel repository, ask the user whether Full reference: `.env.template` and `config/config.yaml` **Env var naming:** GoModel-defined variables are canonically spelled -`GOMODEL_` (`GOMODEL_SQLITE_PATH`, `GOMODEL_LOGGING_ENABLED`, ...). The -unprefixed spellings listed below still resolve but are deprecated and warn once -each at startup; when both are set, the `GOMODEL_` one wins. Two groups keep +`GOMODEL_` (`GOMODEL_SQLITE_PATH`, `GOMODEL_LOGGING_ENABLED`, ...), as +listed below. The unprefixed spellings (`SQLITE_PATH`, ...) still resolve but +are deprecated and warn once each at startup; when both are set, the `GOMODEL_` +one wins. Two groups keep their bare names permanently: `PORT` and `REDIS_URL` (injected by PaaS platforms), and the provider family (`OPENAI_API_KEY`, `_BASE_URL`, `_MODELS`, ...), which lives in each vendor's namespace and is what @@ -120,30 +121,30 @@ fall in one of those two groups. Details and the full mapping: - **Server:** - `PORT` (8080) - `GOMODEL_MASTER_KEY` (empty = unsafe mode) - - `BODY_SIZE_LIMIT` ("10M") - - `USER_PATH_HEADER` (`X-GoModel-User-Path`: Header used to read/write request `user_path` values) - - `ENABLE_PASSTHROUGH_ROUTES` (true: Enable provider-native passthrough routes under /p/{provider}/...) - - `ALLOW_PASSTHROUGH_V1_ALIAS` (true: Allow /p/{provider}/v1/... aliases while keeping /p/{provider}/... canonical) - - `ENABLED_PASSTHROUGH_PROVIDERS` (openai,anthropic,openrouter,zai,vllm: Comma-separated list of enabled passthrough providers) - - `REALTIME_ENABLED` (true: Expose the realtime speech-to-speech websocket at `/v1/realtime` and the `/p/{provider}/v1/realtime` upgrade. The canonical `/v1/realtime` route needs only `REALTIME_ENABLED`; the `/p/{provider}/v1/realtime` upgrade additionally requires passthrough routes enabled (`ENABLE_PASSTHROUGH_ROUTES`) with the provider listed in `ENABLED_PASSTHROUGH_PROVIDERS`. The gateway is a transparent websocket reverse proxy — it injects provider credentials and relays the provider's realtime event schema verbatim (no translation), so clients connect without provider API keys. Only providers implementing realtime accept sessions. Currently: OpenAI and xAI/Grok Voice Agent (both `wss://…/v1/realtime`); Z.ai/Zhipu GLM-Realtime (`wss://…/api/paas/v4/realtime`); Bailian/Qwen-Omni (`wss://dashscope…/api-ws/v1/realtime`); and Azure OpenAI (`wss:///openai/realtime?api-version=…&deployment=…`, `api-key` header). All use OpenAI's realtime event schema (Z.ai adds extensions that relay transparently). Provider-specific notes: xAI voice models (e.g. `grok-voice-latest`) aren't in upstream `/models` discovery, so configure them via `XAI_MODELS`, and xAI bills realtime per-minute (no token usage reported); Azure realtime requires a realtime-capable `AZURE_API_VERSION` (the default may be too old) and the model selects the Azure deployment. (MiniMax was evaluated but skipped — its conversational realtime schema is not OpenAI-compatible.) Sessions are gated by the same model-access and budget rules as other model endpoints; usage is tracked per `response.done` event, accepting both the OpenAI singular and Alibaba plural token-detail spellings. The same flag also exposes the OpenAI-compatible WebRTC surface (via the optional `core.RealtimeCallProvider` interface — OpenAI and xAI at the shared `…/v1/realtime/{calls,client_secrets}` shape, and Azure OpenAI at its GA `/openai/v1/realtime/{calls,client_secrets}` surface with `api-key` auth and no api-version; xAI gates WebRTC calls per team, so unauthorized accounts get the upstream 403 relayed while client_secrets works. Bailian is deliberately not wired: its WebRTC is allowlist-only with a per-customer endpoint provided by sales, plus no call id in the answer; Z.ai has no WebRTC realtime): `POST /v1/realtime/calls` exchanges SDP (raw `application/sdp` offer with `?model=`, or multipart `sdp` + `session` JSON fields; the session/query model is rewritten to the resolved provider model so aliases and virtual models work) and relays the answer with a gateway-relative `Location: /v1/realtime/calls/{call_id}` header; `POST /v1/realtime/client_secrets` mints ephemeral browser credentials routed by `session.model` (falling back to the nested transcription model); and `GET /v1/realtime?call_id=…` attaches to an existing call as a sideband websocket (an in-memory per-instance call registry recalls the route for calls created through the same instance — 6h TTL, capped; otherwise pass explicit `model`+`provider` params). WebRTC media and events flow directly between client and provider, so after creating a call the gateway attaches its own best-effort sideband observer websocket to record usage per `response.done` (entries carry endpoint `/v1/realtime/calls`; skipped when usage tracking is off, and gateway-relayed sideband attaches for registry-known calls don't tap usage to avoid double counting). WebRTC signaling counts toward request-scoped rate limits, but concurrent-scope rules can't span a WebRTC call's lifetime since only signaling transits the gateway; ephemeral client secrets authenticate clients directly against the provider, so those sessions bypass the gateway entirely and are untracked.) -- **Storage:** `STORAGE_TYPE` (sqlite), `SQLITE_PATH` (data/gomodel.db), `POSTGRES_URL`, `MONGODB_URL`. `/v1/responses` snapshots and `/v1/conversations` history persist to the configured backend (30-day TTL, hourly sweep); the in-memory fallback stores are byte-capped and used only by embedded setups that skip app wiring. -- **Models:** `MODELS_ENABLED_BY_DEFAULT` (true), `KEEP_ONLY_ALIASES_AT_MODELS_ENDPOINT` (false), `CONFIGURED_PROVIDER_MODELS_MODE` (`fallback` or `allowlist`, default `fallback`; `allowlist` skips upstream `/models` for providers with configured lists); persisted overrides restrict/allow selectors with `user_paths`. When alias-only models listing is enabled, `GET /v1/models` returns only model aliases, not full concrete model specs, to operators. -- **Virtual models:** Redirects (aliases / load balancers) and access policies are managed in the admin dashboard and persisted to the `virtual_models` store. A redirect with one target is a plain alias; a redirect with several targets is load balanced by `strategy`: `round_robin` (default; rotates across targets, honoring per-target `weight`) or `cost` (always routes to the cheapest catalog-priced available target, falling back to the first target when none are priced). Unavailable targets are skipped, so a redirect works while any target is live. Virtual models can also be declared as infrastructure-as-code under `virtual_models:` in `config.yaml` or via the `VIRTUAL_MODELS` env var (a JSON array; env merges over YAML, winning per `source`). Declarative entries are validated at startup, override admin-store rows with the same `source`, and are read-only in the dashboard. Startup validation is catalog-independent: structure plus explicit target `provider` names — a name matching no configured provider (a typo) aborts startup listing the registered providers; a name declared under `providers:` but unregistered (e.g. credentials unset in this environment) only warns and the target stays unavailable; target *model* availability is never a startup gate (checked at resolve time, since the catalog loads asynchronously). -- **MCP gateway:** `MCP_ENABLED` (true: expose the MCP-protocol endpoints; a no-op until servers are declared). GoModel aggregates upstream MCP (Model Context Protocol) servers behind the authenticated streamable-HTTP endpoint `/mcp` (POST JSON-RPC, GET notification stream, DELETE session end) and per-server endpoints `/mcp/{server}`. On `/mcp`, tools and prompts are namespaced `{server}_{name}` with deterministic ordering; `tools/call` accepts the namespaced name (longest server-prefix match) or a unique bare name; `/mcp/{server}` exposes original names. Tools, prompts, resources, and resource templates relay with raw schemas/results verbatim; upstream `instructions` are merged into the gateway's `initialize` result. Servers come from three sources with the usual precedence: `mcp.servers:` map in `config.yaml`, the `MCP_SERVERS` env var (JSON object merged over YAML per name), and the `mcp_servers` admin store (dashboard MCP Servers page / `/admin/mcp-servers` GET/PUT/DELETE + `POST .../{name}/reconnect` + `GET .../{name}/catalog` for the per-server tools/prompts/resources inspector); declarative entries are validated at startup, shadow same-name store rows, and are read-only in the dashboard (secret header values are redacted as `***` in admin reads, and a `***` value on PUT preserves the stored secret). Per-server fields: `url` + `transport` (`http` streamable default, `sse` legacy), or declarative-only `stdio` (`command`/`args`/`env` — rejected via admin API/dashboard because runtime-registered subprocesses would be an RCE vector), `headers` (upstream credentials, `${ENV}` supported; the gateway is a credential boundary — client bearer tokens are never forwarded upstream), `allowed_tools`/`disallowed_tools`, `user_paths` (visibility subtree scoping like virtual models — filtered out of `tools/list`, not just blocked at call time), `tool_timeout` (30s default). The `X-MCP-Servers` request header narrows a session to a comma-separated server subset. One upstream session is shared per server (lazy dial, redial-once on death); a failed listing marks the server `degraded` keeping its last catalog (stale carry-forward, 60s re-probe, 5m re-list, `list_changed` notifications trigger resync). Downstream sessions are SDK-managed (`Mcp-Session-Id`, 30m idle timeout), bound to the initializing user path (a different principal presenting the session ID gets 404), and each session sees a visibility-filtered tool snapshot taken at initialize. Every MCP POST is gated by user-path rate limits and budgets; every `tools/call` writes a usage entry (`provider="mcp"`, `provider_name`=server, `model`=namespaced tool, duration/sizes/error in raw data, labels/user_path as usual) and MCP paths are audit-logged model interactions whose entries are labelled with the JSON-RPC method (tool/prompt name for calls) and `provider="mcp"`, so request-log and live-log rows are self-describing; with `LOGGING_LOG_BODIES` the JSON-RPC request and response frames (SSE replies decoded) are captured on POST entries too. Server→client MCP features (sampling, elicitation, roots) and resource subscriptions are not negotiated in v1. Spec: `docs/dev/2026-07-07_mcp-gateway-spec.md`. -- **Tagging:** Every request can be labelled from configured HTTP headers. Rules are managed in the dashboard (Settings → "Tagging based on headers", persisted to the `tagging_settings` store) or declared as infrastructure-as-code under `tagging.headers:` in `config.yaml` / numbered env vars `TAGGING_HEADER_1=X-My-Tags` with optional `TAGGING_HEADER_1_PREFIX` (trimmed from each extracted label only), `TAGGING_HEADER_1_DONOTPASS` (default false: headers are forwarded as-is; true strips the header before provider forwarding on passthrough/realtime routes — translated routes never forward client headers), and `TAGGING_HEADER_1_DELIMITER` (default `,`; one header value can carry several labels). An env entry replaces the whole YAML entry with the same header name (unset companion vars reset fields to defaults rather than inheriting YAML values); declarative entries override admin-store rows and are read-only in the dashboard. Credential-bearing headers (`Authorization`, `Cookie`, API-key headers, …) are rejected as tagging sources. Managed API keys can also carry labels (`labels` on `POST /admin/auth-keys`, replaceable later via `PUT /admin/auth-keys/{id}/labels` where `[]` clears, or API Keys → Create API Key / Edit Labels in the dashboard); every request authenticated with the key gets them, merged and de-duplicated with header-extracted labels. Labels are recorded on usage entries (`labels`) and audit log entries (`data.labels`). The dashboard usage page shows a by-label breakdown (`GET /admin/usage/labels`) and label chips with a label filter on the request log (`label` query param on `GET /admin/usage/log`). -- **Audit logging:** `LOGGING_ENABLED` (false), `LOGGING_LOG_BODIES` (false), `LOGGING_LOG_AUDIO_BODIES` (false: refines `LOGGING_LOG_BODIES` for audio endpoints — base64 audio for both `/v1/audio/speech` output and `/v1/audio/transcriptions` upload (≤8 MB each, else `too_large`) + dashboard playback, plus transcription upload metadata; no effect unless `LOGGING_LOG_BODIES` is on, in which case audio-off records a placeholder), `LOGGING_LOG_HEADERS` (false), `LOGGING_RETENTION_DAYS` (30) -- **Usage tracking:** `USAGE_ENABLED` (true), `ENFORCE_RETURNING_USAGE_DATA` (true), `USAGE_RETENTION_DAYS` (90). Callers can read their own status without admin access via `GET /v1/usage`: usage summary over a date window (`start_date`/`end_date`/`days`, default last 30 days UTC) plus budget and rate-limit statuses, all scoped to the caller's effective user path (managed key binding, else the user-path header). -- **Rate limits:** `RATE_LIMITS_ENABLED` (true; no-op until rules exist). Every rule has a scope: `user_path` (consumer control; subtree with ONE shared counter per rule — per-key limits = give each key its own path), `provider` (caps one configured provider instance across all consumers/models), or `model` (subject `openai/gpt-4o` pins one provider's model, bare `gpt-4o` covers it on any provider; matching case-insensitive). Limits: `max_requests`/`max_tokens` per period (`minute`/`hour`/`day`/custom `period_seconds`, sliding window) plus `concurrent` (period_seconds 0: `max_requests` = max in-flight; realtime sessions hold a slot for the session, batch submissions don't — and batch skips provider/model rules since batch files can mix models). Enforcement covers every model endpoint; user-path breaches return 429 (`code: rate_limit_exceeded`) with `Retry-After`, successes carry `x-ratelimit-{limit,remaining,reset}-{requests,tokens}` from the most-constrained matching rule; cache hits bypass. Saturated providers/models are instead routed around: virtual-model load balancing prefers targets with capacity (falling back to the first declared target when all are saturated, so the client gets an honest 429 rather than an unavailable-model error; saturation never affects catalog membership or /v1/models listing), a saturated primary route with configured failover rules skips the primary provider and is served by the sweep (which also skips saturated candidates), and only requests with no viable alternative get 429. Token windows are charged to the provider/model that actually executed (from the usage entry), so accounting stays correct under aliasing/failover. Managed in the dashboard (Rate Limits page: scope selector) / `/admin/rate-limits` (GET/PUT/DELETE + `POST .../reset-one`, `POST .../reset`; requests take `scope`+`subject`, with `user_path` as shorthand for user-path rules), or as infrastructure-as-code under `rate_limits.{user_paths,providers,models}:` in `config.yaml` / `SET_RATE_LIMIT_` env vars (`rpm/tpm/rph/tph/rpd/tpd/concurrent=N` compact syntax or a JSON rule array; `__` separates path segments) and `SET_PROVIDER_RATE_LIMIT_` (same syntax; suffix underscores become hyphens; model rules are YAML/admin-only). Env replaces the whole YAML entry for the same subject; config-sourced rules are read-only in the dashboard and manual edits win over config seeds, like budgets. Token limits are post-accounted from usage entries, so they require `USAGE_ENABLED=true` (startup warns otherwise) and one request can overshoot a token window. Counters are in-memory per instance (N replicas ≈ N× limit) and reset on restart — budgets remain the durable cross-instance control. + - `GOMODEL_BODY_SIZE_LIMIT` ("10M") + - `GOMODEL_USER_PATH_HEADER` (`X-GoModel-User-Path`: Header used to read/write request `user_path` values) + - `GOMODEL_ENABLE_PASSTHROUGH_ROUTES` (true: Enable provider-native passthrough routes under /p/{provider}/...) + - `GOMODEL_ALLOW_PASSTHROUGH_V1_ALIAS` (true: Allow /p/{provider}/v1/... aliases while keeping /p/{provider}/... canonical) + - `GOMODEL_ENABLED_PASSTHROUGH_PROVIDERS` (openai,anthropic,openrouter,zai,vllm: Comma-separated list of enabled passthrough providers) + - `GOMODEL_REALTIME_ENABLED` (true: Expose the realtime speech-to-speech websocket at `/v1/realtime` and the `/p/{provider}/v1/realtime` upgrade. The canonical `/v1/realtime` route needs only `GOMODEL_REALTIME_ENABLED`; the `/p/{provider}/v1/realtime` upgrade additionally requires passthrough routes enabled (`GOMODEL_ENABLE_PASSTHROUGH_ROUTES`) with the provider listed in `GOMODEL_ENABLED_PASSTHROUGH_PROVIDERS`. The gateway is a transparent websocket reverse proxy — it injects provider credentials and relays the provider's realtime event schema verbatim (no translation), so clients connect without provider API keys. Only providers implementing realtime accept sessions. Currently: OpenAI and xAI/Grok Voice Agent (both `wss://…/v1/realtime`); Z.ai/Zhipu GLM-Realtime (`wss://…/api/paas/v4/realtime`); Bailian/Qwen-Omni (`wss://dashscope…/api-ws/v1/realtime`); and Azure OpenAI (`wss:///openai/realtime?api-version=…&deployment=…`, `api-key` header). All use OpenAI's realtime event schema (Z.ai adds extensions that relay transparently). Provider-specific notes: xAI voice models (e.g. `grok-voice-latest`) aren't in upstream `/models` discovery, so configure them via `XAI_MODELS`, and xAI bills realtime per-minute (no token usage reported); Azure realtime requires a realtime-capable `AZURE_API_VERSION` (the default may be too old) and the model selects the Azure deployment. (MiniMax was evaluated but skipped — its conversational realtime schema is not OpenAI-compatible.) Sessions are gated by the same model-access and budget rules as other model endpoints; usage is tracked per `response.done` event, accepting both the OpenAI singular and Alibaba plural token-detail spellings. The same flag also exposes the OpenAI-compatible WebRTC surface (via the optional `core.RealtimeCallProvider` interface — OpenAI and xAI at the shared `…/v1/realtime/{calls,client_secrets}` shape, and Azure OpenAI at its GA `/openai/v1/realtime/{calls,client_secrets}` surface with `api-key` auth and no api-version; xAI gates WebRTC calls per team, so unauthorized accounts get the upstream 403 relayed while client_secrets works. Bailian is deliberately not wired: its WebRTC is allowlist-only with a per-customer endpoint provided by sales, plus no call id in the answer; Z.ai has no WebRTC realtime): `POST /v1/realtime/calls` exchanges SDP (raw `application/sdp` offer with `?model=`, or multipart `sdp` + `session` JSON fields; the session/query model is rewritten to the resolved provider model so aliases and virtual models work) and relays the answer with a gateway-relative `Location: /v1/realtime/calls/{call_id}` header; `POST /v1/realtime/client_secrets` mints ephemeral browser credentials routed by `session.model` (falling back to the nested transcription model); and `GET /v1/realtime?call_id=…` attaches to an existing call as a sideband websocket (an in-memory per-instance call registry recalls the route for calls created through the same instance — 6h TTL, capped; otherwise pass explicit `model`+`provider` params). WebRTC media and events flow directly between client and provider, so after creating a call the gateway attaches its own best-effort sideband observer websocket to record usage per `response.done` (entries carry endpoint `/v1/realtime/calls`; skipped when usage tracking is off, and gateway-relayed sideband attaches for registry-known calls don't tap usage to avoid double counting). WebRTC signaling counts toward request-scoped rate limits, but concurrent-scope rules can't span a WebRTC call's lifetime since only signaling transits the gateway; ephemeral client secrets authenticate clients directly against the provider, so those sessions bypass the gateway entirely and are untracked.) +- **Storage:** `GOMODEL_STORAGE_TYPE` (sqlite), `GOMODEL_SQLITE_PATH` (data/gomodel.db), `GOMODEL_POSTGRES_URL`, `GOMODEL_MONGODB_URL`. `/v1/responses` snapshots and `/v1/conversations` history persist to the configured backend (30-day TTL, hourly sweep); the in-memory fallback stores are byte-capped and used only by embedded setups that skip app wiring. +- **Models:** `GOMODEL_MODELS_ENABLED_BY_DEFAULT` (true), `GOMODEL_KEEP_ONLY_ALIASES_AT_MODELS_ENDPOINT` (false), `GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE` (`fallback` or `allowlist`, default `fallback`; `allowlist` skips upstream `/models` for providers with configured lists); persisted overrides restrict/allow selectors with `user_paths`. When alias-only models listing is enabled, `GET /v1/models` returns only model aliases, not full concrete model specs, to operators. +- **Virtual models:** Redirects (aliases / load balancers) and access policies are managed in the admin dashboard and persisted to the `virtual_models` store. A redirect with one target is a plain alias; a redirect with several targets is load balanced by `strategy`: `round_robin` (default; rotates across targets, honoring per-target `weight`) or `cost` (always routes to the cheapest catalog-priced available target, falling back to the first target when none are priced). Unavailable targets are skipped, so a redirect works while any target is live. Virtual models can also be declared as infrastructure-as-code under `virtual_models:` in `config.yaml` or via the `GOMODEL_VIRTUAL_MODELS` env var (a JSON array; env merges over YAML, winning per `source`). Declarative entries are validated at startup, override admin-store rows with the same `source`, and are read-only in the dashboard. Startup validation is catalog-independent: structure plus explicit target `provider` names — a name matching no configured provider (a typo) aborts startup listing the registered providers; a name declared under `providers:` but unregistered (e.g. credentials unset in this environment) only warns and the target stays unavailable; target *model* availability is never a startup gate (checked at resolve time, since the catalog loads asynchronously). +- **MCP gateway:** `GOMODEL_MCP_ENABLED` (true: expose the MCP-protocol endpoints; a no-op until servers are declared). GoModel aggregates upstream MCP (Model Context Protocol) servers behind the authenticated streamable-HTTP endpoint `/mcp` (POST JSON-RPC, GET notification stream, DELETE session end) and per-server endpoints `/mcp/{server}`. On `/mcp`, tools and prompts are namespaced `{server}_{name}` with deterministic ordering; `tools/call` accepts the namespaced name (longest server-prefix match) or a unique bare name; `/mcp/{server}` exposes original names. Tools, prompts, resources, and resource templates relay with raw schemas/results verbatim; upstream `instructions` are merged into the gateway's `initialize` result. Servers come from three sources with the usual precedence: `mcp.servers:` map in `config.yaml`, the `GOMODEL_MCP_SERVERS` env var (JSON object merged over YAML per name), and the `mcp_servers` admin store (dashboard MCP Servers page / `/admin/mcp-servers` GET/PUT/DELETE + `POST .../{name}/reconnect` + `GET .../{name}/catalog` for the per-server tools/prompts/resources inspector); declarative entries are validated at startup, shadow same-name store rows, and are read-only in the dashboard (secret header values are redacted as `***` in admin reads, and a `***` value on PUT preserves the stored secret). Per-server fields: `url` + `transport` (`http` streamable default, `sse` legacy), or declarative-only `stdio` (`command`/`args`/`env` — rejected via admin API/dashboard because runtime-registered subprocesses would be an RCE vector), `headers` (upstream credentials, `${ENV}` supported; the gateway is a credential boundary — client bearer tokens are never forwarded upstream), `allowed_tools`/`disallowed_tools`, `user_paths` (visibility subtree scoping like virtual models — filtered out of `tools/list`, not just blocked at call time), `tool_timeout` (30s default). The `X-MCP-Servers` request header narrows a session to a comma-separated server subset. One upstream session is shared per server (lazy dial, redial-once on death); a failed listing marks the server `degraded` keeping its last catalog (stale carry-forward, 60s re-probe, 5m re-list, `list_changed` notifications trigger resync). Downstream sessions are SDK-managed (`Mcp-Session-Id`, 30m idle timeout), bound to the initializing user path (a different principal presenting the session ID gets 404), and each session sees a visibility-filtered tool snapshot taken at initialize. Every MCP POST is gated by user-path rate limits and budgets; every `tools/call` writes a usage entry (`provider="mcp"`, `provider_name`=server, `model`=namespaced tool, duration/sizes/error in raw data, labels/user_path as usual) and MCP paths are audit-logged model interactions whose entries are labelled with the JSON-RPC method (tool/prompt name for calls) and `provider="mcp"`, so request-log and live-log rows are self-describing; with `GOMODEL_LOGGING_LOG_BODIES` the JSON-RPC request and response frames (SSE replies decoded) are captured on POST entries too. Server→client MCP features (sampling, elicitation, roots) and resource subscriptions are not negotiated in v1. Spec: `docs/dev/2026-07-07_mcp-gateway-spec.md`. +- **Tagging:** Every request can be labelled from configured HTTP headers. Rules are managed in the dashboard (Settings → "Tagging based on headers", persisted to the `tagging_settings` store) or declared as infrastructure-as-code under `tagging.headers:` in `config.yaml` / numbered env vars `GOMODEL_TAGGING_HEADER_1=X-My-Tags` with optional `GOMODEL_TAGGING_HEADER_1_PREFIX` (trimmed from each extracted label only), `GOMODEL_TAGGING_HEADER_1_DONOTPASS` (default false: headers are forwarded as-is; true strips the header before provider forwarding on passthrough/realtime routes — translated routes never forward client headers), and `GOMODEL_TAGGING_HEADER_1_DELIMITER` (default `,`; one header value can carry several labels). An env entry replaces the whole YAML entry with the same header name (unset companion vars reset fields to defaults rather than inheriting YAML values); declarative entries override admin-store rows and are read-only in the dashboard. Credential-bearing headers (`Authorization`, `Cookie`, API-key headers, …) are rejected as tagging sources. Managed API keys can also carry labels (`labels` on `POST /admin/auth-keys`, replaceable later via `PUT /admin/auth-keys/{id}/labels` where `[]` clears, or API Keys → Create API Key / Edit Labels in the dashboard); every request authenticated with the key gets them, merged and de-duplicated with header-extracted labels. Labels are recorded on usage entries (`labels`) and audit log entries (`data.labels`). The dashboard usage page shows a by-label breakdown (`GET /admin/usage/labels`) and label chips with a label filter on the request log (`label` query param on `GET /admin/usage/log`). +- **Audit logging:** `GOMODEL_LOGGING_ENABLED` (false), `GOMODEL_LOGGING_LOG_BODIES` (false), `GOMODEL_LOGGING_LOG_AUDIO_BODIES` (false: refines `GOMODEL_LOGGING_LOG_BODIES` for audio endpoints — base64 audio for both `/v1/audio/speech` output and `/v1/audio/transcriptions` upload (≤8 MB each, else `too_large`) + dashboard playback, plus transcription upload metadata; no effect unless `GOMODEL_LOGGING_LOG_BODIES` is on, in which case audio-off records a placeholder), `GOMODEL_LOGGING_LOG_HEADERS` (false), `GOMODEL_LOGGING_RETENTION_DAYS` (30) +- **Usage tracking:** `GOMODEL_USAGE_ENABLED` (true), `GOMODEL_ENFORCE_RETURNING_USAGE_DATA` (true), `GOMODEL_USAGE_RETENTION_DAYS` (90). Callers can read their own status without admin access via `GET /v1/usage`: usage summary over a date window (`start_date`/`end_date`/`days`, default last 30 days UTC) plus budget and rate-limit statuses, all scoped to the caller's effective user path (managed key binding, else the user-path header). +- **Rate limits:** `GOMODEL_RATE_LIMITS_ENABLED` (true; no-op until rules exist). Every rule has a scope: `user_path` (consumer control; subtree with ONE shared counter per rule — per-key limits = give each key its own path), `provider` (caps one configured provider instance across all consumers/models), or `model` (subject `openai/gpt-4o` pins one provider's model, bare `gpt-4o` covers it on any provider; matching case-insensitive). Limits: `max_requests`/`max_tokens` per period (`minute`/`hour`/`day`/custom `period_seconds`, sliding window) plus `concurrent` (period_seconds 0: `max_requests` = max in-flight; realtime sessions hold a slot for the session, batch submissions don't — and batch skips provider/model rules since batch files can mix models). Enforcement covers every model endpoint; user-path breaches return 429 (`code: rate_limit_exceeded`) with `Retry-After`, successes carry `x-ratelimit-{limit,remaining,reset}-{requests,tokens}` from the most-constrained matching rule; cache hits bypass. Saturated providers/models are instead routed around: virtual-model load balancing prefers targets with capacity (falling back to the first declared target when all are saturated, so the client gets an honest 429 rather than an unavailable-model error; saturation never affects catalog membership or /v1/models listing), a saturated primary route with configured failover rules skips the primary provider and is served by the sweep (which also skips saturated candidates), and only requests with no viable alternative get 429. Token windows are charged to the provider/model that actually executed (from the usage entry), so accounting stays correct under aliasing/failover. Managed in the dashboard (Rate Limits page: scope selector) / `/admin/rate-limits` (GET/PUT/DELETE + `POST .../reset-one`, `POST .../reset`; requests take `scope`+`subject`, with `user_path` as shorthand for user-path rules), or as infrastructure-as-code under `rate_limits.{user_paths,providers,models}:` in `config.yaml` / `GOMODEL_SET_RATE_LIMIT_` env vars (`rpm/tpm/rph/tph/rpd/tpd/concurrent=N` compact syntax or a JSON rule array; `__` separates path segments) and `GOMODEL_SET_PROVIDER_RATE_LIMIT_` (same syntax; suffix underscores become hyphens; model rules are YAML/admin-only). Env replaces the whole YAML entry for the same subject; config-sourced rules are read-only in the dashboard and manual edits win over config seeds, like budgets. Token limits are post-accounted from usage entries, so they require `GOMODEL_USAGE_ENABLED=true` (startup warns otherwise) and one request can overshoot a token window. Counters are in-memory per instance (N replicas ≈ N× limit) and reset on restart — budgets remain the durable cross-instance control. - **Dashboard live logs:** - - `DASHBOARD_LIVE_LOGS_ENABLED` (true): keep enabled for low-latency dashboard previews; set false only when live streams are not needed or memory/socket usage must be minimized. With `LOGGING_LOG_BODIES` also enabled, in-flight streamed responses render chunk-by-chunk in the request log and Interactions drawer (throttled `audit.stream` events, published only while a dashboard is connected; partial bodies are never buffered server-side). - - `DASHBOARD_LIVE_LOGS_BUFFER_SIZE` (10000): effective size is capped at `DASHBOARD_LIVE_LOGS_REPLAY_LIMIT + 1` (older events can never be replayed); lower it below the replay limit only to shrink memory at the cost of more replay resets. Buffered events are compact previews — request/response bodies are never retained in the buffer (connected dashboards get them live; history hydrates from persisted audit entries). - - `DASHBOARD_LIVE_LOGS_REPLAY_LIMIT` (1000): increase when clients commonly reconnect after long gaps (30+ seconds at high traffic); decrease to reduce replay latency and memory. Also bounds the live log buffer. - - `DASHBOARD_LIVE_LOGS_HEARTBEAT_SECONDS` (15): decrease to 5-10s when proxies need frequent liveness checks; increase to reduce idle network chatter. -- **Cache:** `CACHE_REFRESH_INTERVAL` (3600s: full model re-discovery; also drives dashboard provider health "Last checked"), `PROVIDER_RECHECK_INTERVAL` (60s: fast re-probe of only the providers whose last refresh failed, 0 disables), `REDIS_URL`, `REDIS_KEY_MODELS`, `REDIS_TTL_MODELS`. A provider whose refresh fails keeps its previous inventory marked stale: direct requests still route to it (honest 502/503), virtual-model load balancing skips it (`ModelAvailable`), and the dashboard shows Degraded. Exact response cache uses `cache.response.simple` in `config.yaml` (optional `enabled`); `REDIS_KEY_RESPONSES`, `REDIS_TTL_RESPONSES`, and `REDIS_URL` apply only when that block exists or when `RESPONSE_CACHE_SIMPLE_ENABLED=true`. Semantic response cache uses `cache.response.semantic` (optional `enabled`); when enabled, `embedder.provider` must name a key in the top-level `providers` map (no default embedder). At runtime that key is resolved against the same env-merged, credential-filtered provider set as routing (not YAML-only), so env-only credentials apply. `vector_store.type` must be set explicitly to one of `qdrant`, `pgvector`, `pinecone`, `weaviate` (each has its own nested config and `SEMANTIC_CACHE_*` env vars). Tuning via `SEMANTIC_CACHE_*` applies when the semantic block exists or `SEMANTIC_CACHE_ENABLED=true`. -- **HTTP client:** `HTTP_TIMEOUT` (600s), `HTTP_RESPONSE_HEADER_TIMEOUT` (600s); also settable via the `http:` block in `config.yaml` (env vars win) + - `GOMODEL_DASHBOARD_LIVE_LOGS_ENABLED` (true): keep enabled for low-latency dashboard previews; set false only when live streams are not needed or memory/socket usage must be minimized. With `GOMODEL_LOGGING_LOG_BODIES` also enabled, in-flight streamed responses render chunk-by-chunk in the request log and Interactions drawer (throttled `audit.stream` events, published only while a dashboard is connected; partial bodies are never buffered server-side). + - `GOMODEL_DASHBOARD_LIVE_LOGS_BUFFER_SIZE` (10000): effective size is capped at `GOMODEL_DASHBOARD_LIVE_LOGS_REPLAY_LIMIT + 1` (older events can never be replayed); lower it below the replay limit only to shrink memory at the cost of more replay resets. Buffered events are compact previews — request/response bodies are never retained in the buffer (connected dashboards get them live; history hydrates from persisted audit entries). + - `GOMODEL_DASHBOARD_LIVE_LOGS_REPLAY_LIMIT` (1000): increase when clients commonly reconnect after long gaps (30+ seconds at high traffic); decrease to reduce replay latency and memory. Also bounds the live log buffer. + - `GOMODEL_DASHBOARD_LIVE_LOGS_HEARTBEAT_SECONDS` (15): decrease to 5-10s when proxies need frequent liveness checks; increase to reduce idle network chatter. +- **Cache:** `GOMODEL_CACHE_REFRESH_INTERVAL` (3600s: full model re-discovery; also drives dashboard provider health "Last checked"), `GOMODEL_PROVIDER_RECHECK_INTERVAL` (60s: fast re-probe of only the providers whose last refresh failed, 0 disables), `REDIS_URL`, `GOMODEL_REDIS_KEY_MODELS`, `GOMODEL_REDIS_TTL_MODELS`. A provider whose refresh fails keeps its previous inventory marked stale: direct requests still route to it (honest 502/503), virtual-model load balancing skips it (`ModelAvailable`), and the dashboard shows Degraded. Exact response cache uses `cache.response.simple` in `config.yaml` (optional `enabled`); `GOMODEL_REDIS_KEY_RESPONSES`, `GOMODEL_REDIS_TTL_RESPONSES`, and `REDIS_URL` apply only when that block exists or when `GOMODEL_RESPONSE_CACHE_SIMPLE_ENABLED=true`. Semantic response cache uses `cache.response.semantic` (optional `enabled`); when enabled, `embedder.provider` must name a key in the top-level `providers` map (no default embedder). At runtime that key is resolved against the same env-merged, credential-filtered provider set as routing (not YAML-only), so env-only credentials apply. `vector_store.type` must be set explicitly to one of `qdrant`, `pgvector`, `pinecone`, `weaviate` (each has its own nested config and `SEMANTIC_CACHE_*` env vars). Tuning via `SEMANTIC_CACHE_*` applies when the semantic block exists or `GOMODEL_SEMANTIC_CACHE_ENABLED=true`. +- **HTTP client:** `GOMODEL_HTTP_TIMEOUT` (600s), `GOMODEL_HTTP_RESPONSE_HEADER_TIMEOUT` (600s); also settable via the `http:` block in `config.yaml` (env vars win) - **Resilience:** Configured via `config/config.yaml` - global `resilience.retry.*` and `resilience.circuit_breaker.*` defaults with optional per-provider overrides under `providers..resilience.retry.*` and `providers..resilience.circuit_breaker.*`. Retry defaults: `max_retries` (3), `initial_backoff` (1s), `max_backoff` (30s), `backoff_factor` (2.0), `jitter_factor` (0.1). Circuit breaker defaults: `failure_threshold` (5), `success_threshold` (2), `timeout` (30s). Breaker state is per-process and exported as the `gomodel_circuit_breaker_state` gauge when metrics are enabled. The dashboard's provider status also folds in real-traffic request health: each provider's runtime row carries `request_health` (last observed breaker state plus per-model request/error counts over a 10-minute sliding window; a model with ≥3 errors and a ≥50% error rate is flagged). An open breaker turns the provider card's status pill to "Circuit Open" (unhealthy), a half-open breaker or a flagged model degrades an otherwise healthy provider ("Recovering"/"Degraded"), and the expanded card details list recent per-model traffic with the latest error. Signals only worsen the discovery-based status, never improve it; tracking is in-memory per instance, and providers with no recent requests show discovery-based status only. -- **Metrics:** `METRICS_ENABLED` (false), `METRICS_ENDPOINT` (/metrics) -- **Guardrails:** Definitions are persisted in the `guardrail_definitions` store and managed via the admin API/dashboard; `config/config.yaml` entries are validated and upserted into that store at startup (a seed, not the source of truth). `GUARDRAILS_ENABLED` env var gates the feature. +- **Metrics:** `GOMODEL_METRICS_ENABLED` (false), `GOMODEL_METRICS_ENDPOINT` (/metrics) +- **Guardrails:** Definitions are persisted in the `guardrail_definitions` store and managed via the admin API/dashboard; `config/config.yaml` entries are validated and upserted into that store at startup (a seed, not the source of truth). `GOMODEL_GUARDRAILS_ENABLED` env var gates the feature. - **Provider API key rotation:** Any API-key provider accepts several keys: `[_SUFFIX]_API_KEY_` env vars (numbered from 2; `_1` is accepted as a synonym for the unsuffixed key) or `providers..api_keys` in `config.yaml` (merged after `api_key`, de-duplicated, unresolved `${...}` entries dropped; env replaces the whole YAML list). Two or more keys turn on round-robin rotation, drawn per outbound HTTP request — including retries, so a 429'd request retries under the next key. Realtime websocket sessions pick a key per session. Counters are in-memory per instance. The trailing number names a key, not a provider: `OPENAI_API_KEY_2` is key 2 of `openai`, while `OPENAI_REGION_2_API_KEY` is the sole key of provider `openai-region-2`. **Rotation defeats provider prompt caching** (providers scope the cache to the key that filled it); use it to lift per-key rate limits, not to save cost. Keyless (Ollama, vLLM) and non-API-key providers (Vertex, Bedrock) are unaffected. -- **Providers:** `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `ANTHROPIC_DEFAULT_MAX_TOKENS` (optional default `max_tokens` for Anthropic-translated requests that omit it; default 4096), `GEMINI_API_KEY`, `USE_GOOGLE_GEMINI_NATIVE_API` (true by default; false uses Gemini's OpenAI-compatible chat API), `XAI_API_KEY`, `GROQ_API_KEY`, `FIREWORKS_API_KEY`, `FIREWORKS_BASE_URL` (optional Fireworks AI endpoint override; default `https://api.fireworks.ai/inference/v1`), `META_API_KEY`, `META_BASE_URL` (optional Meta Model API endpoint override; default `https://api.meta.ai/v1`; Muse Spark models, e.g. `muse-spark-1.1`), `OPENROUTER_API_KEY`, `OPENROUTER_SITE_URL`/`OPENROUTER_APP_NAME` (optional OpenRouter attribution headers), `ZAI_API_KEY`, `ZAI_BASE_URL` (optional Z.ai endpoint override), `MINIMAX_API_KEY`, `MINIMAX_BASE_URL` (optional MiniMax endpoint override), `XIAOMI_API_KEY`, `XIAOMI_BASE_URL` (optional Xiaomi MiMo endpoint override), `OPENCODE_GO_API_KEY`, `OPENCODE_GO_BASE_URL` (optional OpenCode Go/Zen endpoint override; default `https://opencode.ai/zen/go/v1`), `OPENCODE_GO_MESSAGES_MODELS` (optional comma-separated model IDs routed to the Anthropic-native `/messages` endpoint instead of `/chat/completions`; default `qwen3.7-max`), `BAILIAN_API_KEY`, `BAILIAN_BASE_URL` (optional Bailian base URL for region switching; default `https://dashscope.aliyuncs.com/compatible-mode/v1`), `AZURE_API_KEY`, `AZURE_BASE_URL` (Azure OpenAI deployment base URL), `AZURE_API_VERSION` (optional Azure API version), `ORACLE_API_KEY` (Oracle API key), `ORACLE_BASE_URL` (Oracle OpenAI-compatible base URL), `[_SUFFIX]_MODELS` (comma-separated configured model list for any provider type), `OLLAMA_BASE_URL`, `VLLM_BASE_URL`, `VLLM_API_KEY` (optional upstream vLLM bearer token) +- **Providers:** `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOMODEL_ANTHROPIC_DEFAULT_MAX_TOKENS` (optional default `max_tokens` for Anthropic-translated requests that omit it; default 4096), `GEMINI_API_KEY`, `GOMODEL_USE_GOOGLE_GEMINI_NATIVE_API` (true by default; false uses Gemini's OpenAI-compatible chat API), `XAI_API_KEY`, `GROQ_API_KEY`, `FIREWORKS_API_KEY`, `FIREWORKS_BASE_URL` (optional Fireworks AI endpoint override; default `https://api.fireworks.ai/inference/v1`), `META_API_KEY`, `META_BASE_URL` (optional Meta Model API endpoint override; default `https://api.meta.ai/v1`; Muse Spark models, e.g. `muse-spark-1.1`), `OPENROUTER_API_KEY`, `GOMODEL_OPENROUTER_SITE_URL`/`GOMODEL_OPENROUTER_APP_NAME` (optional OpenRouter attribution headers), `ZAI_API_KEY`, `ZAI_BASE_URL` (optional Z.ai endpoint override), `MINIMAX_API_KEY`, `MINIMAX_BASE_URL` (optional MiniMax endpoint override), `XIAOMI_API_KEY`, `XIAOMI_BASE_URL` (optional Xiaomi MiMo endpoint override), `OPENCODE_GO_API_KEY`, `OPENCODE_GO_BASE_URL` (optional OpenCode Go/Zen endpoint override; default `https://opencode.ai/zen/go/v1`), `GOMODEL_OPENCODE_GO_MESSAGES_MODELS` (optional comma-separated model IDs routed to the Anthropic-native `/messages` endpoint instead of `/chat/completions`; default `qwen3.7-max`), `BAILIAN_API_KEY`, `BAILIAN_BASE_URL` (optional Bailian base URL for region switching; default `https://dashscope.aliyuncs.com/compatible-mode/v1`), `AZURE_API_KEY`, `AZURE_BASE_URL` (Azure OpenAI deployment base URL), `AZURE_API_VERSION` (optional Azure API version), `ORACLE_API_KEY` (Oracle API key), `ORACLE_BASE_URL` (Oracle OpenAI-compatible base URL), `[_SUFFIX]_MODELS` (comma-separated configured model list for any provider type), `OLLAMA_BASE_URL`, `VLLM_BASE_URL`, `VLLM_API_KEY` (optional upstream vLLM bearer token) - **Provider model metadata:** `providers..models` accepts either model IDs (strings) or `{id, metadata}` objects. When `metadata` is supplied (`display_name`, `context_window`, `max_output_tokens`, `modes`, `capabilities`, `pricing`, …) it is merged onto the remote ai-model-list entry during enrichment, with operator values winning per-field. Primary use case: advertising context windows, capabilities, and pricing for local models (Ollama) and other custom endpoints whose IDs are not in the upstream registry. diff --git a/Makefile b/Makefile index 5368eb740..fea3ffa2d 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,8 @@ VERSION ?= $(shell git describe --tags --always --dirty) COMMIT ?= $(shell git rev-parse --short HEAD) DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") DOCS_API_SERVERS ?= http://localhost:8080 -LOG_LEVEL ?= debug -SWAGGER_ENABLED ?= true +GOMODEL_LOG_LEVEL ?= debug +GOMODEL_SWAGGER_ENABLED ?= true # Build tags covering every file the linter and fixers must see. Without these, # tag-gated files (tests/e2e, tests/integration, tests/contract) are skipped. @@ -28,7 +28,7 @@ build: go build -ldflags '$(LDFLAGS)' -o bin/gomodel ./cmd/gomodel # Run the application run: - LOG_LEVEL=$(LOG_LEVEL) SWAGGER_ENABLED=$(SWAGGER_ENABLED) go run -tags=swagger ./cmd/gomodel + GOMODEL_LOG_LEVEL=$(GOMODEL_LOG_LEVEL) GOMODEL_SWAGGER_ENABLED=$(GOMODEL_SWAGGER_ENABLED) go run -tags=swagger ./cmd/gomodel # Clean build artifacts clean: @@ -47,7 +47,7 @@ image: docker compose --profile app up -d # Seed rolling demo usage/audit data into SQLite. -# Usage: SQLITE_PATH=data/gomodel.db make seed-demo-data +# Usage: GOMODEL_SQLITE_PATH=data/gomodel.db make seed-demo-data seed-demo-data: bash tools/seed-demo-data.sh diff --git a/README.md b/README.md index d23c5e32d..78dcf52ca 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,10 @@ ```bash docker run --rm -p 8080:8080 \ - -e LOGGING_ENABLED=true \ - -e LOGGING_LOG_BODIES=true \ - -e LOG_FORMAT=text \ - -e LOGGING_LOG_HEADERS=true \ + -e GOMODEL_LOGGING_ENABLED=true \ + -e GOMODEL_LOGGING_LOG_BODIES=true \ + -e GOMODEL_LOG_FORMAT=text \ + -e GOMODEL_LOGGING_LOG_HEADERS=true \ -e OPENAI_API_KEY="your-openai-key" \ enterpilot/gomodel ``` diff --git a/config/config.example.yaml b/config/config.example.yaml index d492d7c6f..8cf9d7fc9 100644 --- a/config/config.example.yaml +++ b/config/config.example.yaml @@ -5,20 +5,20 @@ server: port: "8080" - base_path: "/" # env: BASE_PATH; set to "/g" to serve the gateway under https://example.com/g/ + base_path: "/" # env: GOMODEL_BASE_PATH; set to "/g" to serve the gateway under https://example.com/g/ master_key: "your-secret-key" body_size_limit: "10M" - swagger_enabled: false # env: SWAGGER_ENABLED; requires a binary built with -tags=swagger + swagger_enabled: false # env: GOMODEL_SWAGGER_ENABLED; requires a binary built with -tags=swagger pprof_enabled: false # expose /debug/pprof/* for local profiling only enable_passthrough_routes: true # expose /p/{provider}/{endpoint} passthrough routes allow_passthrough_v1_alias: true # allow /p/{provider}/v1/... while keeping /p/{provider}/... canonical - user_path_header: "X-GoModel-User-Path" # env: USER_PATH_HEADER; inbound header used for user_path scoping + user_path_header: "X-GoModel-User-Path" # env: GOMODEL_USER_PATH_HEADER; inbound header used for user_path scoping enabled_passthrough_providers: ["openai", "anthropic", "openrouter", "kilo", "zai", "vllm", "deepseek", "bailian"] # providers enabled on /p/{provider}/... - realtime_enabled: true # env: REALTIME_ENABLED; expose /v1/realtime websocket and /p/{provider}/v1/realtime upgrades (OpenAI only) + realtime_enabled: true # env: GOMODEL_REALTIME_ENABLED; expose /v1/realtime websocket and /p/{provider}/v1/realtime upgrades (OpenAI only) models: - enabled_by_default: true # env: MODELS_ENABLED_BY_DEFAULT; when false, models stay unavailable until an access override allows one or more user paths - configured_provider_models_mode: "fallback" # env: CONFIGURED_PROVIDER_MODELS_MODE; "fallback" uses configured lists only when upstream /models is unavailable/empty, "allowlist" exposes only configured models and skips upstream /models for configured lists + enabled_by_default: true # env: GOMODEL_MODELS_ENABLED_BY_DEFAULT; when false, models stay unavailable until an access override allows one or more user paths + configured_provider_models_mode: "fallback" # env: GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE; "fallback" uses configured lists only when upstream /models is unavailable/empty, "allowlist" exposes only configured models and skips upstream /models for configured lists # Tagging based on headers: label every request from the listed headers. Labels # are recorded in usage tracking and audit logs. A header value can carry several @@ -27,10 +27,10 @@ models: # `do_not_pass: true`. An env entry replaces the whole YAML entry with the same # header name (unset companion vars reset fields to defaults, they do not # inherit the YAML values); other env entries are appended: -# TAGGING_HEADER_1=X-My-Tags -# TAGGING_HEADER_1_PREFIX=tag- # optional -# TAGGING_HEADER_1_DONOTPASS=true # optional, default false (headers are passed as-is) -# TAGGING_HEADER_1_DELIMITER=";" # optional, default "," +# GOMODEL_TAGGING_HEADER_1=X-My-Tags +# GOMODEL_TAGGING_HEADER_1_PREFIX=tag- # optional +# GOMODEL_TAGGING_HEADER_1_DONOTPASS=true # optional, default false (headers are passed as-is) +# GOMODEL_TAGGING_HEADER_1_DELIMITER=";" # optional, default "," # Entries declared here or via env are read-only in the dashboard; omit this # section to manage tagging headers only from the UI (Settings → Tagging based on headers). # tagging: @@ -43,7 +43,7 @@ models: # Virtual models as infrastructure-as-code: redirects, load balancers, and access # policies. These override admin-store rows with the same source and are read-only -# in the dashboard. The VIRTUAL_MODELS env var (a JSON array) merges over this list +# in the dashboard. The GOMODEL_VIRTUAL_MODELS env var (a JSON array) merges over this list # and wins per source. Omit this section to manage virtual models only from the UI. # virtual_models: # - source: regular # a plain alias @@ -62,13 +62,13 @@ models: # MCP gateway: aggregate upstream MCP (Model Context Protocol) servers behind the # authenticated /mcp endpoint. Tools/prompts are namespaced as {server}_{name}; # /mcp/{server} exposes one upstream with original names. Servers declared here or -# via the MCP_SERVERS env var (a JSON object merged over this map, winning per name) +# via the GOMODEL_MCP_SERVERS env var (a JSON object merged over this map, winning per name) # are read-only in the dashboard; the dashboard manages its own additional servers. # The gateway is the credential boundary: client API keys never reach an upstream, # and upstream headers support ${ENV} references. `user_paths` limits which callers # can even discover a server (subtree match, like virtual models). # mcp: -# enabled: true # env: MCP_ENABLED (default: true; a no-op with no servers) +# enabled: true # env: GOMODEL_MCP_ENABLED (default: true; a no-op with no servers) # servers: # github: # url: https://api.githubcopilot.com/mcp @@ -90,7 +90,7 @@ models: cache: model: refresh_interval: 3600 # how often to refresh the model registry (seconds, default: 3600) - recheck_interval: 60 # env: PROVIDER_RECHECK_INTERVAL; how often providers whose last refresh failed are re-probed for recovery (seconds, default: 60; 0 disables) + recheck_interval: 60 # env: GOMODEL_PROVIDER_RECHECK_INTERVAL; how often providers whose last refresh failed are re-probed for recovery (seconds, default: 60; 0 disables) local: cache_dir: ".cache" # local cache directory # To use Redis instead of local cache, remove `local` and uncomment: @@ -99,13 +99,13 @@ cache: # key: "gomodel:models" # ttl: 86400 # 24 hours in seconds # response: - # simple: # omit the whole `simple` key to disable exact-match caching (unless RESPONSE_CACHE_SIMPLE_ENABLED=true) + # simple: # omit the whole `simple` key to disable exact-match caching (unless GOMODEL_RESPONSE_CACHE_SIMPLE_ENABLED=true) # enabled: true # default when `simple` is present; set false to disable while keeping the block # redis: # url: "redis://localhost:6379" # key: "gomodel:response:" # ttl: 3600 - # semantic: # omit the whole `semantic` key to disable semantic caching (unless SEMANTIC_CACHE_ENABLED=true) + # semantic: # omit the whole `semantic` key to disable semantic caching (unless GOMODEL_SEMANTIC_CACHE_ENABLED=true) # enabled: true # embedder: # provider: openai # must match a key under `providers` (openai, gemini, groq, …) @@ -151,7 +151,7 @@ logging: only_model_interactions: true usage: - # Usage actions require USAGE_ENABLED=true (or usage.enabled: true) and a supported + # Usage actions require GOMODEL_USAGE_ENABLED=true (or usage.enabled: true) and a supported # storage backend; pricing recalculation appears only when both usage tracking and # pricing_recalculation_enabled are enabled. enabled: true @@ -162,10 +162,10 @@ usage: retention_days: 90 budgets: - enabled: true # env: BUDGETS_ENABLED; with no configured budgets this has no effect + enabled: true # env: GOMODEL_BUDGETS_ENABLED; with no configured budgets this has no effect user_paths: # Env equivalent: - # SET_BUDGET_USER__PATH__EXAMPLE="daily=10,weekly=50" + # GOMODEL_SET_BUDGET_USER__PATH__EXAMPLE="daily=10,weekly=50" - path: "/user/path/example" limits: - period: "daily" # hourly, daily, weekly, monthly; stored in DB as period_seconds @@ -174,15 +174,15 @@ budgets: amount: 50.00 rate_limits: - enabled: true # env: RATE_LIMITS_ENABLED; with no configured rules this has no effect + enabled: true # env: GOMODEL_RATE_LIMITS_ENABLED; with no configured rules this has no effect user_paths: # Env equivalent: - # SET_RATE_LIMIT_USER__PATH__EXAMPLE="rpm=100,tpm=50000,rpd=10000,concurrent=10" + # GOMODEL_SET_RATE_LIMIT_USER__PATH__EXAMPLE="rpm=100,tpm=50000,rpd=10000,concurrent=10" - path: "/user/path/example" limits: - period: "minute" # minute, hour, day, concurrent; stored in DB as period_seconds max_requests: 100 - max_tokens: 50000 # requires usage tracking (USAGE_ENABLED=true) + max_tokens: 50000 # requires usage tracking (GOMODEL_USAGE_ENABLED=true) - period: "day" max_requests: 10000 - period: "concurrent" # max_requests caps in-flight requests @@ -190,7 +190,7 @@ rate_limits: # Provider rules cap one configured provider across all consumers and # models. Virtual-model load balancing and failover skip a saturated # provider while another target has capacity; when no target can take the - # request it gets 429. Env equivalent: SET_PROVIDER_RATE_LIMIT_OPENAI="rpm=500" + # request it gets 429. Env equivalent: GOMODEL_SET_PROVIDER_RATE_LIMIT_OPENAI="rpm=500" providers: - name: "openai" limits: @@ -280,7 +280,7 @@ guardrails: # # prompt: "Custom rewrite instructions here." failover: - enabled: true # env: FAILOVER_ENABLED; default true + enabled: true # env: GOMODEL_FAILOVER_ENABLED; default true manual_rules_path: "config/failover.example.json" # optional JSON map: {"primary-model": ["fallback-1", "provider/model"]} rules: "gpt-4o": diff --git a/docker-compose.yaml b/docker-compose.yaml index 4e5f24d1d..4eb996967 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -16,15 +16,15 @@ services: # Cache configuration - REDIS_URL=redis://redis:6379 # Metrics - - METRICS_ENABLED=true + - GOMODEL_METRICS_ENABLED=true # Storage configuration (PostgreSQL for audit logs) - - POSTGRES_URL=postgres://gomodel:gomodel@postgres:5432/gomodel + - GOMODEL_POSTGRES_URL=postgres://gomodel:gomodel@postgres:5432/gomodel # MongoDB configuration (uncomment to use MongoDB instead) - - MONGODB_URL=mongodb://mongodb:27017/gomodel + - GOMODEL_MONGODB_URL=mongodb://mongodb:27017/gomodel # Audit logging - all enabled with PostgreSQL - - LOGGING_ENABLED=true - # - STORAGE_TYPE=postgresql - - STORAGE_TYPE=mongodb + - GOMODEL_LOGGING_ENABLED=true + # - GOMODEL_STORAGE_TYPE=postgresql + - GOMODEL_STORAGE_TYPE=mongodb depends_on: redis: condition: service_healthy diff --git a/docs/advanced/admin-endpoints.mdx b/docs/advanced/admin-endpoints.mdx index 2f4b0ba5c..f14d46479 100644 --- a/docs/advanced/admin-endpoints.mdx +++ b/docs/advanced/admin-endpoints.mdx @@ -24,7 +24,7 @@ variables, defaults, and the equivalent `admin:` YAML block. The dashboard UI requires the REST API to be enabled. If you set - `ADMIN_ENDPOINTS_ENABLED=false` but leave `ADMIN_UI_ENABLED=true`, the UI + `GOMODEL_ADMIN_ENDPOINTS_ENABLED=false` but leave `GOMODEL_ADMIN_UI_ENABLED=true`, the UI will be automatically disabled with a warning in the logs. @@ -41,7 +41,7 @@ The dashboard UI pages (`/admin/dashboard`) and static assets (`/admin/static/*` If your GoModel instance is publicly accessible, be aware that the dashboard - UI is unauthenticated. Disable it with `ADMIN_UI_ENABLED=false` or restrict + UI is unauthenticated. Disable it with `GOMODEL_ADMIN_UI_ENABLED=false` or restrict access at the network level. @@ -129,7 +129,7 @@ Returns time-bucketed request counts grouped into `2xx`/`4xx`/`5xx` status classes, an overall success-rate summary, and average request duration per provider. This powers the "Requests by Status" and "Provider Latency" charts on the dashboard's Overview page. Data comes from the audit log, so it requires -`LOGGING_ENABLED=true`. +`GOMODEL_LOGGING_ENABLED=true`. **Query parameters:** @@ -251,7 +251,7 @@ http://localhost:8080/admin/dashboard It provides a visual overview of usage statistics and registered models using the same data as the REST API endpoints above. -When `DASHBOARD_LIVE_LOGS_ENABLED=true`, the dashboard opens +When `GOMODEL_DASHBOARD_LIVE_LOGS_ENABLED=true`, the dashboard opens `GET /admin/live/logs` and streams compact audit/usage lifecycle previews. This lets the Audit Logs and Usage pages show a request as it moves through the workflow before the async database flush finishes. The stream uses sequence @@ -267,14 +267,14 @@ and the dashboard reloads from the normal REST endpoints. To disable all admin features: ```bash -export ADMIN_ENDPOINTS_ENABLED=false +export GOMODEL_ADMIN_ENDPOINTS_ENABLED=false ``` This disables both the REST API and the dashboard UI. To keep the API but hide the dashboard: ```bash -export ADMIN_ENDPOINTS_ENABLED=true -export ADMIN_UI_ENABLED=false +export GOMODEL_ADMIN_ENDPOINTS_ENABLED=true +export GOMODEL_ADMIN_UI_ENABLED=false ``` diff --git a/docs/advanced/api-endpoints.mdx b/docs/advanced/api-endpoints.mdx index 26dff44c6..d2b107020 100644 --- a/docs/advanced/api-endpoints.mdx +++ b/docs/advanced/api-endpoints.mdx @@ -33,9 +33,9 @@ For request and response details, see the dedicated guides: | `/v1/models` | GET | List available models | | `/v1/audio/speech` | POST | Text-to-speech, returning binary audio | | `/v1/audio/transcriptions` | POST | Speech-to-text from a multipart upload | -| `/v1/realtime` | GET | Realtime speech-to-speech websocket upgrade; `?call_id=` attaches to an existing WebRTC/SIP call as a sideband channel (when `REALTIME_ENABLED`) | -| `/v1/realtime/calls` | POST | Realtime WebRTC SDP exchange: `application/sdp` offer with `?model=`, or multipart `sdp` + `session` fields (when `REALTIME_ENABLED`) | -| `/v1/realtime/client_secrets` | POST | Mint an ephemeral realtime client secret for browser clients, routed by `session.model` (when `REALTIME_ENABLED`) | +| `/v1/realtime` | GET | Realtime speech-to-speech websocket upgrade; `?call_id=` attaches to an existing WebRTC/SIP call as a sideband channel (when `GOMODEL_REALTIME_ENABLED`) | +| `/v1/realtime/calls` | POST | Realtime WebRTC SDP exchange: `application/sdp` offer with `?model=`, or multipart `sdp` + `session` fields (when `GOMODEL_REALTIME_ENABLED`) | +| `/v1/realtime/client_secrets` | POST | Mint an ephemeral realtime client secret for browser clients, routed by `session.model` (when `GOMODEL_REALTIME_ENABLED`) | | `/v1/files` | POST | Upload a file (OpenAI-compatible multipart) | | `/v1/files` | GET | List files | | `/v1/files/{id}` | GET | Retrieve file metadata | @@ -69,7 +69,7 @@ For request and response details, see the dedicated guides: ## MCP Gateway Streamable-HTTP MCP (Model Context Protocol) endpoints aggregating the -configured upstream MCP servers (when `MCP_ENABLED`, default on). See +configured upstream MCP servers (when `GOMODEL_MCP_ENABLED`, default on). See [MCP Gateway](/features/mcp-gateway). | Endpoint | Method | Description | diff --git a/docs/advanced/audio-api.mdx b/docs/advanced/audio-api.mdx index ec0a021c8..2c2e96e58 100644 --- a/docs/advanced/audio-api.mdx +++ b/docs/advanced/audio-api.mdx @@ -87,11 +87,11 @@ forward bytes verbatim to that upstream. Audio requests appear in the audit log like any other model interaction. Because audio payloads are binary and large, their bodies are gated by a dedicated -setting, [`LOGGING_LOG_AUDIO_BODIES`](/advanced/configuration#audit-logging) -(default `false`), which **refines** `LOGGING_LOG_BODIES` — it has no effect +setting, [`GOMODEL_LOGGING_LOG_AUDIO_BODIES`](/advanced/configuration#audit-logging) +(default `false`), which **refines** `GOMODEL_LOGGING_LOG_BODIES` — it has no effect unless body logging is enabled: -- **Body logging off** (`LOGGING_LOG_BODIES=false`) — no audio body is stored, +- **Body logging off** (`GOMODEL_LOGGING_LOG_BODIES=false`) — no audio body is stored, regardless of this setting. - **Body logging on, audio off** (the default) — the audio response is recorded as a lightweight `{__audio__, content_type, bytes, stored: false}` placeholder; no audio bytes are stored. diff --git a/docs/advanced/cli.mdx b/docs/advanced/cli.mdx index 6742c1968..48355500e 100644 --- a/docs/advanced/cli.mdx +++ b/docs/advanced/cli.mdx @@ -41,7 +41,7 @@ gomodel --health The probe always targets the loopback interface (`127.0.0.1`) since it runs inside the same container as the server, but it derives the `PORT` and -`BASE_PATH` from configuration instead of hardcoding `8080` and `/health`. Bound +`GOMODEL_BASE_PATH` from configuration instead of hardcoding `8080` and `/health`. Bound the request with `--health-timeout`: ```bash diff --git a/docs/advanced/config-yaml.mdx b/docs/advanced/config-yaml.mdx index 758378952..8161c4987 100644 --- a/docs/advanced/config-yaml.mdx +++ b/docs/advanced/config-yaml.mdx @@ -34,7 +34,7 @@ must use the same suffix for each instance: for example, pair Configured provider model lists can stay in env via `_MODELS`, for example `OPENROUTER_MODELS`, `ORACLE_MODELS`, `AZURE_MODELS`, or `VLLM_MODELS`. -Set `CONFIGURED_PROVIDER_MODELS_MODE=fallback` (default) to use those lists only +Set `GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE=fallback` (default) to use those lists only when upstream `/models` fails or is empty, or `allowlist` to expose only the configured models for providers that define a list and skip their upstream `/models` calls. @@ -60,7 +60,7 @@ silently ignored one. This catches typos (`prot:` instead of `port:`) and, more importantly, misindented sections — see the gotcha below. This applies to `config.yaml` and to the env vars that declare the same -structures as JSON: `VIRTUAL_MODELS`, `SET_RATE_LIMIT_*`, and `SET_BUDGET_*`. +structures as JSON: `GOMODEL_VIRTUAL_MODELS`, `GOMODEL_SET_RATE_LIMIT_*`, and `GOMODEL_SET_BUDGET_*`. Because env entries override YAML entries, a typo in one of them would otherwise silently win over a correct YAML entry. @@ -68,7 +68,7 @@ The default is strict because a dropped `providers`, `rate_limits`, `budgets`, or `guardrails` entry silently changes routing, cost, or security — a cost cap that never applies looks exactly like a cost cap that does. -Set `CONFIG_STRICT=false` to downgrade unknown keys to warnings and boot without +Set `GOMODEL_CONFIG_STRICT=false` to downgrade unknown keys to warnings and boot without them. This is for rolling a binary back under a newer config file, not for everyday use: @@ -77,7 +77,7 @@ WARN unknown config key ignored; it has no effect path=config.yaml line=23 fiel INFO providers resolved total=1 from_config_file=0 from_env=1 ``` -`CONFIG_STRICT` relaxes which *keys* are accepted, never whether a *value* makes +`GOMODEL_CONFIG_STRICT` relaxes which *keys* are accepted, never whether a *value* makes sense. A malformed value (`port: [9999, 8080]`) aborts startup in either mode. Entity-level validation also still applies: a virtual model whose target names a provider that does not exist fails startup even when the key that would have @@ -103,11 +103,11 @@ The current source of truth lives in the main codebase: - [config/config.example.yaml](https://github.com/ENTERPILOT/GoModel/blob/main/config/config.example.yaml) For deployments mounted below a domain root, set `server.base_path` or -`BASE_PATH`. For example, `BASE_PATH=/g` serves the gateway at `/g/v1/...`, +`GOMODEL_BASE_PATH`. For example, `GOMODEL_BASE_PATH=/g` serves the gateway at `/g/v1/...`, `/g/admin/...`, and `/g/health`. To change the inbound user path header, set `server.user_path_header` or -`USER_PATH_HEADER`. The default remains `X-GoModel-User-Path`. +`GOMODEL_USER_PATH_HEADER`. The default remains `X-GoModel-User-Path`. ## Docker @@ -176,12 +176,12 @@ failed to parse config.yaml: line 2: field uranium-geryon-9b not found; line 5: ``` Indent provider entries two spaces under `providers:`. Before strict parsing, -this booted with zero YAML providers and no error. With `CONFIG_STRICT=false` it +this booted with zero YAML providers and no error. With `GOMODEL_CONFIG_STRICT=false` it boots with zero YAML providers and one warning per unindented key. ### Per-provider resilience can only come from YAML -The env-var override walk skips `map` fields. `RETRY_MAX_RETRIES` changes the +The env-var override walk skips `map` fields. `GOMODEL_RETRY_MAX_RETRIES` changes the global default for all providers but cannot target a single provider. Per-provider tuning requires a `providers..resilience:` block in `config.yaml`. diff --git a/docs/advanced/configuration.mdx b/docs/advanced/configuration.mdx index 26c8297a3..591ec224b 100644 --- a/docs/advanced/configuration.mdx +++ b/docs/advanced/configuration.mdx @@ -36,15 +36,27 @@ GoModel automatically discovers providers from well-known environment variables. The most common way to configure GoModel. Set any of the variables below to override defaults. + + GoModel-defined variables are canonically spelled `GOMODEL_`, as shown + below. The unprefixed spellings from earlier releases (`SQLITE_PATH`, + `LOGGING_ENABLED`, ...) still work but are deprecated — each logs a startup + warning naming its replacement, and they will be removed in a future major + release. When both spellings are set, the `GOMODEL_` one wins. Two groups + keep their bare names permanently: `PORT` and `REDIS_URL` (injected by PaaS + platforms), and the provider family (`OPENAI_API_KEY`, `_BASE_URL`, + `_MODELS`, ...), which lives in each vendor's namespace and is what + makes GoModel drop-in compatible. + + #### Server | Variable | Description | Default | | -------------------- | ----------------------------------------------------- | ---------------------- | | `PORT` | HTTP server port | `8080` | -| `BASE_PATH` | Mount path prefix, for example `/g` | `/` | +| `GOMODEL_BASE_PATH` | Mount path prefix, for example `/g` | `/` | | `GOMODEL_MASTER_KEY` | Authentication key for securing the gateway | _(empty, unsafe mode)_ | -| `BODY_SIZE_LIMIT` | Max request body size (e.g., `10M`, `1024K`, `500KB`) | _(no limit)_ | -| `USER_PATH_HEADER` | Header used to read/write request `user_path` values | `X-GoModel-User-Path` | +| `GOMODEL_BODY_SIZE_LIMIT` | Max request body size (e.g., `10M`, `1024K`, `500KB`) | _(no limit)_ | +| `GOMODEL_USER_PATH_HEADER` | Header used to read/write request `user_path` values | `X-GoModel-User-Path` | #### MCP Gateway @@ -52,8 +64,8 @@ See [MCP Gateway](/features/mcp-gateway) for the full feature guide. | Variable | Description | Default | | ------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------ | -| `MCP_ENABLED` | Expose the MCP endpoints `/mcp` and `/mcp/{server}` | `true` | -| `MCP_SERVERS` | JSON object of upstream MCP servers, merged over the `mcp.servers` YAML map per name (entries are read-only in the dashboard) | _(none)_ | +| `GOMODEL_MCP_ENABLED` | Expose the MCP endpoints `/mcp` and `/mcp/{server}` | `true` | +| `GOMODEL_MCP_SERVERS` | JSON object of upstream MCP servers, merged over the `mcp.servers` YAML map per name (entries are read-only in the dashboard) | _(none)_ | #### Logging @@ -62,21 +74,21 @@ bodies, headers), see [Audit Logging](#audit-logging) below. | Variable | Description | Default | | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | -| `LOG_FORMAT` | `text` for colorized human output, `json` for structured logs. Unset auto-detects: text on a TTY, JSON otherwise. Force `json` in production / CloudWatch / Datadog / GCP setups. | _(auto-detect)_ | -| `LOG_LEVEL` | Minimum log level: `debug`, `info`, `warn`, `error`. Aliases `dbg`, `inf`, `warning`, `err` are also accepted. | `info` | +| `GOMODEL_LOG_FORMAT` | `text` for colorized human output, `json` for structured logs. Unset auto-detects: text on a TTY, JSON otherwise. Force `json` in production / CloudWatch / Datadog / GCP setups. | _(auto-detect)_ | +| `GOMODEL_LOG_LEVEL` | Minimum log level: `debug`, `info`, `warn`, `error`. Aliases `dbg`, `inf`, `warning`, `err` are also accepted. | `info` | #### Cache | Variable | Description | Default | | ------------------- | --------------------------------- | ---------------- | | `GOMODEL_CACHE_DIR` | Directory for local cache files | `.cache` | -| `CACHE_REFRESH_INTERVAL` | Seconds between provider model re-discovery runs | `3600` (1h) | -| `PROVIDER_RECHECK_INTERVAL` | Seconds between re-probes of providers whose last refresh failed (`0` disables) | `60` | +| `GOMODEL_CACHE_REFRESH_INTERVAL` | Seconds between provider model re-discovery runs | `3600` (1h) | +| `GOMODEL_PROVIDER_RECHECK_INTERVAL` | Seconds between re-probes of providers whose last refresh failed (`0` disables) | `60` | | `REDIS_URL` | Redis connection URL | _(empty)_ | -| `REDIS_KEY_MODELS` | Redis key for model cache | `gomodel:models` | -| `REDIS_KEY_RESPONSES` | Redis key for response cache | `gomodel:response:` | -| `REDIS_TTL_MODELS` | TTL in seconds for model cache | `86400` (24h) | -| `REDIS_TTL_RESPONSES` | TTL in seconds for response cache | `3600` (1h) | +| `GOMODEL_REDIS_KEY_MODELS` | Redis key for model cache | `gomodel:models` | +| `GOMODEL_REDIS_KEY_RESPONSES` | Redis key for response cache | `gomodel:response:` | +| `GOMODEL_REDIS_TTL_MODELS` | TTL in seconds for model cache | `86400` (24h) | +| `GOMODEL_REDIS_TTL_RESPONSES` | TTL in seconds for response cache | `3600` (1h) | See [Cache](/features/cache) for exact-cache behavior, response headers, @@ -85,8 +97,8 @@ bodies, headers), see [Audit Logging](#audit-logging) below. - `CACHE_REFRESH_INTERVAL` also sets how often the dashboard re-checks - provider health ("Last checked"), and `PROVIDER_RECHECK_INTERVAL` how + `GOMODEL_CACHE_REFRESH_INTERVAL` also sets how often the dashboard re-checks + provider health ("Last checked"), and `GOMODEL_PROVIDER_RECHECK_INTERVAL` how quickly a failed provider is re-probed for recovery. See [Resilience](/advanced/resilience#circuit-breaker-and-dashboard-provider-health). @@ -97,34 +109,34 @@ Storage is shared by audit logging, usage tracking, and future features like IAM | Variable | Description | Default | | -------------------- | --------------------------------------------- | ----------------- | -| `STORAGE_TYPE` | Backend: `sqlite`, `postgresql`, or `mongodb` | `sqlite` | -| `SQLITE_PATH` | SQLite database file path | `data/gomodel.db` | -| `POSTGRES_URL` | PostgreSQL connection string | _(empty)_ | -| `POSTGRES_MAX_CONNS` | PostgreSQL connection pool size | `10` | -| `MONGODB_URL` | MongoDB connection string; a database named in its path is used | _(empty)_ | -| `MONGODB_DATABASE` | MongoDB database name; overrides the URL path | `gomodel` | +| `GOMODEL_STORAGE_TYPE` | Backend: `sqlite`, `postgresql`, or `mongodb` | `sqlite` | +| `GOMODEL_SQLITE_PATH` | SQLite database file path | `data/gomodel.db` | +| `GOMODEL_POSTGRES_URL` | PostgreSQL connection string | _(empty)_ | +| `GOMODEL_POSTGRES_MAX_CONNS` | PostgreSQL connection pool size | `10` | +| `GOMODEL_MONGODB_URL` | MongoDB connection string; a database named in its path is used | _(empty)_ | +| `GOMODEL_MONGODB_DATABASE` | MongoDB database name; overrides the URL path | `gomodel` | #### Audit Logging | Variable | Description | Default | | --------------------------------- | ------------------------------------------ | ------- | -| `LOGGING_ENABLED` | Enable audit logging | `false` | -| `LOGGING_LOG_BODIES` | Log request/response bodies | `true` | -| `LOGGING_LOG_AUDIO_BODIES` | Log audio endpoint inputs/outputs | `false` | -| `LOGGING_LOG_HEADERS` | Log headers (sensitive ones auto-redacted) | `true` | -| `LOGGING_ONLY_MODEL_INTERACTIONS` | Only log AI model endpoints | `true` | -| `LOGGING_BUFFER_SIZE` | In-memory buffer before flush | `1000` | -| `LOGGING_FLUSH_INTERVAL` | Flush interval in seconds | `5` | -| `LOGGING_RETENTION_DAYS` | Auto-delete after N days (0 = forever) | `30` | +| `GOMODEL_LOGGING_ENABLED` | Enable audit logging | `false` | +| `GOMODEL_LOGGING_LOG_BODIES` | Log request/response bodies | `true` | +| `GOMODEL_LOGGING_LOG_AUDIO_BODIES` | Log audio endpoint inputs/outputs | `false` | +| `GOMODEL_LOGGING_LOG_HEADERS` | Log headers (sensitive ones auto-redacted) | `true` | +| `GOMODEL_LOGGING_ONLY_MODEL_INTERACTIONS` | Only log AI model endpoints | `true` | +| `GOMODEL_LOGGING_BUFFER_SIZE` | In-memory buffer before flush | `1000` | +| `GOMODEL_LOGGING_FLUSH_INTERVAL` | Flush interval in seconds | `5` | +| `GOMODEL_LOGGING_RETENTION_DAYS` | Auto-delete after N days (0 = forever) | `30` | - When `LOGGING_LOG_BODIES` is enabled, request and response bodies are stored + When `GOMODEL_LOGGING_LOG_BODIES` is enabled, request and response bodies are stored in full. These may contain sensitive data such as PII or API keys embedded in prompts. - `LOGGING_LOG_AUDIO_BODIES` refines `LOGGING_LOG_BODIES` for audio endpoints — + `GOMODEL_LOGGING_LOG_AUDIO_BODIES` refines `GOMODEL_LOGGING_LOG_BODIES` for audio endpoints — it has no effect unless body logging is enabled. With both on, `/v1/audio/speech` stores its text input and the generated audio (base64, capped at 8 MB) so the dashboard can play it back, and `/v1/audio/transcriptions` stores the uploaded @@ -140,12 +152,12 @@ Storage is shared by audit logging, usage tracking, and future features like IAM | Variable | Description | Default | | ------------------------------ | ---------------------------------------------- | ------- | -| `USAGE_ENABLED` | Enable token usage tracking | `true` | -| `USAGE_PRICING_RECALCULATION_ENABLED` | Enable the admin usage pricing recalculation action when supported | `true` | -| `ENFORCE_RETURNING_USAGE_DATA` | Auto-add `include_usage` to streaming requests | `true` | -| `USAGE_BUFFER_SIZE` | In-memory buffer before flush | `1000` | -| `USAGE_FLUSH_INTERVAL` | Flush interval in seconds | `5` | -| `USAGE_RETENTION_DAYS` | Auto-delete after N days (0 = forever) | `90` | +| `GOMODEL_USAGE_ENABLED` | Enable token usage tracking | `true` | +| `GOMODEL_USAGE_PRICING_RECALCULATION_ENABLED` | Enable the admin usage pricing recalculation action when supported | `true` | +| `GOMODEL_ENFORCE_RETURNING_USAGE_DATA` | Auto-add `include_usage` to streaming requests | `true` | +| `GOMODEL_USAGE_BUFFER_SIZE` | In-memory buffer before flush | `1000` | +| `GOMODEL_USAGE_FLUSH_INTERVAL` | Flush interval in seconds | `5` | +| `GOMODEL_USAGE_RETENTION_DAYS` | Auto-delete after N days (0 = forever) | `90` | #### Budgets @@ -154,26 +166,26 @@ starts with budget management disabled and logs a warning. | Variable | Description | Default | | ------------------- | ------------------------------------------------------- | ------- | -| `BUDGETS_ENABLED` | Enable budget management and workflow budget checks when usage tracking is enabled | `true` | -| `SET_BUDGET_` | Seed budget limits for a user path, such as `daily=10` | _(empty)_ | +| `GOMODEL_BUDGETS_ENABLED` | Enable budget management and workflow budget checks when usage tracking is enabled | `true` | +| `GOMODEL_SET_BUDGET_` | Seed budget limits for a user path, such as `daily=10` | _(empty)_ | -`SET_BUDGET_` supports the standard periods `hourly`, `daily`, `weekly`, +`GOMODEL_SET_BUDGET_` supports the standard periods `hourly`, `daily`, `weekly`, and `monthly`. The `` suffix is lowercased; use double underscores (`__`) between path segments, while single underscores stay inside a segment. -For example, `SET_BUDGET_TEAM__ALPHA__SERVICE="daily=10"` configures -`/team/alpha/service`, and `SET_BUDGET_TEAM_ALPHA="daily=10"` configures +For example, `GOMODEL_SET_BUDGET_TEAM__ALPHA__SERVICE="daily=10"` configures +`/team/alpha/service`, and `GOMODEL_SET_BUDGET_TEAM_ALPHA="daily=10"` configures `/team_alpha`. This differs from provider `` variables below, which convert underscores to hyphens in provider names. -`SET_BUDGET_="monthly=500"` means a literal environment variable named -`SET_BUDGET_`, which configures the root path `/`. POSIX permits that name, but +`GOMODEL_SET_BUDGET_="monthly=500"` means a literal environment variable named +`GOMODEL_SET_BUDGET_`, which configures the root path `/`. POSIX permits that name, but some shells and orchestrators, including some Kubernetes validators, may reject it. Use YAML or the dashboard when your environment cannot set it. Migration note: budget management depends on usage tracking. If -`USAGE_ENABLED=false`, GoModel starts with budgets disabled and logs a warning, -even when `BUDGETS_ENABLED=true`. Set both `USAGE_ENABLED=true` and -`BUDGETS_ENABLED=true` to enforce budgets. +`GOMODEL_USAGE_ENABLED=false`, GoModel starts with budgets disabled and logs a warning, +even when `GOMODEL_BUDGETS_ENABLED=true`. Set both `GOMODEL_USAGE_ENABLED=true` and +`GOMODEL_BUDGETS_ENABLED=true` to enforce budgets. See [Budgets](/features/budgets) for YAML examples, periods, matching, and workflow enforcement. @@ -187,19 +199,19 @@ workflow enforcement. | Variable | Description | Default | | ------------------ | ---------------------------------------- | ---------- | -| `METRICS_ENABLED` | Enable Prometheus metrics (experimental) | `false` | -| `METRICS_ENDPOINT` | HTTP path for metrics | `/metrics` | +| `GOMODEL_METRICS_ENABLED` | Enable Prometheus metrics (experimental) | `false` | +| `GOMODEL_METRICS_ENDPOINT` | HTTP path for metrics | `/metrics` | #### Admin | Variable | Description | Default | | ------------------------------------- | ------------------------------------------------ | ------- | -| `ADMIN_ENDPOINTS_ENABLED` | Enable the admin REST API | `true` | -| `ADMIN_UI_ENABLED` | Enable the admin dashboard UI | `true` | -| `DASHBOARD_LIVE_LOGS_ENABLED` | Stream realtime dashboard audit/usage previews | `true` | -| `DASHBOARD_LIVE_LOGS_BUFFER_SIZE` | In-memory replay window for live dashboard events | `10000` | -| `DASHBOARD_LIVE_LOGS_REPLAY_LIMIT` | Max events replayed to one reconnecting client | `1000` | -| `DASHBOARD_LIVE_LOGS_HEARTBEAT_SECONDS` | Idle stream heartbeat interval in seconds | `15` | +| `GOMODEL_ADMIN_ENDPOINTS_ENABLED` | Enable the admin REST API | `true` | +| `GOMODEL_ADMIN_UI_ENABLED` | Enable the admin dashboard UI | `true` | +| `GOMODEL_DASHBOARD_LIVE_LOGS_ENABLED` | Stream realtime dashboard audit/usage previews | `true` | +| `GOMODEL_DASHBOARD_LIVE_LOGS_BUFFER_SIZE` | In-memory replay window for live dashboard events | `10000` | +| `GOMODEL_DASHBOARD_LIVE_LOGS_REPLAY_LIMIT` | Max events replayed to one reconnecting client | `1000` | +| `GOMODEL_DASHBOARD_LIVE_LOGS_HEARTBEAT_SECONDS` | Idle stream heartbeat interval in seconds | `15` | Live dashboard logs are compact previews for the Audit Logs and Usage pages. They update rows before the async database flush finishes. The stored audit and @@ -212,8 +224,8 @@ These control timeouts for upstream API requests to LLM providers. | Variable | Description | Default | | ------------------------------ | -------------------------------------------- | -------------- | -| `HTTP_TIMEOUT` | Overall request timeout in seconds | `600` (10 min) | -| `HTTP_RESPONSE_HEADER_TIMEOUT` | Time to wait for response headers in seconds | `600` (10 min) | +| `GOMODEL_HTTP_TIMEOUT` | Overall request timeout in seconds | `600` (10 min) | +| `GOMODEL_HTTP_RESPONSE_HEADER_TIMEOUT` | Time to wait for response headers in seconds | `600` (10 min) | #### Provider API Keys @@ -240,14 +252,14 @@ Most providers can use a custom base URL via `_BASE_URL` (for example Every provider type also accepts a comma-separated configured model list via `_MODELS`, for example `OPENROUTER_MODELS`, `ORACLE_MODELS`, `AZURE_MODELS`, or `VLLM_MODELS`. By default, -`CONFIGURED_PROVIDER_MODELS_MODE=fallback` uses configured lists only when +`GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE=fallback` uses configured lists only when upstream `/models` fails, returns nil, or returns an empty list. Set -`CONFIGURED_PROVIDER_MODELS_MODE=allowlist` to expose only configured models for +`GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE=allowlist` to expose only configured models for providers that define a list and skip their upstream `/models` calls. YAML `providers..models` provides the same model-list input for named provider blocks. -For OpenRouter, GoModel also sends default attribution headers unless the request already sets them. Override those defaults with `OPENROUTER_SITE_URL` and `OPENROUTER_APP_NAME`. +For OpenRouter, GoModel also sends default attribution headers unless the request already sets them. Override those defaults with `GOMODEL_OPENROUTER_SITE_URL` and `GOMODEL_OPENROUTER_APP_NAME`. ### 2. `.env` File @@ -256,7 +268,7 @@ GoModel automatically loads a `.env` file from the working directory at startup. ```bash # .env PORT=3000 -BASE_PATH=/g +GOMODEL_BASE_PATH=/g OPENAI_API_KEY=sk-... ANTHROPIC_API_KEY=sk-ant-... ``` @@ -371,7 +383,7 @@ export ORACLE_BASE_URL="https://inference.generativeai.us-chicago-1.oci.oraclecl export ORACLE_MODELS="openai.gpt-oss-120b,xai.grok-3" # Optional configured model list export OPENROUTER_MODELS="openai/gpt-oss-120b,anthropic/claude-sonnet-4" export KILO_MODELS="anthropic/claude-sonnet-4.5,openai/gpt-5.5" -export CONFIGURED_PROVIDER_MODELS_MODE="fallback" # fallback or allowlist +export GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE="fallback" # fallback or allowlist export OLLAMA_BASE_URL="http://localhost:11434/v1" # Registers "ollama" provider export VLLM_BASE_URL="http://localhost:8000/v1" # Registers keyless "vllm" provider # Optional: export VLLM_API_KEY="token-abc123" @@ -544,7 +556,7 @@ them. | Variable | Description | Default | | ------------------------------ | ------------------------------------------------- | ------- | -| `ANTHROPIC_DEFAULT_MAX_TOKENS` | Value injected when the caller omits `max_tokens` | `4096` | +| `GOMODEL_ANTHROPIC_DEFAULT_MAX_TOKENS` | Value injected when the caller omits `max_tokens` | `4096` | Raise this for newer models that routinely produce longer outputs (Sonnet 4.6, Opus 4.7). Setting `max_tokens` explicitly on a request always wins over the diff --git a/docs/advanced/guardrails.mdx b/docs/advanced/guardrails.mdx index 53ff41e05..65de3830b 100644 --- a/docs/advanced/guardrails.mdx +++ b/docs/advanced/guardrails.mdx @@ -105,7 +105,7 @@ guardrails: You can toggle guardrails without editing the config file: ```bash -export GUARDRAILS_ENABLED=true +export GOMODEL_GUARDRAILS_ENABLED=true ``` ### Rule Fields diff --git a/docs/advanced/resilience.mdx b/docs/advanced/resilience.mdx index 450e29cea..465d23345 100644 --- a/docs/advanced/resilience.mdx +++ b/docs/advanced/resilience.mdx @@ -77,19 +77,19 @@ in YAML. | Variable | Type | Default | Description | | ------------------------ | -------- | ------- | ------------------------------------------ | -| `RETRY_MAX_RETRIES` | int | `3` | Maximum retry attempts per request | -| `RETRY_INITIAL_BACKOFF` | duration | `1s` | First retry wait (e.g. `500ms`, `2s`) | -| `RETRY_MAX_BACKOFF` | duration | `30s` | Upper cap on retry wait | -| `RETRY_BACKOFF_FACTOR` | float | `2.0` | Exponential multiplier between retries | -| `RETRY_JITTER_FACTOR` | float | `0.1` | Random jitter as a fraction of the backoff | +| `GOMODEL_RETRY_MAX_RETRIES` | int | `3` | Maximum retry attempts per request | +| `GOMODEL_RETRY_INITIAL_BACKOFF` | duration | `1s` | First retry wait (e.g. `500ms`, `2s`) | +| `GOMODEL_RETRY_MAX_BACKOFF` | duration | `30s` | Upper cap on retry wait | +| `GOMODEL_RETRY_BACKOFF_FACTOR` | float | `2.0` | Exponential multiplier between retries | +| `GOMODEL_RETRY_JITTER_FACTOR` | float | `0.1` | Random jitter as a fraction of the backoff | ### Circuit Breaker | Variable | Type | Default | Description | | ----------------------------------- | -------- | ------- | --------------------------------------------- | -| `CIRCUIT_BREAKER_FAILURE_THRESHOLD` | int | `5` | Consecutive failures before opening | -| `CIRCUIT_BREAKER_SUCCESS_THRESHOLD` | int | `2` | Consecutive successes to close again | -| `CIRCUIT_BREAKER_TIMEOUT` | duration | `30s` | How long the circuit stays open before probing | +| `GOMODEL_CIRCUIT_BREAKER_FAILURE_THRESHOLD` | int | `5` | Consecutive failures before opening | +| `GOMODEL_CIRCUIT_BREAKER_SUCCESS_THRESHOLD` | int | `2` | Consecutive successes to close again | +| `GOMODEL_CIRCUIT_BREAKER_TIMEOUT` | duration | `30s` | How long the circuit stays open before probing | ## YAML @@ -132,7 +132,7 @@ inherits from the built-in defaults. Per-provider tuning **must** come from YAML. Environment variables set - global defaults only — `RETRY_MAX_RETRIES` cannot target a single provider. + global defaults only — `GOMODEL_RETRY_MAX_RETRIES` cannot target a single provider. See [config.yaml gotchas](/advanced/config-yaml#gotchas). @@ -177,10 +177,10 @@ state is also exported as the `gomodel_circuit_breaker_state` gauge Model discovery is re-checked: - at startup, -- on every model registry refresh, controlled by `CACHE_REFRESH_INTERVAL` +- on every model registry refresh, controlled by `GOMODEL_CACHE_REFRESH_INTERVAL` (seconds, default `3600` — hourly), - on the fast recheck loop, which re-probes **only** providers whose latest - refresh failed, controlled by `PROVIDER_RECHECK_INTERVAL` + refresh failed, controlled by `GOMODEL_PROVIDER_RECHECK_INTERVAL` (`cache.model.recheck_interval` in `config.yaml`; seconds, default `60`; `0` disables), and - on demand, when a request asks for a provider-qualified model @@ -199,7 +199,7 @@ and marked stale, and the dashboard shows the provider as Degraded load-balanced redirect keeps working through its healthy targets. The fast recheck loop re-probes the provider every -`PROVIDER_RECHECK_INTERVAL` seconds, updating "Last checked" and restoring +`GOMODEL_PROVIDER_RECHECK_INTERVAL` seconds, updating "Last checked" and restoring normal routing typically within a minute of the provider coming back. A provider that was already down at startup (nothing discovered yet) shows as Unhealthy until its first successful fetch. diff --git a/docs/advanced/usage-api.mdx b/docs/advanced/usage-api.mdx index 6bc70e0d4..23d32b39e 100644 --- a/docs/advanced/usage-api.mdx +++ b/docs/advanced/usage-api.mdx @@ -100,7 +100,7 @@ curl http://localhost:8080/v1/usage \ Aggregated from tracked requests over the date window, with the same token and [estimated-cost](/features/cost-tracking) semantics as the dashboard. Cost fields are `null` when no matched request has pricing. The whole block is -`null` when no storage backend records usage (`USAGE_ENABLED=false` or no +`null` when no storage backend records usage (`GOMODEL_USAGE_ENABLED=false` or no database). ### `budgets` diff --git a/docs/dev/2026-07-17_env-prefix-migration.md b/docs/dev/2026-07-17_env-prefix-migration.md index b7eb65184..8d5f16933 100644 --- a/docs/dev/2026-07-17_env-prefix-migration.md +++ b/docs/dev/2026-07-17_env-prefix-migration.md @@ -1,6 +1,8 @@ # Environment variable prefix migration -Status: mechanism shipped; documentation rename pending. +Status: mechanism shipped; documentation surface renamed to canonical +spellings (docs site, README, .env.template, config.example.yaml, helm, +docker-compose, Makefile). GoModel-defined environment variables are canonically spelled `GOMODEL_`. The unprefixed spelling still resolves but is deprecated and warns once per @@ -121,13 +123,12 @@ the named reads outside the tag walker (`GOMODEL_CONFIG_STRICT`, ## Remaining work -The mechanism is in place and both spellings work. Still to do: +The mechanism is in place, both spellings work, and the documentation surface +(docs site, `README.md`, `.env.template`, `config/config.example.yaml`, +`CLAUDE.md`, `helm/`, `docker-compose.yaml`, `Makefile`) teaches the canonical +spellings. Dated material under `docs/dev/`, `docs/adr/`, and the benchmark +directories deliberately keeps the spellings it was written with. Still to do: -1. Rename the variables throughout the documentation surface to the canonical - spelling: `.env.template`, `config/config.example.yaml`, `README.md`, - `CLAUDE.md`, `helm/`, `docker-compose.yaml`, and the benchmark compose files. - Deliberately not done in the mechanism PR: it is a large mechanical diff, and - the exempt rules above make a blind find-and-replace unsafe. -2. Decide on accepting bare `DATABASE_URL` for Postgres (PaaS interop win, new +1. Decide on accepting bare `DATABASE_URL` for Postgres (PaaS interop win, new behavior rather than a rename). -3. Pick the release that removes the legacy spellings. +2. Pick the release that removes the legacy spellings. diff --git a/docs/features/budgets.mdx b/docs/features/budgets.mdx index 192ef67f9..4c2252f8b 100644 --- a/docs/features/budgets.mdx +++ b/docs/features/budgets.mdx @@ -28,14 +28,14 @@ Use budgets when you want limits such as: Budgets are enabled by default: ```env -BUDGETS_ENABLED=true +GOMODEL_BUDGETS_ENABLED=true ``` Budgets depend on usage tracking because spend is calculated from usage cost records: ```env -USAGE_ENABLED=true +GOMODEL_USAGE_ENABLED=true ``` If usage tracking is disabled, GoModel starts with budget management disabled @@ -73,16 +73,16 @@ budgets: Or use environment variables: ```env -SET_BUDGET_TEAM__ALPHA="daily=10,weekly=50" -SET_BUDGET_="monthly=500" +GOMODEL_SET_BUDGET_TEAM__ALPHA="daily=10,weekly=50" +GOMODEL_SET_BUDGET_="monthly=500" ``` -The suffix after `SET_BUDGET_` becomes a user path. Use double underscores +The suffix after `GOMODEL_SET_BUDGET_` becomes a user path. Use double underscores (`__`) between path segments; single underscores remain part of a segment: -- `SET_BUDGET_TEAM__ALPHA` -> `/team/alpha` -- `SET_BUDGET_USER_123` -> `/user_123` -- `SET_BUDGET_` -> `/` +- `GOMODEL_SET_BUDGET_TEAM__ALPHA` -> `/team/alpha` +- `GOMODEL_SET_BUDGET_USER_123` -> `/user_123` +- `GOMODEL_SET_BUDGET_` -> `/` There is no escape for a literal double underscore inside a path segment. Use YAML or the dashboard for paths that need a `__` segment value. diff --git a/docs/features/cache.mdx b/docs/features/cache.mdx index 4e1321b6e..e97f26e44 100644 --- a/docs/features/cache.mdx +++ b/docs/features/cache.mdx @@ -42,8 +42,8 @@ cache: You can also configure it with environment variables: - `REDIS_URL` -- `REDIS_KEY_RESPONSES` -- `REDIS_TTL_RESPONSES` +- `GOMODEL_REDIS_KEY_RESPONSES` +- `GOMODEL_REDIS_TTL_RESPONSES` ## Enable semantic caching diff --git a/docs/features/cost-tracking.mdx b/docs/features/cost-tracking.mdx index cac9b7269..830e538ba 100644 --- a/docs/features/cost-tracking.mdx +++ b/docs/features/cost-tracking.mdx @@ -68,7 +68,7 @@ A `---` value means the cost is unknown because no pricing was available, which ## Recalculate stored costs -Recalculating recomputes the stored cost of matching usage rows from current pricing. Use it after changing an override, or when pricing was unavailable at the time a request ran. The action is enabled by default (`USAGE_PRICING_RECALCULATION_ENABLED`); on the usage view, choose **Recalculate**, then type `recalculate` to confirm. It is scoped to the selected date range, provider or model, and user path. +Recalculating recomputes the stored cost of matching usage rows from current pricing. Use it after changing an override, or when pricing was unavailable at the time a request ran. The action is enabled by default (`GOMODEL_USAGE_PRICING_RECALCULATION_ENABLED`); on the usage view, choose **Recalculate**, then type `recalculate` to confirm. It is scoped to the selected date range, provider or model, and user path. Recalculation **overwrites** the cost fields of every matching row. If a row's diff --git a/docs/features/failover.mdx b/docs/features/failover.mdx index 3d8ab97fe..f069d59e8 100644 --- a/docs/features/failover.mdx +++ b/docs/features/failover.mdx @@ -49,10 +49,10 @@ Use bare model names like `gpt-4o` or provider-qualified selectors like For env-only deployments, use inline JSON: ```bash -FAILOVER_RULES_JSON='{"gpt-4o":["azure/gpt-4o","gemini/gemini-2.5-pro"]}' +GOMODEL_FAILOVER_RULES_JSON='{"gpt-4o":["azure/gpt-4o","gemini/gemini-2.5-pro"]}' ``` -`failover.default_mode` and `FAILOVER_MODE` are deprecated +`failover.default_mode` and `GOMODEL_FAILOVER_MODE` are deprecated compatibility inputs. They are accepted but ignored by runtime failover. The Dashboard can generate suggested mappings from model metadata, but diff --git a/docs/features/labelling.mdx b/docs/features/labelling.mdx index 74c090fe1..e5d98b500 100644 --- a/docs/features/labelling.mdx +++ b/docs/features/labelling.mdx @@ -71,10 +71,10 @@ Or with environment variables (an env entry replaces the whole YAML entry with the same header name; unset companion vars reset to defaults): ```bash -TAGGING_HEADER_1=X-My-Tags -TAGGING_HEADER_1_PREFIX=tag- # optional, trimmed from each extracted label only -TAGGING_HEADER_1_DONOTPASS=true # optional, default false (headers are forwarded as-is) -TAGGING_HEADER_1_DELIMITER=";" # optional, default "," +GOMODEL_TAGGING_HEADER_1=X-My-Tags +GOMODEL_TAGGING_HEADER_1_PREFIX=tag- # optional, trimmed from each extracted label only +GOMODEL_TAGGING_HEADER_1_DONOTPASS=true # optional, default false (headers are forwarded as-is) +GOMODEL_TAGGING_HEADER_1_DELIMITER=";" # optional, default "," ``` A client then labels a request like this: @@ -98,7 +98,7 @@ routes; translated routes never forward client headers anyway. - **Request log** — label chips per request, filterable with the `label` query param on `GET /admin/usage/log`. - **Audit logs** — recorded under `data.labels` on each entry (requires - `LOGGING_ENABLED=true`). + `GOMODEL_LOGGING_ENABLED=true`). - **API keys page** — each key's labels are listed alongside its user path. ## Defaults diff --git a/docs/features/mcp-gateway.mdx b/docs/features/mcp-gateway.mdx index 8506e5926..9a5e24185 100644 --- a/docs/features/mcp-gateway.mdx +++ b/docs/features/mcp-gateway.mdx @@ -26,7 +26,7 @@ the same way it already sits between your apps and model providers: the audit log and request log like any other model traffic — labelled with the JSON-RPC method, and with the tool or prompt name for calls, so a `tools/call` row reads `github_create_issue`, not a bare path. With - `LOGGING_LOG_BODIES=true` the JSON-RPC request and response frames are + `GOMODEL_LOGGING_LOG_BODIES=true` the JSON-RPC request and response frames are recorded on the audit entry as well (tool arguments and results included), subject to the same 1 MB capture cap as other endpoints. User-path rate limits and budgets gate MCP requests too. @@ -78,7 +78,7 @@ shown as the name in the dashboard. ```yaml mcp: - enabled: true # default; MCP_ENABLED=false disables the endpoints + enabled: true # default; GOMODEL_MCP_ENABLED=false disables the endpoints servers: github: url: https://api.githubcopilot.com/mcp @@ -92,10 +92,10 @@ mcp: args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"] ``` -Or via the `MCP_SERVERS` env var (a JSON object, merged over YAML per name): +Or via the `GOMODEL_MCP_SERVERS` env var (a JSON object, merged over YAML per name): ```bash -MCP_SERVERS='{"github":{"url":"https://api.githubcopilot.com/mcp","headers":{"Authorization":"Bearer ${GITHUB_PAT}"}}}' +GOMODEL_MCP_SERVERS='{"github":{"url":"https://api.githubcopilot.com/mcp","headers":{"Authorization":"Bearer ${GITHUB_PAT}"}}}' ``` Per-server fields: @@ -144,7 +144,7 @@ resource templates with their descriptions, after your `allowed_tools` / - **Streamable HTTP origin checks.** Requests carrying an `Origin` header must match the GoModel host, preventing browser-based DNS rebinding attacks. - **stdio is declarative-only.** stdio servers spawn subprocesses on the - gateway host, so they can only be declared in `config.yaml` / `MCP_SERVERS`. + gateway host, so they can only be declared in `config.yaml` / `GOMODEL_MCP_SERVERS`. The admin API and dashboard reject them — a dashboard login must never be equivalent to code execution on the gateway. - **stdio subprocesses get a minimal environment.** Only `PATH`, `HOME`, @@ -155,7 +155,7 @@ resource templates with their descriptions, after your `allowed_tools` / admin-created servers are stored in the configured storage backend (they are only redacted at the API/UI layer). If your threat model excludes secrets in the database, declare those servers in `config.yaml` / - `MCP_SERVERS` instead — `${ENV}` references keep the secret in the + `GOMODEL_MCP_SERVERS` instead — `${ENV}` references keep the secret in the environment, and declarative servers never touch the store. - **Remote URLs are server-side egress.** Dashboard-managed HTTP/SSE servers deliberately support private and loopback endpoints. Protect admin write diff --git a/docs/features/passthrough-api.mdx b/docs/features/passthrough-api.mdx index 8607aae95..2e79ae454 100644 --- a/docs/features/passthrough-api.mdx +++ b/docs/features/passthrough-api.mdx @@ -46,7 +46,7 @@ Passthrough routes use this shape: ``` For Anthropic, these two paths map to the same upstream endpoint when -`ALLOW_PASSTHROUGH_V1_ALIAS=true`, which is the default: +`GOMODEL_ALLOW_PASSTHROUGH_V1_ALIAS=true`, which is the default: ```text /p/anthropic/messages @@ -143,9 +143,9 @@ Passthrough is intentionally narrow while the API is in beta. Passthrough routes are enabled by default: ```env -ENABLE_PASSTHROUGH_ROUTES=true -ALLOW_PASSTHROUGH_V1_ALIAS=true -ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,openrouter,kilo,zai,vllm,deepseek +GOMODEL_ENABLE_PASSTHROUGH_ROUTES=true +GOMODEL_ALLOW_PASSTHROUGH_V1_ALIAS=true +GOMODEL_ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,openrouter,kilo,zai,vllm,deepseek ``` -Set `ENABLED_PASSTHROUGH_PROVIDERS` to the provider types you want to expose. +Set `GOMODEL_ENABLED_PASSTHROUGH_PROVIDERS` to the provider types you want to expose. diff --git a/docs/features/rate-limits.mdx b/docs/features/rate-limits.mdx index 121825e39..388be3a01 100644 --- a/docs/features/rate-limits.mdx +++ b/docs/features/rate-limits.mdx @@ -39,14 +39,14 @@ request and token velocity over seconds-to-a-day with fast in-memory counters. Rate limits are enabled by default and cost nothing until rules exist: ```env -RATE_LIMITS_ENABLED=true +GOMODEL_RATE_LIMITS_ENABLED=true ``` Token limits (`max_tokens`) are counted from usage records, so they require usage tracking: ```env -USAGE_ENABLED=true +GOMODEL_USAGE_ENABLED=true ``` Request and concurrency limits work without usage tracking. If token rules are @@ -103,15 +103,15 @@ rate_limits: max_tokens: 90000 ``` -Or use environment variables. User-path rules use `SET_RATE_LIMIT_` -(the suffix maps to a user path exactly like `SET_BUDGET_*`; `__` separates -path segments); provider rules use `SET_PROVIDER_RATE_LIMIT_` +Or use environment variables. User-path rules use `GOMODEL_SET_RATE_LIMIT_` +(the suffix maps to a user path exactly like `GOMODEL_SET_BUDGET_*`; `__` separates +path segments); provider rules use `GOMODEL_SET_PROVIDER_RATE_LIMIT_` (underscores become hyphens, like provider-instance env vars): ```env -SET_RATE_LIMIT_TEAM__ALPHA="rpm=100,tpm=50000,rpd=10000,concurrent=10" -SET_RATE_LIMIT_="rpm=1000" -SET_PROVIDER_RATE_LIMIT_OPENAI="rpm=500,concurrent=50" +GOMODEL_SET_RATE_LIMIT_TEAM__ALPHA="rpm=100,tpm=50000,rpd=10000,concurrent=10" +GOMODEL_SET_RATE_LIMIT_="rpm=1000" +GOMODEL_SET_PROVIDER_RATE_LIMIT_OPENAI="rpm=500,concurrent=50" ``` Compact names map to periods: `rpm`/`tpm` per minute, `rph`/`tph` per hour, @@ -119,7 +119,7 @@ Compact names map to periods: `rpm`/`tpm` per minute, `rph`/`tph` per hour, rule objects is also accepted for custom windows: ```env -SET_RATE_LIMIT_TEAM__ALPHA='[{"period_seconds":300,"max_requests":50}]' +GOMODEL_SET_RATE_LIMIT_TEAM__ALPHA='[{"period_seconds":300,"max_requests":50}]' ``` Model rules have no env form (model ids are not env-name safe); declare them diff --git a/docs/features/user-path.mdx b/docs/features/user-path.mdx index d0a5d2b82..409fe6532 100644 --- a/docs/features/user-path.mdx +++ b/docs/features/user-path.mdx @@ -29,7 +29,7 @@ Clients can also send a user path directly: X-GoModel-User-Path: /team/alpha ``` -The header name is configurable with `USER_PATH_HEADER` or +The header name is configurable with `GOMODEL_USER_PATH_HEADER` or `server.user_path_header` in `config.yaml`. If unset, GoModel uses `X-GoModel-User-Path`. diff --git a/docs/features/virtual-models.mdx b/docs/features/virtual-models.mdx index 55548b8e6..82e6b57a3 100644 --- a/docs/features/virtual-models.mdx +++ b/docs/features/virtual-models.mdx @@ -90,7 +90,7 @@ fixed in configuration; rename them by editing the declaration. ## Define virtual models as code Operators who manage configuration as infrastructure-as-code can declare virtual -models in `config.yaml` or the `VIRTUAL_MODELS` environment variable instead of +models in `config.yaml` or the `GOMODEL_VIRTUAL_MODELS` environment variable instead of the dashboard. Declarative entries are applied at startup, **override** any admin-store row with the same `source`, and are shown read-only in the dashboard (a **Config** badge). Edit the configuration to change them. @@ -123,7 +123,7 @@ The same list as a single environment variable (JSON), which overrides `config.yaml` entries with a matching `source`: ```env -VIRTUAL_MODELS=[{"source":"smart","strategy":"cost","targets":[{"model":"openai/gpt-4o"},{"model":"groq/llama-3.3-70b"}]}] +GOMODEL_VIRTUAL_MODELS=[{"source":"smart","strategy":"cost","targets":[{"model":"openai/gpt-4o"},{"model":"groq/llama-3.3-70b"}]}] ``` Each entry accepts `source`, a single `target` (shorthand) or a `targets` list, @@ -148,7 +148,7 @@ Two lenient exceptions keep valid setups booting: To hide provider models from `GET /v1/models`, set: ```env -KEEP_ONLY_ALIASES_AT_MODELS_ENDPOINT=true +GOMODEL_KEEP_ONLY_ALIASES_AT_MODELS_ENDPOINT=true ``` When this is enabled, GoModel returns enabled redirects from `/v1/models` diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index 25b0ea4e0..d4a2f516b 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -15,10 +15,10 @@ admin visibility in one place. ```bash docker run --rm -p 8080:8080 \ - -e LOGGING_ENABLED=true \ - -e LOGGING_LOG_BODIES=true \ - -e LOG_FORMAT=text \ - -e LOGGING_LOG_HEADERS=true \ + -e GOMODEL_LOGGING_ENABLED=true \ + -e GOMODEL_LOGGING_LOG_BODIES=true \ + -e GOMODEL_LOG_FORMAT=text \ + -e GOMODEL_LOGGING_LOG_HEADERS=true \ -e GOMODEL_MASTER_KEY="change-me" \ -e OPENAI_API_KEY="sk-..." \ enterpilot/gomodel @@ -92,7 +92,7 @@ Open this URL in your browser: `http://localhost:8080/admin/dashboard` - Dashboard UI is enabled by default (`ADMIN_UI_ENABLED=true`). Admin API + Dashboard UI is enabled by default (`GOMODEL_ADMIN_UI_ENABLED=true`). Admin API endpoints are at `/admin/*` and use the same bearer auth as the main API. diff --git a/docs/guides/prometheus-metrics.mdx b/docs/guides/prometheus-metrics.mdx index d70496788..347628356 100644 --- a/docs/guides/prometheus-metrics.mdx +++ b/docs/guides/prometheus-metrics.mdx @@ -18,19 +18,19 @@ endpoint in GoModel. ### Disabled by Default Metrics are **disabled by default**. To enable metrics collection, set -`METRICS_ENABLED=true` and start GoModel: +`GOMODEL_METRICS_ENABLED=true` and start GoModel: ```bash Docker (.env file) -# Add METRICS_ENABLED=true to your .env file, then: +# Add GOMODEL_METRICS_ENABLED=true to your .env file, then: docker run --rm -p 8080:8080 --env-file .env enterpilot/gomodel # Metrics available at http://localhost:8080/metrics ``` ```bash Docker (inline -e) docker run --rm -p 8080:8080 \ - -e METRICS_ENABLED=true \ + -e GOMODEL_METRICS_ENABLED=true \ enterpilot/gomodel # Metrics available at http://localhost:8080/metrics ``` @@ -39,7 +39,7 @@ docker run --rm -p 8080:8080 \ # Requires Go 1.26.4+; produces bin/gomodel. make build -export METRICS_ENABLED=true +export GOMODEL_METRICS_ENABLED=true ./bin/gomodel # Metrics available at http://localhost:8080/metrics ``` @@ -57,13 +57,13 @@ export METRICS_ENABLED=true **Option 1: Environment Variable** ```bash -METRICS_ENABLED=false +GOMODEL_METRICS_ENABLED=false ``` **Option 2: .env file** ```bash -echo "METRICS_ENABLED=false" >> .env +echo "GOMODEL_METRICS_ENABLED=false" >> .env ``` **Option 3: config.yaml** @@ -78,8 +78,8 @@ metrics: Change the default `/metrics` path: ```bash -METRICS_ENABLED=true -METRICS_ENDPOINT=/internal/prometheus +GOMODEL_METRICS_ENABLED=true +GOMODEL_METRICS_ENDPOINT=/internal/prometheus ``` ## Configuration Options @@ -88,8 +88,8 @@ METRICS_ENDPOINT=/internal/prometheus | Variable | Default | Description | | ------------------ | ---------- | --------------------------------- | -| `METRICS_ENABLED` | `false` | Enable/disable metrics collection | -| `METRICS_ENDPOINT` | `/metrics` | HTTP path for metrics endpoint | +| `GOMODEL_METRICS_ENABLED` | `false` | Enable/disable metrics collection | +| `GOMODEL_METRICS_ENDPOINT` | `/metrics` | HTTP path for metrics endpoint | ### Via config.yaml @@ -112,8 +112,8 @@ metrics: ```bash PORT=8080 GOMODEL_MASTER_KEY=your-secret-key -METRICS_ENABLED=true -METRICS_ENDPOINT=/metrics +GOMODEL_METRICS_ENABLED=true +GOMODEL_METRICS_ENDPOINT=/metrics OPENAI_API_KEY=sk-... ``` @@ -123,7 +123,7 @@ OPENAI_API_KEY=sk-... ```bash PORT=8080 -METRICS_ENABLED=false +GOMODEL_METRICS_ENABLED=false OPENAI_API_KEY=sk-... ``` @@ -264,11 +264,11 @@ scrape_configs: ```bash # Check configuration -echo $METRICS_ENABLED # "true" enables; empty or "false" disables +echo $GOMODEL_METRICS_ENABLED # "true" enables; empty or "false" disables ``` -Set `METRICS_ENABLED=true` via your `.env` file, an inline `-e METRICS_ENABLED=true` -flag, or `export METRICS_ENABLED=true` (see the [Quick Start](#disabled-by-default) +Set `GOMODEL_METRICS_ENABLED=true` via your `.env` file, an inline `-e GOMODEL_METRICS_ENABLED=true` +flag, or `export GOMODEL_METRICS_ENABLED=true` (see the [Quick Start](#disabled-by-default) for the three launch forms), then restart GoModel. ### No Metrics Data Appearing @@ -294,13 +294,13 @@ curl http://localhost:8080/metrics | grep gomodel_requests_total **Incorrect:** ```bash -export METRICS_ENDPOINT=metrics # Missing leading slash +export GOMODEL_METRICS_ENDPOINT=metrics # Missing leading slash ``` **Correct:** ```bash -export METRICS_ENDPOINT=/metrics # Has leading slash +export GOMODEL_METRICS_ENDPOINT=/metrics # Has leading slash ``` ## Best Practices diff --git a/docs/providers/anthropic.mdx b/docs/providers/anthropic.mdx index 634acff98..c3c5ab109 100644 --- a/docs/providers/anthropic.mdx +++ b/docs/providers/anthropic.mdx @@ -13,7 +13,7 @@ thinking and effort controls, which differ by model generation. ```bash ANTHROPIC_API_KEY=sk-ant-... # ANTHROPIC_BASE_URL=https://api.anthropic.com/v1 # optional override -# ANTHROPIC_DEFAULT_MAX_TOKENS=4096 # injected when callers omit max_tokens +# GOMODEL_ANTHROPIC_DEFAULT_MAX_TOKENS=4096 # injected when callers omit max_tokens ``` Or in `config.yaml`: @@ -27,7 +27,7 @@ providers: Anthropic's `/v1/messages` requires `max_tokens` on every request. GoModel - injects `ANTHROPIC_DEFAULT_MAX_TOKENS` (default `4096`) when a caller omits + injects `GOMODEL_ANTHROPIC_DEFAULT_MAX_TOKENS` (default `4096`) when a caller omits it, keeping the OpenAI-compatible surface lenient. diff --git a/docs/providers/bailian.mdx b/docs/providers/bailian.mdx index 05eb915dc..b767bb60d 100644 --- a/docs/providers/bailian.mdx +++ b/docs/providers/bailian.mdx @@ -101,7 +101,7 @@ curl http://localhost:8080/v1/chat/completions \ Embedding models (`text-embedding-v3`, `text-embedding-v4`) are served by the compatible-mode API but are **not** auto-discovered from the upstream `/v1/models` endpoint. Set `BAILIAN_MODELS=text-embedding-v3,text-embedding-v4` - or use `CONFIGURED_PROVIDER_MODELS_MODE=allowlist` to make them available. + or use `GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE=allowlist` to make them available. ## References diff --git a/docs/providers/bedrock.mdx b/docs/providers/bedrock.mdx index 6ef5a1542..0f8038aed 100644 --- a/docs/providers/bedrock.mdx +++ b/docs/providers/bedrock.mdx @@ -90,7 +90,7 @@ providers: - amazon.nova-lite-v1:0 ``` -Behavior follows the global `CONFIGURED_PROVIDER_MODELS_MODE` — `fallback` +Behavior follows the global `GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE` — `fallback` (default) uses the list only when `ListFoundationModels` is unavailable or empty; `allowlist` exposes only configured models and skips the upstream call. diff --git a/docs/providers/gemini.mdx b/docs/providers/gemini.mdx index b50da6a00..cc18a4679 100644 --- a/docs/providers/gemini.mdx +++ b/docs/providers/gemini.mdx @@ -30,7 +30,7 @@ providers: ``` - `USE_GOOGLE_GEMINI_NATIVE_API` is still honored as a legacy global toggle + `GOMODEL_USE_GOOGLE_GEMINI_NATIVE_API` is still honored as a legacy global toggle when per-provider `GEMINI_API_MODE` is unset. Prefer `GEMINI_API_MODE`. diff --git a/docs/providers/opencode-go.mdx b/docs/providers/opencode-go.mdx index 43be376c4..6f75095ca 100644 --- a/docs/providers/opencode-go.mdx +++ b/docs/providers/opencode-go.mdx @@ -59,7 +59,7 @@ The set of `/messages`-only models is currently maintained manually (default: metadata. Override it when needed: ```bash -OPENCODE_GO_MESSAGES_MODELS=qwen3.7-max +GOMODEL_OPENCODE_GO_MESSAGES_MODELS=qwen3.7-max ``` diff --git a/docs/providers/oracle.mdx b/docs/providers/oracle.mdx index c70a39223..01951ca81 100644 --- a/docs/providers/oracle.mdx +++ b/docs/providers/oracle.mdx @@ -28,7 +28,7 @@ GOMODEL_MASTER_KEY=change-me ``` `ORACLE_MODELS` is a comma-separated list (whitespace trimmed). Behavior -follows the global `CONFIGURED_PROVIDER_MODELS_MODE` — `fallback` (default) +follows the global `GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE` — `fallback` (default) uses the list when Oracle's `/models` is unavailable or empty; `allowlist` exposes only configured models and skips the upstream call. diff --git a/docs/providers/overview.mdx b/docs/providers/overview.mdx index bdff30395..ee897473f 100644 --- a/docs/providers/overview.mdx +++ b/docs/providers/overview.mdx @@ -69,7 +69,7 @@ support, not every individual model capability exposed by an upstream provider. `XIAOMI_MODELS`. - **OpenCode Go (OpenCode Zen)** — routes per model: most models use OpenAI-style `/chat/completions`, while `/messages`-only models (default - `qwen3.7-max`, override with `OPENCODE_GO_MESSAGES_MODELS`) are sent to the + `qwen3.7-max`, override with `GOMODEL_OPENCODE_GO_MESSAGES_MODELS`) are sent to the Anthropic-native endpoint. Set `OPENCODE_GO_API_KEY`; the base URL defaults to `https://opencode.ai/zen/go/v1`. - **Kimi Code** — no per-token pricing; quota refreshes weekly and is also @@ -82,9 +82,9 @@ support, not every individual model capability exposed by an upstream provider. `ORACLE_MODELS=openai.gpt-oss-120b,xai.grok-3`. DeepSeek defaults to `https://api.deepseek.com`; set `DEEPSEEK_BASE_URL` only when using a compatible proxy or alternate DeepSeek endpoint. By default, - `CONFIGURED_PROVIDER_MODELS_MODE=fallback` uses those lists only when upstream + `GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE=fallback` uses those lists only when upstream `/models` is unavailable or empty. Set - `CONFIGURED_PROVIDER_MODELS_MODE=allowlist` to expose only configured models + `GOMODEL_CONFIGURED_PROVIDER_MODELS_MODE=allowlist` to expose only configured models for providers that define a list, skipping their upstream `/models` calls. - **vLLM** — set `VLLM_API_KEY` only if the upstream server was started with `--api-key`. diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index 1ac6472f5..5466a21fa 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -174,7 +174,7 @@ Generate provider environment variables for the Deployment. {{- end }} {{- end }} {{- if and (eq $name "gemini") (hasKey $config "useNativeApi") }} -- name: USE_GOOGLE_GEMINI_NATIVE_API +- name: GOMODEL_USE_GOOGLE_GEMINI_NATIVE_API value: {{ $config.useNativeApi | quote }} {{- end }} {{- end }} diff --git a/helm/templates/configmap.yaml b/helm/templates/configmap.yaml index cd8debbe7..7329b8c11 100644 --- a/helm/templates/configmap.yaml +++ b/helm/templates/configmap.yaml @@ -6,16 +6,16 @@ metadata: {{- include "gomodel.labels" . | nindent 4 }} data: PORT: {{ .Values.server.port | quote }} - BASE_PATH: {{ .Values.server.basePath | default "/" | quote }} - USER_PATH_HEADER: {{ .Values.server.userPathHeader | default "X-GoModel-User-Path" | quote }} - BODY_SIZE_LIMIT: {{ .Values.server.bodySizeLimit | quote }} + GOMODEL_BASE_PATH: {{ .Values.server.basePath | default "/" | quote }} + GOMODEL_USER_PATH_HEADER: {{ .Values.server.userPathHeader | default "X-GoModel-User-Path" | quote }} + GOMODEL_BODY_SIZE_LIMIT: {{ .Values.server.bodySizeLimit | quote }} {{- if or .Values.redis.enabled .Values.cache.redis.url }} - REDIS_KEY_MODELS: {{ .Values.cache.redis.keyModels | default "gomodel:models" | quote }} - REDIS_KEY_RESPONSES: {{ .Values.cache.redis.keyResponses | default "gomodel:response:" | quote }} - REDIS_TTL_MODELS: {{ .Values.cache.redis.ttlModels | default 86400 | quote }} - REDIS_TTL_RESPONSES: {{ .Values.cache.redis.ttlResponses | default 3600 | quote }} - RESPONSE_CACHE_SIMPLE_ENABLED: "true" + GOMODEL_REDIS_KEY_MODELS: {{ .Values.cache.redis.keyModels | default "gomodel:models" | quote }} + GOMODEL_REDIS_KEY_RESPONSES: {{ .Values.cache.redis.keyResponses | default "gomodel:response:" | quote }} + GOMODEL_REDIS_TTL_MODELS: {{ .Values.cache.redis.ttlModels | default 86400 | quote }} + GOMODEL_REDIS_TTL_RESPONSES: {{ .Values.cache.redis.ttlResponses | default 3600 | quote }} + GOMODEL_RESPONSE_CACHE_SIMPLE_ENABLED: "true" {{- end }} - METRICS_ENABLED: {{ .Values.metrics.enabled | quote }} - METRICS_ENDPOINT: {{ .Values.metrics.endpoint | quote }} - LOG_FORMAT: {{ .Values.logging.format | quote }} + GOMODEL_METRICS_ENABLED: {{ .Values.metrics.enabled | quote }} + GOMODEL_METRICS_ENDPOINT: {{ .Values.metrics.endpoint | quote }} + GOMODEL_LOG_FORMAT: {{ .Values.logging.format | quote }} diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index e12dd561f..c31abfc19 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -48,21 +48,21 @@ spec: configMapKeyRef: name: {{ include "gomodel.fullname" . }} key: PORT - - name: BASE_PATH + - name: GOMODEL_BASE_PATH valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: BASE_PATH - - name: USER_PATH_HEADER + key: GOMODEL_BASE_PATH + - name: GOMODEL_USER_PATH_HEADER valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: USER_PATH_HEADER - - name: BODY_SIZE_LIMIT + key: GOMODEL_USER_PATH_HEADER + - name: GOMODEL_BODY_SIZE_LIMIT valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: BODY_SIZE_LIMIT + key: GOMODEL_BODY_SIZE_LIMIT # Cache configuration {{- if or .Values.redis.enabled .Values.cache.redis.url }} - name: REDIS_URL @@ -71,44 +71,44 @@ spec: name: {{ include "gomodel.providerSecretName" . }} key: REDIS_URL optional: true - - name: REDIS_KEY_MODELS + - name: GOMODEL_REDIS_KEY_MODELS valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: REDIS_KEY_MODELS - - name: REDIS_KEY_RESPONSES + key: GOMODEL_REDIS_KEY_MODELS + - name: GOMODEL_REDIS_KEY_RESPONSES valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: REDIS_KEY_RESPONSES - - name: REDIS_TTL_MODELS + key: GOMODEL_REDIS_KEY_RESPONSES + - name: GOMODEL_REDIS_TTL_MODELS valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: REDIS_TTL_MODELS - - name: REDIS_TTL_RESPONSES + key: GOMODEL_REDIS_TTL_MODELS + - name: GOMODEL_REDIS_TTL_RESPONSES valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: REDIS_TTL_RESPONSES + key: GOMODEL_REDIS_TTL_RESPONSES {{- end }} # Logging configuration - - name: LOG_FORMAT + - name: GOMODEL_LOG_FORMAT valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: LOG_FORMAT + key: GOMODEL_LOG_FORMAT # Metrics configuration - - name: METRICS_ENABLED + - name: GOMODEL_METRICS_ENABLED valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: METRICS_ENABLED - - name: METRICS_ENDPOINT + key: GOMODEL_METRICS_ENABLED + - name: GOMODEL_METRICS_ENDPOINT valueFrom: configMapKeyRef: name: {{ include "gomodel.fullname" . }} - key: METRICS_ENDPOINT + key: GOMODEL_METRICS_ENDPOINT # Authentication {{- if or .Values.auth.masterKey .Values.auth.existingSecret }} - name: GOMODEL_MASTER_KEY