feat: 更新主流大模型供应商目录

- 按云服务、Coding Plan、API 网关和本地模型分组

- 新增兼容模型发现与供应商协议校验
This commit is contained in:
Logan.Li committed 2026-07-30 21:34:33 +08:00
1 parent 01e4435f29
commit d8b124fc7c
15 files changed
+838 -237

No files matched your search

+8 -4
View File
@@ -1,7 +1,7 @@
// anthropic.go - Anthropic Messages 协议 LLM 客户端实现 // Logan.Li
// 支持官方 Claude(api.anthropic.com)及智谱 GLM 编码套餐(open.bigmodel.cn/api/anthropic)等
// 所有 Anthropic /v1/messages 兼容端点。与 openai.go 实现同一 Client 接口,由 client.go 按 Protocol 路由。
// 鉴权统一用 Authorization: Bearer(智谱编码套餐实测通过;官方 Claude 较新版本亦支持)+ anthropic-version。
// 同时发送 x-api-key 与 Bearer,兼容 Anthropic 官方和国内编码套餐网关。 // Logan.Li
package llm
import (
@@ -167,7 +167,10 @@ func buildTools(tools []Tool) []anthropicTool {
// isAnthropicThinkingModel 判断是否为支持 thinking 的 Claude 模型 // Logan.Li
func isAnthropicThinkingModel(model string) bool {
m := strings.ToLower(model)
return strings.Contains(m, "claude-3-7") || strings.Contains(m, "claude-3.7")
return strings.Contains(m, "claude-3-7") || strings.Contains(m, "claude-3.7") ||
strings.Contains(m, "claude-sonnet-4") || strings.Contains(m, "claude-opus-4") ||
strings.Contains(m, "claude-sonnet-5") || strings.Contains(m, "claude-opus-5") ||
strings.Contains(m, "claude-fable-5")
}
// buildRequest 构造 Anthropic 请求体(Complete 与 Stream 共用) // Logan.Li
@@ -209,6 +212,7 @@ func (c *AnthropicClient) newHTTPRequest(ctx context.Context, body []byte) (*htt
}
httpReq.Header.Set("Content-Type", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+c.cfg.APIKey)
httpReq.Header.Set("x-api-key", c.cfg.APIKey)
httpReq.Header.Set("anthropic-version", "2023-06-01")
return httpReq, nil
}
@@ -356,8 +360,8 @@ func (c *AnthropicClient) Stream(ctx context.Context, req *CompletionRequest, on
}
}
return &CompletionResponse{
Model: c.cfg.Model,
Model: c.cfg.Model,
Message: Message{Role: "assistant", Content: contentBuilder.String()},
Usage: usage,
Usage: usage,
}, nil
}
+26 -20
View File
@@ -49,10 +49,14 @@ func NewOpenAIClient(cfg Config) Client {
// Name 返回客户端名称 // Logan.Li
func (c *OpenAIClient) Name() string { return "openai-compatible(" + c.cfg.Model + ")" }
// isOpenAIReasoningModel 判断是否为需要 reasoning_effort 的 OpenAI o-series 模型 // Logan.Li
// isOpenAIReasoningModel 判断是否为使用新推理参数的 OpenAI 模型。 // Logan.Li
func isOpenAIReasoningModel(model string) bool {
m := strings.ToLower(model)
return strings.Contains(m, "o1") || strings.Contains(m, "o3")
if index := strings.LastIndex(m, "/"); index >= 0 {
m = m[index+1:]
}
return strings.HasPrefix(m, "o1") || strings.HasPrefix(m, "o3") ||
strings.HasPrefix(m, "o4") || strings.HasPrefix(m, "gpt-5")
}
// isGenericReasoningModel 判断是否为通用推理开关模型(DeepSeek R1 等) // Logan.Li
@@ -61,33 +65,35 @@ func isGenericReasoningModel(model string) bool {
return strings.Contains(m, "reasoning") || strings.Contains(m, "r1") || strings.Contains(m, "deepseek-reasoner")
}
// applyReasoningParams 根据模型类型应用 reasoning 参数 // Logan.Li
// applyReasoningParams 根据模型类型应用 token 与 reasoning 参数。 // Logan.Li
func applyReasoningParams(payload *openaiRequest, enabled bool) {
if !enabled {
return
}
if isOpenAIReasoningModel(payload.Model) {
// OpenAI o-series 使用 reasoning_effort,temperature 必须固定为 1(不传) // Logan.Li
payload.ReasoningEffort = "medium"
// 新版 OpenAI 推理模型使用 max_completion_tokens 且不接收 temperature。 // Logan.Li
payload.MaxCompletionTokens = payload.MaxTokens
payload.MaxTokens = 0
payload.Temperature = 0
if enabled {
payload.ReasoningEffort = "medium"
}
return
}
if isGenericReasoningModel(payload.Model) {
if enabled && isGenericReasoningModel(payload.Model) {
payload.Reasoning = true
}
}
// openaiRequest OpenAI chat/completions 请求体 // Logan.Li
type openaiRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
Tools []Tool `json:"tools,omitempty"`
ToolChoice string `json:"tool_choice,omitempty"` // "auto" 让模型自主决定
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
Stream bool `json:"stream,omitempty"` // 流式补全(Stream 方法置 true)
ReasoningEffort string `json:"reasoning_effort,omitempty"` // OpenAI o-series 推理强度 // Logan.Li
Reasoning bool `json:"reasoning,omitempty"` // DeepSeek 等部分平台推理开关 // Logan.Li
Model string `json:"model"`
Messages []Message `json:"messages"`
Tools []Tool `json:"tools,omitempty"`
ToolChoice string `json:"tool_choice,omitempty"` // "auto" 让模型自主决定
Temperature float64 `json:"temperature,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"` // 新版 OpenAI 推理模型的输出上限 // Logan.Li
Stream bool `json:"stream,omitempty"` // 流式补全(Stream 方法置 true)
ReasoningEffort string `json:"reasoning_effort,omitempty"` // OpenAI o-series 推理强度 // Logan.Li
Reasoning bool `json:"reasoning,omitempty"` // DeepSeek 等部分平台推理开关 // Logan.Li
}
// openaiResponse OpenAI chat/completions 响应体 // Logan.Li
@@ -245,9 +251,9 @@ func (c *OpenAIClient) Stream(ctx context.Context, req *CompletionRequest, onDel
}
}
return &CompletionResponse{
Model: c.cfg.Model,
Model: c.cfg.Model,
Message: Message{Role: "assistant", Content: contentBuilder.String()},
Usage: usage,
Usage: usage,
}, nil
}
@@ -0,0 +1,39 @@
package llm
import "testing"
func TestApplyReasoningParamsForModernOpenAIModels(t *testing.T) {
payload := openaiRequest{
Model: "openai/gpt-5.4",
Temperature: 0.7,
MaxTokens: 8192,
}
applyReasoningParams(&payload, true)
if payload.Temperature != 0 || payload.MaxTokens != 0 {
t.Fatalf("unsupported parameters were not cleared: %+v", payload)
}
if payload.MaxCompletionTokens != 8192 || payload.ReasoningEffort != "medium" {
t.Fatalf("modern OpenAI parameters were not applied: %+v", payload)
}
}
func TestApplyReasoningParamsWithoutReasoningEffort(t *testing.T) {
payload := openaiRequest{Model: "gpt-5.4-mini", Temperature: 0.7, MaxTokens: 4096}
applyReasoningParams(&payload, false)
if payload.MaxCompletionTokens != 4096 || payload.ReasoningEffort != "" {
t.Fatalf("model compatibility parameters are incorrect: %+v", payload)
}
}
func TestAnthropicThinkingModels(t *testing.T) {
for _, model := range []string{"claude-3-7-sonnet", "claude-sonnet-4-6", "claude-opus-4-8", "claude-sonnet-5", "claude-fable-5"} {
if !isAnthropicThinkingModel(model) {
t.Fatalf("%q should support thinking parameters", model)
}
}
if isAnthropicThinkingModel("claude-haiku-4-5") {
t.Fatal("Claude Haiku should not receive thinking parameters")
}
}