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"