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:
24
v2/chat.go
24
v2/chat.go
@@ -38,44 +38,50 @@ func (c *Chat) SetTools(tb *ToolBox) {
|
||||
c.tools = tb
|
||||
}
|
||||
|
||||
// Send sends a user message and returns the assistant's text response.
|
||||
// Send sends a user message and returns the assistant's text response along with
|
||||
// accumulated token usage from all iterations of the tool-call loop.
|
||||
// If the model calls tools, they are executed automatically and the loop
|
||||
// continues until the model produces a text response (the "agent loop").
|
||||
func (c *Chat) Send(ctx context.Context, text string) (string, error) {
|
||||
func (c *Chat) Send(ctx context.Context, text string) (string, *Usage, error) {
|
||||
return c.SendMessage(ctx, UserMessage(text))
|
||||
}
|
||||
|
||||
// SendWithImages sends a user message with images attached.
|
||||
func (c *Chat) SendWithImages(ctx context.Context, text string, images ...Image) (string, error) {
|
||||
func (c *Chat) SendWithImages(ctx context.Context, text string, images ...Image) (string, *Usage, error) {
|
||||
return c.SendMessage(ctx, UserMessageWithImages(text, images...))
|
||||
}
|
||||
|
||||
// SendMessage sends an arbitrary message and returns the final text response.
|
||||
// SendMessage sends an arbitrary message and returns the final text response along with
|
||||
// accumulated token usage from all iterations of the tool-call loop.
|
||||
// Handles the full tool-call loop automatically.
|
||||
func (c *Chat) SendMessage(ctx context.Context, msg Message) (string, error) {
|
||||
func (c *Chat) SendMessage(ctx context.Context, msg Message) (string, *Usage, error) {
|
||||
c.messages = append(c.messages, msg)
|
||||
|
||||
opts := c.buildOpts()
|
||||
|
||||
var totalUsage *Usage
|
||||
|
||||
for {
|
||||
resp, err := c.model.Complete(ctx, c.messages, opts...)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("completion failed: %w", err)
|
||||
return "", totalUsage, fmt.Errorf("completion failed: %w", err)
|
||||
}
|
||||
|
||||
totalUsage = addUsage(totalUsage, resp.Usage)
|
||||
|
||||
c.messages = append(c.messages, resp.Message())
|
||||
|
||||
if !resp.HasToolCalls() {
|
||||
return resp.Text, nil
|
||||
return resp.Text, totalUsage, nil
|
||||
}
|
||||
|
||||
if c.tools == nil {
|
||||
return "", ErrNoToolsConfigured
|
||||
return "", totalUsage, ErrNoToolsConfigured
|
||||
}
|
||||
|
||||
toolResults, err := c.tools.ExecuteAll(ctx, resp.ToolCalls)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("tool execution failed: %w", err)
|
||||
return "", totalUsage, fmt.Errorf("tool execution failed: %w", err)
|
||||
}
|
||||
|
||||
c.messages = append(c.messages, toolResults...)
|
||||
|
||||
Reference in New Issue
Block a user