Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 128 additions & 128 deletions .env.template

Large diffs are not rendered by default.

53 changes: 27 additions & 26 deletions CLAUDE.md

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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:
Expand All @@ -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

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
48 changes: 24 additions & 24 deletions config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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, …)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -174,23 +174,23 @@ 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
max_requests: 10
# 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:
Expand Down Expand Up @@ -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":
Expand Down
12 changes: 6 additions & 6 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 7 additions & 7 deletions docs/advanced/admin-endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ variables, defaults, and the equivalent `admin:` YAML block.

<Note>
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.
</Note>

Expand All @@ -41,7 +41,7 @@ The dashboard UI pages (`/admin/dashboard`) and static assets (`/admin/static/*`

<Warning>
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.
</Warning>

Expand Down Expand Up @@ -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:**

Expand Down Expand Up @@ -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
Expand All @@ -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
```

<Tip>
Expand Down
8 changes: 4 additions & 4 deletions docs/advanced/api-endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -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 |
Expand Down
6 changes: 3 additions & 3 deletions docs/advanced/audio-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion docs/advanced/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading