Files
sub2api/backend/internal/service/thinking_protocol_test.go
李建琦 6d655c9903
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
Sub2API v1.0 - AI API 网关(二开初始版本,基于上游 Wei-Shaw/sub2api)
2026-08-21 18:30:13 +08:00

126 lines
5.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package service
import "testing"
func TestResolveThinkingProtocol(t *testing.T) {
tests := []struct {
name string
modelID string
want ThinkingProtocol
}{
// Anthropic 官方
{"claude-sonnet-4-5", "claude-sonnet-4-5", ThinkingProtocolAnthropicStrict},
{"claude-opus-4-5", "claude-opus-4-5-20251101", ThinkingProtocolAnthropicStrict},
{"claude-haiku full id", "claude-haiku-4-5-20251001", ThinkingProtocolAnthropicStrict},
{"opus short", "opus-4-5", ThinkingProtocolAnthropicStrict},
{"sonnet short", "sonnet-4-5", ThinkingProtocolAnthropicStrict},
{"haiku short", "haiku-4-5", ThinkingProtocolAnthropicStrict},
{"upper case Claude", "Claude-Sonnet-4-5", ThinkingProtocolAnthropicStrict},
// 第三方兼容上游
{"deepseek-v4-pro", "deepseek-v4-pro", ThinkingProtocolPassbackRequired},
{"deepseek-r2-thinking", "deepseek-r2-thinking", ThinkingProtocolPassbackRequired},
{"kimi-coding", "kimi-coding-v2", ThinkingProtocolPassbackRequired},
{"kimi-k2-thinking", "kimi-k2-thinking", ThinkingProtocolPassbackRequired},
{"kimi-k3 platform", "kimi-k3", ThinkingProtocolPassbackRequired},
{"kimi code bare k3", "k3", ThinkingProtocolPassbackRequired},
{"kimi code bare k3-256k", "k3-256k", ThinkingProtocolPassbackRequired},
{"moonshot-v1", "moonshot-v1-32k", ThinkingProtocolPassbackRequired},
{"glm-5.1", "glm-5.1", ThinkingProtocolPassbackRequired},
{"qwen-2 thinking variant", "qwen-2-72b-thinking", ThinkingProtocolPassbackRequired},
{"qwen3 thinking (real Alibaba naming)", "qwen3-235b-a22b-thinking-2507", ThinkingProtocolPassbackRequired},
{"qwen3-next thinking", "qwen3-next-80b-a3b-thinking", ThinkingProtocolPassbackRequired},
{"upper case Deepseek", "DeepSeek-V4-Pro", ThinkingProtocolPassbackRequired},
// MiniMax M 系列(Anthropic 兼容端点要求 thinking round-trip
{"MiniMax-M2 (case-sensitive original)", "MiniMax-M2", ThinkingProtocolPassbackRequired},
{"MiniMax-M2.1", "MiniMax-M2.1", ThinkingProtocolPassbackRequired},
{"MiniMax-M2.5", "MiniMax-M2.5", ThinkingProtocolPassbackRequired},
{"MiniMax-M2.7", "MiniMax-M2.7", ThinkingProtocolPassbackRequired},
{"MiniMax-M2.7-highspeed", "MiniMax-M2.7-highspeed", ThinkingProtocolPassbackRequired},
{"minimax-m2 lowercase", "minimax-m2", ThinkingProtocolPassbackRequired},
// 未知 / 保守
{"empty", "", ThinkingProtocolUnknown},
{"gpt-5", "gpt-5.1", ThinkingProtocolUnknown},
{"gemini", "gemini-3-pro-preview", ThinkingProtocolUnknown},
{"qwen3 non-thinking", "qwen3-32b", ThinkingProtocolUnknown},
{"qwen2 non-thinking", "qwen-2-72b", ThinkingProtocolUnknown},
{"random vendor", "yi-large", ThinkingProtocolUnknown},
// 相似但未知的 k3 型号:不得因含 k3 被宽泛匹配为 passback-required
{"k3-like unknown", "foo-k3-bar", ThinkingProtocolUnknown},
// MiniMax 非 M 系列(如 abab、speech 等其他产品线)—— unknown
{"minimax abab non-M", "abab6.5-chat", ThinkingProtocolUnknown},
// Doubao 走 OpenAI 协议,不属于本网关 Anthropic 路径——归 unknown
{"doubao goes via openai", "doubao-1-5-thinking-vision-pro-250428", ThinkingProtocolUnknown},
// Hunyuan T1 未暴露 Anthropic 端点——归 unknown
{"hunyuan t1 no anthropic endpoint", "hunyuan-t1", ThinkingProtocolUnknown},
{"hy-t1 short alias", "hy-t1", ThinkingProtocolUnknown},
// claude-something 但不是 anthropic 官方命名风格——也归 strict(前缀匹配优先)
{"weird claude prefix", "claude-experimental-fork", ThinkingProtocolAnthropicStrict},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ResolveThinkingProtocol(tt.modelID)
if got != tt.want {
t.Errorf("ResolveThinkingProtocol(%q) = %v, want %v", tt.modelID, got, tt.want)
}
})
}
}
func TestShouldPreFilterThinkingBlocks(t *testing.T) {
tests := []struct {
modelID string
want bool
}{
{"claude-sonnet-4-5", true},
{"deepseek-v4-pro", false},
{"kimi-coding", false},
{"glm-5.1", false},
{"gpt-5.1", false},
{"", false},
}
for _, tt := range tests {
t.Run(tt.modelID, func(t *testing.T) {
if got := ShouldPreFilterThinkingBlocks(tt.modelID); got != tt.want {
t.Errorf("ShouldPreFilterThinkingBlocks(%q) = %v, want %v", tt.modelID, got, tt.want)
}
})
}
}
func TestShouldRectifyThinkingSignatureError(t *testing.T) {
if !ShouldRectifyThinkingSignatureError("claude-sonnet-4-5") {
t.Error("anthropic-strict should rectify signature error")
}
if ShouldRectifyThinkingSignatureError("deepseek-v4-pro") {
t.Error("passback-required must NOT rectify (would break protocol contract)")
}
if ShouldRectifyThinkingSignatureError("gpt-5.1") {
t.Error("unknown should NOT rectify (conservative default)")
}
if ShouldRectifyThinkingSignatureError("") {
t.Error("empty model id should NOT rectify")
}
}
// ShouldApplyRetryFilters 与 ShouldPreFilterThinkingBlocks 必须语义一致:
// 仅 anthropic-strict 走变形,避免预过滤跳过但 retry 路径反而剥离的语义裂缝。
func TestShouldApplyRetryFiltersMirrorsPreFilter(t *testing.T) {
models := []string{
"claude-sonnet-4-5", "claude-opus-4-5-20251101", "haiku-4-5",
"deepseek-v4-pro", "kimi-coding", "glm-5.1",
"qwen3-235b-a22b-thinking-2507", "qwen3-32b",
"gpt-5.1", "gemini-3-pro-preview", "yi-large", "",
}
for _, m := range models {
t.Run(m, func(t *testing.T) {
if got := ShouldApplyRetryFilters(m); got != ShouldPreFilterThinkingBlocks(m) {
t.Errorf("ShouldApplyRetryFilters(%q)=%v but ShouldPreFilterThinkingBlocks=%v — must match",
m, got, ShouldPreFilterThinkingBlocks(m))
}
})
}
}