# Usage Types

Types for tracking token usage and costs across API calls with detailed breakdowns for multimodal inputs, caching, and reasoning.

## Usage

```go
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

```go
func (u Usage) Add(other Usage) Usage
```

Adds two usage objects together, merging all detailed breakdowns.

```go
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

```go
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

```go
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

```go
type EmbeddingUsage struct {
    InputTokens int
    TotalTokens int
}
```

### ImageUsage

```go
type ImageUsage struct {
    ImageCount int
}
```

### TranscriptionUsage

```go
type TranscriptionUsage struct {
    DurationSeconds float64
}
```

### VideoUsage

```go
type VideoUsage struct {
    VideoCount           int
    TotalDurationSeconds float64
}
```

## Examples

### Basic Usage Tracking

```go
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)

```go
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

```go
// 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

```go
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)

```go
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

```go
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

```go
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

```go
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           |
| Google   | ✅ 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](https://goaisdk.com/docs/reference/ai/generate-text.md) - Text generation with usage tracking
- [Embed](https://goaisdk.com/docs/reference/ai/embed.md) - Embedding generation with usage tracking
- [Error Types](https://goaisdk.com/docs/reference/types/errors.md) - Error handling
- [Migration Guide](https://goaisdk.com/docs/migration-guides/token-usage-differentiation.md) - Migrating to new usage structure
