Add reasoning effort to usage events
This commit is contained in:
@@ -339,6 +339,56 @@ func hasThinkingConfig(config ThinkingConfig) bool {
|
||||
return config.Mode != ModeBudget || config.Budget != 0 || config.Level != ""
|
||||
}
|
||||
|
||||
// ExtractReasoningEffort returns the request's thinking setting as a canonical
|
||||
// reasoning_effort label for usage logging. Model suffixes have the same
|
||||
// priority as ApplyThinking: a valid suffix overrides body fields.
|
||||
func ExtractReasoningEffort(body []byte, provider, model string) string {
|
||||
if effort := reasoningEffortFromSuffix(ParseSuffix(model)); effort != "" {
|
||||
return effort
|
||||
}
|
||||
|
||||
provider = strings.ToLower(strings.TrimSpace(provider))
|
||||
config := extractThinkingConfig(body, provider)
|
||||
if !hasThinkingConfig(config) {
|
||||
switch provider {
|
||||
case "openai-response":
|
||||
config = extractCodexConfig(body)
|
||||
case "openai":
|
||||
config = extractCodexConfig(body)
|
||||
}
|
||||
}
|
||||
return reasoningEffortFromConfig(config)
|
||||
}
|
||||
|
||||
func reasoningEffortFromSuffix(suffix SuffixResult) string {
|
||||
if !suffix.HasSuffix {
|
||||
return ""
|
||||
}
|
||||
return reasoningEffortFromConfig(parseSuffixToConfig(suffix.RawSuffix, "", suffix.ModelName))
|
||||
}
|
||||
|
||||
func reasoningEffortFromConfig(config ThinkingConfig) string {
|
||||
if !hasThinkingConfig(config) {
|
||||
return ""
|
||||
}
|
||||
switch config.Mode {
|
||||
case ModeNone:
|
||||
return string(LevelNone)
|
||||
case ModeAuto:
|
||||
return string(LevelAuto)
|
||||
case ModeLevel:
|
||||
return strings.ToLower(strings.TrimSpace(string(config.Level)))
|
||||
case ModeBudget:
|
||||
level, ok := ConvertBudgetToLevel(config.Budget)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return level
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// extractClaudeConfig extracts thinking configuration from Claude format request body.
|
||||
//
|
||||
// Claude API format:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package thinking
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestExtractReasoningEffortUsesSuffixOverBody(t *testing.T) {
|
||||
got := ExtractReasoningEffort([]byte(`{"reasoning_effort":"low"}`), "openai", "gpt-5.4(high)")
|
||||
if got != "high" {
|
||||
t.Fatalf("ExtractReasoningEffort() = %q, want %q", got, "high")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractReasoningEffortConvertsBudgetToLevel(t *testing.T) {
|
||||
got := ExtractReasoningEffort([]byte(`{"thinking":{"type":"enabled","budget_tokens":8192}}`), "claude", "claude-sonnet-4-5")
|
||||
if got != "medium" {
|
||||
t.Fatalf("ExtractReasoningEffort() = %q, want %q", got, "medium")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractReasoningEffortSupportsOpenAIResponses(t *testing.T) {
|
||||
got := ExtractReasoningEffort([]byte(`{"reasoning":{"effort":"medium"}}`), "openai-response", "gpt-5.4")
|
||||
if got != "medium" {
|
||||
t.Fatalf("ExtractReasoningEffort() = %q, want %q", got, "medium")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractReasoningEffortMissingConfigIsEmpty(t *testing.T) {
|
||||
got := ExtractReasoningEffort([]byte(`{"messages":[{"role":"user","content":"hi"}]}`), "openai", "gpt-5.4")
|
||||
if got != "" {
|
||||
t.Fatalf("ExtractReasoningEffort() = %q, want empty", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user