mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-20 00:22:31 +00:00
feat(providers): add Skywork provider support
This commit is contained in:
parent
4dccee56a7
commit
e00220bdb6
@ -134,6 +134,7 @@ ANTHROPIC_API_KEY="$(bw get password api/anthropic)" nanobot agent
|
|||||||
| `custom` | Any OpenAI-compatible endpoint | — |
|
| `custom` | Any OpenAI-compatible endpoint | — |
|
||||||
| `openrouter` | LLM (recommended, access to all models) | [openrouter.ai](https://openrouter.ai) |
|
| `openrouter` | LLM (recommended, access to all models) | [openrouter.ai](https://openrouter.ai) |
|
||||||
| `huggingface` | LLM (Hugging Face Inference Providers) | [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) |
|
| `huggingface` | LLM (Hugging Face Inference Providers) | [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens) |
|
||||||
|
| `skywork` | LLM (Skywork / APIFree API gateway) | [apifree.ai](https://www.apifree.ai) |
|
||||||
| `volcengine` | LLM (VolcEngine, pay-per-use) | [Coding Plan](https://www.volcengine.com/activity/codingplan?utm_campaign=nanobot&utm_content=nanobot&utm_medium=devrel&utm_source=OWO&utm_term=nanobot) · [volcengine.com](https://www.volcengine.com) |
|
| `volcengine` | LLM (VolcEngine, pay-per-use) | [Coding Plan](https://www.volcengine.com/activity/codingplan?utm_campaign=nanobot&utm_content=nanobot&utm_medium=devrel&utm_source=OWO&utm_term=nanobot) · [volcengine.com](https://www.volcengine.com) |
|
||||||
| `byteplus` | LLM (VolcEngine international, pay-per-use) | [Coding Plan](https://www.byteplus.com/en/activity/codingplan?utm_campaign=nanobot&utm_content=nanobot&utm_medium=devrel&utm_source=OWO&utm_term=nanobot) · [byteplus.com](https://www.byteplus.com) |
|
| `byteplus` | LLM (VolcEngine international, pay-per-use) | [Coding Plan](https://www.byteplus.com/en/activity/codingplan?utm_campaign=nanobot&utm_content=nanobot&utm_medium=devrel&utm_source=OWO&utm_term=nanobot) · [byteplus.com](https://www.byteplus.com) |
|
||||||
| `anthropic` | LLM (Claude direct) | [console.anthropic.com](https://console.anthropic.com) |
|
| `anthropic` | LLM (Claude direct) | [console.anthropic.com](https://console.anthropic.com) |
|
||||||
@ -164,6 +165,36 @@ ANTHROPIC_API_KEY="$(bw get password api/anthropic)" nanobot agent
|
|||||||
| `github_copilot` | LLM (GitHub Copilot, OAuth) | `nanobot provider login github-copilot` |
|
| `github_copilot` | LLM (GitHub Copilot, OAuth) | `nanobot provider login github-copilot` |
|
||||||
| `qianfan` | LLM (Baidu Qianfan) | [cloud.baidu.com](https://cloud.baidu.com/doc/qianfan/s/Hmh4suq26) |
|
| `qianfan` | LLM (Baidu Qianfan) | [cloud.baidu.com](https://cloud.baidu.com/doc/qianfan/s/Hmh4suq26) |
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><b>Skywork / APIFree</b></summary>
|
||||||
|
|
||||||
|
Skywork uses the OpenAI-compatible APIFree API endpoint. Configure the provider
|
||||||
|
once, then use Skywork model IDs such as `skywork-ai/skyclaw-v1`.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"providers": {
|
||||||
|
"skywork": {
|
||||||
|
"apiKey": "${SKYWORK_API_KEY}",
|
||||||
|
"apiBase": "https://api.apifree.ai/v1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"provider": "skywork",
|
||||||
|
"model": "skywork-ai/skyclaw-v1",
|
||||||
|
"maxTokens": 32768,
|
||||||
|
"contextWindowTokens": 131072
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also reference `${APIFREE_API_KEY}` in `apiKey` if that is how your
|
||||||
|
environment names the credential.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>AWS Bedrock (Converse API)</b></summary>
|
<summary><b>AWS Bedrock (Converse API)</b></summary>
|
||||||
|
|
||||||
|
|||||||
@ -190,6 +190,7 @@ class ProvidersConfig(Base):
|
|||||||
openai: ProviderConfig = Field(default_factory=ProviderConfig)
|
openai: ProviderConfig = Field(default_factory=ProviderConfig)
|
||||||
openrouter: ProviderConfig = Field(default_factory=ProviderConfig)
|
openrouter: ProviderConfig = Field(default_factory=ProviderConfig)
|
||||||
huggingface: ProviderConfig = Field(default_factory=ProviderConfig)
|
huggingface: ProviderConfig = Field(default_factory=ProviderConfig)
|
||||||
|
skywork: ProviderConfig = Field(default_factory=ProviderConfig) # Skywork / APIFree API gateway
|
||||||
deepseek: ProviderConfig = Field(default_factory=ProviderConfig)
|
deepseek: ProviderConfig = Field(default_factory=ProviderConfig)
|
||||||
groq: ProviderConfig = Field(default_factory=ProviderConfig)
|
groq: ProviderConfig = Field(default_factory=ProviderConfig)
|
||||||
zhipu: ProviderConfig = Field(default_factory=ProviderConfig)
|
zhipu: ProviderConfig = Field(default_factory=ProviderConfig)
|
||||||
|
|||||||
@ -155,6 +155,18 @@ PROVIDERS: tuple[ProviderSpec, ...] = (
|
|||||||
detect_by_base_keyword="huggingface",
|
detect_by_base_keyword="huggingface",
|
||||||
default_api_base="https://router.huggingface.co/v1",
|
default_api_base="https://router.huggingface.co/v1",
|
||||||
),
|
),
|
||||||
|
# Skywork API platform (APIFree): OpenAI-compatible MaaS gateway.
|
||||||
|
ProviderSpec(
|
||||||
|
name="skywork",
|
||||||
|
keywords=("skywork", "skyclaw", "apifree"),
|
||||||
|
env_key="SKYWORK_API_KEY",
|
||||||
|
display_name="Skywork",
|
||||||
|
backend="openai_compat",
|
||||||
|
env_extras=(("APIFREE_API_KEY", "{api_key}"),),
|
||||||
|
is_gateway=True,
|
||||||
|
detect_by_base_keyword="apifree.ai",
|
||||||
|
default_api_base="https://api.apifree.ai/v1",
|
||||||
|
),
|
||||||
# AiHubMix: global gateway, OpenAI-compatible interface.
|
# AiHubMix: global gateway, OpenAI-compatible interface.
|
||||||
# strip_model_prefix=True: doesn't understand "anthropic/claude-3",
|
# strip_model_prefix=True: doesn't understand "anthropic/claude-3",
|
||||||
# strips to bare "claude-3".
|
# strips to bare "claude-3".
|
||||||
|
|||||||
@ -1030,6 +1030,8 @@ async def test_settings_api_returns_safe_subset_and_updates_whitelist(
|
|||||||
assert providers["azure_openai"]["api_key_required"] is True
|
assert providers["azure_openai"]["api_key_required"] is True
|
||||||
assert providers["openrouter"]["configured"] is False
|
assert providers["openrouter"]["configured"] is False
|
||||||
assert providers["openrouter"]["api_key_required"] is True
|
assert providers["openrouter"]["api_key_required"] is True
|
||||||
|
assert providers["skywork"]["label"] == "Skywork"
|
||||||
|
assert providers["skywork"]["default_api_base"] == "https://api.apifree.ai/v1"
|
||||||
assert providers["ant_ling"]["label"] == "Ant Ling"
|
assert providers["ant_ling"]["label"] == "Ant Ling"
|
||||||
assert providers["ant_ling"]["default_api_base"] == "https://api.ant-ling.com/v1"
|
assert providers["ant_ling"]["default_api_base"] == "https://api.ant-ling.com/v1"
|
||||||
assert providers["atomic_chat"]["configured"] is False
|
assert providers["atomic_chat"]["configured"] is False
|
||||||
|
|||||||
80
tests/providers/test_skywork_provider.py
Normal file
80
tests/providers/test_skywork_provider.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
"""Tests for the Skywork provider registration."""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from nanobot.config.schema import Config, ProvidersConfig
|
||||||
|
from nanobot.providers.openai_compat_provider import OpenAICompatProvider
|
||||||
|
from nanobot.providers.registry import PROVIDERS, find_by_name
|
||||||
|
|
||||||
|
|
||||||
|
def test_skywork_config_field_exists() -> None:
|
||||||
|
config = ProvidersConfig()
|
||||||
|
|
||||||
|
assert hasattr(config, "skywork")
|
||||||
|
|
||||||
|
|
||||||
|
def test_skywork_provider_in_registry() -> None:
|
||||||
|
specs = {spec.name: spec for spec in PROVIDERS}
|
||||||
|
|
||||||
|
assert "skywork" in specs
|
||||||
|
skywork = specs["skywork"]
|
||||||
|
assert skywork.backend == "openai_compat"
|
||||||
|
assert skywork.env_key == "SKYWORK_API_KEY"
|
||||||
|
assert ("APIFREE_API_KEY", "{api_key}") in skywork.env_extras
|
||||||
|
assert skywork.display_name == "Skywork"
|
||||||
|
assert skywork.is_gateway is True
|
||||||
|
assert skywork.detect_by_base_keyword == "apifree.ai"
|
||||||
|
assert skywork.default_api_base == "https://api.apifree.ai/v1"
|
||||||
|
assert skywork.supports_max_completion_tokens is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_find_by_name_skywork() -> None:
|
||||||
|
spec = find_by_name("skywork")
|
||||||
|
|
||||||
|
assert spec is not None
|
||||||
|
assert spec.name == "skywork"
|
||||||
|
|
||||||
|
|
||||||
|
def test_skywork_model_auto_matches_with_default_api_base() -> None:
|
||||||
|
config = Config.model_validate(
|
||||||
|
{
|
||||||
|
"providers": {
|
||||||
|
"skywork": {
|
||||||
|
"apiKey": "sky-key",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"agents": {
|
||||||
|
"defaults": {
|
||||||
|
"model": "skywork-ai/skyclaw-v1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert config.get_provider_name("skywork-ai/skyclaw-v1") == "skywork"
|
||||||
|
assert config.get_api_key("skywork-ai/skyclaw-v1") == "sky-key"
|
||||||
|
assert config.get_api_base("skywork-ai/skyclaw-v1") == "https://api.apifree.ai/v1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_skywork_preserves_model_id_and_uses_chat_completion_max_tokens() -> None:
|
||||||
|
spec = find_by_name("skywork")
|
||||||
|
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI"):
|
||||||
|
provider = OpenAICompatProvider(
|
||||||
|
api_key="sky-key",
|
||||||
|
default_model="skywork-ai/skyclaw-v1",
|
||||||
|
spec=spec,
|
||||||
|
)
|
||||||
|
|
||||||
|
kwargs = provider._build_kwargs(
|
||||||
|
messages=[{"role": "user", "content": "hi"}],
|
||||||
|
tools=None,
|
||||||
|
model="skywork-ai/skyclaw-v1",
|
||||||
|
max_tokens=1024,
|
||||||
|
temperature=0.7,
|
||||||
|
reasoning_effort=None,
|
||||||
|
tool_choice=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert kwargs["model"] == "skywork-ai/skyclaw-v1"
|
||||||
|
assert kwargs["max_tokens"] == 1024
|
||||||
|
assert "max_completion_tokens" not in kwargs
|
||||||
@ -2129,6 +2129,7 @@ function providerLabel(
|
|||||||
const PROVIDER_ICONS: Record<string, LucideIcon> = {
|
const PROVIDER_ICONS: Record<string, LucideIcon> = {
|
||||||
custom: Hexagon,
|
custom: Hexagon,
|
||||||
openrouter: Sparkles,
|
openrouter: Sparkles,
|
||||||
|
skywork: Sparkles,
|
||||||
aihubmix: Triangle,
|
aihubmix: Triangle,
|
||||||
anthropic: Brain,
|
anthropic: Brain,
|
||||||
openai: Bot,
|
openai: Bot,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user