| package thinking | |
| import ( | |
| "strings" | |
| "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" | |
| ) | |
| // levelToBudgetMap defines the standard Level β Budget mapping. | |
| // All keys are lowercase; lookups should use strings.ToLower. | |
| var levelToBudgetMap = map[string]int{ | |
| "none": 0, | |
| "auto": -1, | |
| "minimal": 512, | |
| "low": 1024, | |
| "medium": 8192, | |
| "high": 24576, | |
| "xhigh": 32768, | |
| } | |
| // ConvertLevelToBudget converts a thinking level to a budget value. | |
| // | |
| // This is a semantic conversion that maps discrete levels to numeric budgets. | |
| // Level matching is case-insensitive. | |
| // | |
| // Level β Budget mapping: | |
| // - none β 0 | |
| // - auto β -1 | |
| // - minimal β 512 | |
| // - low β 1024 | |
| // - medium β 8192 | |
| // - high β 24576 | |
| // - xhigh β 32768 | |
| // | |
| // Returns: | |
| // - budget: The converted budget value | |
| // - ok: true if level is valid, false otherwise | |
| func ConvertLevelToBudget(level string) (int, bool) { | |
| budget, ok := levelToBudgetMap[strings.ToLower(level)] | |
| return budget, ok | |
| } | |
| // BudgetThreshold constants define the upper bounds for each thinking level. | |
| // These are used by ConvertBudgetToLevel for range-based mapping. | |
| const ( | |
| // ThresholdMinimal is the upper bound for "minimal" level (1-512) | |
| ThresholdMinimal = 512 | |
| // ThresholdLow is the upper bound for "low" level (513-1024) | |
| ThresholdLow = 1024 | |
| // ThresholdMedium is the upper bound for "medium" level (1025-8192) | |
| ThresholdMedium = 8192 | |
| // ThresholdHigh is the upper bound for "high" level (8193-24576) | |
| ThresholdHigh = 24576 | |
| ) | |
| // ConvertBudgetToLevel converts a budget value to the nearest thinking level. | |
| // | |
| // This is a semantic conversion that maps numeric budgets to discrete levels. | |
| // Uses threshold-based mapping for range conversion. | |
| // | |
| // Budget β Level thresholds: | |
| // - -1 β auto | |
| // - 0 β none | |
| // - 1-512 β minimal | |
| // - 513-1024 β low | |
| // - 1025-8192 β medium | |
| // - 8193-24576 β high | |
| // - 24577+ β xhigh | |
| // | |
| // Returns: | |
| // - level: The converted thinking level string | |
| // - ok: true if budget is valid, false for invalid negatives (< -1) | |
| func ConvertBudgetToLevel(budget int) (string, bool) { | |
| switch { | |
| case budget < -1: | |
| // Invalid negative values | |
| return "", false | |
| case budget == -1: | |
| return string(LevelAuto), true | |
| case budget == 0: | |
| return string(LevelNone), true | |
| case budget <= ThresholdMinimal: | |
| return string(LevelMinimal), true | |
| case budget <= ThresholdLow: | |
| return string(LevelLow), true | |
| case budget <= ThresholdMedium: | |
| return string(LevelMedium), true | |
| case budget <= ThresholdHigh: | |
| return string(LevelHigh), true | |
| default: | |
| return string(LevelXHigh), true | |
| } | |
| } | |
| // ModelCapability describes the thinking format support of a model. | |
| type ModelCapability int | |
| const ( | |
| // CapabilityUnknown indicates modelInfo is nil (passthrough behavior, internal use). | |
| CapabilityUnknown ModelCapability = iota - 1 | |
| // CapabilityNone indicates model doesn't support thinking (Thinking is nil). | |
| CapabilityNone | |
| // CapabilityBudgetOnly indicates the model supports numeric budgets only. | |
| CapabilityBudgetOnly | |
| // CapabilityLevelOnly indicates the model supports discrete levels only. | |
| CapabilityLevelOnly | |
| // CapabilityHybrid indicates the model supports both budgets and levels. | |
| CapabilityHybrid | |
| ) | |
| // detectModelCapability determines the thinking format capability of a model. | |
| // | |
| // This is an internal function used by validation and conversion helpers. | |
| // It analyzes the model's ThinkingSupport configuration to classify the model: | |
| // - CapabilityNone: modelInfo.Thinking is nil (model doesn't support thinking) | |
| // - CapabilityBudgetOnly: Has Min/Max but no Levels (Claude, Gemini 2.5) | |
| // - CapabilityLevelOnly: Has Levels but no Min/Max (OpenAI, iFlow) | |
| // - CapabilityHybrid: Has both Min/Max and Levels (Gemini 3) | |
| // | |
| // Note: Returns a special sentinel value when modelInfo itself is nil (unknown model). | |
| func detectModelCapability(modelInfo *registry.ModelInfo) ModelCapability { | |
| if modelInfo == nil { | |
| return CapabilityUnknown // sentinel for "passthrough" behavior | |
| } | |
| if modelInfo.Thinking == nil { | |
| return CapabilityNone | |
| } | |
| support := modelInfo.Thinking | |
| hasBudget := support.Min > 0 || support.Max > 0 | |
| hasLevels := len(support.Levels) > 0 | |
| switch { | |
| case hasBudget && hasLevels: | |
| return CapabilityHybrid | |
| case hasBudget: | |
| return CapabilityBudgetOnly | |
| case hasLevels: | |
| return CapabilityLevelOnly | |
| default: | |
| return CapabilityNone | |
| } | |
| } | |