diff --git a/agent-schema.json b/agent-schema.json index 3194d4d08..00e0aecbe 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, atlascloud, 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", diff --git a/docs/concepts/models/index.md b/docs/concepts/models/index.md index e12fb5fd5..64ef35b45 100644 --- a/docs/concepts/models/index.md +++ b/docs/concepts/models/index.md @@ -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` | +| Atlas Cloud | `atlascloud` | Qwen and other open models | `ATLASCLOUD_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` or `GH_TOKEN` (PAT with `copilot`) | diff --git a/docs/configuration/models/index.md b/docs/configuration/models/index.md index 7e7656c1e..f2eaa173f 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, ovhcloud, groq, fireworks, deepseek, cerebras, together, huggingface, moonshot, vercel, cloudflare-workers-ai, cloudflare-ai-gateway, requesty, openrouter, 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`, `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`, `atlascloud`, `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. | diff --git a/docs/data/nav.yml b/docs/data/nav.yml index 88e362a4d..73c069ce6 100644 --- a/docs/data/nav.yml +++ b/docs/data/nav.yml @@ -165,6 +165,8 @@ url: /providers/overview/ - title: Anthropic url: /providers/anthropic/ + - title: Atlas Cloud + url: /providers/atlascloud/ - title: AWS Bedrock url: /providers/bedrock/ - title: Baseten diff --git a/docs/providers/atlascloud/index.md b/docs/providers/atlascloud/index.md new file mode 100644 index 000000000..bef5f9c7b --- /dev/null +++ b/docs/providers/atlascloud/index.md @@ -0,0 +1,65 @@ +--- +title: "Atlas Cloud" +description: "Use Atlas Cloud models with Docker Agent." +keywords: docker agent, ai agents, model providers, llm, atlas cloud +weight: 25 +canonical: https://docs.docker.com/ai/docker-agent/providers/atlascloud/ +--- + +_Use Atlas Cloud models with Docker Agent._ + +## Overview + +Atlas Cloud provides access to multiple model families through an OpenAI-compatible API. Docker Agent includes built-in support for Atlas Cloud as an alias provider. + +## Setup + +1. [Create an Atlas Cloud API key](https://www.atlascloud.ai/docs/api-keys) +2. Set the environment variable: + + ```bash + export ATLASCLOUD_API_KEY=your-api-key + ``` + +## Usage + +### Inline Syntax + +Use an Atlas Cloud model by prefixing its model ID with `atlascloud/`: + +```yaml +agents: + root: + model: atlascloud/qwen/qwen3.8-max + description: Assistant using Atlas Cloud + instruction: You are a helpful assistant. +``` + +Docker Agent splits only the first slash, so model IDs that include a model family prefix are preserved. + +### Named Model + +For more control over parameters: + +```yaml +models: + atlas_qwen: + provider: atlascloud + model: qwen/qwen3.8-max + temperature: 0.7 + max_tokens: 8192 + +agents: + root: + model: atlas_qwen + description: Assistant using Atlas Cloud + instruction: You are a helpful assistant. +``` + +## How It Works + +Atlas Cloud is implemented as a built-in alias in Docker Agent: + +- **API Type:** OpenAI-compatible (`openai`) +- **Base URL:** `https://api.atlascloud.ai/v1` +- **Token Variable:** `ATLASCLOUD_API_KEY` diff --git a/docs/providers/overview/index.md b/docs/providers/overview/index.md index a7a048f1f..6aa7c09f2 100644 --- a/docs/providers/overview/index.md +++ b/docs/providers/overview/index.md @@ -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` | +| Atlas Cloud | [`atlascloud`](../atlascloud/index.md) | `ATLASCLOUD_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` or `GH_TOKEN` (PAT with `copilot` scope) | diff --git a/pkg/config/auto.go b/pkg/config/auto.go index cc16f7bab..498af1349 100644 --- a/pkg/config/auto.go +++ b/pkg/config/auto.go @@ -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"}, + {"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"}, {"groq", []string{"GROQ_API_KEY"}, "GROQ_API_KEY", "GROQ_API_KEY"}, @@ -157,6 +158,7 @@ var DefaultModels = map[string]string{ "dmr": "ai/qwen3:latest", "mistral": "mistral-small-latest", "openrouter": "meta-llama/llama-4-maverick", + "atlascloud": "qwen/qwen3.8-max", "baseten": "deepseek-ai/DeepSeek-V4-Pro", "ovhcloud": "Qwen3.5-397B-A17B", "groq": "llama-3.3-70b-versatile", diff --git a/pkg/config/auto_test.go b/pkg/config/auto_test.go index f2db1d298..ad0d5de33 100644 --- a/pkg/config/auto_test.go +++ b/pkg/config/auto_test.go @@ -73,6 +73,13 @@ func TestAvailableProviders_NoGateway(t *testing.T) { }, expectedProvider: "openrouter", }, + { + name: "atlascloud api key present", + envVars: map[string]string{ + "ATLASCLOUD_API_KEY": "test-key", + }, + expectedProvider: "atlascloud", + }, { name: "baseten api key present", envVars: map[string]string{ @@ -477,7 +484,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", "atlascloud", "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) { @@ -495,6 +502,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-4-maverick", DefaultModels["openrouter"]) + assert.Equal(t, "qwen/qwen3.8-max", DefaultModels["atlascloud"]) assert.Equal(t, "deepseek-ai/DeepSeek-V4-Pro", DefaultModels["baseten"]) assert.Equal(t, "Qwen3.5-397B-A17B", DefaultModels["ovhcloud"]) assert.Equal(t, "llama-3.3-70b-versatile", DefaultModels["groq"]) @@ -514,7 +522,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", "atlascloud", "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) { @@ -536,6 +544,8 @@ func TestAutoModelConfig_IntegrationWithDefaultModels(t *testing.T) { envVars["MISTRAL_API_KEY"] = "test-key" case "openrouter": envVars["OPENROUTER_API_KEY"] = "test-key" + case "atlascloud": + envVars["ATLASCLOUD_API_KEY"] = "test-key" case "baseten": envVars["BASETEN_API_KEY"] = "test-key" case "ovhcloud": @@ -1130,6 +1140,7 @@ func TestProviderAPIKeyEnvVars(t *testing.T) { "GOOGLE_API_KEY", "MISTRAL_API_KEY", "OPENROUTER_API_KEY", + "ATLASCLOUD_API_KEY", "XAI_API_KEY", "NEBIUS_API_KEY", } { diff --git a/pkg/config/examples_test.go b/pkg/config/examples_test.go index 09f59f7a4..642a2ea0f 100644 --- a/pkg/config/examples_test.go +++ b/pkg/config/examples_test.go @@ -39,6 +39,7 @@ var modelsDevAbsentProviders = map[string]bool{ "ovhcloud": true, // models.dev lower-cases OVHcloud model ids (e.g. "qwen3.5-397b-a17b"); the provider API is case-sensitive and takes "Qwen3.5-397B-A17B" "cloudflare-workers-ai": true, // example uses an @cf/... model id not present in the models.dev snapshot (only variant ids like -fp8 are listed) "cloudflare-ai-gateway": true, // multi-provider router; example model ids use the gateway's provider/model form, not guaranteed to match a models.dev id + "atlascloud": true, // Atlas Cloud is not listed in the models.dev catalog; its model ids come from its own /api/v1/models endpoint } func collectExamples(t *testing.T) []string { diff --git a/pkg/model/provider/aliases.go b/pkg/model/provider/aliases.go index 49e612364..09d4e3a83 100644 --- a/pkg/model/provider/aliases.go +++ b/pkg/model/provider/aliases.go @@ -64,6 +64,11 @@ var Aliases = map[string]Alias{ BaseURL: "https://openrouter.ai/api/v1", TokenEnvVar: "OPENROUTER_API_KEY", }, + "atlascloud": { + APIType: "openai", + BaseURL: "https://api.atlascloud.ai/v1", + TokenEnvVar: "ATLASCLOUD_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 e0c48a1b5..d6b35c2d3 100644 --- a/pkg/model/provider/aliases_test.go +++ b/pkg/model/provider/aliases_test.go @@ -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"}, + "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"}, "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"}, diff --git a/pkg/model/provider/openai/client.go b/pkg/model/provider/openai/client.go index 604c56b90..53e0d815a 100644 --- a/pkg/model/provider/openai/client.go +++ b/pkg/model/provider/openai/client.go @@ -264,6 +264,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{ + "atlascloud": true, "baseten": true, "ovhcloud": true, "openrouter": true, diff --git a/pkg/model/provider/openai_alias_providers_test.go b/pkg/model/provider/openai_alias_providers_test.go index cc0f3c03d..ec009c58c 100644 --- a/pkg/model/provider/openai_alias_providers_test.go +++ b/pkg/model/provider/openai_alias_providers_test.go @@ -49,6 +49,14 @@ type openAIAliasProvider struct { } var openAIAliasProviders = []openAIAliasProvider{ + { + provider: "atlascloud", + envVar: "ATLASCLOUD_API_KEY", + testKey: "test-atlascloud-key", + model: "qwen/qwen3.8-max", + greeting: "Hello from Atlas Cloud", + mergesSystemMessages: true, + }, { provider: "deepseek", envVar: "DEEPSEEK_API_KEY",