| // Package thinking provides unified thinking configuration processing. | |
| // | |
| // This file implements suffix parsing functionality for extracting | |
| // thinking configuration from model names in the format model(value). | |
| package thinking | |
| import ( | |
| "strconv" | |
| "strings" | |
| ) | |
| // ParseSuffix extracts thinking suffix from a model name. | |
| // | |
| // The suffix format is: model-name(value) | |
| // Examples: | |
| // - "claude-sonnet-4-5(16384)" -> ModelName="claude-sonnet-4-5", RawSuffix="16384" | |
| // - "gpt-5.2(high)" -> ModelName="gpt-5.2", RawSuffix="high" | |
| // - "gemini-2.5-pro" -> ModelName="gemini-2.5-pro", HasSuffix=false | |
| // | |
| // This function only extracts the suffix; it does not validate or interpret | |
| // the suffix content. Use ParseNumericSuffix, ParseLevelSuffix, etc. for | |
| // content interpretation. | |
| func ParseSuffix(model string) SuffixResult { | |
| // Find the last opening parenthesis | |
| lastOpen := strings.LastIndex(model, "(") | |
| if lastOpen == -1 { | |
| return SuffixResult{ModelName: model, HasSuffix: false} | |
| } | |
| // Check if the string ends with a closing parenthesis | |
| if !strings.HasSuffix(model, ")") { | |
| return SuffixResult{ModelName: model, HasSuffix: false} | |
| } | |
| // Extract components | |
| modelName := model[:lastOpen] | |
| rawSuffix := model[lastOpen+1 : len(model)-1] | |
| return SuffixResult{ | |
| ModelName: modelName, | |
| HasSuffix: true, | |
| RawSuffix: rawSuffix, | |
| } | |
| } | |
| // ParseNumericSuffix attempts to parse a raw suffix as a numeric budget value. | |
| // | |
| // This function parses the raw suffix content (from ParseSuffix.RawSuffix) as an integer. | |
| // Only non-negative integers are considered valid numeric suffixes. | |
| // | |
| // Platform note: The budget value uses Go's int type, which is 32-bit on 32-bit | |
| // systems and 64-bit on 64-bit systems. Values exceeding the platform's int range | |
| // will return ok=false. | |
| // | |
| // Leading zeros are accepted: "08192" parses as 8192. | |
| // | |
| // Examples: | |
| // - "8192" -> budget=8192, ok=true | |
| // - "0" -> budget=0, ok=true (represents ModeNone) | |
| // - "08192" -> budget=8192, ok=true (leading zeros accepted) | |
| // - "-1" -> budget=0, ok=false (negative numbers are not valid numeric suffixes) | |
| // - "high" -> budget=0, ok=false (not a number) | |
| // - "9223372036854775808" -> budget=0, ok=false (overflow on 64-bit systems) | |
| // | |
| // For special handling of -1 as auto mode, use ParseSpecialSuffix instead. | |
| func ParseNumericSuffix(rawSuffix string) (budget int, ok bool) { | |
| if rawSuffix == "" { | |
| return 0, false | |
| } | |
| value, err := strconv.Atoi(rawSuffix) | |
| if err != nil { | |
| return 0, false | |
| } | |
| // Negative numbers are not valid numeric suffixes | |
| // -1 should be handled by special value parsing as "auto" | |
| if value < 0 { | |
| return 0, false | |
| } | |
| return value, true | |
| } | |
| // ParseSpecialSuffix attempts to parse a raw suffix as a special thinking mode value. | |
| // | |
| // This function handles special strings that represent a change in thinking mode: | |
| // - "none" -> ModeNone (disables thinking) | |
| // - "auto" -> ModeAuto (automatic/dynamic thinking) | |
| // - "-1" -> ModeAuto (numeric representation of auto mode) | |
| // | |
| // String values are case-insensitive. | |
| func ParseSpecialSuffix(rawSuffix string) (mode ThinkingMode, ok bool) { | |
| if rawSuffix == "" { | |
| return ModeBudget, false | |
| } | |
| // Case-insensitive matching | |
| switch strings.ToLower(rawSuffix) { | |
| case "none": | |
| return ModeNone, true | |
| case "auto", "-1": | |
| return ModeAuto, true | |
| default: | |
| return ModeBudget, false | |
| } | |
| } | |
| // ParseLevelSuffix attempts to parse a raw suffix as a discrete thinking level. | |
| // | |
| // This function parses the raw suffix content (from ParseSuffix.RawSuffix) as a level. | |
| // Only discrete effort levels are valid: minimal, low, medium, high, xhigh. | |
| // Level matching is case-insensitive. | |
| // | |
| // Special values (none, auto) are NOT handled by this function; use ParseSpecialSuffix | |
| // instead. This separation allows callers to prioritize special value handling. | |
| // | |
| // Examples: | |
| // - "high" -> level=LevelHigh, ok=true | |
| // - "HIGH" -> level=LevelHigh, ok=true (case insensitive) | |
| // - "medium" -> level=LevelMedium, ok=true | |
| // - "none" -> level="", ok=false (special value, use ParseSpecialSuffix) | |
| // - "auto" -> level="", ok=false (special value, use ParseSpecialSuffix) | |
| // - "8192" -> level="", ok=false (numeric, use ParseNumericSuffix) | |
| // - "ultra" -> level="", ok=false (unknown level) | |
| func ParseLevelSuffix(rawSuffix string) (level ThinkingLevel, ok bool) { | |
| if rawSuffix == "" { | |
| return "", false | |
| } | |
| // Case-insensitive matching | |
| switch strings.ToLower(rawSuffix) { | |
| case "minimal": | |
| return LevelMinimal, true | |
| case "low": | |
| return LevelLow, true | |
| case "medium": | |
| return LevelMedium, true | |
| case "high": | |
| return LevelHigh, true | |
| case "xhigh": | |
| return LevelXHigh, true | |
| default: | |
| return "", false | |
| } | |
| } | |