feat: comprehensive token usage tracking for V2
Add provider-specific usage details, fix streaming usage, and return usage from all high-level APIs (Chat.Send, Generate[T], Agent.Run). Breaking changes: - Chat.Send/SendMessage/SendWithImages now return (string, *Usage, error) - Generate[T]/GenerateWith[T] now return (T, *Usage, error) - Agent.Run/RunMessages now return (string, *Usage, error) New features: - Usage.Details map for provider-specific token breakdowns (reasoning, cached, audio, thoughts tokens) - OpenAI streaming now captures usage via StreamOptions.IncludeUsage - Google streaming now captures UsageMetadata from final chunk - UsageTracker.Details() for accumulated detail totals - ModelPricing and PricingRegistry for cost computation Closes #2 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
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.
|
||||
@@ -31,4 +33,45 @@ 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
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user