feat(provider): add SiliconFlow provider support (#2885)

This commit is contained in:
LC
2026-05-18 10:16:09 +08:00
committed by GitHub
parent 789f907f6d
commit 57876248e2
7 changed files with 147 additions and 17 deletions
+2 -1
View File
@@ -37,6 +37,7 @@ var protocolMetaByName = map[string]protocolMeta{
"ollama": {defaultAPIBase: "http://localhost:11434/v1", emptyAPIKeyAllowed: true},
"moonshot": {defaultAPIBase: "https://api.moonshot.cn/v1"},
"shengsuanyun": {defaultAPIBase: "https://router.shengsuanyun.com/api/v1"},
"siliconflow": {defaultAPIBase: "https://api.siliconflow.cn/v1"},
"deepseek": {defaultAPIBase: "https://api.deepseek.com/v1"},
"cerebras": {defaultAPIBase: "https://api.cerebras.ai/v1"},
"vivgrid": {defaultAPIBase: "https://api.vivgrid.com/v1"},
@@ -239,7 +240,7 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
return finalizeProviderFromConfig(provider, modelID, cfg)
case "litellm", "lmstudio", "openrouter", "groq", "zhipu", "nvidia", "venice",
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
"ollama", "moonshot", "shengsuanyun", "siliconflow", "deepseek", "cerebras",
"vivgrid", "volcengine", "vllm", "qwen", "qwen-portal", "qwen-intl", "qwen-international", "dashscope-intl",
"qwen-us", "dashscope-us", "mistral", "avian", "longcat", "modelscope", "novita",
"coding-plan", "alibaba-coding", "qwen-coding", "zai", "mimo":
+38
View File
@@ -204,6 +204,7 @@ func TestCreateProviderFromConfig_DefaultAPIBase(t *testing.T) {
{"openrouter", "openrouter"},
{"cerebras", "cerebras"},
{"vivgrid", "vivgrid"},
{"siliconflow", "siliconflow"},
{"qwen", "qwen"},
{"vllm", "vllm"},
{"deepseek", "deepseek"},
@@ -253,6 +254,12 @@ func TestGetDefaultAPIBase_Venice(t *testing.T) {
}
}
func TestGetDefaultAPIBase_SiliconFlow(t *testing.T) {
if got := getDefaultAPIBase("siliconflow"); got != "https://api.siliconflow.cn/v1" {
t.Fatalf("getDefaultAPIBase(%q) = %q, want %q", "siliconflow", got, "https://api.siliconflow.cn/v1")
}
}
func TestCreateProviderFromConfig_LiteLLM(t *testing.T) {
cfg := &config.ModelConfig{
ModelName: "test-litellm",
@@ -477,6 +484,28 @@ func TestCreateProviderFromConfig_Venice(t *testing.T) {
}
}
func TestCreateProviderFromConfig_SiliconFlow(t *testing.T) {
cfg := &config.ModelConfig{
ModelName: "test-siliconflow",
Model: "siliconflow/deepseek-ai/DeepSeek-V3",
}
cfg.SetAPIKey("test-key")
provider, modelID, err := CreateProviderFromConfig(cfg)
if err != nil {
t.Fatalf("CreateProviderFromConfig() error = %v", err)
}
if provider == nil {
t.Fatal("CreateProviderFromConfig() returned nil provider")
}
if modelID != "deepseek-ai/DeepSeek-V3" {
t.Errorf("modelID = %q, want %q", modelID, "deepseek-ai/DeepSeek-V3")
}
if _, ok := provider.(*HTTPProvider); !ok {
t.Fatalf("expected *HTTPProvider, got %T", provider)
}
}
func TestGetDefaultAPIBase_Mimo(t *testing.T) {
if got := getDefaultAPIBase("mimo"); got != "https://api.xiaomimimo.com/v1" {
t.Fatalf("getDefaultAPIBase(%q) = %q, want %q", "mimo", got, "https://api.xiaomimimo.com/v1")
@@ -974,6 +1003,15 @@ func TestModelProviderOptions(t *testing.T) {
} else if !option.EmptyAPIKeyAllowed {
t.Fatal("lmstudio should allow empty API keys")
}
if option, ok := seen["siliconflow"]; !ok {
t.Fatal("siliconflow option missing")
} else if option.DefaultAPIBase != "https://api.siliconflow.cn/v1" {
t.Fatalf(
"siliconflow default_api_base = %q, want %q",
option.DefaultAPIBase,
"https://api.siliconflow.cn/v1",
)
}
if option, ok := seen["anthropic"]; !ok {
t.Fatal("anthropic option missing")
} else if option.DefaultAPIBase != "https://api.anthropic.com/v1" {
+16 -15
View File
@@ -48,21 +48,22 @@ type Option func(*Provider)
const defaultRequestTimeout = common.DefaultRequestTimeout
var stripModelPrefixProviders = map[string]struct{}{
"litellm": {},
"venice": {},
"moonshot": {},
"nvidia": {},
"groq": {},
"ollama": {},
"deepseek": {},
"google": {},
"openrouter": {},
"zhipu": {},
"mistral": {},
"vivgrid": {},
"minimax": {},
"novita": {},
"lmstudio": {},
"litellm": {},
"venice": {},
"moonshot": {},
"nvidia": {},
"groq": {},
"ollama": {},
"deepseek": {},
"google": {},
"openrouter": {},
"siliconflow": {},
"zhipu": {},
"mistral": {},
"vivgrid": {},
"minimax": {},
"novita": {},
"lmstudio": {},
}
func WithMaxTokensField(maxTokensField string) Option {
@@ -931,6 +931,11 @@ func TestProviderChat_StripsKnownProviderPrefixes(t *testing.T) {
input: "vivgrid/auto",
wantModel: "auto",
},
{
name: "strips siliconflow prefix and keeps nested model",
input: "siliconflow/deepseek-ai/DeepSeek-V3",
wantModel: "deepseek-ai/DeepSeek-V3",
},
{
name: "strips novita prefix deepseek model",
input: "novita/deepseek/deepseek-v3.2",
@@ -1041,6 +1046,16 @@ func TestNormalizeModel_UsesAPIBase(t *testing.T) {
if got := normalizeModel("vivgrid/auto", "https://api.vivgrid.com/v1"); got != "auto" {
t.Fatalf("normalizeModel(vivgrid auto) = %q, want %q", got, "auto")
}
if got := normalizeModel(
"siliconflow/deepseek-ai/DeepSeek-V3",
"https://api.siliconflow.cn/v1",
); got != "deepseek-ai/DeepSeek-V3" {
t.Fatalf(
"normalizeModel(siliconflow) = %q, want %q",
got,
"deepseek-ai/DeepSeek-V3",
)
}
if got := normalizeModel(
"novita/deepseek/deepseek-v3.2",
"https://api.novita.ai/openai",
@@ -1606,6 +1621,7 @@ func TestProviderChat_PromptCacheKeyOmittedForNonOpenAI(t *testing.T) {
{"gemini", "https://generativelanguage.googleapis.com/v1beta"},
{"deepseek", "https://api.deepseek.com/v1"},
{"groq", "https://api.groq.com/openai/v1"},
{"siliconflow", "https://api.siliconflow.cn/v1"},
{"minimax", "https://api.minimaxi.com/v1"},
{"ollama_local", "http://localhost:11434/v1"},
}