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
2 changes: 1 addition & 1 deletion agent-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,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, orcarouter, 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.).",
"examples": [
"openai",
"anthropic",
Expand Down
1 change: 1 addition & 0 deletions docs/concepts/models/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ for details.
| Cloudflare AI Gateway | `cloudflare-ai-gateway` | Multi-provider gateway | `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_GATEWAY_ID` |
| Requesty | `requesty` | Multi-provider gateway | `REQUESTY_API_KEY` |
| OpenRouter | `openrouter` | Multi-provider gateway | `OPENROUTER_API_KEY` |
| OrcaRouter | `orcarouter` | Multi-provider gateway | `ORCAROUTER_API_KEY` |
| Azure OpenAI | `azure` | gpt-4o, gpt-5 on Azure | `AZURE_API_KEY` + `base_url` |
| [Ollama](../../providers/local/index.md) | `ollama` | Any local Ollama model | None (local; optional `base_url`) |
| GitHub Copilot | `github-copilot` | Copilot-hosted OpenAI/Anthropic | `GITHUB_TOKEN` (PAT with `copilot`) |
Expand Down
6 changes: 3 additions & 3 deletions docs/configuration/models/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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, ovhcloud, groq, fireworks, deepseek, cerebras, together, huggingface, moonshot, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, requesty, openrouter, orcarouter,
# azure, ollama, github-copilot, or a named provider defined
# under the top-level `providers:` section.
model: string # Required: model identifier
Expand Down Expand Up @@ -56,7 +56,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`, `ovhcloud`, `groq`, `fireworks`, `deepseek`, `cerebras`, `together`, `huggingface`, `moonshot`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `orcarouter`, `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. |
Expand Down Expand Up @@ -508,7 +508,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`, `ovhcloud`, `groq`, `fireworks`, `deepseek`, `cerebras`, `together`, `huggingface`, `moonshot`, `vercel`, `cloudflare-workers-ai`, `cloudflare-ai-gateway`, `requesty`, `openrouter`, `orcarouter`, `ollama`, and any custom provider using the OpenAI API),
`provider_opts.http_headers` adds arbitrary HTTP headers to every outgoing
request:

Expand Down
88 changes: 88 additions & 0 deletions docs/providers/orcarouter/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
title: "OrcaRouter"
description: "Use OrcaRouter models with Docker Agent."
keywords: docker agent, ai agents, model providers, llm, orcarouter
weight: 231
canonical: https://docs.docker.com/ai/docker-agent/providers/orcarouter/
---

_Use OrcaRouter models with Docker Agent._

## Overview

[OrcaRouter](https://www.orcarouter.ai) provides access to models from many providers through an OpenAI-compatible API. Docker Agent includes built-in support for OrcaRouter as an alias provider. It also runs gateway-level, zero-trust security for AI agents on the same endpoint — screening every prompt/response and governing every tool call on a default-deny basis, with no application code changes.

## Setup

1. Get an API key from [OrcaRouter](https://www.orcarouter.ai)
2. Set the environment variable:

```bash
export ORCAROUTER_API_KEY=your-api-key
```

## Usage

### Inline Syntax

The simplest way to use OrcaRouter:

```yaml
agents:
root:
model: orcarouter/orcarouter/auto
description: Assistant using OrcaRouter
instruction: You are a helpful assistant.
```

`orcarouter/auto` is OrcaRouter's adaptive router, which routes each request to the best available model for the task. You can also reference any model served by the gateway with its upstream provider prefix, such as `orcarouter/anthropic/claude-sonnet-4-5` or `orcarouter/deepseek/deepseek-v4-pro`. Docker Agent splits only the first slash, so the full upstream model ID is preserved.

### Named Model

For more control over parameters:

```yaml
models:
orca_router:
provider: orcarouter
model: orcarouter/auto
temperature: 0.7
max_tokens: 8192

agents:
root:
model: orca_router
description: Assistant using OrcaRouter
instruction: You are a helpful assistant.
```

## Pricing and Model Metadata

Docker Agent fetches OrcaRouter model metadata from [models.dev](https://models.dev/), including pricing per 1M input/output tokens, cache pricing when available, context limits, output limits, and modalities. This powers cost tracking and the model picker in the same way as other first-class providers.

If models.dev is unavailable, Docker Agent falls back to its embedded catalog snapshot.

## How It Works

OrcaRouter is implemented as a built-in alias in Docker Agent:

- **API Type:** OpenAI-compatible (`openai`)
- **Base URL:** `https://api.orcarouter.ai/v1`
- **Token Variable:** `ORCAROUTER_API_KEY`

## Example: Code Assistant

```yaml
agents:
coder:
model: orcarouter/orcarouter/auto
description: Code assistant using OrcaRouter
instruction: |
You are an expert programmer.
Write clean, maintainable code.
Explain trade-offs when helpful.
toolsets:
- type: filesystem
- type: shell
- type: think
```
1 change: 1 addition & 0 deletions docs/providers/overview/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Docker Agent also includes built-in aliases for these providers:
| 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` |
| OrcaRouter | `orcarouter` | `ORCAROUTER_API_KEY` |
| Azure OpenAI | `azure` | `AZURE_API_KEY` + `base_url` |
| [Ollama](../local/index.md) | `ollama` | None (local; optional `base_url`) |
| GitHub Copilot | `github-copilot` | `GITHUB_TOKEN` (PAT with `copilot` scope) |
Expand Down
2 changes: 2 additions & 0 deletions pkg/config/auto.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,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"},
{"orcarouter", []string{"ORCAROUTER_API_KEY"}, "ORCAROUTER_API_KEY", "ORCAROUTER_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"},
{"groq", []string{"GROQ_API_KEY"}, "GROQ_API_KEY", "GROQ_API_KEY"},
Expand Down Expand Up @@ -157,6 +158,7 @@ var DefaultModels = map[string]string{
"dmr": "ai/qwen3:latest",
"mistral": "mistral-small-latest",
"openrouter": "meta-llama/llama-3.3-70b-instruct",
"orcarouter": "orcarouter/auto",
"baseten": "deepseek-ai/DeepSeek-V3.1",
"ovhcloud": "Qwen3.5-397B-A17B",
"groq": "llama-3.3-70b-versatile",
Expand Down
27 changes: 25 additions & 2 deletions pkg/config/auto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ func TestAvailableProviders_NoGateway(t *testing.T) {
},
expectedProvider: "openrouter",
},
{
name: "orcarouter api key present",
envVars: map[string]string{
"ORCAROUTER_API_KEY": "test-key",
},
expectedProvider: "orcarouter",
},
{
name: "baseten api key present",
envVars: map[string]string{
Expand Down Expand Up @@ -304,6 +311,15 @@ func TestAutoModelConfig(t *testing.T) {
expectedModel: "meta-llama/llama-3.3-70b-instruct",
expectedMaxTokens: 32000,
},
{
name: "orcarouter provider",
envVars: map[string]string{
"ORCAROUTER_API_KEY": "test-key",
},
expectedProvider: "orcarouter",
expectedModel: "orcarouter/auto",
expectedMaxTokens: 32000,
},
{
name: "baseten provider",
envVars: map[string]string{
Expand Down Expand Up @@ -455,6 +471,10 @@ func TestPreferredMaxTokens(t *testing.T) {
provider: "openrouter",
expectedTokens: 32000,
},
{
provider: "orcarouter",
expectedTokens: 32000,
},
{
provider: "unknown-provider",
expectedTokens: 32000,
Expand All @@ -476,7 +496,7 @@ func TestDefaultModels(t *testing.T) {
t.Parallel()

// Test that DefaultModels map has all expected providers
expectedProviders := []string{"openai", "anthropic", "google", "dmr", "mistral", "openrouter", "baseten", "ovhcloud", "groq", "fireworks", "deepseek", "cerebras", "together", "huggingface", "moonshot", "vercel", "amazon-bedrock", "opencode-zen", "opencode-go", "github-copilot"}
expectedProviders := []string{"openai", "anthropic", "google", "dmr", "mistral", "openrouter", "orcarouter", "baseten", "ovhcloud", "groq", "fireworks", "deepseek", "cerebras", "together", "huggingface", "moonshot", "vercel", "amazon-bedrock", "opencode-zen", "opencode-go", "github-copilot"}

for _, provider := range expectedProviders {
t.Run(provider, func(t *testing.T) {
Expand All @@ -494,6 +514,7 @@ func TestDefaultModels(t *testing.T) {
assert.Equal(t, "ai/qwen3:latest", DefaultModels["dmr"])
assert.Equal(t, "mistral-small-latest", DefaultModels["mistral"])
assert.Equal(t, "meta-llama/llama-3.3-70b-instruct", DefaultModels["openrouter"])
assert.Equal(t, "orcarouter/auto", DefaultModels["orcarouter"])
assert.Equal(t, "deepseek-ai/DeepSeek-V3.1", DefaultModels["baseten"])
assert.Equal(t, "Qwen3.5-397B-A17B", DefaultModels["ovhcloud"])
assert.Equal(t, "llama-3.3-70b-versatile", DefaultModels["groq"])
Expand All @@ -513,7 +534,7 @@ func TestAutoModelConfig_IntegrationWithDefaultModels(t *testing.T) {
t.Parallel()

// Verify that AutoModelConfig always returns a model from DefaultModels
providers := []string{"openai", "anthropic", "google", "mistral", "openrouter", "baseten", "ovhcloud", "groq", "fireworks", "deepseek", "cerebras", "together", "huggingface", "moonshot", "vercel", "opencode-zen", "github-copilot"}
providers := []string{"openai", "anthropic", "google", "mistral", "openrouter", "orcarouter", "baseten", "ovhcloud", "groq", "fireworks", "deepseek", "cerebras", "together", "huggingface", "moonshot", "vercel", "opencode-zen", "github-copilot"}

for _, provider := range providers {
t.Run(provider, func(t *testing.T) {
Expand All @@ -535,6 +556,8 @@ func TestAutoModelConfig_IntegrationWithDefaultModels(t *testing.T) {
envVars["MISTRAL_API_KEY"] = "test-key"
case "openrouter":
envVars["OPENROUTER_API_KEY"] = "test-key"
case "orcarouter":
envVars["ORCAROUTER_API_KEY"] = "test-key"
case "baseten":
envVars["BASETEN_API_KEY"] = "test-key"
case "ovhcloud":
Expand Down
5 changes: 5 additions & 0 deletions pkg/model/provider/aliases.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ var Aliases = map[string]Alias{
BaseURL: "https://openrouter.ai/api/v1",
TokenEnvVar: "OPENROUTER_API_KEY",
},
"orcarouter": {
APIType: "openai",
BaseURL: "https://api.orcarouter.ai/v1",
TokenEnvVar: "ORCAROUTER_API_KEY",
},
"mistral": {
APIType: "openai",
BaseURL: "https://api.mistral.ai/v1",
Expand Down
1 change: 1 addition & 0 deletions pkg/model/provider/aliases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func TestCatalogAliases(t *testing.T) {

expected := map[string]Alias{
"openrouter": {APIType: "openai", BaseURL: "https://openrouter.ai/api/v1", TokenEnvVar: "OPENROUTER_API_KEY"},
"orcarouter": {APIType: "openai", BaseURL: "https://api.orcarouter.ai/v1", TokenEnvVar: "ORCAROUTER_API_KEY"},
"baseten": {APIType: "openai", BaseURL: "https://inference.baseten.co/v1", TokenEnvVar: "BASETEN_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"},
Expand Down
1 change: 1 addition & 0 deletions pkg/model/provider/openai/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ var openModelHostProviders = map[string]bool{
"baseten": true,
"ovhcloud": true,
"openrouter": true,
"orcarouter": true,
"nebius": true,
"nvidia": true,
"cerebras": true,
Expand Down
Loading