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
@@ -25,7 +25,8 @@ var fetchableProviders = map[string]bool{
"volcengine": true, "zhipu": true, "groq": true,
"mistral": true, "nvidia": true, "cerebras": true,
"venice": true, "shengsuanyun": true, "vivgrid": true,
"minimax": true, "longcat": true, "modelscope": true,
"siliconflow": true,
"minimax": true, "longcat": true, "modelscope": true,
"mimo": true, "avian": true, "zai": true, "novita": true,
"litellm": true, "vllm": true, "lmstudio": true, "ollama": true,
}
+62
View File
@@ -1819,6 +1819,15 @@ func TestHandleListModels_ReturnsProviderOptionsWithoutPersistingLegacyMigration
} else if !option.EmptyAPIKeyAllowed {
t.Fatal("lmstudio should allow empty api keys")
}
if option, ok := optionsByID["siliconflow"]; !ok {
t.Fatal("siliconflow provider 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 := optionsByID["bedrock"]; !ok {
t.Fatal("bedrock provider option missing")
} else if !option.CreateAllowed {
@@ -2391,3 +2400,56 @@ func TestFetchOpenAICompatibleModels_NoAuthHeaderWhenKeyEmpty(t *testing.T) {
t.Fatalf("Authorization = %q, want empty", gotAuth)
}
}
func TestHandleFetchModels_SiliconFlowUsesOpenAICompatibleEndpoint(t *testing.T) {
configPath, cleanup := setupOAuthTestEnv(t)
defer cleanup()
var gotPath string
var gotAuth string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPath = r.URL.Path
gotAuth = r.Header.Get("Authorization")
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"data":[{"id":"deepseek-ai/DeepSeek-V3","owned_by":"siliconflow"}]}`)
}))
defer srv.Close()
h := NewHandler(configPath)
mux := http.NewServeMux()
h.RegisterRoutes(mux)
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodPost, "/api/models/fetch", bytes.NewBufferString(fmt.Sprintf(`{
"provider":"siliconflow",
"api_key":"sk-siliconflow",
"api_base":"%s"
}`, srv.URL)))
req.Header.Set("Content-Type", "application/json")
mux.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, want %d, body=%s", rec.Code, http.StatusOK, rec.Body.String())
}
if gotPath != "/models" {
t.Fatalf("path = %q, want %q", gotPath, "/models")
}
if gotAuth != "Bearer sk-siliconflow" {
t.Fatalf("Authorization = %q, want %q", gotAuth, "Bearer sk-siliconflow")
}
var resp struct {
Models []upstreamModel `json:"models"`
Total int `json:"total"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
t.Fatalf("Unmarshal() error = %v", err)
}
if resp.Total != 1 || len(resp.Models) != 1 {
t.Fatalf("response = %+v, want one fetched model", resp)
}
if resp.Models[0].ID != "deepseek-ai/DeepSeek-V3" {
t.Fatalf("model id = %q, want %q", resp.Models[0].ID, "deepseek-ai/DeepSeek-V3")
}
}
@@ -289,6 +289,17 @@ export const PROVIDERS: ProviderDefinition[] = [
priority: 44,
supportsFetch: true,
},
{
key: "siliconflow",
label: "SiliconFlow",
labelZh: "硅基流动",
domain: "siliconflow.cn",
defaultApiBase: "https://api.siliconflow.cn/v1",
requiresApiKey: true,
isLocal: false,
priority: 43.5,
supportsFetch: true,
},
{
key: "vivgrid",
label: "Vivgrid",