mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-02 20:40:52 +08:00
refactor(thinking): use bracket tags for thinking meta
Align thinking suffix handling on a single bracket-style marker. NormalizeThinkingModel strips a terminal `[value]` segment from model identifiers and turns it into either a thinking budget (for numeric values) or a reasoning effort hint (for strings). Emission of `ThinkingIncludeThoughtsMetadataKey` is removed. Executor helpers and the example config are updated so their comments reference the new `[value]` suffix format instead of the legacy dash variants. BREAKING CHANGE: dash-based thinking suffixes (`-thinking`, `-thinking-N`, `-reasoning`, `-nothinking`) are no longer parsed for thinking metadata; only `[value]` annotations are recognized.
This commit is contained in:
@@ -100,7 +100,7 @@ ws-auth: false
|
|||||||
# excluded-models:
|
# excluded-models:
|
||||||
# - "claude-opus-4-5-20251101" # exclude specific models (exact match)
|
# - "claude-opus-4-5-20251101" # exclude specific models (exact match)
|
||||||
# - "claude-3-*" # wildcard matching prefix (e.g. claude-3-7-sonnet-20250219)
|
# - "claude-3-*" # wildcard matching prefix (e.g. claude-3-7-sonnet-20250219)
|
||||||
# - "*-think" # wildcard matching suffix (e.g. claude-opus-4-5-thinking)
|
# - "*-thinking" # wildcard matching suffix (e.g. claude-opus-4-5-thinking)
|
||||||
# - "*haiku*" # wildcard matching substring (e.g. claude-3-5-haiku-20241022)
|
# - "*haiku*" # wildcard matching substring (e.g. claude-3-5-haiku-20241022)
|
||||||
|
|
||||||
# OpenAI compatibility providers
|
# OpenAI compatibility providers
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/tidwall/sjson"
|
"github.com/tidwall/sjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
// applyThinkingMetadata applies thinking config from model suffix metadata (e.g., -reasoning, -thinking-N)
|
// applyThinkingMetadata applies thinking config from model suffix metadata (e.g., [high], [8192])
|
||||||
// for standard Gemini format payloads. It normalizes the budget when the model supports thinking.
|
// for standard Gemini format payloads. It normalizes the budget when the model supports thinking.
|
||||||
func applyThinkingMetadata(payload []byte, metadata map[string]any, model string) []byte {
|
func applyThinkingMetadata(payload []byte, metadata map[string]any, model string) []byte {
|
||||||
budgetOverride, includeOverride, ok := util.ResolveThinkingConfigFromMetadata(model, metadata)
|
budgetOverride, includeOverride, ok := util.ResolveThinkingConfigFromMetadata(model, metadata)
|
||||||
@@ -28,7 +28,7 @@ func applyThinkingMetadata(payload []byte, metadata map[string]any, model string
|
|||||||
return util.ApplyGeminiThinkingConfig(payload, budgetOverride, includeOverride)
|
return util.ApplyGeminiThinkingConfig(payload, budgetOverride, includeOverride)
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyThinkingMetadataCLI applies thinking config from model suffix metadata (e.g., -reasoning, -thinking-N)
|
// applyThinkingMetadataCLI applies thinking config from model suffix metadata (e.g., [high], [8192])
|
||||||
// for Gemini CLI format payloads (nested under "request"). It normalizes the budget when the model supports thinking.
|
// for Gemini CLI format payloads (nested under "request"). It normalizes the budget when the model supports thinking.
|
||||||
func applyThinkingMetadataCLI(payload []byte, metadata map[string]any, model string) []byte {
|
func applyThinkingMetadataCLI(payload []byte, metadata map[string]any, model string) []byte {
|
||||||
budgetOverride, includeOverride, ok := util.ResolveThinkingConfigFromMetadata(model, metadata)
|
budgetOverride, includeOverride, ok := util.ResolveThinkingConfigFromMetadata(model, metadata)
|
||||||
|
|||||||
@@ -14,100 +14,57 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NormalizeThinkingModel parses dynamic thinking suffixes on model names and returns
|
// NormalizeThinkingModel parses dynamic thinking suffixes on model names and returns
|
||||||
// the normalized base model with extracted metadata. Supported patterns:
|
// the normalized base model with extracted metadata. Supported pattern:
|
||||||
// - "-thinking-<number>" extracts a numeric budget
|
// - "[<value>]" where value can be:
|
||||||
// - "-thinking-<level>" extracts a reasoning effort level (minimal/low/medium/high/xhigh/auto/none)
|
// - A numeric budget (e.g., "[8192]", "[16384]")
|
||||||
// - "-thinking" maps to a default reasoning effort of "medium"
|
// - A reasoning effort level (e.g., "[high]", "[medium]", "[low]")
|
||||||
// - "-reasoning" maps to dynamic budget (-1) and include_thoughts=true
|
//
|
||||||
// - "-nothinking" maps to budget=0 and include_thoughts=false
|
// Examples:
|
||||||
|
// - "claude-sonnet-4-5-20250929[16384]" → budget=16384
|
||||||
|
// - "gpt-5.1[high]" → reasoning_effort="high"
|
||||||
|
// - "gemini-2.5-pro[32768]" → budget=32768
|
||||||
|
//
|
||||||
|
// Note: Empty brackets "[]" are not supported and will be ignored.
|
||||||
func NormalizeThinkingModel(modelName string) (string, map[string]any) {
|
func NormalizeThinkingModel(modelName string) (string, map[string]any) {
|
||||||
if modelName == "" {
|
if modelName == "" {
|
||||||
return modelName, nil
|
return modelName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
lower := strings.ToLower(modelName)
|
|
||||||
baseModel := modelName
|
baseModel := modelName
|
||||||
|
|
||||||
var (
|
var (
|
||||||
budgetOverride *int
|
budgetOverride *int
|
||||||
includeThoughts *bool
|
|
||||||
reasoningEffort *string
|
reasoningEffort *string
|
||||||
matched bool
|
matched bool
|
||||||
)
|
)
|
||||||
|
|
||||||
switch {
|
// Match "[value]" pattern at the end of the model name
|
||||||
case strings.HasSuffix(lower, "-nothinking"):
|
if idx := strings.LastIndex(modelName, "["); idx != -1 {
|
||||||
baseModel = modelName[:len(modelName)-len("-nothinking")]
|
if !strings.HasSuffix(modelName, "]") {
|
||||||
budget := 0
|
// Incomplete bracket, ignore
|
||||||
include := false
|
return baseModel, nil
|
||||||
budgetOverride = &budget
|
}
|
||||||
includeThoughts = &include
|
|
||||||
matched = true
|
value := modelName[idx+1 : len(modelName)-1] // Extract content between [ and ]
|
||||||
case strings.HasSuffix(lower, "-reasoning"):
|
if value == "" {
|
||||||
baseModel = modelName[:len(modelName)-len("-reasoning")]
|
// Empty brackets not supported
|
||||||
budget := -1
|
return baseModel, nil
|
||||||
include := true
|
}
|
||||||
budgetOverride = &budget
|
|
||||||
includeThoughts = &include
|
candidateBase := modelName[:idx]
|
||||||
matched = true
|
|
||||||
default:
|
// Auto-detect: pure numeric → budget, string → reasoning effort level
|
||||||
if idx := strings.LastIndex(lower, "-thinking-"); idx != -1 {
|
if parsed, ok := parseIntPrefix(value); ok {
|
||||||
// Skip stripping if the original model is a registered thinking model.
|
// Numeric value: treat as thinking budget
|
||||||
// This prevents "-thinking-2507" in "qwen3-235b-a22b-thinking-2507" from being parsed.
|
baseModel = candidateBase
|
||||||
if ModelSupportsThinking(modelName) {
|
budgetOverride = &parsed
|
||||||
break
|
matched = true
|
||||||
}
|
} else {
|
||||||
value := modelName[idx+len("-thinking-"):]
|
// String value: treat as reasoning effort level
|
||||||
if value != "" {
|
baseModel = candidateBase
|
||||||
if parsed, ok := parseIntPrefix(value); ok {
|
raw := strings.ToLower(strings.TrimSpace(value))
|
||||||
candidateBase := modelName[:idx]
|
if raw != "" {
|
||||||
if ModelUsesThinkingLevels(candidateBase) {
|
reasoningEffort = &raw
|
||||||
baseModel = candidateBase
|
|
||||||
// Numeric suffix on level-aware models should still surface as reasoning effort metadata.
|
|
||||||
raw := strings.ToLower(strings.TrimSpace(value))
|
|
||||||
if raw != "" {
|
|
||||||
reasoningEffort = &raw
|
|
||||||
}
|
|
||||||
matched = true
|
|
||||||
} else {
|
|
||||||
baseModel = candidateBase
|
|
||||||
budgetOverride = &parsed
|
|
||||||
matched = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
baseModel = modelName[:idx]
|
|
||||||
if normalized, ok := NormalizeReasoningEffortLevel(baseModel, value); ok {
|
|
||||||
reasoningEffort = &normalized
|
|
||||||
matched = true
|
|
||||||
} else if !ModelUsesThinkingLevels(baseModel) {
|
|
||||||
// Keep unknown effort tokens so callers can honor user intent even without normalization.
|
|
||||||
raw := strings.ToLower(strings.TrimSpace(value))
|
|
||||||
if raw != "" {
|
|
||||||
reasoningEffort = &raw
|
|
||||||
matched = true
|
|
||||||
} else {
|
|
||||||
baseModel = modelName
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
raw := strings.ToLower(strings.TrimSpace(value))
|
|
||||||
if raw != "" {
|
|
||||||
reasoningEffort = &raw
|
|
||||||
matched = true
|
|
||||||
} else {
|
|
||||||
baseModel = modelName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if strings.HasSuffix(lower, "-thinking") {
|
|
||||||
candidateBase := modelName[:len(modelName)-len("-thinking")]
|
|
||||||
// Only strip the suffix if the original model is NOT a registered thinking model.
|
|
||||||
// This prevents stripping "-thinking" from models like "kimi-k2-thinking" where
|
|
||||||
// the suffix is part of the model's actual name.
|
|
||||||
if !ModelSupportsThinking(modelName) {
|
|
||||||
baseModel = candidateBase
|
|
||||||
effort := "medium"
|
|
||||||
reasoningEffort = &effort
|
|
||||||
matched = true
|
matched = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,9 +80,6 @@ func NormalizeThinkingModel(modelName string) (string, map[string]any) {
|
|||||||
if budgetOverride != nil {
|
if budgetOverride != nil {
|
||||||
metadata[ThinkingBudgetMetadataKey] = *budgetOverride
|
metadata[ThinkingBudgetMetadataKey] = *budgetOverride
|
||||||
}
|
}
|
||||||
if includeThoughts != nil {
|
|
||||||
metadata[ThinkingIncludeThoughtsMetadataKey] = *includeThoughts
|
|
||||||
}
|
|
||||||
if reasoningEffort != nil {
|
if reasoningEffort != nil {
|
||||||
metadata[ReasoningEffortMetadataKey] = *reasoningEffort
|
metadata[ReasoningEffortMetadataKey] = *reasoningEffort
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user