mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
110 lines
3.4 KiB
Go
110 lines
3.4 KiB
Go
// Package claude implements thinking configuration scaffolding for Claude models.
|
|
//
|
|
// Claude models use the thinking.budget_tokens format with values in the range
|
|
// 1024-128000. Some Claude models support ZeroAllowed (sonnet-4-5, opus-4-5),
|
|
// while older models do not.
|
|
// See: _bmad-output/planning-artifacts/architecture.md#Epic-6
|
|
package claude
|
|
|
|
import (
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
|
"github.com/tidwall/gjson"
|
|
"github.com/tidwall/sjson"
|
|
)
|
|
|
|
// Applier implements thinking.ProviderApplier for Claude models.
|
|
// This applier is stateless and holds no configuration.
|
|
type Applier struct{}
|
|
|
|
// NewApplier creates a new Claude thinking applier.
|
|
func NewApplier() *Applier {
|
|
return &Applier{}
|
|
}
|
|
|
|
func init() {
|
|
thinking.RegisterProvider("claude", NewApplier())
|
|
}
|
|
|
|
// Apply applies thinking configuration to Claude request body.
|
|
//
|
|
// IMPORTANT: This method expects config to be pre-validated by thinking.ValidateConfig.
|
|
// ValidateConfig handles:
|
|
// - Mode conversion (Level→Budget, Auto→Budget)
|
|
// - Budget clamping to model range
|
|
// - ZeroAllowed constraint enforcement
|
|
//
|
|
// Apply only processes ModeBudget and ModeNone; other modes are passed through unchanged.
|
|
//
|
|
// Expected output format when enabled:
|
|
//
|
|
// {
|
|
// "thinking": {
|
|
// "type": "enabled",
|
|
// "budget_tokens": 16384
|
|
// }
|
|
// }
|
|
//
|
|
// Expected output format when disabled:
|
|
//
|
|
// {
|
|
// "thinking": {
|
|
// "type": "disabled"
|
|
// }
|
|
// }
|
|
func (a *Applier) Apply(body []byte, config thinking.ThinkingConfig, modelInfo *registry.ModelInfo) ([]byte, error) {
|
|
if thinking.IsUserDefinedModel(modelInfo) {
|
|
return applyCompatibleClaude(body, config)
|
|
}
|
|
if modelInfo.Thinking == nil {
|
|
return body, nil
|
|
}
|
|
|
|
// Only process ModeBudget and ModeNone; other modes pass through
|
|
// (caller should use ValidateConfig first to normalize modes)
|
|
if config.Mode != thinking.ModeBudget && config.Mode != thinking.ModeNone {
|
|
return body, nil
|
|
}
|
|
|
|
if len(body) == 0 || !gjson.ValidBytes(body) {
|
|
body = []byte(`{}`)
|
|
}
|
|
|
|
// Budget is expected to be pre-validated by ValidateConfig (clamped, ZeroAllowed enforced)
|
|
// Decide enabled/disabled based on budget value
|
|
if config.Budget == 0 {
|
|
result, _ := sjson.SetBytes(body, "thinking.type", "disabled")
|
|
result, _ = sjson.DeleteBytes(result, "thinking.budget_tokens")
|
|
return result, nil
|
|
}
|
|
|
|
result, _ := sjson.SetBytes(body, "thinking.type", "enabled")
|
|
result, _ = sjson.SetBytes(result, "thinking.budget_tokens", config.Budget)
|
|
return result, nil
|
|
}
|
|
|
|
func applyCompatibleClaude(body []byte, config thinking.ThinkingConfig) ([]byte, error) {
|
|
if config.Mode != thinking.ModeBudget && config.Mode != thinking.ModeNone && config.Mode != thinking.ModeAuto {
|
|
return body, nil
|
|
}
|
|
|
|
if len(body) == 0 || !gjson.ValidBytes(body) {
|
|
body = []byte(`{}`)
|
|
}
|
|
|
|
switch config.Mode {
|
|
case thinking.ModeNone:
|
|
result, _ := sjson.SetBytes(body, "thinking.type", "disabled")
|
|
result, _ = sjson.DeleteBytes(result, "thinking.budget_tokens")
|
|
return result, nil
|
|
case thinking.ModeAuto:
|
|
result, _ := sjson.SetBytes(body, "thinking.type", "enabled")
|
|
result, _ = sjson.DeleteBytes(result, "thinking.budget_tokens")
|
|
return result, nil
|
|
default:
|
|
result, _ := sjson.SetBytes(body, "thinking.type", "enabled")
|
|
result, _ = sjson.SetBytes(result, "thinking.budget_tokens", config.Budget)
|
|
return result, nil
|
|
}
|
|
}
|