cbaf41f50c
Introduces an opt-in level-based reasoning toggle (low/medium/high) that each provider translates to its native parameter: - Anthropic: thinking.budget_tokens (1024/8000/24000), with temperature forced to default and MaxTokens auto-grown above the budget. - OpenAI/xAI/Groq via openaicompat: reasoning_effort string, gated by a new Rules.SupportsReasoning predicate so non-reasoning models don't receive the parameter. xAI uses Rules.MapReasoningEffort to remap "medium" to "high" since its API only accepts low|high. - Google: thinking_config.thinking_budget + include_thoughts:true. - DeepSeek: SupportsReasoning=false (reasoner is always-on; the reasoning_content trace was already extracted via openaicompat). Reasoning content is surfaced as Response.Thinking on Complete and as StreamEventThinking deltas during streaming. Provider-side: extracted from Anthropic thinking content blocks, Google's part.Thought=true parts, and the non-standard reasoning_content field that DeepSeek and Groq emit (parsed out of raw JSON since openai-go doesn't type it). Public API: - llm.ReasoningLevel + ReasoningLow/Medium/High constants - llm.WithReasoning(level) request option - Model.WithReasoning(level) for baked-in defaults - provider.Request.Reasoning, provider.Response.Thinking - provider.StreamEventThinking Tests cover Rules-based gating, MapReasoningEffort, reasoning_content extraction (Complete + Stream), Anthropic budget mapping, and temperature suppression when thinking is enabled. Existing behavior is unchanged when Reasoning is the empty string. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
82 lines
2.4 KiB
Go
82 lines
2.4 KiB
Go
package llm
|
|
|
|
import "gitea.stevedudenhoeffer.com/steve/go-llm/v2/provider"
|
|
|
|
// Response represents the result of a completion request.
|
|
type Response struct {
|
|
// Text is the assistant's text content. Empty if only tool calls.
|
|
Text string
|
|
|
|
// Thinking is the assistant's reasoning/thinking trace, when reasoning
|
|
// was requested and the provider exposed it. Empty otherwise.
|
|
Thinking string
|
|
|
|
// ToolCalls contains any tool invocations the assistant requested.
|
|
ToolCalls []ToolCall
|
|
|
|
// Usage contains token usage information (if available from provider).
|
|
Usage *Usage
|
|
|
|
// message is the full assistant message for this response.
|
|
message Message
|
|
}
|
|
|
|
// Message returns the full assistant Message for this response,
|
|
// suitable for appending to the conversation history.
|
|
func (r Response) Message() Message {
|
|
return r.message
|
|
}
|
|
|
|
// HasToolCalls returns true if the response contains tool call requests.
|
|
func (r Response) HasToolCalls() bool {
|
|
return len(r.ToolCalls) > 0
|
|
}
|
|
|
|
// Usage captures token consumption.
|
|
type Usage struct {
|
|
InputTokens int
|
|
OutputTokens int
|
|
TotalTokens int
|
|
Details map[string]int // provider-specific breakdown (e.g., cached, reasoning tokens)
|
|
}
|
|
|
|
// addUsage merges usage u into the receiver, accumulating token counts and details.
|
|
// If the receiver is nil, it returns a copy of u. If u is nil, it returns the receiver unchanged.
|
|
func addUsage(total *Usage, u *Usage) *Usage {
|
|
if u == nil {
|
|
return total
|
|
}
|
|
if total == nil {
|
|
cp := *u
|
|
if u.Details != nil {
|
|
cp.Details = make(map[string]int, len(u.Details))
|
|
for k, v := range u.Details {
|
|
cp.Details[k] = v
|
|
}
|
|
}
|
|
return &cp
|
|
}
|
|
total.InputTokens += u.InputTokens
|
|
total.OutputTokens += u.OutputTokens
|
|
total.TotalTokens += u.TotalTokens
|
|
if u.Details != nil {
|
|
if total.Details == nil {
|
|
total.Details = make(map[string]int, len(u.Details))
|
|
}
|
|
for k, v := range u.Details {
|
|
total.Details[k] += v
|
|
}
|
|
}
|
|
return total
|
|
}
|
|
|
|
// Re-export detail key constants from provider package for convenience.
|
|
const (
|
|
UsageDetailReasoningTokens = provider.UsageDetailReasoningTokens
|
|
UsageDetailCachedInputTokens = provider.UsageDetailCachedInputTokens
|
|
UsageDetailCacheCreationTokens = provider.UsageDetailCacheCreationTokens
|
|
UsageDetailAudioInputTokens = provider.UsageDetailAudioInputTokens
|
|
UsageDetailAudioOutputTokens = provider.UsageDetailAudioOutputTokens
|
|
UsageDetailThoughtsTokens = provider.UsageDetailThoughtsTokens
|
|
)
|