From 8372794d72a8766ca1f52cf28d6c9cf40ffa2731 Mon Sep 17 00:00:00 2001 From: seven7763 Date: Sat, 12 Sep 2026 03:54:08 +0800 Subject: [PATCH] feat: add DaoXE as an OpenAI-compatible provider alias Mirrors the OVHcloud alias-provider pattern: alias entry with DAOXE_API_KEY, auto-config support, system-message merge behavior shared with the other OpenAI-compatible gateway aliases, provider docs page, example recipe, and test coverage across the touched packages. The models.dev catalog already carries the daoxe entry, so the default model resolves through it. Signed-off-by: seven7763 --- agent-schema.json | 2 +- docs/concepts/models/index.md | 1 + docs/configuration/models/index.md | 6 +- docs/data/nav.yml | 2 + docs/providers/daoxe/index.md | 66 +++++++++++++++++++ docs/providers/overview/index.md | 1 + examples/README.md | 1 + examples/daoxe.yaml | 17 +++++ pkg/config/auto.go | 2 + pkg/config/auto_test.go | 16 +++++ pkg/model/provider/aliases.go | 5 ++ pkg/model/provider/aliases_test.go | 1 + pkg/model/provider/openai/client.go | 1 + .../openai/system_message_merge_test.go | 10 +++ 14 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 docs/providers/daoxe/index.md create mode 100644 examples/daoxe.yaml diff --git a/agent-schema.json b/agent-schema.json index 3194d4d081..b2dcd47522 100644 --- a/agent-schema.json +++ b/agent-schema.json @@ -214,7 +214,7 @@ "properties": { "provider": { "type": "string", - "description": "The underlying provider type. Defaults to \"openai\" when not set. Supported values: openai, anthropic, google, amazon-bedrock, dmr, and any built-in alias (requesty, openrouter, azure, xai, ollama, mistral, baseten, ovhcloud, groq, fireworks, deepseek, cerebras, together, huggingface, moonshot, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, nvidia, github-copilot, chatgpt, etc.).", + "description": "The underlying provider type. Defaults to \"openai\" when not set. Supported values: openai, anthropic, google, amazon-bedrock, dmr, and any built-in alias (requesty, openrouter, azure, xai, ollama, mistral, baseten, daoxe, ovhcloud, groq, fireworks, deepseek, cerebras, together, huggingface, moonshot, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, nvidia, github-copilot, chatgpt, etc.).", "examples": [ "openai", "anthropic", diff --git a/docs/concepts/models/index.md b/docs/concepts/models/index.md index e12fb5fd54..8cb8f242c7 100644 --- a/docs/concepts/models/index.md +++ b/docs/concepts/models/index.md @@ -83,6 +83,7 @@ for details. | NVIDIA NIM | `nvidia` | Nemotron, Llama, Qwen, DeepSeek (open models) | `NVIDIA_API_KEY` | | MiniMax | `minimax` | MiniMax models | `MINIMAX_API_KEY` | | Baseten | `baseten` | DeepSeek, Kimi, GLM, Llama models | `BASETEN_API_KEY` | +| DaoXE | `daoxe` | Multi-model gateway (OpenAI-compatible) | `DAOXE_API_KEY` | | OVHcloud | `ovhcloud` | Qwen, Llama, Mistral, DeepSeek (EU-hosted) | `OVH_AI_ENDPOINTS_ACCESS_TOKEN` | | Groq | `groq` | Llama, Qwen, GPT-OSS (fast inference) | `GROQ_API_KEY` | | Fireworks AI | `fireworks` | Kimi, Llama, Qwen, DeepSeek, GLM (open models) | `FIREWORKS_API_KEY` | diff --git a/docs/configuration/models/index.md b/docs/configuration/models/index.md index 7e7656c1e8..50e59b9654 100644 --- a/docs/configuration/models/index.md +++ b/docs/configuration/models/index.md @@ -18,7 +18,7 @@ models: first_available: [list] # Optional: candidate model refs, tried in order by available credentials. # Mutually exclusive with other model settings. provider: string # Required unless using first_available. One of: openai, anthropic, google, amazon-bedrock, - # dmr, mistral, xai, nebius, nvidia, minimax, baseten, ovhcloud, groq, fireworks, deepseek, cerebras, together, huggingface, moonshot, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, requesty, openrouter, + # dmr, mistral, xai, nebius, nvidia, minimax, baseten, daoxe, ovhcloud, groq, fireworks, deepseek, cerebras, together, huggingface, moonshot, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, requesty, openrouter, # azure, ollama, github-copilot, or a named provider defined # under the top-level `providers:` section. model: string # Required: model identifier @@ -60,7 +60,7 @@ models: | Property | Type | Required | Description | | --------------------- | ---------- | -------- | ------------------------------------------------------------------------------------- | | `first_available` | array | ✗ | Candidate model references tried in order; selects the first whose credentials are configured. Mutually exclusive with other model settings. | -| `provider` | string | ✓/✗ | Required for regular model definitions; omitted for `first_available` selectors. Provider: `openai`, `anthropic`, `google`, `amazon-bedrock`, `dmr`, `mistral`, `xai`, `nebius`, `nvidia`, `minimax`, `baseten`, `ovhcloud`, `groq`, `fireworks`, `deepseek`, `cerebras`, `together`, `huggingface`, `moonshot`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `azure`, `ollama`, `github-copilot`, `chatgpt`, or any [named provider](../../providers/custom/index.md). | +| `provider` | string | ✓/✗ | Required for regular model definitions; omitted for `first_available` selectors. Provider: `openai`, `anthropic`, `google`, `amazon-bedrock`, `dmr`, `mistral`, `xai`, `nebius`, `nvidia`, `minimax`, `baseten`, `daoxe`, `ovhcloud`, `groq`, `fireworks`, `deepseek`, `cerebras`, `together`, `huggingface`, `moonshot`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `azure`, `ollama`, `github-copilot`, `chatgpt`, or any [named provider](../../providers/custom/index.md). | | `model` | string | ✓/✗ | Required for regular model definitions; omitted for `first_available` selectors. Model name (e.g., `gpt-4o`, `claude-sonnet-4-5`, `gemini-3.5-flash`) | | `description` | string | ✗ | Informational, human-readable summary of the model's purpose or strengths (e.g., "fast and cheap, good for summaries"). Not sent to the model. Can be combined with `first_available` (a selector's description is kept when it resolves). | | `temperature` | float | ✗ | Sampling randomness. Range is provider-dependent — typically `0.0–2.0` (Anthropic caps at `1.0`). `0.0` is deterministic. | @@ -591,7 +591,7 @@ See the [Anthropic provider page](../../providers/anthropic/index.md#thinking-di ## Custom HTTP Headers For OpenAI-compatible providers (`openai`, `github-copilot`, `mistral`, `xai`, -`nebius`, `nvidia`, `minimax`, `baseten`, `ovhcloud`, `groq`, `fireworks`, `deepseek`, `cerebras`, `together`, `huggingface`, `moonshot`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `ollama`, and any custom provider using the OpenAI API), +`nebius`, `nvidia`, `minimax`, `baseten`, `daoxe`, `ovhcloud`, `groq`, `fireworks`, `deepseek`, `cerebras`, `together`, `huggingface`, `moonshot`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `ollama`, and any custom provider using the OpenAI API), `provider_opts.http_headers` adds arbitrary HTTP headers to every outgoing request: diff --git a/docs/data/nav.yml b/docs/data/nav.yml index 88e362a4db..99fa3145d9 100644 --- a/docs/data/nav.yml +++ b/docs/data/nav.yml @@ -177,6 +177,8 @@ url: /providers/cloudflare-workers-ai/ - title: ChatGPT url: /providers/chatgpt/ + - title: DaoXE + url: /providers/daoxe/ - title: DeepSeek url: /providers/deepseek/ - title: Docker Model Runner diff --git a/docs/providers/daoxe/index.md b/docs/providers/daoxe/index.md new file mode 100644 index 0000000000..5e72e059c4 --- /dev/null +++ b/docs/providers/daoxe/index.md @@ -0,0 +1,66 @@ +--- +title: "DaoXE" +description: "Use DaoXE models with Docker Agent." +keywords: docker agent, ai agents, model providers, llm, daoxe +weight: 245 +canonical: https://docs.docker.com/ai/docker-agent/providers/daoxe/ +--- + +_Use DaoXE models with Docker Agent._ + +## Overview + +[DaoXE](https://daoxe.com) is a multi-model LLM gateway that serves models from +multiple vendors behind one OpenAI-compatible endpoint. Docker Agent includes +built-in support for DaoXE as an alias provider. + +## Setup + +1. Create an API key from your DaoXE account dashboard. +2. Set the environment variable: + + ```bash + export DAOXE_API_KEY=your-api-key + ``` + +## Usage + +### Inline Syntax + +```yaml +agents: + root: + model: daoxe/ + description: Assistant using DaoXE + instruction: You are a helpful assistant. +``` + +### Named Model + +```yaml +models: + daoxe_model: + provider: daoxe + model: + temperature: 0.7 + max_tokens: 8192 + +agents: + root: + model: daoxe_model + description: Assistant using DaoXE + instruction: You are a helpful assistant. +``` + +## Available Models + +DaoXE's catalog is account-scoped and changes over time: new models appear as +upstream vendors release them. List the model IDs your account can call with: + +```bash +curl https://api.daoxe.com/v1/models \ + -H "Authorization: Bearer $DAOXE_API_KEY" +``` + +Use any ID from that response as the `model` value. Docker Agent's default for +DaoXE is `claude-sonnet-4-6`. diff --git a/docs/providers/overview/index.md b/docs/providers/overview/index.md index a7a048f1f3..fe4308b408 100644 --- a/docs/providers/overview/index.md +++ b/docs/providers/overview/index.md @@ -47,6 +47,7 @@ Docker Agent also includes built-in aliases for these providers: | NVIDIA NIM | `nvidia` | `NVIDIA_API_KEY` | | MiniMax | `minimax` | `MINIMAX_API_KEY` | | Baseten | `baseten` | `BASETEN_API_KEY` | +| DaoXE | `daoxe` | `DAOXE_API_KEY` | | OVHcloud | `ovhcloud` | `OVH_AI_ENDPOINTS_ACCESS_TOKEN` | | Groq | `groq` | `GROQ_API_KEY` | | Fireworks AI | `fireworks` | `FIREWORKS_API_KEY` | diff --git a/examples/README.md b/examples/README.md index a5b8ad5666..af3c6a30fa 100644 --- a/examples/README.md +++ b/examples/README.md @@ -197,6 +197,7 @@ remote MCP endpoints. | [`model_env_substitution.yaml`](model_env_substitution.yaml) | `${env.VAR}` substitution in a model's `model` / `base_url`. | | [`nebius.yaml`](nebius.yaml) | Nebius cloud provider. | | [`baseten.yaml`](baseten.yaml) | Baseten cloud provider. | +| [`daoxe.yaml`](daoxe.yaml) | DaoXE multi-model gateway provider. | | [`ovhcloud.yaml`](ovhcloud.yaml) | OVHcloud AI Endpoints provider. | | [`groq.yaml`](groq.yaml) | Groq fast-inference provider. | | [`fireworks.yaml`](fireworks.yaml) | Fireworks AI open-model inference provider. | diff --git a/examples/daoxe.yaml b/examples/daoxe.yaml new file mode 100644 index 0000000000..4ab1c96c6c --- /dev/null +++ b/examples/daoxe.yaml @@ -0,0 +1,17 @@ +# yaml-language-server: $schema=../agent-schema.json + +models: + daoxe_model: + provider: daoxe + model: claude-sonnet-4-6 + +agents: + root: + model: daoxe_model + description: Assistant using DaoXE + instruction: | + You are a helpful assistant. + toolsets: + - type: filesystem + - type: shell + - type: think diff --git a/pkg/config/auto.go b/pkg/config/auto.go index cc16f7babc..09b1c9f113 100644 --- a/pkg/config/auto.go +++ b/pkg/config/auto.go @@ -63,6 +63,7 @@ var cloudProviders = []providerConfig{ {"openrouter", []string{"OPENROUTER_API_KEY"}, "OPENROUTER_API_KEY", "OPENROUTER_API_KEY"}, {"baseten", []string{"BASETEN_API_KEY"}, "BASETEN_API_KEY", "BASETEN_API_KEY"}, {"ovhcloud", []string{"OVH_AI_ENDPOINTS_ACCESS_TOKEN"}, "OVH_AI_ENDPOINTS_ACCESS_TOKEN", "OVH_AI_ENDPOINTS_ACCESS_TOKEN"}, + {"daoxe", []string{"DAOXE_API_KEY"}, "DAOXE_API_KEY", "DAOXE_API_KEY"}, {"groq", []string{"GROQ_API_KEY"}, "GROQ_API_KEY", "GROQ_API_KEY"}, {"fireworks", []string{"FIREWORKS_API_KEY"}, "FIREWORKS_API_KEY", "FIREWORKS_API_KEY"}, {"deepseek", []string{"DEEPSEEK_API_KEY"}, "DEEPSEEK_API_KEY", "DEEPSEEK_API_KEY"}, @@ -159,6 +160,7 @@ var DefaultModels = map[string]string{ "openrouter": "meta-llama/llama-4-maverick", "baseten": "deepseek-ai/DeepSeek-V4-Pro", "ovhcloud": "Qwen3.5-397B-A17B", + "daoxe": "claude-sonnet-4-6", "groq": "llama-3.3-70b-versatile", "fireworks": "accounts/fireworks/models/kimi-k3", "deepseek": "deepseek-v4-pro", diff --git a/pkg/config/auto_test.go b/pkg/config/auto_test.go index f2db1d2988..aa9ee0bd8d 100644 --- a/pkg/config/auto_test.go +++ b/pkg/config/auto_test.go @@ -87,6 +87,13 @@ func TestAvailableProviders_NoGateway(t *testing.T) { }, expectedProvider: "ovhcloud", }, + { + name: "daoxe api key present", + envVars: map[string]string{ + "DAOXE_API_KEY": "test-key", + }, + expectedProvider: "daoxe", + }, { name: "groq api key present", envVars: map[string]string{ @@ -323,6 +330,15 @@ func TestAutoModelConfig(t *testing.T) { expectedModel: "Qwen3.5-397B-A17B", expectedMaxTokens: 32000, }, + { + name: "daoxe provider", + envVars: map[string]string{ + "DAOXE_API_KEY": "test-key", + }, + expectedProvider: "daoxe", + expectedModel: "claude-sonnet-4-6", + expectedMaxTokens: 32000, + }, { name: "groq provider", envVars: map[string]string{ diff --git a/pkg/model/provider/aliases.go b/pkg/model/provider/aliases.go index 49e6123643..265e1bf23c 100644 --- a/pkg/model/provider/aliases.go +++ b/pkg/model/provider/aliases.go @@ -88,6 +88,11 @@ var Aliases = map[string]Alias{ BaseURL: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1", TokenEnvVar: "OVH_AI_ENDPOINTS_ACCESS_TOKEN", }, + "daoxe": { + APIType: "openai", + BaseURL: "https://api.daoxe.com/v1", + TokenEnvVar: "DAOXE_API_KEY", + }, "groq": { APIType: "openai", BaseURL: "https://api.groq.com/openai/v1", diff --git a/pkg/model/provider/aliases_test.go b/pkg/model/provider/aliases_test.go index e0c48a1b56..6db47f729b 100644 --- a/pkg/model/provider/aliases_test.go +++ b/pkg/model/provider/aliases_test.go @@ -38,6 +38,7 @@ func TestCatalogAliases(t *testing.T) { expected := map[string]Alias{ "openrouter": {APIType: "openai", BaseURL: "https://openrouter.ai/api/v1", TokenEnvVar: "OPENROUTER_API_KEY"}, "baseten": {APIType: "openai", BaseURL: "https://inference.baseten.co/v1", TokenEnvVar: "BASETEN_API_KEY"}, + "daoxe": {APIType: "openai", BaseURL: "https://api.daoxe.com/v1", TokenEnvVar: "DAOXE_API_KEY"}, "ovhcloud": {APIType: "openai", BaseURL: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1", TokenEnvVar: "OVH_AI_ENDPOINTS_ACCESS_TOKEN"}, "groq": {APIType: "openai", BaseURL: "https://api.groq.com/openai/v1", TokenEnvVar: "GROQ_API_KEY"}, "deepseek": {APIType: "openai", BaseURL: "https://api.deepseek.com/v1", TokenEnvVar: "DEEPSEEK_API_KEY"}, diff --git a/pkg/model/provider/openai/client.go b/pkg/model/provider/openai/client.go index 604c56b909..f1bb84352f 100644 --- a/pkg/model/provider/openai/client.go +++ b/pkg/model/provider/openai/client.go @@ -265,6 +265,7 @@ func (c *Client) convertMessages(ctx context.Context, messages []chat.Message) [ // system messages and are deliberately absent so their behavior is unchanged. var openModelHostProviders = map[string]bool{ "baseten": true, + "daoxe": true, "ovhcloud": true, "openrouter": true, "nebius": true, diff --git a/pkg/model/provider/openai/system_message_merge_test.go b/pkg/model/provider/openai/system_message_merge_test.go index ccb962f641..6db0426816 100644 --- a/pkg/model/provider/openai/system_message_merge_test.go +++ b/pkg/model/provider/openai/system_message_merge_test.go @@ -35,6 +35,16 @@ func TestChatCompletions_MergesConsecutiveSystemMessages(t *testing.T) { }) } +func TestDaoxe_MergesConsecutiveSystemMessages(t *testing.T) { + t.Parallel() + + assertMergesConsecutiveMessages(t, &latest.ModelConfig{ + Provider: "daoxe", + Model: "gpt-4o", + TokenKey: "MY_TOKEN", + }) +} + func TestBaseten_MergesConsecutiveSystemMessages(t *testing.T) { t.Parallel()