Usage Types
Types for tracking token usage and costs across API calls with detailed breakdowns for multimodal inputs, caching, and reasoning.
Usage
type Usage struct {
InputTokens *int64 // Total input tokens (prompt)
OutputTokens *int64 // Total output tokens (completion)
TotalTokens *int64 // Total tokens (input + output)
InputDetails *InputTokenDetails // Detailed input token breakdown
OutputDetails *OutputTokenDetails // Detailed output token breakdown
Raw map[string]interface{} // Provider-specific raw usage data
}
Token usage information for language model calls with v6.0 detailed tracking.
Fields
| Field | Type | Description |
|---|---|---|
| InputTokens | *int64 | Total tokens in the input (prompt) |
| OutputTokens | *int64 | Total tokens in the output (completion) |
| TotalTokens | *int64 | Total tokens (input + output) |
| InputDetails | *InputTokenDetails | Detailed breakdown of input tokens (cache, text/image) |
| OutputDetails | *OutputTokenDetails | Detailed breakdown of output tokens (text/reasoning) |
| Raw | map[string]interface | Provider-specific usage data |
Methods
func (u Usage) Add(other Usage) Usage
Adds two usage objects together, merging all detailed breakdowns.
func (u Usage) GetInputTokens() int64
func (u Usage) GetOutputTokens() int64
func (u Usage) GetTotalTokens() int64
Helper methods that return 0 if the pointer is nil.
InputTokenDetails
type InputTokenDetails struct {
NoCacheTokens *int64 // Tokens not served from cache
CacheReadTokens *int64 // Tokens read from cache
CacheWriteTokens *int64 // Tokens written to cache
TextTokens *int64 // Text input tokens (for multimodal)
ImageTokens *int64 // Image input tokens (for multimodal)
}
Detailed breakdown of input token usage, particularly useful for:
- Tracking prompt caching benefits (cost savings)
- Differentiating text vs image costs in multimodal models
Fields
| Field | Type | Description |
|---|---|---|
| NoCacheTokens | *int64 | Input tokens processed normally (not from cache) |
| CacheReadTokens | *int64 | Input tokens read from cache (cheaper) |
| CacheWriteTokens | *int64 | Input tokens written to cache for future use |
| TextTokens | *int64 | Text input tokens (for multimodal cost tracking) |
| ImageTokens | *int64 | Image input tokens (for multimodal cost tracking) |
OutputTokenDetails
type OutputTokenDetails struct {
TextTokens *int64 // Tokens for regular text generation
ReasoningTokens *int64 // Tokens for internal reasoning (o1/o3)
}
Detailed breakdown of output token usage for models with reasoning capabilities.
Fields
| Field | Type | Description |
|---|---|---|
| TextTokens | *int64 | Tokens used for regular text generation |
| ReasoningTokens | *int64 | Tokens used for internal reasoning (not shown in output) |
Other Usage Types
EmbeddingUsage
type EmbeddingUsage struct {
InputTokens int
TotalTokens int
}
ImageUsage
type ImageUsage struct {
ImageCount int
}
TranscriptionUsage
type TranscriptionUsage struct {
DurationSeconds float64
}
VideoUsage
type VideoUsage struct {
VideoCount int
TotalDurationSeconds float64
}
Examples
Basic Usage Tracking
package main
import (
"context"
"fmt"
"log"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
)
func main() {
provider := openai.New(openai.Config{APIKey: "your-api-key"})
model, err := provider.LanguageModel("gpt-4")
if err != nil {
log.Fatal(err)
}
result, err := ai.GenerateText(context.Background(), ai.GenerateTextOptions{
Model: model,
Prompt: "Hello, world!",
})
if err != nil {
log.Fatal(err)
}
usage := result.Usage
fmt.Printf("Input tokens: %d\n", usage.GetInputTokens())
fmt.Printf("Output tokens: %d\n", usage.GetOutputTokens())
fmt.Printf("Total tokens: %d\n", usage.GetTotalTokens())
}
Track Text vs Image Tokens (Multimodal)
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Messages: []types.Message{
{
Role: types.RoleUser,
Content: []types.ContentPart{
types.TextContent{Text: "Describe this image:"},
types.ImageContent{URL: imageURL},
},
},
},
})
if err != nil {
log.Fatal(err)
}
// Check if provider supports text/image breakdown
if result.Usage.InputDetails != nil {
if result.Usage.InputDetails.TextTokens != nil {
fmt.Printf("Text input tokens: %d\n", *result.Usage.InputDetails.TextTokens)
}
if result.Usage.InputDetails.ImageTokens != nil {
fmt.Printf("Image input tokens: %d\n", *result.Usage.InputDetails.ImageTokens)
}
}
Estimate Cost with Multimodal Pricing
// Example pricing (OpenAI GPT-4o)
const (
textInputCost = 0.0000025 // $2.50 per 1M tokens
imageInputCost = 0.0000075 // $7.50 per 1M tokens (approx)
outputCost = 0.0000100 // $10.00 per 1M tokens
)
usage := result.Usage
var cost float64
// Calculate input cost (text + image)
if usage.InputDetails != nil && usage.InputDetails.TextTokens != nil && usage.InputDetails.ImageTokens != nil {
// Accurate multimodal cost
cost += float64(*usage.InputDetails.TextTokens) * textInputCost
cost += float64(*usage.InputDetails.ImageTokens) * imageInputCost
} else {
// Fallback to average rate if breakdown not available
cost += float64(usage.GetInputTokens()) * textInputCost
}
// Add output cost
cost += float64(usage.GetOutputTokens()) * outputCost
fmt.Printf("Estimated cost: $%.6f\n", cost)
Track Cached Tokens
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: longPrompt,
})
if err != nil {
log.Fatal(err)
}
if result.Usage.InputDetails != nil && result.Usage.InputDetails.CacheReadTokens != nil {
cacheRead := *result.Usage.InputDetails.CacheReadTokens
if cacheRead > 0 {
total := result.Usage.GetInputTokens()
savings := float64(cacheRead) / float64(total) * 100
fmt.Printf("Cache hit! %d tokens from cache (%.1f%% savings)\n", cacheRead, savings)
}
}
Track Reasoning Tokens (o1/o3 Models)
model, _ := openaiProvider.LanguageModel("o3-mini") // OpenAI reasoning model
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: "Solve this complex problem...",
})
if err != nil {
log.Fatal(err)
}
if result.Usage.OutputDetails != nil && result.Usage.OutputDetails.ReasoningTokens != nil {
reasoning := *result.Usage.OutputDetails.ReasoningTokens
text := *result.Usage.OutputDetails.TextTokens
fmt.Printf("Text tokens: %d\n", text)
fmt.Printf("Reasoning tokens: %d (hidden)\n", reasoning)
fmt.Printf("Reasoning overhead: %.1f%%\n", float64(reasoning)/float64(text)*100)
}
Accumulate Usage Across Calls
var totalUsage types.Usage
for i := 0; i < 5; i++ {
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: fmt.Sprintf("Question %d", i),
})
if err != nil {
log.Printf("Error on call %d: %v", i, err)
continue
}
totalUsage = totalUsage.Add(result.Usage)
}
fmt.Printf("Total usage across all calls:\n")
fmt.Printf(" Input tokens: %d\n", totalUsage.GetInputTokens())
fmt.Printf(" Output tokens: %d\n", totalUsage.GetOutputTokens())
fmt.Printf(" Total tokens: %d\n", totalUsage.GetTotalTokens())
Provider-Specific Usage Data
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: "Hello",
})
if err != nil {
log.Fatal(err)
}
// Access raw provider-specific data
if result.Usage.Raw != nil {
if promptDetails, ok := result.Usage.Raw["prompt_tokens_details"]; ok {
fmt.Printf("Provider details: %+v\n", promptDetails)
}
}
Usage Budget Enforcement
const maxTokens = 10000
var usedTokens int64
for _, task := range tasks {
if usedTokens >= maxTokens {
fmt.Println("Token budget exceeded")
break
}
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: task,
})
if err != nil {
log.Printf("Error: %v", err)
continue
}
usedTokens += result.Usage.GetTotalTokens()
fmt.Printf("Used %d/%d tokens\n", usedTokens, maxTokens)
}
Provider Support
Different providers support different levels of usage detail:
| Provider | Text/Image Tokens | Cache Tracking | Reasoning Tokens |
|---|---|---|---|
| OpenAI | ✅ Yes | ✅ Yes | ✅ Yes (o1/o3) |
| XAI | ✅ Yes | ✅ Yes | ✅ Yes |
| ✅ Yes | ✅ Yes | ✅ Yes (thinking) | |
| Anthropic | ❌ No | ✅ Yes | ❌ No |
| Azure | ✅ Yes | ✅ Yes | ✅ Yes |
| Groq | ⚠️ Partial | ⚠️ Partial | ⚠️ Partial |
| DeepSeek | ✅ Yes | ✅ Yes | ✅ Yes |
| Together | ✅ Yes | ✅ Yes | ✅ Yes |
| Fireworks | ✅ Yes | ✅ Yes | ✅ Yes |
| Others | Varies | Varies | Varies |
When detailed fields are not available from a provider, the pointers will be nil.
See Also
- GenerateText - Text generation with usage tracking
- Embed - Embedding generation with usage tracking
- Error Types - Error handling
- Migration Guide - Migrating to new usage structure