diff --git a/agent-schema.json b/agent-schema.json index f4feefffd1..de8c6edb1c 100644 --- a/agent-schema.json +++ b/agent-schema.json @@ -224,7 +224,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, atlascloud, azure, xai, ollama, mistral, baseten, daoxe, ovhcloud, groq, fireworks-ai, deepseek, cerebras, togetherai, huggingface, moonshotai, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, nvidia, github-copilot, chatgpt, opencode, opencode-go, 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, api-route, atlascloud, azure, xai, ollama, mistral, baseten, daoxe, ovhcloud, groq, fireworks-ai, deepseek, cerebras, togetherai, huggingface, moonshotai, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, nvidia, github-copilot, chatgpt, opencode, opencode-go, etc.).", "examples": [ "openai", "anthropic", diff --git a/docs/configuration/models/index.md b/docs/configuration/models/index.md index 9f9ab756e7..23618ef3f8 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, daoxe, ovhcloud, groq, fireworks-ai, deepseek, cerebras, togetherai, huggingface, moonshotai, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, requesty, openrouter, atlascloud, + # dmr, mistral, xai, nebius, nvidia, minimax, baseten, daoxe, ovhcloud, groq, fireworks-ai, deepseek, cerebras, togetherai, huggingface, moonshotai, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, requesty, openrouter, api-route, atlascloud, # 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`, `daoxe`, `ovhcloud`, `groq`, `fireworks-ai`, `deepseek`, `cerebras`, `togetherai`, `huggingface`, `moonshotai`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `atlascloud`, `azure`, `ollama`, `github-copilot`, `chatgpt`, `opencode`, `opencode-go`, 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-ai`, `deepseek`, `cerebras`, `togetherai`, `huggingface`, `moonshotai`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `api-route`, `atlascloud`, `azure`, `ollama`, `github-copilot`, `chatgpt`, `opencode`, `opencode-go`, 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. | diff --git a/docs/data/nav.yml b/docs/data/nav.yml index de0b4b47ab..0c00645bf2 100644 --- a/docs/data/nav.yml +++ b/docs/data/nav.yml @@ -171,6 +171,8 @@ url: /providers/overview/ - title: Anthropic url: /providers/anthropic/ + - title: API Route + url: /providers/api-route/ - title: Atlas Cloud url: /providers/atlascloud/ - title: AWS Bedrock diff --git a/docs/providers/api-route/index.md b/docs/providers/api-route/index.md new file mode 100644 index 0000000000..4719837022 --- /dev/null +++ b/docs/providers/api-route/index.md @@ -0,0 +1,98 @@ +--- +title: "API Route" +description: "Use API Route models with Docker Agent." +keywords: docker agent, ai agents, model providers, llm, api route +weight: 23 +canonical: https://docs.docker.com/ai/docker-agent/providers/api-route/ +--- + +_Use API Route models with Docker Agent._ + +## Overview + +[API Route](https://api-route.com) provides access to multiple model families +through an OpenAI-compatible API. Docker Agent includes API Route as a built-in +alias provider using the global endpoint. + +## Setup + +1. Create an API key in your [API Route account](https://api-route.com). +2. Set the environment variable: + + ```bash + export API_ROUTE_API_KEY=your-api-key + ``` + +## Usage + +### Inline Syntax + +```yaml +agents: + root: + model: api-route/gpt-6.1-sol + description: Assistant using API Route + instruction: You are a helpful assistant. +``` + +### Named Model + +```yaml +models: + api_route_model: + provider: api-route + model: gpt-6.1-sol + +agents: + root: + model: api_route_model + description: Assistant using API Route + instruction: You are a helpful assistant. +``` + +## Available Models + +The examples use the exact model ID `gpt-6.1-sol`. Retrieve your account's current +model IDs before choosing another model: + +```bash +curl https://global.api-route.com/v1/models \ + -H "Authorization: Bearer $API_ROUTE_API_KEY" +``` + +Use an ID from the response unchanged as the `model` value. A catalog listing +does not guarantee that inference is currently available. + +API Route is absent from Docker Agent's embedded models.dev catalog. Docker +Agent does not import pricing, context limits or attachment capabilities from +API Route's model list. Attachment detection falls back to text-only, so image +and PDF attachments are dropped with a warning. If you have verified support +for an attachment type on your chosen model and endpoint, set +`models..capabilities`; see +[Attachment Capability Overrides](../../configuration/models/index.md#attachment-capability-overrides). + +## How It Works + +- **API type:** OpenAI-compatible Chat Completions (`openai_chatcompletions`) +- **Base URL:** `https://global.api-route.com/v1` +- **Token variable:** `API_ROUTE_API_KEY` + +As with other multi-model hosts, Docker Agent combines consecutive system +messages for compatibility with models that require a single leading system +message. + +## Example: Code Assistant + +```yaml +agents: + coder: + model: api-route/gpt-6.1-sol + description: Code assistant using API Route + instruction: | + You are an expert programmer. + Write clean code and follow language best practices. + toolsets: + - type: filesystem + - type: shell + - type: think +``` diff --git a/docs/providers/overview/index.md b/docs/providers/overview/index.md index e1b26302f5..da4255f389 100644 --- a/docs/providers/overview/index.md +++ b/docs/providers/overview/index.md @@ -67,6 +67,7 @@ Use this table to find a built-in provider's config key and authentication metho | Cloudflare AI Gateway | `cloudflare-ai-gateway` | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_GATEWAY_ID` | | Requesty | `requesty` | `REQUESTY_API_KEY` | | OpenRouter | `openrouter` | `OPENROUTER_API_KEY` | +| API Route | `api-route` | `API_ROUTE_API_KEY` | | Atlas Cloud | [`atlascloud`](../atlascloud/index.md) | `ATLASCLOUD_API_KEY` | | Azure OpenAI | `azure` | `AZURE_API_KEY` + `base_url` (override the key with `token_key`) | | [Ollama](../local/index.md) | `ollama` | None (local; optional `base_url`) | diff --git a/examples/README.md b/examples/README.md index b9b6a6ff6d..5b77fca141 100644 --- a/examples/README.md +++ b/examples/README.md @@ -206,6 +206,7 @@ remote MCP endpoints. | [`env_placeholders.yaml`](env_placeholders.yaml) | `${env.VAR}` substitution inside the YAML. | | [`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. | +| [`api-route.yaml`](api-route.yaml) | API Route multi-model gateway provider. | | [`atlascloud.yaml`](atlascloud.yaml) | Atlas Cloud open-model host (`atlascloud`). | | [`baseten.yaml`](baseten.yaml) | Baseten cloud provider. | | [`daoxe.yaml`](daoxe.yaml) | DaoXE multi-model gateway provider. | diff --git a/examples/api-route.yaml b/examples/api-route.yaml new file mode 100644 index 0000000000..82745b1429 --- /dev/null +++ b/examples/api-route.yaml @@ -0,0 +1,16 @@ +# yaml-language-server: $schema=../agent-schema.json + +models: + api_route_model: + provider: api-route + model: gpt-6.1-sol + +agents: + root: + model: api_route_model + description: Assistant using API Route + 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 50e36bbc0c..332e292f99 100644 --- a/pkg/config/auto.go +++ b/pkg/config/auto.go @@ -62,6 +62,7 @@ var cloudProviders = []providerConfig{ }, "GOOGLE_API_KEY (or GEMINI_API_KEY, GOOGLE_GENAI_USE_VERTEXAI)", "GOOGLE_API_KEY"}, {"mistral", []string{"MISTRAL_API_KEY"}, "MISTRAL_API_KEY", "MISTRAL_API_KEY"}, {"openrouter", []string{"OPENROUTER_API_KEY"}, "OPENROUTER_API_KEY", "OPENROUTER_API_KEY"}, + {"api-route", []string{"API_ROUTE_API_KEY"}, "API_ROUTE_API_KEY", "API_ROUTE_API_KEY"}, {"atlascloud", []string{"ATLASCLOUD_API_KEY"}, "ATLASCLOUD_API_KEY", "ATLASCLOUD_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"}, @@ -160,6 +161,7 @@ var DefaultModels = map[string]string{ "dmr": "ai/qwen3:latest", "mistral": "mistral-small-latest", "openrouter": "meta-llama/llama-4-maverick", + "api-route": "gpt-6.1-sol", "atlascloud": "qwen/qwen3.8-max", "baseten": "deepseek-ai/DeepSeek-V4-Pro", "ovhcloud": "Qwen3.5-397B-A17B", diff --git a/pkg/config/auto_test.go b/pkg/config/auto_test.go index 8fa07703c4..74e4158d85 100644 --- a/pkg/config/auto_test.go +++ b/pkg/config/auto_test.go @@ -74,6 +74,13 @@ func TestAvailableProviders_NoGateway(t *testing.T) { }, expectedProvider: "openrouter", }, + { + name: "api-route api key present", + envVars: map[string]string{ + "API_ROUTE_API_KEY": "test-key", + }, + expectedProvider: "api-route", + }, { name: "atlascloud api key present", envVars: map[string]string{ @@ -320,6 +327,15 @@ func TestAutoModelConfig(t *testing.T) { expectedModel: "meta-llama/llama-4-maverick", expectedMaxTokens: 32000, }, + { + name: "api-route provider", + envVars: map[string]string{ + "API_ROUTE_API_KEY": "test-key", + }, + expectedProvider: "api-route", + expectedModel: "gpt-6.1-sol", + expectedMaxTokens: 32000, + }, { name: "baseten provider", envVars: map[string]string{ diff --git a/pkg/config/examples_test.go b/pkg/config/examples_test.go index f41c42911b..82a4f79e53 100644 --- a/pkg/config/examples_test.go +++ b/pkg/config/examples_test.go @@ -29,6 +29,7 @@ var modelsDevCatalogProviders = map[string]string{ // even via modelsDevCatalogProviders above. The test skips models.dev // lookups for these to avoid false failures. var modelsDevAbsentProviders = map[string]bool{ + "api-route": true, // API Route model IDs come from its own /v1/models endpoint, not models.dev. "atlascloud": true, // Atlas Cloud model IDs come from its own /v1/models endpoint, not models.dev. "dmr": true, // Docker Model Runner (local, not in catalog) "cloudflare-workers-ai": true, // example uses an @cf/... model id not present in the models.dev snapshot (only variant ids like -fp8 are listed) diff --git a/pkg/model/provider/aliases.go b/pkg/model/provider/aliases.go index 06a6e19682..53d1cca4e0 100644 --- a/pkg/model/provider/aliases.go +++ b/pkg/model/provider/aliases.go @@ -65,6 +65,11 @@ var Aliases = map[string]Alias{ BaseURL: "https://openrouter.ai/api/v1", TokenEnvVar: "OPENROUTER_API_KEY", }, + "api-route": { + APIType: "openai", + BaseURL: "https://global.api-route.com/v1", + TokenEnvVar: "API_ROUTE_API_KEY", + }, "atlascloud": { APIType: "openai", BaseURL: "https://api.atlascloud.ai/v1", diff --git a/pkg/model/provider/aliases_test.go b/pkg/model/provider/aliases_test.go index 630414e4f5..3284941807 100644 --- a/pkg/model/provider/aliases_test.go +++ b/pkg/model/provider/aliases_test.go @@ -39,6 +39,7 @@ func TestCatalogAliases(t *testing.T) { expected := map[string]Alias{ "openrouter": {APIType: "openai", BaseURL: "https://openrouter.ai/api/v1", TokenEnvVar: "OPENROUTER_API_KEY"}, + "api-route": {APIType: "openai", BaseURL: "https://global.api-route.com/v1", TokenEnvVar: "API_ROUTE_API_KEY"}, "atlascloud": {APIType: "openai", BaseURL: "https://api.atlascloud.ai/v1", TokenEnvVar: "ATLASCLOUD_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"}, @@ -127,6 +128,7 @@ func TestProviderIDsMatchEmbeddedCatalog(t *testing.T) { t.Parallel() db := modelsdev.EmbeddedSnapshot() absent := map[string]string{ + "api-route": "model IDs come from API Route's /v1/models endpoint, not models.dev", "atlascloud": "model IDs come from Atlas Cloud's /v1/models endpoint, not models.dev", "chatgpt": "subscription backend, distinct from OpenAI", "dmr": "local Docker Model Runner", diff --git a/pkg/model/provider/openai/client.go b/pkg/model/provider/openai/client.go index 89233da32d..bc9d7621cf 100644 --- a/pkg/model/provider/openai/client.go +++ b/pkg/model/provider/openai/client.go @@ -262,6 +262,7 @@ func (c *Client) convertMessages(ctx context.Context, messages []chat.Message) [ // (openai, mistral, xai, minimax, github-copilot, opencode) tolerate multiple // system messages and are deliberately absent so their behavior is unchanged. var openModelHostProviders = map[string]bool{ + "api-route": true, "atlascloud": true, "baseten": true, "daoxe": true, diff --git a/pkg/model/provider/openai/system_message_merge_test.go b/pkg/model/provider/openai/system_message_merge_test.go index 37a13da96a..7a5f2c0cbf 100644 --- a/pkg/model/provider/openai/system_message_merge_test.go +++ b/pkg/model/provider/openai/system_message_merge_test.go @@ -169,6 +169,7 @@ func TestShouldMergeConsecutiveMessages_Gating(t *testing.T) { {"open-model host alias openrouter", &latest.ModelConfig{Provider: "openrouter", Model: "qwen/qwen3.6-35b"}, true}, {"open-model host alias atlascloud", &latest.ModelConfig{Provider: "atlascloud", Model: "qwen/qwen3.8-max"}, true}, {"open-model host alias nebius", &latest.ModelConfig{Provider: "nebius", Model: "Qwen/Qwen3"}, true}, + {"api-route, no base_url", &latest.ModelConfig{Provider: "api-route", Model: "gpt-6.1-sol"}, true}, {"baseten", &latest.ModelConfig{Provider: "baseten", Model: "zai-org/GLM-5.2"}, true}, {"daoxe, no base_url", &latest.ModelConfig{Provider: "daoxe", Model: "claude-sonnet-4-6"}, true}, {"ovhcloud", &latest.ModelConfig{Provider: "ovhcloud", Model: "Qwen3.5-397B-A17B"}, true}, diff --git a/pkg/model/provider/openai_alias_providers_test.go b/pkg/model/provider/openai_alias_providers_test.go index a2d502e767..2813d9c38b 100644 --- a/pkg/model/provider/openai_alias_providers_test.go +++ b/pkg/model/provider/openai_alias_providers_test.go @@ -51,6 +51,14 @@ type openAIAliasProvider struct { } var openAIAliasProviders = []openAIAliasProvider{ + { + provider: "api-route", + envVar: "API_ROUTE_API_KEY", + testKey: "test-api-route-key", + model: "gpt-6.1-sol", + greeting: "Hello from API Route", + mergesSystemMessages: true, + }, { provider: "atlascloud", envVar: "ATLASCLOUD_API_KEY",