From 24eb1109abbe39358f7db7112596ba9808b32800 Mon Sep 17 00:00:00 2001 From: binyangzhu000-sudo <224954946+binyangzhu000-sudo@users.noreply.github.com> Date: Mon, 10 Aug 2026 23:04:05 +0800 Subject: [PATCH] feat: add Atlas Cloud provider Signed-off-by: binyangzhu000-sudo <224954946+binyangzhu000-sudo@users.noreply.github.com> --- docs/configuration/models/index.md | 4 +- docs/providers/atlascloud/index.md | 65 +++++++++++++++++++ docs/providers/overview/index.md | 1 + pkg/config/auto.go | 2 + pkg/config/auto_test.go | 15 ++++- pkg/model/provider/aliases.go | 5 ++ pkg/model/provider/aliases_test.go | 1 + pkg/model/provider/openai/client.go | 1 + .../provider/openai_alias_providers_test.go | 8 +++ 9 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 docs/providers/atlascloud/index.md diff --git a/docs/configuration/models/index.md b/docs/configuration/models/index.md index c696794e60..08f15ab739 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 @@ -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`, `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/providers/atlascloud/index.md b/docs/providers/atlascloud/index.md new file mode 100644 index 0000000000..1648375c7e --- /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: 235 +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 662ca2a430..2a10cce7fc 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` (PAT with `copilot` scope) | diff --git a/pkg/config/auto.go b/pkg/config/auto.go index f66b22f042..90f067b2b2 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-3.3-70b-instruct", + "atlascloud": "qwen/qwen3.8-max", "baseten": "deepseek-ai/DeepSeek-V3.1", "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 8e6b4dfe2b..139bd39d3d 100644 --- a/pkg/config/auto_test.go +++ b/pkg/config/auto_test.go @@ -72,6 +72,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{ @@ -476,7 +483,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) { @@ -494,6 +501,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, "qwen/qwen3.8-max", DefaultModels["atlascloud"]) 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"]) @@ -513,7 +521,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) { @@ -535,6 +543,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": @@ -1129,6 +1139,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/model/provider/aliases.go b/pkg/model/provider/aliases.go index 49e6123643..09d4e3a831 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 e0c48a1b56..d6b35c2d33 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 73511c6783..a28d0c16c1 100644 --- a/pkg/model/provider/openai/client.go +++ b/pkg/model/provider/openai/client.go @@ -257,6 +257,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 8aedaab24d..bc7c19fc0e 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",