diff --git a/agent-schema.json b/agent-schema.json index 39fc2d5866..f0ad1fb223 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, 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.).", + "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, opper, 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 34b876ae26..6b30c46a5e 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, api-route, 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, opper, 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`, `api-route`, `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`, `opper`, `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. | @@ -558,7 +558,7 @@ for a complete local-server configuration. ## Custom HTTP Headers For OpenAI-compatible providers (`openai`, `github-copilot`, `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`, `ollama`, and any custom provider using the OpenAI API), +`nebius`, `nvidia`, `minimax`, `baseten`, `daoxe`, `ovhcloud`, `groq`, `fireworks-ai`, `deepseek`, `cerebras`, `togetherai`, `huggingface`, `moonshotai`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `opper`, `atlascloud`, `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 ffaa521e6b..7a7f1ab145 100644 --- a/docs/data/nav.yml +++ b/docs/data/nav.yml @@ -227,6 +227,8 @@ url: /providers/opencode-go/ - title: OpenCode Zen url: /providers/opencode-zen/ + - title: Opper + url: /providers/opper/ - title: OpenRouter url: /providers/openrouter/ - title: OVHcloud diff --git a/docs/providers/opper/index.md b/docs/providers/opper/index.md new file mode 100644 index 0000000000..716d940b49 --- /dev/null +++ b/docs/providers/opper/index.md @@ -0,0 +1,69 @@ +--- +title: "Opper" +description: "Use Opper models with Docker Agent." +keywords: docker agent, ai agents, model providers, llm, opper +weight: 225 +canonical: https://docs.docker.com/ai/docker-agent/providers/opper/ +--- + +_Use Opper models with Docker Agent._ + +## Overview + +[Opper](https://opper.ai) is an EU-hosted AI gateway that serves models from +many providers behind one OpenAI-compatible endpoint, with one API key. +Docker Agent includes built-in support for Opper as an alias provider. + +## Setup + +1. Create an API key at [platform.opper.ai](https://platform.opper.ai). +2. Set the environment variable: + + ```bash + export OPPER_API_KEY=your-api-key + ``` + +## Usage + +### Inline Syntax + +```yaml +agents: + root: + model: opper/claude-sonnet-4-6 + description: Assistant using Opper + instruction: You are a helpful assistant. +``` + +### Named Model + +```yaml +models: + opper_model: + provider: opper + model: claude-sonnet-4-6 + temperature: 0.7 + max_tokens: 8192 + +agents: + root: + model: opper_model + description: Assistant using Opper + instruction: You are a helpful assistant. +``` + +## Available Models + +Opper model IDs come in two forms. A bare pool name such as +`claude-sonnet-4-6` or `gpt-5.4-mini` lets Opper pick a provider for each +request. A `provider/model` ID such as `anthropic/claude-sonnet-4-6` pins one +route. List the model IDs your key can call with: + +```bash +curl https://api.opper.ai/v3/compat/models \ + -H "Authorization: Bearer $OPPER_API_KEY" +``` + +The public catalogue is also available without a key at +`https://api.opper.ai/v3/models`. Docker Agent's default for Opper is +`claude-sonnet-4-6`. diff --git a/docs/providers/overview/index.md b/docs/providers/overview/index.md index 2f6e18ea85..b9347bf9b7 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` | +| Opper | `opper` | `OPPER_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`) | diff --git a/examples/README.md b/examples/README.md index 5b77fca141..de091668ee 100644 --- a/examples/README.md +++ b/examples/README.md @@ -210,6 +210,7 @@ remote MCP endpoints. | [`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. | +| [`opper.yaml`](opper.yaml) | Opper 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 (`fireworks-ai`). | diff --git a/examples/opper.yaml b/examples/opper.yaml new file mode 100644 index 0000000000..3c2362d714 --- /dev/null +++ b/examples/opper.yaml @@ -0,0 +1,17 @@ +# yaml-language-server: $schema=../agent-schema.json + +models: + opper_model: + provider: opper + model: claude-sonnet-4-6 + +agents: + root: + model: opper_model + description: Assistant using Opper + 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 4bd613ca86..13670a6f2f 100644 --- a/pkg/config/auto.go +++ b/pkg/config/auto.go @@ -64,6 +64,7 @@ var cloudProviders = []providerConfig{ {"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"}, + {"opper", []string{"OPPER_API_KEY"}, "OPPER_API_KEY", "OPPER_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"}, @@ -163,6 +164,7 @@ var DefaultModels = map[string]string{ "openrouter": "meta-llama/llama-4-maverick", "api-route": "gpt-6.1-sol", "atlascloud": "qwen/qwen3.8-max", + "opper": "claude-sonnet-4-6", "baseten": "deepseek-ai/DeepSeek-V4-Pro", "ovhcloud": "Qwen3.5-397B-A17B", "daoxe": "claude-sonnet-4-6", diff --git a/pkg/config/auto_test.go b/pkg/config/auto_test.go index 8566c6801a..ee08d0fbcd 100644 --- a/pkg/config/auto_test.go +++ b/pkg/config/auto_test.go @@ -88,6 +88,13 @@ func TestAvailableProviders_NoGateway(t *testing.T) { }, expectedProvider: "atlascloud", }, + { + name: "opper api key present", + envVars: map[string]string{ + "OPPER_API_KEY": "test-key", + }, + expectedProvider: "opper", + }, { name: "baseten api key present", envVars: map[string]string{ @@ -336,6 +343,15 @@ func TestAutoModelConfig(t *testing.T) { expectedModel: "gpt-6.1-sol", expectedMaxTokens: 32000, }, + { + name: "opper provider", + envVars: map[string]string{ + "OPPER_API_KEY": "test-key", + }, + expectedProvider: "opper", + expectedModel: "claude-sonnet-4-6", + expectedMaxTokens: 32000, + }, { name: "baseten provider", envVars: map[string]string{ diff --git a/pkg/model/provider/aliases.go b/pkg/model/provider/aliases.go index 53d1cca4e0..0c18b3fe63 100644 --- a/pkg/model/provider/aliases.go +++ b/pkg/model/provider/aliases.go @@ -75,6 +75,11 @@ var Aliases = map[string]Alias{ BaseURL: "https://api.atlascloud.ai/v1", TokenEnvVar: "ATLASCLOUD_API_KEY", }, + "opper": { + APIType: "openai", + BaseURL: "https://api.opper.ai/v3/compat", + TokenEnvVar: "OPPER_API_KEY", + }, "mistral": { APIType: "openai", BaseURL: "https://api.mistral.ai/v1", diff --git a/pkg/model/provider/aliases_test.go b/pkg/model/provider/aliases_test.go index 3284941807..e8e4489a1c 100644 --- a/pkg/model/provider/aliases_test.go +++ b/pkg/model/provider/aliases_test.go @@ -41,6 +41,7 @@ func TestCatalogAliases(t *testing.T) { "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"}, + "opper": {APIType: "openai", BaseURL: "https://api.opper.ai/v3/compat", TokenEnvVar: "OPPER_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"}, diff --git a/pkg/model/provider/openai/client.go b/pkg/model/provider/openai/client.go index 9a1f188e0c..4b5b5fa521 100644 --- a/pkg/model/provider/openai/client.go +++ b/pkg/model/provider/openai/client.go @@ -268,6 +268,7 @@ var openModelHostProviders = map[string]bool{ "daoxe": true, "ovhcloud": true, "openrouter": true, + "opper": true, "nebius": true, "nvidia": true, "cerebras": true, diff --git a/pkg/model/provider/openai/system_message_merge_test.go b/pkg/model/provider/openai/system_message_merge_test.go index 7a5f2c0cbf..c9e34da517 100644 --- a/pkg/model/provider/openai/system_message_merge_test.go +++ b/pkg/model/provider/openai/system_message_merge_test.go @@ -45,6 +45,16 @@ func TestDaoxe_MergesConsecutiveSystemMessages(t *testing.T) { }) } +func TestOpper_MergesConsecutiveSystemMessages(t *testing.T) { + t.Parallel() + + assertMergesConsecutiveMessages(t, &latest.ModelConfig{ + Provider: "opper", + Model: "claude-sonnet-4-6", + TokenKey: "MY_TOKEN", + }) +} + func TestBaseten_MergesConsecutiveSystemMessages(t *testing.T) { t.Parallel() @@ -172,6 +182,7 @@ func TestShouldMergeConsecutiveMessages_Gating(t *testing.T) { {"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}, + {"opper, no base_url", &latest.ModelConfig{Provider: "opper", Model: "claude-sonnet-4-6"}, true}, {"ovhcloud", &latest.ModelConfig{Provider: "ovhcloud", Model: "Qwen3.5-397B-A17B"}, true}, {"open-model host alias cerebras", &latest.ModelConfig{Provider: "cerebras", Model: "qwen-3-coder-480b"}, true}, {"open-model host fireworks", &latest.ModelConfig{Provider: "fireworks", Model: "accounts/fireworks/models/kimi-k3"}, true},