feat(provider): add LongCat via OpenAI-compatible backend
This commit is contained in:
@@ -76,6 +76,7 @@ IMAP_PASSWORD=your-password-here
|
|||||||
| `moonshot` | LLM (Moonshot/Kimi) | [platform.moonshot.cn](https://platform.moonshot.cn) |
|
| `moonshot` | LLM (Moonshot/Kimi) | [platform.moonshot.cn](https://platform.moonshot.cn) |
|
||||||
| `zhipu` | LLM (Zhipu GLM) | [open.bigmodel.cn](https://open.bigmodel.cn) |
|
| `zhipu` | LLM (Zhipu GLM) | [open.bigmodel.cn](https://open.bigmodel.cn) |
|
||||||
| `mimo` | LLM (MiMo) | [platform.xiaomimimo.com](https://platform.xiaomimimo.com) |
|
| `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) | — |
|
| `ollama` | LLM (local, Ollama) | — |
|
||||||
| `lm_studio` | LLM (local, LM Studio) | — |
|
| `lm_studio` | LLM (local, LM Studio) | — |
|
||||||
| `mistral` | LLM | [docs.mistral.ai](https://docs.mistral.ai/) |
|
| `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 -
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>LongCat (OpenAI-compatible)</b></summary>
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>Custom Provider (Any OpenAI-compatible API)</b></summary>
|
<summary><b>Custom Provider (Any OpenAI-compatible API)</b></summary>
|
||||||
|
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ class ProvidersConfig(Base):
|
|||||||
mistral: ProviderConfig = Field(default_factory=ProviderConfig)
|
mistral: ProviderConfig = Field(default_factory=ProviderConfig)
|
||||||
stepfun: ProviderConfig = Field(default_factory=ProviderConfig) # Step Fun (阶跃星辰)
|
stepfun: ProviderConfig = Field(default_factory=ProviderConfig) # Step Fun (阶跃星辰)
|
||||||
xiaomi_mimo: ProviderConfig = Field(default_factory=ProviderConfig) # Xiaomi MIMO (小米)
|
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
|
aihubmix: ProviderConfig = Field(default_factory=ProviderConfig) # AiHubMix API gateway
|
||||||
siliconflow: ProviderConfig = Field(default_factory=ProviderConfig) # SiliconFlow (硅基流动)
|
siliconflow: ProviderConfig = Field(default_factory=ProviderConfig) # SiliconFlow (硅基流动)
|
||||||
volcengine: ProviderConfig = Field(default_factory=ProviderConfig) # VolcEngine (火山引擎)
|
volcengine: ProviderConfig = Field(default_factory=ProviderConfig) # VolcEngine (火山引擎)
|
||||||
|
|||||||
@@ -376,6 +376,15 @@ PROVIDERS: tuple[ProviderSpec, ...] = (
|
|||||||
backend="openai_compat",
|
backend="openai_compat",
|
||||||
default_api_base="https://api.xiaomimimo.com/v1",
|
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) =========
|
# === Local deployment (matched by config key, NOT by api_base) =========
|
||||||
# vLLM / any OpenAI-compatible local server
|
# vLLM / any OpenAI-compatible local server
|
||||||
ProviderSpec(
|
ProviderSpec(
|
||||||
|
|||||||
@@ -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("volcengineCodingPlan").name == "volcengine_coding_plan"
|
||||||
assert find_by_name("github-copilot") is not None
|
assert find_by_name("github-copilot") is not None
|
||||||
assert find_by_name("github-copilot").name == "github_copilot"
|
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():
|
def test_config_explicit_xiaomi_mimo_provider_uses_default_api_base():
|
||||||
|
|||||||
@@ -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"
|
||||||
Reference in New Issue
Block a user