From d71c328ec1409942d580db3844144012deb180b0 Mon Sep 17 00:00:00 2001 From: binyangzhu000-sudo <224954946+binyangzhu000-sudo@users.noreply.github.com> Date: Sat, 25 Jul 2026 13:36:25 +0800 Subject: [PATCH] feat: add Atlas Cloud LLM provider --- backend/app/services/llm/client.py | 9 ++ .../tests/test_atlas_cloud_llm_provider.py | 88 +++++++++++++++++++ .../pages/enterprise-settings/tabs/LlmTab.tsx | 1 + 3 files changed, 98 insertions(+) create mode 100644 backend/tests/test_atlas_cloud_llm_provider.py diff --git a/backend/app/services/llm/client.py b/backend/app/services/llm/client.py index 8e5f88b56..2a571f59d 100644 --- a/backend/app/services/llm/client.py +++ b/backend/app/services/llm/client.py @@ -2035,6 +2035,8 @@ class ProviderSpec: # Provider aliases accepted for compatibility PROVIDER_ALIASES: dict[str, str] = { + "atlas": "atlascloud", + "atlas-cloud": "atlascloud", "openai_response": "openai-response", "openairesponses": "openai-response", } @@ -2064,6 +2066,13 @@ class ProviderSpec: default_base_url="https://api.openai.com/v1", default_max_tokens=16384, ), + "atlascloud": ProviderSpec( + provider="atlascloud", + display_name="Atlas Cloud", + protocol="openai_compatible", + default_base_url="https://api.atlascloud.ai/v1", + default_max_tokens=8192, + ), "azure": ProviderSpec( provider="azure", display_name="Azure OpenAI", diff --git a/backend/tests/test_atlas_cloud_llm_provider.py b/backend/tests/test_atlas_cloud_llm_provider.py new file mode 100644 index 000000000..18c873b0a --- /dev/null +++ b/backend/tests/test_atlas_cloud_llm_provider.py @@ -0,0 +1,88 @@ +"""Atlas Cloud provider registry coverage.""" + +from __future__ import annotations + +from app.services.llm.client import ( + LLMMessage, + OpenAICompatibleClient, + create_llm_client, + get_max_tokens, + get_provider_base_url, + get_provider_manifest, + get_provider_spec, + normalize_provider, +) + + +def test_atlas_cloud_aliases_resolve_to_canonical_provider() -> None: + assert normalize_provider("atlas") == "atlascloud" + assert normalize_provider("atlas-cloud") == "atlascloud" + assert normalize_provider("ATLASCloud") == "atlascloud" + + +def test_atlas_cloud_provider_spec_and_manifest_defaults() -> None: + spec = get_provider_spec("atlas-cloud") + + assert spec is not None + assert spec.provider == "atlascloud" + assert spec.display_name == "Atlas Cloud" + assert spec.protocol == "openai_compatible" + assert spec.default_base_url == "https://api.atlascloud.ai/v1" + assert spec.supports_tool_choice is True + assert spec.default_max_tokens == 8192 + assert get_provider_base_url("atlas", None) == "https://api.atlascloud.ai/v1" + assert get_provider_base_url("atlascloud", "https://proxy.example/v1") == "https://proxy.example/v1" + + manifest_entry = next(item for item in get_provider_manifest() if item["provider"] == "atlascloud") + assert manifest_entry["display_name"] == "Atlas Cloud" + assert manifest_entry["default_base_url"] == "https://api.atlascloud.ai/v1" + assert manifest_entry["supports_tool_choice"] is True + assert set(manifest_entry["aliases"]) == {"atlas", "atlas-cloud"} + + +def test_atlas_cloud_factory_uses_openai_compatible_client() -> None: + client = create_llm_client( + provider="atlas", + api_key="test-key", + model="qwen/qwen3.5-flash", + ) + + assert isinstance(client, OpenAICompatibleClient) + assert client.base_url == "https://api.atlascloud.ai/v1" + assert client.model == "qwen/qwen3.5-flash" + assert client.supports_tool_choice is True + assert client._normalize_base_url() == "https://api.atlascloud.ai/v1" + + +def test_atlas_cloud_uses_provider_token_defaults_and_overrides() -> None: + assert get_max_tokens("atlascloud", "qwen/qwen3.5-flash") == 8192 + assert get_max_tokens("atlas", "deepseek-ai/deepseek-v4-pro", max_output_tokens=2048) == 2048 + + +def test_atlas_cloud_openai_compatible_payload_keeps_one_leading_system_message() -> None: + client = create_llm_client( + provider="atlas-cloud", + api_key="test-key", + model="deepseek-ai/deepseek-v4-pro", + ) + + payload = client._build_payload( + [ + LLMMessage(role="user", content="Earlier user turn"), + LLMMessage(role="system", content="Static prompt", dynamic_content="Dynamic context"), + LLMMessage(role="system", content="Later system note"), + LLMMessage(role="user", content="Current user turn"), + ], + tools=[{"type": "function", "function": {"name": "finish", "parameters": {"type": "object"}}}], + temperature=0.2, + max_tokens=1024, + ) + + assert isinstance(client, OpenAICompatibleClient) + assert payload["model"] == "deepseek-ai/deepseek-v4-pro" + assert payload["tool_choice"] == "auto" + assert [message["role"] for message in payload["messages"]].count("system") == 1 + assert payload["messages"][0]["role"] == "system" + assert "Static prompt" in payload["messages"][0]["content"] + assert "Dynamic context" in payload["messages"][0]["content"] + assert "Later system note" in payload["messages"][0]["content"] diff --git a/frontend/src/pages/enterprise-settings/tabs/LlmTab.tsx b/frontend/src/pages/enterprise-settings/tabs/LlmTab.tsx index 64b03f2ac..8fc4f84b3 100644 --- a/frontend/src/pages/enterprise-settings/tabs/LlmTab.tsx +++ b/frontend/src/pages/enterprise-settings/tabs/LlmTab.tsx @@ -49,6 +49,7 @@ interface RuntimeModelSettings { const FALLBACK_LLM_PROVIDERS: LLMProviderSpec[] = [ { provider: 'anthropic', display_name: 'Anthropic', protocol: 'anthropic', default_base_url: 'https://api.anthropic.com', supports_tool_choice: false, default_max_tokens: 8192 }, { provider: 'openai', display_name: 'OpenAI', protocol: 'openai_compatible', default_base_url: 'https://api.openai.com/v1', supports_tool_choice: true, default_max_tokens: 16384 }, + { provider: 'atlascloud', display_name: 'Atlas Cloud', protocol: 'openai_compatible', default_base_url: 'https://api.atlascloud.ai/v1', supports_tool_choice: true, default_max_tokens: 8192 }, { provider: 'azure', display_name: 'Azure OpenAI', protocol: 'openai_compatible', default_base_url: '', supports_tool_choice: true, default_max_tokens: 16384 }, { provider: 'deepseek', display_name: 'DeepSeek', protocol: 'openai_compatible', default_base_url: 'https://api.deepseek.com/v1', supports_tool_choice: true, default_max_tokens: 8192 }, { provider: 'minimax', display_name: 'MiniMax', protocol: 'openai_compatible', default_base_url: 'https://api.minimaxi.com/v1', supports_tool_choice: true, default_max_tokens: 16384 },