|
|
package util |
|
|
|
|
|
import ( |
|
|
"strings" |
|
|
|
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry" |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
func ModelSupportsThinking(model string) bool { |
|
|
if model == "" { |
|
|
return false |
|
|
} |
|
|
|
|
|
if info := registry.GetGlobalRegistry().GetModelInfo(model); info != nil { |
|
|
return info.Thinking != nil |
|
|
} |
|
|
|
|
|
if info := registry.LookupStaticModelInfo(model); info != nil { |
|
|
return info.Thinking != nil |
|
|
} |
|
|
|
|
|
if cfg := registry.GetAntigravityModelConfig()[model]; cfg != nil { |
|
|
return cfg.Thinking != nil |
|
|
} |
|
|
return false |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func NormalizeThinkingBudget(model string, budget int) int { |
|
|
if budget == -1 { |
|
|
if found, minBudget, maxBudget, zeroAllowed, dynamicAllowed := thinkingRangeFromRegistry(model); found { |
|
|
if dynamicAllowed { |
|
|
return -1 |
|
|
} |
|
|
mid := (minBudget + maxBudget) / 2 |
|
|
if mid <= 0 && zeroAllowed { |
|
|
return 0 |
|
|
} |
|
|
if mid <= 0 { |
|
|
return minBudget |
|
|
} |
|
|
return mid |
|
|
} |
|
|
return -1 |
|
|
} |
|
|
if found, minBudget, maxBudget, zeroAllowed, _ := thinkingRangeFromRegistry(model); found { |
|
|
if budget == 0 { |
|
|
if zeroAllowed { |
|
|
return 0 |
|
|
} |
|
|
return minBudget |
|
|
} |
|
|
if budget < minBudget { |
|
|
return minBudget |
|
|
} |
|
|
if budget > maxBudget { |
|
|
return maxBudget |
|
|
} |
|
|
return budget |
|
|
} |
|
|
return budget |
|
|
} |
|
|
|
|
|
|
|
|
func thinkingRangeFromRegistry(model string) (found bool, min int, max int, zeroAllowed bool, dynamicAllowed bool) { |
|
|
if model == "" { |
|
|
return false, 0, 0, false, false |
|
|
} |
|
|
|
|
|
if info := registry.GetGlobalRegistry().GetModelInfo(model); info != nil && info.Thinking != nil { |
|
|
return true, info.Thinking.Min, info.Thinking.Max, info.Thinking.ZeroAllowed, info.Thinking.DynamicAllowed |
|
|
} |
|
|
|
|
|
if info := registry.LookupStaticModelInfo(model); info != nil && info.Thinking != nil { |
|
|
return true, info.Thinking.Min, info.Thinking.Max, info.Thinking.ZeroAllowed, info.Thinking.DynamicAllowed |
|
|
} |
|
|
|
|
|
if cfg := registry.GetAntigravityModelConfig()[model]; cfg != nil && cfg.Thinking != nil { |
|
|
return true, cfg.Thinking.Min, cfg.Thinking.Max, cfg.Thinking.ZeroAllowed, cfg.Thinking.DynamicAllowed |
|
|
} |
|
|
return false, 0, 0, false, false |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func GetModelThinkingLevels(model string) []string { |
|
|
if model == "" { |
|
|
return nil |
|
|
} |
|
|
info := registry.GetGlobalRegistry().GetModelInfo(model) |
|
|
if info == nil || info.Thinking == nil { |
|
|
return nil |
|
|
} |
|
|
return info.Thinking.Levels |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func ModelUsesThinkingLevels(model string) bool { |
|
|
levels := GetModelThinkingLevels(model) |
|
|
return len(levels) > 0 |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func NormalizeReasoningEffortLevel(model, effort string) (string, bool) { |
|
|
levels := GetModelThinkingLevels(model) |
|
|
if len(levels) == 0 { |
|
|
return "", false |
|
|
} |
|
|
loweredEffort := strings.ToLower(strings.TrimSpace(effort)) |
|
|
for _, lvl := range levels { |
|
|
if strings.ToLower(lvl) == loweredEffort { |
|
|
return lvl, true |
|
|
} |
|
|
} |
|
|
return "", false |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func IsOpenAICompatibilityModel(model string) bool { |
|
|
if model == "" { |
|
|
return false |
|
|
} |
|
|
info := registry.GetGlobalRegistry().GetModelInfo(model) |
|
|
if info == nil { |
|
|
return false |
|
|
} |
|
|
return strings.EqualFold(strings.TrimSpace(info.Type), "openai-compatibility") |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ThinkingEffortToBudget(model, effort string) (int, bool) { |
|
|
if effort == "" { |
|
|
return 0, false |
|
|
} |
|
|
normalized, ok := NormalizeReasoningEffortLevel(model, effort) |
|
|
if !ok { |
|
|
normalized = strings.ToLower(strings.TrimSpace(effort)) |
|
|
} |
|
|
switch normalized { |
|
|
case "none": |
|
|
return 0, true |
|
|
case "auto": |
|
|
return NormalizeThinkingBudget(model, -1), true |
|
|
case "minimal": |
|
|
return NormalizeThinkingBudget(model, 512), true |
|
|
case "low": |
|
|
return NormalizeThinkingBudget(model, 1024), true |
|
|
case "medium": |
|
|
return NormalizeThinkingBudget(model, 8192), true |
|
|
case "high": |
|
|
return NormalizeThinkingBudget(model, 24576), true |
|
|
case "xhigh": |
|
|
return NormalizeThinkingBudget(model, 32768), true |
|
|
default: |
|
|
return 0, false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ThinkingLevelToBudget(level string) (int, bool) { |
|
|
if level == "" { |
|
|
return 0, false |
|
|
} |
|
|
normalized := strings.ToLower(strings.TrimSpace(level)) |
|
|
switch normalized { |
|
|
case "minimal": |
|
|
return 512, true |
|
|
case "low": |
|
|
return 1024, true |
|
|
case "medium": |
|
|
return 8192, true |
|
|
case "high": |
|
|
return 32768, true |
|
|
default: |
|
|
return 0, false |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func ThinkingBudgetToEffort(model string, budget int) (string, bool) { |
|
|
switch { |
|
|
case budget == -1: |
|
|
return "auto", true |
|
|
case budget < -1: |
|
|
return "", false |
|
|
case budget == 0: |
|
|
if levels := GetModelThinkingLevels(model); len(levels) > 0 { |
|
|
return levels[0], true |
|
|
} |
|
|
return "none", true |
|
|
case budget > 0 && budget <= 1024: |
|
|
return "low", true |
|
|
case budget <= 8192: |
|
|
return "medium", true |
|
|
case budget <= 24576: |
|
|
return "high", true |
|
|
case budget > 24576: |
|
|
if levels := GetModelThinkingLevels(model); len(levels) > 0 { |
|
|
return levels[len(levels)-1], true |
|
|
} |
|
|
return "xhigh", true |
|
|
default: |
|
|
return "", false |
|
|
} |
|
|
} |
|
|
|