From 051037ff087c18156237e5385f7cc6b5d032ec6f Mon Sep 17 00:00:00 2001 From: moranfong <274257964+zijiefang@users.noreply.github.com> Date: Mon, 13 Apr 2026 23:45:26 +0800 Subject: [PATCH] feat(provider): add LongCat via OpenAI-compatible backend --- docs/configuration.md | 29 +++++++++++++++++++++ nanobot/config/schema.py | 1 + nanobot/providers/registry.py | 9 +++++++ tests/cli/test_commands.py | 33 ++++++++++++++++++++++++ tests/providers/test_longcat_provider.py | 29 +++++++++++++++++++++ 5 files changed, 101 insertions(+) create mode 100644 tests/providers/test_longcat_provider.py diff --git a/docs/configuration.md b/docs/configuration.md index f20cec5f..51e9f32d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -76,6 +76,7 @@ IMAP_PASSWORD=your-password-here | `moonshot` | LLM (Moonshot/Kimi) | [platform.moonshot.cn](https://platform.moonshot.cn) | | `zhipu` | LLM (Zhipu GLM) | [open.bigmodel.cn](https://open.bigmodel.cn) | | `mimo` | LLM (MiMo) | [platform.xiaomimimo.com](https://platform.xiaomimimo.com) | +| `longcat` | LLM (LongCat) | [longcat.chat](https://longcat.chat/platform/docs/zh/) | | `ollama` | LLM (local, Ollama) | — | | `lm_studio` | LLM (local, LM Studio) | — | | `mistral` | LLM | [docs.mistral.ai](https://docs.mistral.ai/) | @@ -339,6 +340,34 @@ nanobot agent -c ~/.nanobot-telegram/config.json -w /tmp/nanobot-telegram-test - +
+LongCat (OpenAI-compatible) + +LongCat is available through nanobot's built-in OpenAI-compatible provider flow. +The default API base already points to `https://api.longcat.chat/openai`, so you +usually only need to set `apiKey`. + +```json +{ + "providers": { + "longcat": { + "apiKey": "${LONGCAT_API_KEY}" + } + }, + "agents": { + "defaults": { + "provider": "longcat", + "model": "LongCat-Flash-Chat" + } + } +} +``` + +Official model names include `LongCat-Flash-Chat`, `LongCat-Flash-Thinking`, +`LongCat-Flash-Thinking-2601`, and `LongCat-Flash-Lite`. + +
+
Custom Provider (Any OpenAI-compatible API) diff --git a/nanobot/config/schema.py b/nanobot/config/schema.py index 210767bf..bb7ed073 100644 --- a/nanobot/config/schema.py +++ b/nanobot/config/schema.py @@ -151,6 +151,7 @@ class ProvidersConfig(Base): mistral: ProviderConfig = Field(default_factory=ProviderConfig) stepfun: ProviderConfig = Field(default_factory=ProviderConfig) # Step Fun (阶跃星辰) xiaomi_mimo: ProviderConfig = Field(default_factory=ProviderConfig) # Xiaomi MIMO (小米) + longcat: ProviderConfig = Field(default_factory=ProviderConfig) # LongCat aihubmix: ProviderConfig = Field(default_factory=ProviderConfig) # AiHubMix API gateway siliconflow: ProviderConfig = Field(default_factory=ProviderConfig) # SiliconFlow (硅基流动) volcengine: ProviderConfig = Field(default_factory=ProviderConfig) # VolcEngine (火山引擎) diff --git a/nanobot/providers/registry.py b/nanobot/providers/registry.py index a6404168..44fcd1ee 100644 --- a/nanobot/providers/registry.py +++ b/nanobot/providers/registry.py @@ -376,6 +376,15 @@ PROVIDERS: tuple[ProviderSpec, ...] = ( backend="openai_compat", default_api_base="https://api.xiaomimimo.com/v1", ), + # LongCat: OpenAI-compatible API + ProviderSpec( + name="longcat", + keywords=("longcat",), + env_key="LONGCAT_API_KEY", + display_name="LongCat", + backend="openai_compat", + default_api_base="https://api.longcat.chat/openai", + ), # === Local deployment (matched by config key, NOT by api_base) ========= # vLLM / any OpenAI-compatible local server ProviderSpec( diff --git a/tests/cli/test_commands.py b/tests/cli/test_commands.py index 6439c73f..2360ac28 100644 --- a/tests/cli/test_commands.py +++ b/tests/cli/test_commands.py @@ -285,6 +285,39 @@ def test_find_by_name_accepts_camel_case_and_hyphen_aliases(): assert find_by_name("volcengineCodingPlan").name == "volcengine_coding_plan" assert find_by_name("github-copilot") is not None assert find_by_name("github-copilot").name == "github_copilot" + assert find_by_name("longcat") is not None + assert find_by_name("longcat").name == "longcat" + + +def test_config_explicit_longcat_provider_resolves_provider_name(): + config = Config.model_validate( + { + "agents": { + "defaults": { + "provider": "longcat", + "model": "LongCat-Flash-Chat", + } + }, + "providers": { + "longcat": { + "apiKey": "test-key", + } + }, + } + ) + + assert config.get_provider_name() == "longcat" + + +def test_config_auto_detects_longcat_from_model_keyword(): + config = Config.model_validate( + { + "agents": {"defaults": {"provider": "auto", "model": "longcat/LongCat-Flash-Chat"}}, + "providers": {"longcat": {"apiKey": "test-key"}}, + } + ) + + assert config.get_provider_name() == "longcat" def test_config_explicit_xiaomi_mimo_provider_uses_default_api_base(): diff --git a/tests/providers/test_longcat_provider.py b/tests/providers/test_longcat_provider.py new file mode 100644 index 00000000..20190098 --- /dev/null +++ b/tests/providers/test_longcat_provider.py @@ -0,0 +1,29 @@ +"""Tests for the LongCat provider registration.""" + +from nanobot.config.schema import ProvidersConfig +from nanobot.providers.registry import PROVIDERS, find_by_name + + +def test_longcat_config_field_exists(): + """ProvidersConfig should have a longcat field.""" + config = ProvidersConfig() + assert hasattr(config, "longcat") + + +def test_longcat_provider_in_registry(): + """LongCat should be registered in the provider registry.""" + specs = {s.name: s for s in PROVIDERS} + assert "longcat" in specs + + longcat = specs["longcat"] + assert longcat.backend == "openai_compat" + assert longcat.env_key == "LONGCAT_API_KEY" + assert longcat.default_api_base == "https://api.longcat.chat/openai" + + +def test_find_by_name_longcat(): + """find_by_name should resolve the LongCat provider.""" + spec = find_by_name("longcat") + + assert spec is not None + assert spec.name == "longcat"