chore: update project files

This commit is contained in:
CJACK
2026-04-27 02:09:11 +08:00
parent 40d5e3ebb5
commit 90ce595325
27 changed files with 511 additions and 150 deletions

View File

@@ -19,6 +19,16 @@ func TestGetModelConfigDeepSeekChat(t *testing.T) {
}
}
func TestGetModelConfigDeepSeekChatNoThinking(t *testing.T) {
thinking, search, ok := GetModelConfig("deepseek-v4-flash-nothinking")
if !ok {
t.Fatal("expected ok for deepseek-v4-flash-nothinking")
}
if thinking || search {
t.Fatalf("expected thinking=false search=false for deepseek-v4-flash-nothinking, got thinking=%v search=%v", thinking, search)
}
}
func TestGetModelConfigDeepSeekReasoner(t *testing.T) {
thinking, search, ok := GetModelConfig("deepseek-v4-pro")
if !ok {
@@ -84,6 +94,10 @@ func TestGetModelTypeDefaultExpertAndVision(t *testing.T) {
if !ok || defaultType != "default" {
t.Fatalf("expected default model_type, got ok=%v model_type=%q", ok, defaultType)
}
defaultNoThinkingType, ok := GetModelType("deepseek-v4-flash-nothinking")
if !ok || defaultNoThinkingType != "default" {
t.Fatalf("expected default model_type for nothinking, got ok=%v model_type=%q", ok, defaultNoThinkingType)
}
expertType, ok := GetModelType("deepseek-v4-pro")
if !ok || expertType != "expert" {
t.Fatalf("expected expert model_type, got ok=%v model_type=%q", ok, expertType)
@@ -734,12 +748,18 @@ func TestOpenAIModelsResponse(t *testing.T) {
t.Fatal("expected non-empty models list")
}
expected := map[string]bool{
"deepseek-v4-flash": false,
"deepseek-v4-pro": false,
"deepseek-v4-flash-search": false,
"deepseek-v4-pro-search": false,
"deepseek-v4-vision": false,
"deepseek-v4-vision-search": false,
"deepseek-v4-flash": false,
"deepseek-v4-flash-nothinking": false,
"deepseek-v4-pro": false,
"deepseek-v4-pro-nothinking": false,
"deepseek-v4-flash-search": false,
"deepseek-v4-flash-search-nothinking": false,
"deepseek-v4-pro-search": false,
"deepseek-v4-pro-search-nothinking": false,
"deepseek-v4-vision": false,
"deepseek-v4-vision-nothinking": false,
"deepseek-v4-vision-search": false,
"deepseek-v4-vision-search-nothinking": false,
}
for _, model := range data {
if _, ok := expected[model.ID]; ok {

View File

@@ -13,6 +13,13 @@ func TestResolveModelDirectDeepSeek(t *testing.T) {
}
}
func TestResolveModelDirectDeepSeekNoThinking(t *testing.T) {
got, ok := ResolveModel(nil, "deepseek-v4-flash-nothinking")
if !ok || got != "deepseek-v4-flash-nothinking" {
t.Fatalf("expected deepseek-v4-flash-nothinking, got ok=%v model=%q", ok, got)
}
}
func TestResolveModelAlias(t *testing.T) {
got, ok := ResolveModel(nil, "gpt-4.1")
if !ok || got != "deepseek-v4-flash" {
@@ -34,6 +41,13 @@ func TestResolveLatestClaudeAlias(t *testing.T) {
}
}
func TestResolveLatestClaudeAliasNoThinking(t *testing.T) {
got, ok := ResolveModel(nil, "claude-sonnet-4-6-nothinking")
if !ok || got != "deepseek-v4-flash-nothinking" {
t.Fatalf("expected alias claude-sonnet-4-6-nothinking -> deepseek-v4-flash-nothinking, got ok=%v model=%q", ok, got)
}
}
func TestResolveExpandedHistoricalAliases(t *testing.T) {
cases := []struct {
name string
@@ -68,6 +82,13 @@ func TestResolveModelHeuristicReasoner(t *testing.T) {
}
}
func TestResolveModelHeuristicReasonerNoThinking(t *testing.T) {
got, ok := ResolveModel(nil, "o3-super-nothinking")
if !ok || got != "deepseek-v4-pro-nothinking" {
t.Fatalf("expected heuristic reasoner nothinking, got ok=%v model=%q", ok, got)
}
}
func TestResolveModelUnknown(t *testing.T) {
_, ok := ResolveModel(nil, "totally-custom-model")
if ok {

View File

@@ -14,7 +14,9 @@ type ModelAliasReader interface {
ModelAliases() map[string]string
}
var DeepSeekModels = []ModelInfo{
const noThinkingModelSuffix = "-nothinking"
var deepSeekBaseModels = []ModelInfo{
{ID: "deepseek-v4-flash", Object: "model", Created: 1677610602, OwnedBy: "deepseek", Permission: []any{}},
{ID: "deepseek-v4-pro", Object: "model", Created: 1677610602, OwnedBy: "deepseek", Permission: []any{}},
{ID: "deepseek-v4-flash-search", Object: "model", Created: 1677610602, OwnedBy: "deepseek", Permission: []any{}},
@@ -23,7 +25,9 @@ var DeepSeekModels = []ModelInfo{
{ID: "deepseek-v4-vision-search", Object: "model", Created: 1677610602, OwnedBy: "deepseek", Permission: []any{}},
}
var ClaudeModels = []ModelInfo{
var DeepSeekModels = appendNoThinkingVariants(deepSeekBaseModels)
var claudeBaseModels = []ModelInfo{
// Current aliases
{ID: "claude-opus-4-6", Object: "model", Created: 1715635200, OwnedBy: "anthropic"},
{ID: "claude-sonnet-4-6", Object: "model", Created: 1715635200, OwnedBy: "anthropic"},
@@ -53,19 +57,26 @@ var ClaudeModels = []ModelInfo{
{ID: "claude-3-haiku-20240307", Object: "model", Created: 1715635200, OwnedBy: "anthropic"},
}
var ClaudeModels = appendNoThinkingVariants(claudeBaseModels)
func GetModelConfig(model string) (thinking bool, search bool, ok bool) {
switch lower(model) {
baseModel, noThinking := splitNoThinkingModel(model)
if baseModel == "" {
return false, false, false
}
switch baseModel {
case "deepseek-v4-flash", "deepseek-v4-pro", "deepseek-v4-vision":
return true, false, true
return !noThinking, false, true
case "deepseek-v4-flash-search", "deepseek-v4-pro-search", "deepseek-v4-vision-search":
return true, true, true
return !noThinking, true, true
default:
return false, false, false
}
}
func GetModelType(model string) (modelType string, ok bool) {
switch lower(model) {
baseModel, _ := splitNoThinkingModel(model)
switch baseModel {
case "deepseek-v4-flash", "deepseek-v4-flash-search":
return "default", true
case "deepseek-v4-pro", "deepseek-v4-pro-search":
@@ -82,6 +93,11 @@ func IsSupportedDeepSeekModel(model string) bool {
return ok
}
func IsNoThinkingModel(model string) bool {
_, noThinking := splitNoThinkingModel(model)
return noThinking
}
func DefaultModelAliases() map[string]string {
return map[string]string{
// OpenAI GPT / ChatGPT families
@@ -191,62 +207,19 @@ func ResolveModel(store ModelAliasReader, requested string) (string, bool) {
if model == "" {
return "", false
}
if isRetiredHistoricalModel(model) {
return "", false
}
aliases := loadModelAliases(store)
if IsSupportedDeepSeekModel(model) {
return model, true
}
aliases := DefaultModelAliases()
if store != nil {
for k, v := range store.ModelAliases() {
aliases[lower(strings.TrimSpace(k))] = lower(strings.TrimSpace(v))
}
}
if mapped, ok := aliases[model]; ok && IsSupportedDeepSeekModel(mapped) {
return mapped, true
}
if strings.HasPrefix(model, "deepseek-") {
baseModel, noThinking := splitNoThinkingModel(model)
resolvedModel, ok := resolveCanonicalModel(aliases, baseModel)
if !ok {
return "", false
}
knownFamily := false
for _, prefix := range []string{
"gpt-", "o1", "o3", "claude-", "gemini-", "llama-", "qwen-", "mistral-", "command-",
} {
if strings.HasPrefix(model, prefix) {
knownFamily = true
break
}
}
if !knownFamily {
return "", false
}
useVision := strings.Contains(model, "vision")
useReasoner := strings.Contains(model, "reason") ||
strings.Contains(model, "reasoner") ||
strings.HasPrefix(model, "o1") ||
strings.HasPrefix(model, "o3") ||
strings.Contains(model, "opus") ||
strings.Contains(model, "slow") ||
strings.Contains(model, "r1")
useSearch := strings.Contains(model, "search")
switch {
case useVision && useSearch:
return "deepseek-v4-vision-search", true
case useVision:
return "deepseek-v4-vision", true
case useReasoner && useSearch:
return "deepseek-v4-pro-search", true
case useReasoner:
return "deepseek-v4-pro", true
case useSearch:
return "deepseek-v4-flash-search", true
default:
return "deepseek-v4-flash", true
}
return withNoThinkingVariant(resolvedModel, noThinking), true
}
func isRetiredHistoricalModel(model string) bool {
@@ -303,3 +276,100 @@ func ClaudeModelsResponse() map[string]any {
resp["has_more"] = false
return resp
}
func appendNoThinkingVariants(models []ModelInfo) []ModelInfo {
out := make([]ModelInfo, 0, len(models)*2)
for _, model := range models {
out = append(out, model)
variant := model
variant.ID = withNoThinkingVariant(model.ID, true)
out = append(out, variant)
}
return out
}
func splitNoThinkingModel(model string) (string, bool) {
model = lower(strings.TrimSpace(model))
if strings.HasSuffix(model, noThinkingModelSuffix) {
return strings.TrimSuffix(model, noThinkingModelSuffix), true
}
return model, false
}
func withNoThinkingVariant(model string, enabled bool) string {
baseModel, _ := splitNoThinkingModel(model)
if !enabled {
return baseModel
}
if baseModel == "" {
return ""
}
return baseModel + noThinkingModelSuffix
}
func loadModelAliases(store ModelAliasReader) map[string]string {
aliases := DefaultModelAliases()
if store != nil {
for k, v := range store.ModelAliases() {
aliases[lower(strings.TrimSpace(k))] = lower(strings.TrimSpace(v))
}
}
return aliases
}
func resolveCanonicalModel(aliases map[string]string, model string) (string, bool) {
model = lower(strings.TrimSpace(model))
if model == "" {
return "", false
}
if isRetiredHistoricalModel(model) {
return "", false
}
if IsSupportedDeepSeekModel(model) {
return model, true
}
if mapped, ok := aliases[model]; ok && IsSupportedDeepSeekModel(mapped) {
return mapped, true
}
if strings.HasPrefix(model, "deepseek-") {
return "", false
}
knownFamily := false
for _, prefix := range []string{
"gpt-", "o1", "o3", "claude-", "gemini-", "llama-", "qwen-", "mistral-", "command-",
} {
if strings.HasPrefix(model, prefix) {
knownFamily = true
break
}
}
if !knownFamily {
return "", false
}
useVision := strings.Contains(model, "vision")
useReasoner := strings.Contains(model, "reason") ||
strings.Contains(model, "reasoner") ||
strings.HasPrefix(model, "o1") ||
strings.HasPrefix(model, "o3") ||
strings.Contains(model, "opus") ||
strings.Contains(model, "slow") ||
strings.Contains(model, "r1")
useSearch := strings.Contains(model, "search")
switch {
case useVision && useSearch:
return "deepseek-v4-vision-search", true
case useVision:
return "deepseek-v4-vision", true
case useReasoner && useSearch:
return "deepseek-v4-pro-search", true
case useReasoner:
return "deepseek-v4-pro", true
case useSearch:
return "deepseek-v4-flash-search", true
default:
return "deepseek-v4-flash", true
}
}