Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions opencane/config/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class ProvidersConfig(BaseModel):
moonshot: ProviderConfig = Field(default_factory=ProviderConfig)
minimax: ProviderConfig = Field(default_factory=ProviderConfig)
aihubmix: ProviderConfig = Field(default_factory=ProviderConfig) # AiHubMix API gateway
siliconflow: ProviderConfig = Field(default_factory=ProviderConfig) # SiliconFlow API gateway


class GatewayConfig(BaseModel):
Expand Down
19 changes: 19 additions & 0 deletions opencane/providers/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,25 @@ def label(self) -> str:
model_overrides=(),
),

# SiliconFlow: OpenAI-compatible gateway hosting multiple models.
# Keep model org prefixes (e.g. "Qwen/Qwen2.5-14B-Instruct").
ProviderSpec(
name="siliconflow",
keywords=("siliconflow",),
env_key="OPENAI_API_KEY", # OpenAI-compatible
display_name="SiliconFlow",
litellm_prefix="openai", # → openai/{model}
skip_prefixes=(),
env_extras=(),
is_gateway=True,
is_local=False,
detect_by_key_prefix="",
detect_by_base_keyword="siliconflow",
default_api_base="https://api.siliconflow.cn/v1",
strip_model_prefix=False,
model_overrides=(),
),

# === Standard providers (matched by model-name keywords) ===============

# Anthropic: LiteLLM recognizes "claude-*" natively, no prefix needed.
Expand Down
16 changes: 15 additions & 1 deletion tests/test_provider_routing.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from opencane.config.schema import Config
from opencane.providers.registry import find_by_model
from opencane.providers.registry import find_by_model, find_gateway


def test_find_by_model_prefers_explicit_prefix_over_keyword_match() -> None:
Expand All @@ -16,3 +16,17 @@ def test_config_match_prefers_explicit_prefix_when_multiple_provider_keys_presen

assert cfg.get_provider_name() == "deepseek"


def test_find_gateway_detects_siliconflow_by_api_base_keyword() -> None:
spec = find_gateway(api_base="https://api.siliconflow.cn/v1")
assert spec is not None
assert spec.name == "siliconflow"


def test_config_uses_siliconflow_gateway_defaults() -> None:
cfg = Config()
cfg.agents.defaults.model = "siliconflow/Qwen/Qwen2.5-14B-Instruct"
cfg.providers.siliconflow.api_key = "sf-key"

assert cfg.get_provider_name() == "siliconflow"
assert cfg.get_api_base() == "https://api.siliconflow.cn/v1"
Loading