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
20 changes: 18 additions & 2 deletions docs/docs/models/providers/additional.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
title: Additional Providers
social:
title: Additional Providers
tagline: Configure Groq, Aliyun, OpenRouter, TensorZero, and generic endpoints.
description: Configure Groq, Aliyun, OpenRouter, TensorZero, and generic endpoints.
tagline: Configure Atlas Cloud, Groq, Aliyun, OpenRouter, TensorZero, and generic endpoints.
description: Configure Atlas Cloud, Groq, Aliyun, OpenRouter, TensorZero, and generic endpoints.
alt: fast-agent social card — Additional model providers
---

Expand Down Expand Up @@ -33,6 +33,7 @@ Run `fast-agent check` after adding credentials to confirm they are visible to f

| Provider | Config key | API key environment variable | Default endpoint | Model string examples |
| --- | --- | --- | --- | --- |
| Atlas Cloud | `atlascloud` | `ATLASCLOUD_API_KEY` | `https://api.atlascloud.ai/v1` | `atlascloud.qwen/qwen3.8-max` |
| Groq | `groq` | `GROQ_API_KEY` | `https://api.groq.com/openai/v1` | `groq.openai/gpt-oss-120b` |
| Aliyun | `aliyun` | `ALIYUN_API_KEY` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | `qwen-turbo`, `aliyun.qwen3-max` |
| OpenRouter | `openrouter` | `OPENROUTER_API_KEY` | `https://openrouter.ai/api/v1` | `openrouter.google/gemini-2.5-pro-exp-03-25:free` |
Expand All @@ -48,6 +49,21 @@ Run `fast-agent check` after adding credentials to confirm they are visible to f

Use these when the provider exposes an OpenAI-compatible API but has its own credentials, model catalog, or small behavior differences.

### Atlas Cloud

```yaml
atlascloud:
api_key: "${ATLASCLOUD_API_KEY}"
```

```bash
fast-agent --model atlascloud.qwen/qwen3.8-max
```

Atlas Cloud uses its OpenAI-compatible Chat Completions endpoint. The provider
defaults to `qwen/qwen3.8-max`; set `default_model` or pass an explicit model
string to use another model from the Atlas Cloud catalog.

### Groq

```yaml
Expand Down
6 changes: 6 additions & 0 deletions examples/setup/fast-agent.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ anthropic:
# # base_url: "https://api.moonshot.ai/v1"
# # default_model: "kimi-k3"

# Atlas Cloud OpenAI-compatible Chat Completions provider:
# atlascloud:
# api_key: "${ATLASCLOUD_API_KEY}"
# # base_url: "https://api.atlascloud.ai/v1"
# # default_model: "qwen/qwen3.8-max"

#shell_execution:
# model-facing shell tools: auto (Grok shell/Process; others Bash/Process),
# minimal_process, grok_shell, or native
Expand Down
5 changes: 5 additions & 0 deletions src/fast_agent/cli/commands/check_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ class ProviderCatalogScope:
display_name="OpenRouter",
providers=(Provider.OPENROUTER,),
),
"atlascloud": ProviderCatalogScope(
display_name="Atlas Cloud",
providers=(Provider.ATLASCLOUD,),
),
}

_PROVIDER_CATALOG_SCOPE_ALIASES: dict[str, str] = {
Expand All @@ -147,6 +151,7 @@ class ProviderCatalogScope:
"xai",
"metaai",
"openrouter",
"atlascloud",
"responses",
"codexresponses",
)
Expand Down
23 changes: 23 additions & 0 deletions src/fast_agent/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,26 @@ class OpenRouterSettings(BaseModel):
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)


class AtlasCloudSettings(BaseModel):
"""Settings for Atlas Cloud's OpenAI-compatible API."""

api_key: str | None = Field(default=None, description="Atlas Cloud API key")
base_url: str | None = Field(
default=None,
description="Override API endpoint (default: https://api.atlascloud.ai/v1)",
)
default_model: str | None = Field(
default=None,
description="Default model when Atlas Cloud is selected without an explicit model",
)
default_headers: dict[str, str] | None = Field(
default=None,
description="Custom headers for all API requests",
)

model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)


class AzureSettings(BaseModel):
"""Settings for using Azure OpenAI Service in the fast-agent application."""

Expand Down Expand Up @@ -2314,6 +2334,9 @@ def _migrate_legacy_mcp_settings(cls, values: Any) -> Any:
openrouter: OpenRouterSettings | None = None
"""Settings for using OpenRouter models in the fast-agent application"""

atlascloud: AtlasCloudSettings | None = None
"""Settings for using Atlas Cloud models in the fast-agent application"""

generic: GenericSettings | None = None
"""Settings for using Generic models in the fast-agent application"""

Expand Down
4 changes: 4 additions & 0 deletions src/fast_agent/llm/model_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ def __call__(self, **kwargs: Any) -> FastAgentLLMProtocol: ...
"MetaAIResponsesLLM",
),
Provider.OPENROUTER: ("fast_agent.llm.provider.openai.llm_openrouter", "OpenRouterLLM"),
Provider.ATLASCLOUD: (
"fast_agent.llm.provider.openai.llm_atlascloud",
"AtlasCloudLLM",
),
Provider.TENSORZERO: (
"fast_agent.llm.provider.openai.llm_tensorzero_openai",
"TensorZeroOpenAILLM",
Expand Down
8 changes: 8 additions & 0 deletions src/fast_agent/llm/model_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ class ModelSelectionCatalog:
Provider.ZAI: (_builtin_entry("zaiglm", display_label="GLM 5.2"),),
Provider.MOONSHOT: (_builtin_entry("kimik3", display_label="Kimi K3"),),
Provider.OPENROUTER: (),
Provider.ATLASCLOUD: (
CatalogModelEntry(
alias="qwen3.8-max",
model="atlascloud.qwen/qwen3.8-max",
display_label="Qwen3.8 Max",
fast=True,
),
),
Provider.ALIYUN: (
_builtin_entry("qwen-turbo", fast=True),
_builtin_entry("qwen3-max"),
Expand Down
24 changes: 24 additions & 0 deletions src/fast_agent/llm/provider/openai/llm_atlascloud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from fast_agent.llm.provider.openai.llm_openai_compatible import OpenAICompatibleLLM
from fast_agent.llm.provider_types import Provider
from fast_agent.types import RequestParams

ATLASCLOUD_BASE_URL = "https://api.atlascloud.ai/v1"
DEFAULT_ATLASCLOUD_MODEL = "qwen/qwen3.8-max"


class AtlasCloudLLM(OpenAICompatibleLLM):
"""Atlas Cloud provider using its OpenAI-compatible Chat Completions API."""

def __init__(self, **kwargs) -> None:
kwargs.pop("provider", None)
super().__init__(provider=Provider.ATLASCLOUD, **kwargs)

def _initialize_default_params(self, kwargs: dict) -> RequestParams:
return self._initialize_default_params_with_model_fallback(kwargs, DEFAULT_ATLASCLOUD_MODEL)

def _provider_base_url(self) -> str:
base_url = None
if self.context.config and self.context.config.atlascloud:
base_url = self.context.config.atlascloud.base_url

return base_url if base_url else ATLASCLOUD_BASE_URL
1 change: 1 addition & 0 deletions src/fast_agent/llm/provider_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def config_name(self) -> str:
GOOGLE = ("google", "Google") # For Google GenAI native library
OPENAI = ("openai", "OpenAI")
OPENROUTER = ("openrouter", "OpenRouter")
ATLASCLOUD = ("atlascloud", "Atlas Cloud")
TENSORZERO = ("tensorzero", "TensorZero") # For TensorZero Gateway
AZURE = ("azure", "Azure") # Azure OpenAI Service
ALIYUN = ("aliyun", "Aliyun") # Aliyun Bailian OpenAI Service
Expand Down
1 change: 1 addition & 0 deletions src/fast_agent/ui/model_picker_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
Provider.BEDROCK,
Provider.ALIYUN,
Provider.OPENROUTER,
Provider.ATLASCLOUD,
Provider.FAST_AGENT,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ def test_show_provider_model_catalog_moonshot_includes_curated_model(capsys) ->
assert "moonshot.kimi-k3" in _collapse(output)


def test_show_provider_model_catalog_atlascloud_includes_curated_model(capsys) -> None:
show_provider_model_catalog("atlascloud")

output = capsys.readouterr().out
assert "Atlas Cloud model catalog (curated)" in output
assert "qwen3.8-max" in output
assert "atlascloud.qwen/qwen3.8-max" in _collapse(output)


def test_show_models_overview_includes_provider_args_and_named_aliases(
tmp_path: Path,
capsys,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import os

from fast_agent.config import (
AtlasCloudSettings,
AzureSettings,
DeepSeekSettings,
HuggingFaceSettings,
Expand All @@ -25,6 +26,7 @@
from fast_agent.context import Context
from fast_agent.llm.model_database import ModelDatabase
from fast_agent.llm.provider.google.llm_google_native import GoogleNativeLLM
from fast_agent.llm.provider.openai.llm_atlascloud import AtlasCloudLLM
from fast_agent.llm.provider.openai.llm_azure import AzureOpenAILLM
from fast_agent.llm.provider.openai.llm_deepseek import DeepSeekResponsesLLM
from fast_agent.llm.provider.openai.llm_generic import GenericLLM
Expand Down Expand Up @@ -155,6 +157,26 @@ def test_openrouter_provider_base_url_prefers_config_over_env() -> None:
os.environ["OPENROUTER_BASE_URL"] = original_env


def test_atlascloud_provider_uses_default_model_and_base_url() -> None:
llm = AtlasCloudLLM(context=Context(config=Settings()), model="")

assert llm.default_request_params.model == "qwen/qwen3.8-max"
assert llm._base_url() == "https://api.atlascloud.ai/v1"


def test_atlascloud_provider_uses_config_overrides() -> None:
settings = Settings(
atlascloud=AtlasCloudSettings(
base_url="https://atlas-gateway.example/v1",
default_model="openai/gpt-oss-120b",
)
)
llm = AtlasCloudLLM(context=Context(config=settings), model="")

assert llm.default_request_params.model == "openai/gpt-oss-120b"
assert llm._base_url() == "https://atlas-gateway.example/v1"


def test_huggingface_provider_default_model_used_with_provider_suffix() -> None:
settings = Settings(
hf=HuggingFaceSettings(
Expand Down
13 changes: 13 additions & 0 deletions tests/unit/fast_agent/llm/test_model_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from fast_agent.llm.model_selection import ModelSelectionCatalog
from fast_agent.llm.provider.anthropic.llm_anthropic import AnthropicLLM
from fast_agent.llm.provider.anthropic.llm_anthropic_vertex import AnthropicVertexLLM
from fast_agent.llm.provider.openai.llm_atlascloud import AtlasCloudLLM
from fast_agent.llm.provider.openai.llm_generic import GenericLLM
from fast_agent.llm.provider.openai.llm_huggingface import HuggingFaceLLM
from fast_agent.llm.provider.openai.llm_openai import OpenAILLM
Expand Down Expand Up @@ -87,6 +88,18 @@ def test_full_model_strings():
assert config.reasoning_effort == exp_effort


def test_atlascloud_model_string_and_factory() -> None:
config = ModelFactory.parse_model_string("atlascloud.qwen/qwen3.8-max")
assert config.provider == Provider.ATLASCLOUD
assert config.model_name == "qwen/qwen3.8-max"

factory = ModelFactory.create_factory("atlascloud.qwen/qwen3.8-max")
llm = factory(LlmAgent(AgentConfig(name="Atlas Cloud Test")))

assert isinstance(llm, AtlasCloudLLM)
assert llm.provider == Provider.ATLASCLOUD


def test_deprecated_reasoning_suffix_is_rejected() -> None:
with pytest.raises(ModelConfigError, match=r"Use '\?reasoning=<value>' instead"):
ModelFactory.parse_model_string("openai.o1.high")
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/fast_agent/llm/test_model_selection_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,15 @@ def test_openrouter_all_models_use_discovered_models_when_no_curated(monkeypatch
assert models == ["openrouter.openai/gpt-4.1-mini"]


def test_atlascloud_catalog_includes_default_model() -> None:
entries = ModelSelectionCatalog.list_current_entries(Provider.ATLASCLOUD)

assert len(entries) == 1
assert entries[0].alias == "qwen3.8-max"
assert entries[0].model == "atlascloud.qwen/qwen3.8-max"
assert entries[0].fast is True


def test_overlay_catalog_uses_explicit_environment_context(
monkeypatch,
tmp_path: Path,
Expand Down
Loading