Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,11 @@
# MINIMAX_API_KEY=...
# MINIMAX_BASE_URL=https://api.minimax.io/v1

# ElevenLabs (voice: text-to-speech + speech-to-text; default base URL: https://api.elevenlabs.io)
# The OpenAI "voice" field must be an ElevenLabs voice_id.
# ELEVENLABS_API_KEY=...
# ELEVENLABS_BASE_URL=https://api.elevenlabs.io

# Xiaomi MiMo (default base URL: https://api.xiaomimimo.com/v1)
# XIAOMI_API_KEY=...
# XIAOMI_BASE_URL=https://api.xiaomimimo.com/v1
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,5 +138,5 @@ Full reference: `.env.template` and `config/config.yaml`
- **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.
- **Provider API key rotation:** Any API-key provider accepts several keys: `<PROVIDER>[_SUFFIX]_API_KEY_<n>` env vars (numbered from 2; `_1` is accepted as a synonym for the unsuffixed key) or `providers.<name>.api_keys` in `config.yaml` (merged after `api_key`, de-duplicated, unresolved `${...}` entries dropped; env replaces the whole YAML list). Identified sessions deterministically stay on one key by default, preserving provider prompt-cache affinity while spreading different sessions across the configured keys; sessionless requests remain round robin. Set `providers.<name>.session_sticky_keys: false` or untick **Session-sticky API keys** in the provider editor for strict per-request round robin. Realtime sessions use the same affinity. 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`. Providers configured without keys are unaffected; Ollama is normally keyless, while SGLang and vLLM participate in rotation when optional API keys are configured. Non-API-key providers (Vertex, Bedrock) are unaffected.
- **Provider credentials without env vars:** Every provider below can instead be configured from the admin dashboard's Providers page (or `/admin/provider-credentials` GET/PUT/DELETE), persisted to the `provider_credentials` store β€” the same declarative-shadows-store precedence as MCP servers: a provider name declared via env vars/`config.yaml` is read-only in the dashboard (`managed: true`), and a store row upsert/delete hot-registers or unregisters the provider into the live registry immediately, no restart. `GOMODEL` boots fine with zero providers configured (empty catalog) so this is a complete alternative to env-var credentials, not just a supplement. API keys (`api_keys`, an ordered rotation list, same semantics as `providers.<name>.api_keys`) and service-account secrets are redacted as `***********` on read; an upsert echoing any all-asterisk mask of at least three characters at a position preserves the stored value there (rejected if that position was never set). Disabling a row (`enabled: false`) unregisters it from routing without deleting the stored credentials. `GET /admin/provider-credentials/types` lists every constructible provider type with the credential form it accepts (`fields[]` of `name`/`required`/`advanced`/`options`, plus `default_base_url`), derived from each provider's `DiscoveryConfig` β€” the dashboard renders only those fields, so an OpenAI-type provider asks for an API key while a Vertex one asks for project/location/service account and no key at all. Upserts are validated against that form (and against Google's project-or-base-URL and service-account rules) *before* anything is persisted, so an unusable credential is rejected with a 400 naming the offending field in `error.param` rather than stored as a broken row.
- **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), `BEDROCK_BASE_URL` (Bedrock Runtime region or endpoint), `BEDROCK_MANTLE_API_KEY`, `BEDROCK_MANTLE_BASE_URL` (Mantle region or endpoint), `BEDROCK_MANTLE_API_MODE` (`auto`, `openai`, or `standard`), `<PROVIDER>[_SUFFIX]_MODELS` (comma-separated configured model list for any provider type), `OLLAMA_BASE_URL`, `SGLANG_BASE_URL`, `SGLANG_API_KEY` (optional upstream SGLang bearer token), `VLLM_BASE_URL`, `VLLM_API_KEY` (optional upstream vLLM bearer token), `LLMD_BASE_URL`, `LLMD_API_KEY` (optional Gateway bearer token), `LLMD_INFERENCE_OBJECTIVE`, `LLMD_FAIRNESS_FROM_USER_PATH`
- **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), `ELEVENLABS_API_KEY`, `ELEVENLABS_BASE_URL` (optional ElevenLabs endpoint override; default `https://api.elevenlabs.io`; voice-only provider exposing `/v1/audio/speech` and `/v1/audio/transcriptions` β€” no chat, `/responses`, or embeddings; the OpenAI `voice` field must be an ElevenLabs voice_id), `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), `BEDROCK_BASE_URL` (Bedrock Runtime region or endpoint), `BEDROCK_MANTLE_API_KEY`, `BEDROCK_MANTLE_BASE_URL` (Mantle region or endpoint), `BEDROCK_MANTLE_API_MODE` (`auto`, `openai`, or `standard`), `<PROVIDER>[_SUFFIX]_MODELS` (comma-separated configured model list for any provider type), `OLLAMA_BASE_URL`, `SGLANG_BASE_URL`, `SGLANG_API_KEY` (optional upstream SGLang bearer token), `VLLM_BASE_URL`, `VLLM_API_KEY` (optional upstream vLLM bearer token), `LLMD_BASE_URL`, `LLMD_API_KEY` (optional Gateway bearer token), `LLMD_INFERENCE_OBJECTIVE`, `LLMD_FAIRNESS_FROM_USER_PATH`
- **Provider model metadata:** `providers.<name>.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.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ GoModel supports OpenAI, Anthropic, Cohere, Google Gemini, Vertex AI, DeepSeek,
Groq, Fireworks AI, Meta (Muse Spark), OpenRouter, Z.ai, xAI (Grok), Alibaba
Cloud Model Studio (Bailian), Kilo AI, MiniMax, Xiaomi MiMo, OpenCode Go, Azure
OpenAI, Oracle, Ollama, SGLang, vLLM, llm-d, Amazon Bedrock Runtime, Amazon
Bedrock Mantle, and all OpenAI-compatible providers.
Bedrock Mantle, and all OpenAI-compatible providers. Voice: ElevenLabs
(text-to-speech and speech-to-text).

See the [Providers Overview](https://gomodel.enterpilot.io/docs/providers/overview?utm_source=readme) for the full
per-provider feature matrix (chat, `/responses`, embeddings, files, batches,
Expand Down
7 changes: 7 additions & 0 deletions config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,13 @@ providers:
# base_url defaults to "https://llm.chutes.ai/v1".
# Set base_url when using a different compatible endpoint.

elevenlabs:
type: elevenlabs
api_key: "${ELEVENLABS_API_KEY}"
# base_url defaults to "https://api.elevenlabs.io".
# Voice-only provider: text-to-speech and speech-to-text, no chat. The
# OpenAI "voice" field must be an ElevenLabs voice_id.

meta:
type: meta
api_key: "..."
Expand Down
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
"providers/bailian",
"providers/xiaomi",
"providers/minimax",
"providers/elevenlabs",
"providers/opencode-go",
"providers/sglang",
"providers/vllm",
Expand Down
110 changes: 110 additions & 0 deletions docs/providers/elevenlabs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: "ElevenLabs"
description: "Configure ElevenLabs in GoModel: voice_id vs named voices, supported audio formats, and speech-to-text timestamps."
icon: "waveform-lines"
keywords: ["ElevenLabs", "text-to-speech", "TTS", "speech-to-text", "STT", "voice", "provider setup"]
---

ElevenLabs is a voice-only provider: it exposes text-to-speech and
speech-to-text behind the standard `/v1/audio/speech` and
`/v1/audio/transcriptions` endpoints. It has no chat, `/responses`, or
embeddings API, so those endpoints return `invalid_request_error` for
ElevenLabs-routed models.

## Configure

```bash
ELEVENLABS_API_KEY=...
```

Or in `config.yaml`:

```yaml
providers:
elevenlabs:
type: elevenlabs
api_key: "${ELEVENLABS_API_KEY}"
# base_url defaults to "https://api.elevenlabs.io".
```

## Voices are IDs, not names

Unlike OpenAI's fixed voice names (`alloy`, `verse`, ...), ElevenLabs has no
built-in named voices β€” every voice is an ID from your ElevenLabs voice
library (built-in, cloned, or shared). Pass that ID as the OpenAI-compatible
`voice` field:

```json
{
"model": "eleven_multilingual_v2",
"input": "Hello there",
"voice": "21m00Tcm4TlvDq8ikWAM"
}
```

List your available voice IDs from the ElevenLabs dashboard, or via
[passthrough](/features/passthrough-api) (once `elevenlabs` is added to
`ENABLED_PASSTHROUGH_PROVIDERS`) at `/p/elevenlabs/v2/voices` β€” the newer
`GET /v2/voices` search endpoint. `/p/elevenlabs/v1/voices` does not currently
work: GoModel's provider-passthrough router treats a leading `v1/` segment in
the path as an alias for providers whose base URL already embeds `/v1` (e.g.
OpenAI), and strips it before forwarding. ElevenLabs' base URL is
`https://api.elevenlabs.io` with no `/v1`, so every `/p/elevenlabs/v1/...`
passthrough call currently 404s. This affects all of ElevenLabs' native `/v1`
surface via passthrough, not just voice listing β€” see "Not implemented" below.

## Supported speech formats

`response_format` accepts `mp3` (default), `opus`, `pcm`, and `wav`; each maps
to a fixed ElevenLabs `output_format` (`mp3_44100_128`, `opus_48000_128`,
`pcm_44100`, `wav_44100`). `aac` and `flac` are not supported and return
`invalid_request_error`. `speed`, when set, is clamped to ElevenLabs' `0.7`-`1.2`
voice setting range (OpenAI accepts `0.25`-`4.0`); `instructions` is not
supported.

## Speech-to-text models and timestamps

Transcription models (`scribe_v2`, current; `scribe_v1`, still valid) are a
separate model family from the text-to-speech catalog and are not returned by
ElevenLabs' `/v1/models` listing β€” GoModel adds them to `/v1/models` output
itself. `response_format` accepts `json` (default), `text`, and
`verbose_json`; `srt`/`vtt` are not supported. Requesting `verbose_json`, or
`word` in `timestamp_granularities`, asks ElevenLabs for word-level timing,
which GoModel maps into the OpenAI `words` array. `prompt` is not supported.

## Not supported by ElevenLabs

All of these return `invalid_request_error` rather than silently dropping the
option:

- Chat completions, `/v1/responses`, and embeddings.
- Speech `instructions`, and `response_format` values other than
`mp3`/`opus`/`pcm`/`wav`.
- Transcription `prompt`, and `response_format` values other than
`json`/`text`/`verbose_json`.

## Not implemented

GoModel only implements the two ElevenLabs capabilities that map onto
OpenAI-compatible endpoints: text-to-speech and speech-to-text. Everything
else ElevenLabs offers has no typed support in GoModel today:

- **Speech-to-speech (voice changer)** β€” `POST /v1/speech-to-speech/{voice_id}`
has no OpenAI-compatible equivalent to translate from, so there's no typed
endpoint for it. It could be added as a native passthrough route in the
future, but **passthrough for it does not currently work either** β€” see the
`/v1` alias limitation above; speech-to-speech has no `/v2` path to work
around it with, so it is entirely unreachable through GoModel right now.
- **Dubbing, voice cloning/design, projects (Studio), and conversational AI
(agents)** β€” same reasoning: no OpenAI-compatible shape to translate to, and
(for the `/v1`-only parts of these APIs) the same passthrough limitation
applies. These are reasonable candidates for future native passthrough
support once that limitation is fixed.
- **Realtime/streaming TTS and STT** (`/v1/text-to-speech/{voice_id}/stream`,
WebSocket streaming) β€” GoModel's `/v1/audio/speech` and
`/v1/audio/transcriptions` are synchronous request/response; no streaming
variant is implemented for ElevenLabs.

None of this is ElevenLabs-specific scope creep avoidance β€” it reflects that
GoModel's audio surface is deliberately OpenAI-shaped, and ElevenLabs' API is
much larger than OpenAI's TTS/STT pair.
7 changes: 7 additions & 0 deletions docs/providers/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ support, not every individual model capability exposed by an upstream provider.
| Alibaba Cloud Model Studio (Bailian) | `BAILIAN_API_KEY` (`BAILIAN_BASE_URL` optional) | `qwen3-max` | βœ… | βœ… | βœ… | βœ… | βœ… | βœ… | [Alibaba Cloud Model Studio](/providers/bailian) |
| MiniMax | `MINIMAX_API_KEY` (`MINIMAX_BASE_URL` optional) | `MiniMax-M3` | βœ… | βœ… | βœ… | ❌ | ❌ | βœ… | [MiniMax](/providers/minimax) |
| Xiaomi MiMo | `XIAOMI_API_KEY` (`XIAOMI_BASE_URL` optional) | `mimo-v2.5-pro` | βœ… | βœ… | ❌ | ❌ | ❌ | βœ… | [Xiaomi MiMo](/providers/xiaomi) |
| ElevenLabs (voice only) | `ELEVENLABS_API_KEY` (`ELEVENLABS_BASE_URL` optional) | `eleven_multilingual_v2` | ❌ | ❌ | ❌ | ❌ | ❌ | βœ… | [ElevenLabs](/providers/elevenlabs) |
| OpenCode Go | `OPENCODE_GO_API_KEY` (`OPENCODE_GO_BASE_URL` optional) | `glm-5.1` | βœ… | βœ… | ❌ | ❌ | ❌ | ❌ | [OpenCode Go](/providers/opencode-go) |
| Kimi Code | `KIMICODE_API_KEY` | `kimi-for-coding` | βœ… | βœ… | βœ… | ❌ | ❌ | βœ… | [Kimi Code](/providers/kimicode) |
| Azure OpenAI | `AZURE_API_KEY` + `AZURE_BASE_URL` (`AZURE_API_VERSION` optional) | `gpt-5` | βœ… | βœ… | βœ… | βœ… | βœ… | βœ… | [Azure OpenAI](/providers/azure) |
Expand Down Expand Up @@ -91,6 +92,12 @@ support, not every individual model capability exposed by an upstream provider.
- **Fireworks AI** β€” model IDs are account-scoped paths such as
`accounts/fireworks/models/gpt-oss-120b`; use them verbatim in requests and
in `FIREWORKS_MODELS`.
- **ElevenLabs** β€” a voice-only provider: `/v1/audio/speech` (text-to-speech)
and `/v1/audio/transcriptions` (speech-to-text) are supported, but chat,
`/v1/responses`, and embeddings are not (the ❌s above reflect that, not a
gateway limitation). The OpenAI `voice` field must be an ElevenLabs
voice_id. See the [ElevenLabs guide](/providers/elevenlabs) for supported
audio formats and speech-to-text models.
- **Chutes AI** β€” defaults to `https://llm.chutes.ai/v1` and discovers its
current model IDs, context limits, capabilities, and pricing from the live
catalog. GoModel translates `/v1/responses` requests to chat completions;
Expand Down

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion internal/admin/dashboard/static/dist/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading