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
12 changes: 10 additions & 2 deletions .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@
# Allow optional /p/{provider}/v1/... passthrough aliases while keeping /p/{provider}/... canonical (default: true)
# ALLOW_PASSTHROUGH_V1_ALIAS=true

# Comma-separated list of provider types enabled for /p/{provider}/... passthrough (default: openai,anthropic,openrouter,kilo,zai,sglang,vllm,llamacpp,llmd,deepseek,jev)
# Comma-separated list of provider types enabled for /p/{provider}/... passthrough (default: openai,anthropic,openrouter,kilo,zai,sglang,vllm,llamacpp,llmd,deepseek,edenai,jev)
# Cohere and audio.cpp native passthrough are opt-in; add cohere or audiocpp when
# those routes are needed. audio.cpp's native surface includes model management
# and server-local file paths, so enable it only for trusted callers.
# ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,cohere,openrouter,kilo,zai,sglang,vllm,llamacpp,llmd,audiocpp,deepseek,hetzner,jev
# ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,cohere,openrouter,kilo,zai,sglang,vllm,llamacpp,llmd,audiocpp,deepseek,hetzner,edenai,jev

# Enable the realtime (speech-to-speech) endpoints (default: true): the /v1/realtime
# websocket (and /p/{provider}/v1/realtime passthrough upgrade), the WebRTC SDP
Expand Down Expand Up @@ -726,6 +726,14 @@
# Optional configured model list; see CONFIGURED_PROVIDER_MODELS_MODE below
# KILO_MODELS=anthropic/claude-sonnet-4.5,openai/gpt-5.5

# Eden AI (default base URL: https://api.edenai.run/v3)
# Multi-provider gateway. Model IDs use provider/model and pass through unchanged.
# EDENAI_BASE_URL is optional: the default above is used when it is unset.
# EDENAI_API_KEY=
# EDENAI_BASE_URL=https://api.edenai.run/v3
# Optional configured model list; see CONFIGURED_PROVIDER_MODELS_MODE below
# EDENAI_MODELS=openai/gpt-4,anthropic/claude-sonnet-latest

# Z.ai (default base URL: https://api.z.ai/api/paas/v4)
# For GLM Coding Plan, use: https://api.z.ai/api/coding/paas/v4
# ZAI_API_KEY=...
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ The official SDKs therefore work unchanged. Configure their base URLs as follows
- Z.ai
- Alibaba Cloud Model Studio (Bailian)
- Kilo AI
- Eden AI
- MiniMax
- Xiaomi MiMo
- OpenCode Go
Expand Down
4 changes: 4 additions & 0 deletions cmd/gomodel/docs/docs.go

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

15 changes: 15 additions & 0 deletions config/config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,21 @@ providers:
# base_url defaults to "https://llm.chutes.ai/v1".
# Set base_url when using a different compatible endpoint.

edenai:
type: edenai
api_key: "${EDENAI_API_KEY}"
# base_url defaults to "https://api.edenai.run/v3".
# Multi-provider gateway: model IDs use provider/model notation and pass
# through unchanged. Models, context windows, capabilities, and per-token
# pricing are discovered from Eden's own /v3/models, and per-request cost
# comes from the exact USD "cost" Eden returns on each response, so no
# pricing metadata needs declaring here.
# GoModel's /v1/responses is served by translating to Eden's chat
# completions; Eden's own /v3/responses is a different API and is never used.
# models:
# - id: "openai/gpt-4"
# - id: "anthropic/claude-sonnet-latest"

elevenlabs:
type: elevenlabs
api_key: "${ELEVENLABS_API_KEY}"
Expand Down
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func buildDefaultConfig() *Config {
"llamacpp",
"llmd",
"deepseek",
"edenai",
"jev",
},
},
Expand Down
2 changes: 1 addition & 1 deletion config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func TestBuildDefaultConfig(t *testing.T) {
assert.Equal(t, DefaultStreamStallTimeoutSeconds, cfg.Server.StreamStallTimeout)
assert.True(t, cfg.Server.EnablePassthroughRoutes)
assert.True(t, cfg.Server.AllowPassthroughV1Alias)
assert.Equal(t, []string{"openai", "anthropic", "openrouter", "kilo", "zai", "sglang", "vllm", "llamacpp", "llmd", "deepseek", "jev"}, cfg.Server.EnabledPassthroughProviders)
assert.Equal(t, []string{"openai", "anthropic", "openrouter", "kilo", "zai", "sglang", "vllm", "llamacpp", "llmd", "deepseek", "edenai", "jev"}, cfg.Server.EnabledPassthroughProviders)
assert.Equal(t, ConfiguredProviderModelsModeFallback, cfg.Models.ConfiguredProviderModelsMode)
assert.Nil(t, cfg.Cache.Model.Local)
assert.Equal(t, 3600, cfg.Cache.Model.RefreshInterval)
Expand Down
4 changes: 3 additions & 1 deletion docs/advanced/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ Set these to automatically register providers. No YAML configuration required.
| `DEEPSEEK_API_KEY` | DeepSeek |
| `OPENROUTER_API_KEY` | OpenRouter |
| `KILO_API_KEY` | Kilo AI Gateway |
| `EDENAI_API_KEY` | Eden AI |
| `ZAI_API_KEY` | Z.ai |
| `XAI_API_KEY` | xAI (Grok) |
| `GROQ_API_KEY` | Groq |
Expand All @@ -446,7 +447,7 @@ Set these to automatically register providers. No YAML configuration required.
| `LLAMACPP_BASE_URL` | llama.cpp llama-server / LM Studio (no API key needed unless started with `--api-key`) |
| `LLMD_BASE_URL` | llm-d Router/EPP (no API key needed unless its Gateway requires one) |

Most providers can use a custom base URL via `<PROVIDER>_BASE_URL` (for example `OPENAI_BASE_URL`). Chutes AI defaults to `https://llm.chutes.ai/v1` and can be overridden with `CHUTES_BASE_URL`. DeepSeek defaults to `https://api.deepseek.com`; set `DEEPSEEK_BASE_URL` only for a compatible proxy or alternate DeepSeek endpoint. OpenRouter defaults to `https://openrouter.ai/api/v1` and can be overridden with `OPENROUTER_BASE_URL`. Kilo AI defaults to `https://api.kilo.ai/api/gateway` and can be overridden with `KILO_BASE_URL`. Z.ai defaults to `https://api.z.ai/api/paas/v4`; set `ZAI_BASE_URL=https://api.z.ai/api/coding/paas/v4` for the GLM Coding Plan endpoint. SGLang defaults to `http://localhost:30000/v1` when `SGLANG_API_KEY` is set, but keyless deployments should set `SGLANG_BASE_URL` explicitly to register the provider. vLLM follows the same pattern at `http://localhost:8000/v1`. llama.cpp's `LLAMACPP_BASE_URL` is always required (llama-server's default port collides with GoModel's own 8080, so there is no default); `LLAMACPP_API_KEY` is optional. llm-d has no universal endpoint, so `LLMD_BASE_URL` is always required; `LLMD_API_KEY` is optional. Azure uses `AZURE_BASE_URL` for its deployment base URL and accepts an optional `AZURE_API_VERSION` override; otherwise it defaults to `2024-10-21`. Oracle requires `ORACLE_BASE_URL` because its OpenAI-compatible endpoint is region-specific.
Most providers can use a custom base URL via `<PROVIDER>_BASE_URL` (for example `OPENAI_BASE_URL`). Chutes AI defaults to `https://llm.chutes.ai/v1` and can be overridden with `CHUTES_BASE_URL`. DeepSeek defaults to `https://api.deepseek.com`; set `DEEPSEEK_BASE_URL` only for a compatible proxy or alternate DeepSeek endpoint. OpenRouter defaults to `https://openrouter.ai/api/v1` and can be overridden with `OPENROUTER_BASE_URL`. Kilo AI defaults to `https://api.kilo.ai/api/gateway` and can be overridden with `KILO_BASE_URL`. Eden AI defaults to `https://api.edenai.run/v3` and can be overridden with `EDENAI_BASE_URL`. Z.ai defaults to `https://api.z.ai/api/paas/v4`; set `ZAI_BASE_URL=https://api.z.ai/api/coding/paas/v4` for the GLM Coding Plan endpoint. SGLang defaults to `http://localhost:30000/v1` when `SGLANG_API_KEY` is set, but keyless deployments should set `SGLANG_BASE_URL` explicitly to register the provider. vLLM follows the same pattern at `http://localhost:8000/v1`. llama.cpp's `LLAMACPP_BASE_URL` is always required (llama-server's default port collides with GoModel's own 8080, so there is no default); `LLAMACPP_API_KEY` is optional. llm-d has no universal endpoint, so `LLMD_BASE_URL` is always required; `LLMD_API_KEY` is optional. Azure uses `AZURE_BASE_URL` for its deployment base URL and accepts an optional `AZURE_API_VERSION` override; otherwise it defaults to `2024-10-21`. Oracle requires `ORACLE_BASE_URL` because its OpenAI-compatible endpoint is region-specific.

Every provider type also accepts a comma-separated configured model list via
`<PROVIDER>_MODELS`, for example `OPENROUTER_MODELS`, `ORACLE_MODELS`,
Expand Down Expand Up @@ -594,6 +595,7 @@ export GROQ_API_KEY="gsk_..." # Registers "groq" provider
export CHUTES_API_KEY="cpk_..." # Registers "chutes" provider
export OPENROUTER_API_KEY="sk-or-..." # Registers "openrouter" provider
export KILO_API_KEY="..." # Registers "kilo" provider
export EDENAI_API_KEY="..." # Registers "edenai" provider
export ZAI_API_KEY="..." # Registers "zai" provider
# Optional: export ZAI_BASE_URL="https://api.z.ai/api/coding/paas/v4"
export AZURE_API_KEY="..." # Registers "azure" provider when paired with AZURE_BASE_URL
Expand Down
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@
"providers/multiple-ollama",
"providers/kimicode",
"providers/hetzner",
"providers/edenai",
{
"group": "Cloud Platforms",
"icon": "cloud",
Expand Down
4 changes: 2 additions & 2 deletions docs/features/passthrough-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ from passthrough requests before forwarding them upstream.

Passthrough is intentionally narrow while the API is in beta.

- `openai`, `anthropic`, `openrouter`, `kilo`, `zai`, `sglang`, `vllm`, `llamacpp`, `llmd`, `deepseek`, and `jev`
- `openai`, `anthropic`, `openrouter`, `kilo`, `zai`, `sglang`, `vllm`, `llamacpp`, `llmd`, `deepseek`, `edenai`, and `jev`
are enabled by default.
- Chutes supports passthrough but requires explicit operator opt-in because
passthrough can forward provider-native routes that do not identify a model.
Expand All @@ -160,7 +160,7 @@ Passthrough routes are enabled by default:
```env
ENABLE_PASSTHROUGH_ROUTES=true
ALLOW_PASSTHROUGH_V1_ALIAS=true
ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,openrouter,kilo,zai,sglang,vllm,llamacpp,llmd,deepseek,jev
ENABLED_PASSTHROUGH_PROVIDERS=openai,anthropic,openrouter,kilo,zai,sglang,vllm,llamacpp,llmd,deepseek,edenai,jev
```

Set `ENABLED_PASSTHROUGH_PROVIDERS` to the provider types you want to expose.
4 changes: 4 additions & 0 deletions docs/openapi.json

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

219 changes: 219 additions & 0 deletions docs/providers/edenai.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
---
title: "Eden AI"
description: "Configure Eden AI's OpenAI-compatible multi-provider API in GoModel."
icon: "leaf"
keywords: ["Eden AI", "edenai", "multi-provider", "OpenAI-compatible", "provider setup"]
---

Eden AI is a multi-provider gateway exposing an OpenAI-compatible REST API at
`https://api.edenai.run/v3`. GoModel routes chat completions, streaming, model
listing, embeddings, and passthrough through the shared OpenAI adapter, so one
Eden key reaches models from OpenAI, Anthropic, Google, Mistral, Cohere,
DeepInfra, and others.

## Configure

Create an API key in the [Eden AI console](https://app.edenai.run/) and set:

```bash
EDENAI_API_KEY=
```

`EDENAI_BASE_URL` is optional — the provider defaults to
`https://api.edenai.run/v3`. Set it only to reach a different Eden-compatible
endpoint:

```bash
EDENAI_BASE_URL=https://api.edenai.run/v3
```

<Warning>
Use an `https://` endpoint. GoModel **refuses** any Eden request bound for a
cleartext destination rather than sending it, because the request body
carries the prompt or the embedding input, not just the API key. A
non-loopback `http://` base URL therefore fails with an explicit error
instead of transmitting anything.

Redirects are held to the same standard, and only a redirect that stays on
the host you configured is followed. A redirect to `http://`, or to any
other host — including a subdomain, which Go would otherwise let carry the
`Authorization` header — is refused rather than followed, so an
upstream-chosen `Location` cannot move your key or your prompt somewhere you
did not configure.

Cleartext to `localhost` (or `127.0.0.1`) is allowed, since that traffic
never reaches a network -- this is what keeps a local Eden-compatible proxy
usable. If you front Eden with a proxy on another host, terminate TLS on it
and point `EDENAI_BASE_URL` at its `https://` address.
</Warning>

Or in `config.yaml`:

```yaml
providers:
edenai:
type: edenai
api_key: "${EDENAI_API_KEY}"
# base_url: "https://api.edenai.run/v3"
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

You can also add the credential from the **Providers** page in the admin
dashboard instead of using env vars.

## Models

GoModel discovers Eden's catalog from Eden's own `GET /v3/models` on startup
and on every registry refresh. **There is no built-in model list**: a model
Eden adds is routable as soon as the catalog refreshes, with no GoModel
upgrade and no configuration change.

Model IDs use `provider/model` notation and are forwarded unchanged:

```json
{ "model": "openai/gpt-4", "messages": [{ "role": "user", "content": "Hi" }] }
```

Because the ID already contains a slash, qualify it with the provider name when
another configured provider exposes the same raw ID:
`edenai/anthropic/claude-sonnet-latest`. GoModel strips only the outer
`edenai/` routing qualifier before forwarding.

Optionally pin a configured subset:

```bash
EDENAI_MODELS=openai/gpt-4,anthropic/claude-sonnet-latest
```

### Discovered metadata

Each catalog entry contributes metadata that `GET /v1/models` returns and that
the router, filters, and cost strategies use:

| Eden field | GoModel metadata |
| ---------- | ---------------- |
| `context_length` | context window |
| `capabilities.supports_*` | capabilities, with the prefix stripped (`reasoning`, `function_calling`, `prompt_caching`, …) |
| `capabilities.input_modalities` | `vision` / `audio` / `video` capabilities |
| `capabilities.output_modalities` | modes and categories |
| `pricing` | per-model pricing (see below) |

Output modalities also decide what GoModel advertises: a model whose only
output is audio or images is left out of `/v1/models`, because Eden here
serves chat, embeddings, and passthrough only. In practice Eden's catalog
publishes text output for every model, including the handful that also return
images, so nothing is currently filtered.

## Pricing

Eden publishes per-token USD rates per model, and GoModel converts them to its
per-million-token representation (`input_cost_per_token: 6e-8` → `$0.06 /
MTok`).

Rates come from Eden's `pricing` block, which is what the account is actually
charged — the undiscounted `list_pricing` with any account discount already
applied. Each rate is resolved on its own: a usable account rate always wins,
and `list_pricing` supplies only the individual rates `pricing` does not carry.
Eden currently publishes the same rates in both blocks, so in practice
everything resolves from `pricing`; the per-rate fallback is what keeps a
partially priced model from losing the rates it is missing, which would
otherwise bill those token types at $0.

A rate Eden reports as `0` is treated as genuinely free rather than missing,
and a rate neither block publishes usably is left unset rather than invented.

These rates cover input, output, cache reads, and cache writes. Eden's
context-length-tiered rates (`input_cost_per_token_above_200k_tokens`), its
`tiered_pricing` list, and its per-query search fees have no GoModel
equivalent and are not read.

Eden's reasoning and audio rates (`output_cost_per_reasoning_token`,
`input_cost_per_audio_token`) are also left unread. GoModel would price those
token types by subtracting the base rate, which assumes the counts are already
part of the base totals — and Eden's usage object reports only
`prompt_tokens`, `completion_tokens`, and `total_tokens`, so there is no
breakdown to confirm that against. Those token types fall back to the base
input/output rates instead.

Pricing is read live from Eden — nothing is hard-coded, and Eden models do not
need to be present in GoModel's central model catalog. This makes
`EDENAI_MODEL_FILTER_MAX_PRICE_PER_MTOK`, cost-based load balancing, and
price display work for Eden models.

This discovered pricing is what model metadata shows, and what cost accounting
falls back to when a response carries no exact charge. The exact per-request
cost Eden returns takes precedence whenever it is available (see below).

## Request cost

Eden returns the exact USD charge for each request as a top-level `cost`
member — on chat completions and on embeddings alike — and GoModel
records that figure as the request's cost instead of recomputing it from token
counts. Eden reprices its upstreams automatically and applies account-level
discounts, so its own number is authoritative in a way a rate-card
reconstruction is not.

This makes Eden spend visible to usage records, budgets, cost dashboards, and
observability, and the usage entry is labelled with the cost source
`edenai_cost`. If a response carries no usable cost, GoModel falls back to the
discovered per-model pricing above.

<Note>
The `provider` member Eden returns (`"openai"`, `"deepinfra"`) names the
upstream Eden routed to. GoModel reports `edenai` as the executing provider —
that is the provider it called — and re-exposes Eden's value on the response
as `edenai_upstream_provider` so clients can still see which upstream served
the request.
</Note>

## Responses API

GoModel serves `/v1/responses` for Eden by **translating the request to Eden's
chat-completions endpoint**.

<Warning>
Eden's own `/v3/responses` route is **not** the OpenAI Responses API. It takes
Eden-specific inputs (`routing`, `router_candidates`, `fallbacks`) and returns
its own response object, so GoModel never forwards to it. Treat `/v1/responses`
support here as chat-completion translation, not native compatibility.
</Warning>

## Eden-specific request fields

Eden accepts extra top-level fields on chat completions — `routing`,
`fallbacks`, `session_id`, `pre_hooks`, and `post_hooks`. GoModel preserves
unknown top-level JSON fields on chat requests, so these reach Eden unchanged:

```json
{
"model": "openai/gpt-4",
"messages": [{ "role": "user", "content": "Hi" }],
"fallbacks": ["anthropic/claude-sonnet-latest"]
}
```

## Embeddings

Eden's `/embeddings` route is OpenAI-compatible and uses the same
`provider/model` IDs:

```json
{ "model": "openai/text-embedding-3-small", "input": "hello" }
```

Embeddings responses carry the same Eden extensions as chat completions, so the
exact `cost` Eden reports is recorded for them too. Eden's LLM catalog
(`GET /v3/models`) does not list embedding models, so embedding IDs are
forwarded without discovered metadata; pin them under `models:` if you want
them advertised on `/v1/models`.

## Passthrough

`edenai` is in the default `ENABLED_PASSTHROUGH_PROVIDERS` allowlist, so
`/p/edenai/...` routes work without operator opt-in. Passthrough is a generic
forwarder: it sends any path you give it to Eden unchanged, under the
gateway's own credential.

## Unsupported surfaces

Files, batches, and audio are not exposed for Eden. Requests to those gateway
endpoints will not route to this provider.
Loading
Loading