# Anthropic Provider

Anthropic provides the Claude family of models, known for their helpfulness, honesty, and harmlessness. Claude excels at analysis, coding, creative writing, and extended conversations with industry-leading context windows.

## Setup

### Installation

The Anthropic provider is included in the Go-AI SDK:

```go
import (
    "github.com/digitallysavvy/go-ai/pkg/ai"
    "github.com/digitallysavvy/go-ai/pkg/providers/anthropic"
)
```

### Configuration

```go
provider := anthropic.New(anthropic.Config{
    APIKey: os.Getenv("ANTHROPIC_API_KEY"),
})

model, err := provider.LanguageModel("claude-sonnet-4-5")
if err != nil {
    log.Fatal(err)
}
```

### Get API Key

1. Sign up at [console.anthropic.com](https://console.anthropic.com)
2. Navigate to API Keys section
3. Create new API key
4. Set environment variable:

```bash
export ANTHROPIC_API_KEY=sk-ant-...
```

## Claude Platform on AWS

Use `pkg/providers/anthropicaws` when calling Claude Platform through AWS. The provider reuses the Anthropic language model, Files API, Skills API, and Anthropic tool factories, but signs requests for AWS or sends the Anthropic AWS API key headers.

### API Key Authentication

```go
provider, err := anthropicaws.New(anthropicaws.Config{
    Region:      os.Getenv("AWS_REGION"),
    WorkspaceID: os.Getenv("ANTHROPIC_AWS_WORKSPACE_ID"),
    APIKey:      os.Getenv("ANTHROPIC_AWS_API_KEY"),
})
if err != nil {
    log.Fatal(err)
}

model, err := provider.LanguageModel(anthropic.ClaudeOpus4_8)
```

### SigV4 Authentication

When `APIKey` and `ANTHROPIC_AWS_API_KEY` are empty, the provider signs POST requests with SigV4. Credentials may come from `Config`, environment variables, or `CredentialProvider`.

```go
provider, err := anthropicaws.New(anthropicaws.Config{
    Region:          os.Getenv("AWS_REGION"),
    WorkspaceID:     os.Getenv("ANTHROPIC_AWS_WORKSPACE_ID"),
    AccessKeyID:     os.Getenv("AWS_ACCESS_KEY_ID"),
    SecretAccessKey: os.Getenv("AWS_SECRET_ACCESS_KEY"),
    SessionToken:    os.Getenv("AWS_SESSION_TOKEN"),
})
```

The default base URL is `https://aws-external-anthropic.{region}.api.aws/v1`. `WorkspaceID` is required and is sent as `anthropic-workspace-id`.

## Available Models

### Claude 4.7/4.6/4.5 Series (Latest)

| Model ID | Context | Input Price | Output Price | Best For |
|----------|---------|-------------|--------------|----------|
| claude-opus-4-7 | 200K | TBA | TBA | Latest Opus reasoning and coding |
| claude-opus-4-6 | 200K | TBA | TBA | Complex tasks, fast mode |
| claude-sonnet-4-6 | 200K | TBA | TBA | Balanced performance and capability |
| claude-opus-4-5 | 200K | $15.00/1M | $75.00/1M | Most capable, complex tasks |
| claude-sonnet-4-5 | 200K | $3.00/1M | $15.00/1M | Best balance of speed & quality |
| claude-haiku-4-5 | 200K | $0.80/1M | $4.00/1M | Fast, cost-effective |

### Claude 4 Series

| Model ID | Context | Input Price | Output Price | Best For |
|----------|---------|-------------|--------------|----------|
| claude-opus-4 | 200K | $15.00/1M | $75.00/1M | Complex analysis |
| claude-sonnet-4 | 200K | $3.00/1M | $15.00/1M | General purpose |
| claude-haiku-4 | 200K | $0.25/1M | $1.25/1M | High-volume tasks |

### Claude 3.5 Series (Previous Generation)

| Model ID | Context | Input Price | Output Price | Best For |
|----------|---------|-------------|--------------|----------|
| claude-3-5-sonnet-20241022 | 200K | $3.00/1M | $15.00/1M | Latest 3.5 with tool use |
| claude-3-5-haiku-20241022 | 200K | $0.80/1M | $4.00/1M | Fast responses |

### Claude 3 Series (Legacy)

| Model ID | Context | Input Price | Output Price | Best For |
|----------|---------|-------------|--------------|----------|
| claude-3-opus-20240229 | 200K | $15.00/1M | $75.00/1M | Legacy flagship |
| claude-3-sonnet-20240229 | 200K | $3.00/1M | $15.00/1M | Legacy balanced |
| claude-3-haiku-20240307 | 200K | $0.25/1M | $1.25/1M | Legacy fast |

## Provider-Specific Features

### Inference Region

Set `InferenceGeo` on Anthropic model options to forward the TypeScript SDK `inferenceGeo` provider option as `inference_geo`. Supported values are `"us"` and `"global"`.

`anthropic.ClaudeFable5` is available for Claude Fable 5. `ModelOptions.Fallbacks` forwards Anthropic's server-side fallback configuration and enables the `server-side-fallback-2026-06-01` beta header. Fallback response blocks are consumed like the TypeScript provider and are not returned as content; fallback activity is visible through the raw usage `iterations` metadata.

```go
model := anthropic.NewLanguageModel(provider, anthropic.ClaudeOpus4_7, &anthropic.ModelOptions{
    InferenceGeo: "us",
})
```

Anthropic JSON schema serialization sanitizes unsupported validation keywords before sending requests, matching the TypeScript provider behavior. This keeps structured output/tool schemas compatible with Anthropic without requiring callers to manually strip incompatible fields.

### Extended Context (200K tokens)

Claude models support up to 200K token context windows:

```go
// Process entire codebases or documents
largeDocument := readLargeFile("document.txt") // up to 200K tokens

result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  model,
    Prompt: fmt.Sprintf("Analyze this document and provide insights:\n\n%s", largeDocument),
})
```

### Vision Capabilities

Claude 4.5 and 3.5 models support image understanding:

```go
// Read image
imageData, err := os.ReadFile("chart.png")
if err != nil {
    log.Fatal(err)
}

result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model: model,
    Messages: []types.Message{
        {
            Role: types.RoleUser,
            Content: []types.ContentPart{
                types.TextContent{Text: "Analyze this chart"},
                types.FileContent{
                    Data:      imageData,
                    MediaType: "image/png",
                },
            },
        },
    },
})
```

### Tool Use (Function Calling)

Claude excels at tool use with precise parameter extraction:

```go
searchTool := types.Tool{
    Name:        "search_database",
    Description: "Search the customer database",
    Parameters: map[string]interface{}{
        "type": "object",
        "properties": map[string]interface{}{
            "query": map[string]interface{}{
                "type":        "string",
                "description": "Search query",
            },
            "filters": map[string]interface{}{
                "type": "object",
                "properties": map[string]interface{}{
                    "status": map[string]interface{}{
                        "type": "string",
                        "enum": []string{"active", "inactive"},
                    },
                    "created_after": map[string]string{
                        "type": "string",
                    },
                },
            },
        },
        "required": []string{"query"},
    },
}

result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  model,
    Prompt: "Find all active customers who signed up in 2024",
    Tools:  []types.Tool{searchTool},
    StopWhen: []ai.StopCondition{ai.IsStepCount(5)},
})

for _, call := range result.ToolCalls {
    fmt.Printf("Tool: %s\nArgs: %v\n", call.ToolName, call.Arguments)
}
```

### Prompt Caching

Reduce costs by caching frequently used prompt segments:

```go
// Cache system prompt and context
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  model,
    System: "You are an expert Go programmer...",
    Messages: []types.Message{
        {
            Role: types.RoleUser,
            Content: []types.ContentPart{
                types.TextContent{Text: "Review this code: " + largeCodebase},
            },
        },
        {
            Role: types.RoleUser,
            Content: []types.ContentPart{
                types.TextContent{Text: "How can I improve error handling?"},
            },
        },
    },
    ProviderOptions: map[string]interface{}{
        "anthropic": anthropic.ModelOptions{AutomaticCaching: true},
    },
})

// Subsequent requests with same cached content cost significantly less
```

Pricing with caching:
- Cache write: $3.75/1M tokens (1.25x normal)
- Cache read: $0.30/1M tokens (0.1x normal)
- Cached content valid for 5 minutes

### Thinking (Extended Reasoning)

Claude 4.5 supports extended thinking for complex problems:

```go
thinkingModel, err := provider.LanguageModelWithOptions("claude-sonnet-4-5", &anthropic.ModelOptions{
    Thinking: &anthropic.ThinkingConfig{Type: anthropic.ThinkingTypeAdaptive},
})
if err != nil {
    log.Fatal(err)
}

result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  thinkingModel,
    Prompt: "Solve this complex optimization problem...",
})

// Response includes thinking process as ordered reasoning content parts
for _, part := range result.Content {
    if reasoning, ok := part.(types.ReasoningContent); ok {
        fmt.Println("Claude's reasoning:", reasoning.Text)
    }
}
fmt.Println("Final answer:", result.Text)
```

When streaming (`ai.StreamText`) extended thinking together with tool calls across multiple steps, each thinking block's cryptographic signature streams as its own `signature_delta` event, after the block's text deltas. The provider carries it through as the reasoning content part's `Signature`, so a later step's request replays the thinking block with its signature intact (required for Anthropic to accept the replayed block) instead of dropping it.

#### Between-Tools Thinking (Claude Sonnet 5.5)

`anthropic.ClaudeSonnet5_5` rejects `thinking.type` `"disabled"` and budget-based thinking, and adds `anthropic.ThinkingTypeBetweenTools`, its lowest thinking setting: no upfront thinking, but short progress notes between tool calls stream back as summarized thinking blocks. It's only accepted at `"low"`, `"medium"`, and `"high"` effort — `"xhigh"`/`"max"` is lowered to `"high"` with a warning. `Reasoning: types.ReasoningNone` and `ThinkingConfig{Type: anthropic.ThinkingTypeDisabled}` both map to `between_tools` automatically for this model, with a warning in the latter case.

```go
model, err := provider.LanguageModelWithOptions(anthropic.ClaudeSonnet5_5, &anthropic.ModelOptions{
    Thinking: &anthropic.ThinkingConfig{Type: anthropic.ThinkingTypeBetweenTools},
    Effort:   anthropic.EffortMedium,
})
```

### Context Management (Beta)

Automatically manage conversation history to prevent context window overflows. Claude can intelligently clear old tool calls, thinking blocks, or compact entire conversations:

```go
import "github.com/digitallysavvy/go-ai/pkg/providers/anthropic"

// Create model with context management
model, err := provider.LanguageModelWithOptions("claude-sonnet-4-5", &anthropic.ModelOptions{
    ContextManagement: &anthropic.ContextManagement{
        Edits: []anthropic.ContextManagementEdit{
            // Clear old tool calls after 10,000 input tokens
            anthropic.NewClearToolUsesEdit().
                WithInputTokensTrigger(10000).
                WithKeepToolUses(3),

            // Clear thinking blocks, keeping last 2 turns
            anthropic.NewClearThinkingEdit().
                WithKeepRecentTurns(2),

            // Compact conversation at 50,000 tokens
            anthropic.NewCompactEdit().
                WithTrigger(50000),
        },
    },
})

// Use in long conversations - context automatically managed
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:    model,
    Messages: longConversation,
})

// Check if context management was applied
if result.ContextManagement != nil {
    cmr := result.ContextManagement.(*anthropic.ContextManagementResponse)
    for _, edit := range cmr.AppliedEdits {
        switch e := edit.(type) {
        case *anthropic.AppliedClearToolUsesEdit:
            fmt.Printf("Cleared %d tool uses, freed %d tokens\n",
                e.ClearedToolUses, e.ClearedInputTokens)
        case *anthropic.AppliedClearThinkingEdit:
            fmt.Printf("Cleared %d thinking turns, freed %d tokens\n",
                e.ClearedThinkingTurns, e.ClearedInputTokens)
        case *anthropic.AppliedCompactEdit:
            fmt.Println("Conversation compacted")
        }
    }
}
```

**Available Edit Types:**

1. **ClearToolUses** - Removes old tool call details
   - Configure trigger thresholds (token count or tool use count)
   - Keep recent tool uses
   - Exclude specific tools
   - Control minimum tokens to clear

2. **ClearThinking** - Removes extended thinking blocks
   - Keep all thinking or specify number of recent turns
   - Preserves final conclusions

3. **Compact** - Summarizes entire conversation
   - Most aggressive space-saving
   - Optional pause after compaction
   - Custom compaction instructions

**Beta Headers Required:**
- `context-management-2025-06-27` for clear_tool_uses and clear_thinking
- `compact-2026-01-12` for compact edits
(Headers are automatically added by the SDK)

### Streaming

Stream responses for real-time output:

```go
stream, err := ai.StreamText(ctx, ai.StreamTextOptions{Model: model, Prompt: "Write a detailed essay about AI safety"})
if err != nil {
    log.Fatal(err)
}
defer stream.Close()

for chunk := range stream.Chunks() {
    fmt.Print(chunk.Text)
}

if stream.Err() != nil {
    log.Fatal(stream.Err())
}
```

## Examples

### Basic Text Generation

```go
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/digitallysavvy/go-ai/pkg/ai"
    "github.com/digitallysavvy/go-ai/pkg/providers/anthropic"
)

func main() {
    ctx := context.Background()
    provider := anthropic.New(anthropic.Config{
        APIKey: os.Getenv("ANTHROPIC_API_KEY"),
    })

    model, err := provider.LanguageModel("claude-sonnet-4-5")
    if err != nil {
        log.Fatal(err)
    }

    result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
        Model:  model,
        Prompt: "Explain the Go concurrency model with examples",
    })
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(result.Text)
    fmt.Printf("\nTokens - Input: %d, Output: %d\n",
        result.Usage.GetInputTokens(),
        result.Usage.GetOutputTokens())
}
```

### Multi-Turn Conversation

```go
messages := []types.Message{
    {
        Role: types.RoleUser,
        Content: []types.ContentPart{
            types.TextContent{Text: "Review this function:\n\n" + code},
        },
    },
}

result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:    model,
    System:   "You are a helpful code review assistant",
    Messages: messages,
})
if err != nil {
    log.Fatal(err)
}

fmt.Println("Review:", result.Text)

// Continue conversation
messages = append(messages,
    types.Message{
        Role: types.RoleAssistant,
        Content: []types.ContentPart{
            types.TextContent{Text: result.Text},
        },
    },
    types.Message{
        Role: types.RoleUser,
        Content: []types.ContentPart{
            types.TextContent{Text: "Can you suggest specific improvements?"},
        },
    },
)

result, err = ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:    model,
    System:   "You are a helpful code review assistant",
    Messages: messages,
})
```

### Document Analysis with Vision

```go
// Analyze a PDF page rendered as image
imageData, err := os.ReadFile("invoice.png")
if err != nil {
    log.Fatal(err)
}

result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model: model,
    Messages: []types.Message{
        {
            Role: types.RoleUser,
            Content: []types.ContentPart{
                types.TextContent{Text: "Extract all line items from this invoice with prices"},
                types.FileContent{
                    Data:      imageData,
                    MediaType: "image/png",
                },
            },
        },
    },
    ResponseFormat: &provider.ResponseFormat{Type: "json_object"},
})

fmt.Println("Extracted data:", result.Text)
```

### Code Generation with Tool Use

```go
fileReadTool := types.Tool{
    Name:        "read_file",
    Description: "Read contents of a file",
    Parameters: map[string]interface{}{
        "type": "object",
        "properties": map[string]interface{}{
            "path": map[string]string{
                "type":        "string",
                "description": "File path",
            },
        },
        "required": []string{"path"},
    },
}

fileWriteTool := types.Tool{
    Name:        "write_file",
    Description: "Write content to a file",
    Parameters: map[string]interface{}{
        "type": "object",
        "properties": map[string]interface{}{
            "path": map[string]string{"type": "string"},
            "content": map[string]string{"type": "string"},
        },
        "required": []string{"path", "content"},
    },
}

result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  model,
    Prompt: "Read main.go, add error handling, and save the improved version",
    Tools:  []types.Tool{fileReadTool, fileWriteTool},
    StopWhen: []ai.StopCondition{ai.IsStepCount(5)},
})

// Process tool calls
for _, call := range result.ToolCalls {
    switch call.ToolName {
    case "read_file":
        // Execute file read
    case "write_file":
        // Execute file write
    }
}
```

### Large Document Processing with Caching

```go
// Load large codebase
codebase := loadCodebase("./src") // ~100K tokens

messages := []types.Message{
    {
        Role: types.RoleUser,
        Content: []types.ContentPart{
            types.TextContent{Text: "Here is the codebase:\n\n" + codebase}, // Cached
        },
    },
}

// First query - caches codebase
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  model,
    System: "You are an expert code reviewer",
    Messages: append(messages, types.Message{
        Role: types.RoleUser,
        Content: []types.ContentPart{
            types.TextContent{Text: "List all exported functions"},
        },
    }),
    ProviderOptions: map[string]interface{}{
        "anthropic": anthropic.ModelOptions{AutomaticCaching: true},
    },
})

// Second query - uses cached codebase (90% cost reduction)
result, err = ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  model,
    System: "You are an expert code reviewer",
    Messages: append(messages, types.Message{
        Role: types.RoleUser,
        Content: []types.ContentPart{
            types.TextContent{Text: "Find potential security issues"},
        },
    }),
    ProviderOptions: map[string]interface{}{
        "anthropic": anthropic.ModelOptions{AutomaticCaching: true},
    },
})
```

## Advanced Configuration

### Custom HTTP Client

```go
provider := anthropic.New(anthropic.Config{
    APIKey: os.Getenv("ANTHROPIC_API_KEY"),
    HTTPClient: &http.Client{
        Timeout: time.Minute * 5,
        Transport: &http.Transport{
            MaxIdleConns:        100,
            MaxIdleConnsPerHost: 10,
            IdleConnTimeout:     90 * time.Second,
        },
    },
})
```

### Custom Base URL

> **Breaking change:** the default base URL now includes `/v1` (TS parity).
> A bare `https://api.anthropic.com` is normalized automatically, but a
> custom `BaseURL` (proxy, test server) must include `/v1` itself.

```go
provider := anthropic.New(anthropic.Config{
    APIKey:  os.Getenv("ANTHROPIC_API_KEY"),
    BaseURL: "https://your-proxy.com/v1",
})
```

### Beta Features

Most beta headers (prompt caching, extended thinking, context management, and so on) are added automatically based on the `ModelOptions` you set. To send an additional `anthropic-beta` flag that isn't covered by a dedicated option, set `AnthropicBeta` on `ModelOptions`:

```go
provider := anthropic.New(anthropic.Config{
    APIKey: os.Getenv("ANTHROPIC_API_KEY"),
})

model, err := provider.LanguageModelWithOptions("claude-sonnet-4-5", &anthropic.ModelOptions{
    AnthropicBeta: []string{"some-other-beta-flag"},
})
```

## Error Handling

### Common Errors

```go
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  model,
    Prompt: prompt,
})
if err != nil {
    var providerErr *providererrors.ProviderError
    if errors.As(err, &providerErr) {
        switch providerErr.ErrorCode {
        case "invalid_request_error":
            log.Printf("Invalid request: %s", providerErr.Message)
        case "authentication_error":
            log.Fatal("Invalid API key")
        case "permission_error":
            log.Fatal("Permission denied")
        case "not_found_error":
            log.Fatal("Model not found")
        case "rate_limit_error":
            log.Printf("Rate limited: %s", providerErr.Message)
            time.Sleep(time.Second * 5)
        case "overloaded_error":
            log.Println("Service overloaded, retrying...")
            time.Sleep(time.Second * 10)
        default:
            log.Printf("Anthropic error: %s", providerErr.Message)
        }
    }
    return nil, err
}
```

### Retry with Exponential Backoff

```go
func generateWithRetry(ctx context.Context, model provider.LanguageModel, prompt string) (*ai.GenerateTextResult, error) {
    maxRetries := 5
    baseDelay := time.Second

    for attempt := 0; attempt < maxRetries; attempt++ {
        result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
            Model:  model,
            Prompt: prompt,
        })
        if err == nil {
            return result, nil
        }

        var providerErr *providererrors.ProviderError
        if errors.As(err, &providerErr) {
            if providerErr.ErrorCode == "rate_limit_error" || providerErr.ErrorCode == "overloaded_error" {
                delay := baseDelay * time.Duration(math.Pow(2, float64(attempt)))
                log.Printf("Attempt %d failed, waiting %v", attempt+1, delay)
                time.Sleep(delay)
                continue
            }
        }

        return nil, err
    }

    return nil, fmt.Errorf("max retries exceeded")
}
```

## Best Practices

1. **Model Selection**
   - Use `claude-sonnet-4-5` for best balance of quality and speed
   - Use `claude-opus-4-5` for most complex tasks requiring deep analysis
   - Use `claude-haiku-4-5` for high-volume, latency-sensitive applications

2. **Prompt Engineering**
   - Be specific and detailed in instructions
   - Use XML tags for clear structure: `<document>`, `<context>`, `<instruction>`
   - Provide examples when possible
   - Place most important information at the start or end

3. **Cost Optimization**
   - Enable prompt caching for repeated context (large documents, system prompts)
   - Use Haiku for simple tasks
   - Monitor token usage with `result.Usage`
   - Cache responses on your side for identical queries

4. **Context Management**
   - Take advantage of 200K context window
   - Include full context rather than summarizing
   - Use structured formats (JSON, XML) for large data

5. **Tool Use**
   - Provide clear, detailed tool descriptions
   - Use precise parameter schemas
   - Handle tool results in follow-up messages
   - Chain multiple tool calls for complex workflows

## Rate Limits & Pricing

### Rate Limits (Standard Tier)

| Model | RPM | TPM | RPD |
|-------|-----|-----|-----|
| Claude Opus 4.5 | 50 | 40,000 | 1,000 |
| Claude Sonnet 4.5 | 50 | 40,000 | 1,000 |
| Claude Haiku 4.5 | 50 | 50,000 | 1,000 |

RPM = Requests per minute, TPM = Tokens per minute (input), RPD = Requests per day

Higher tiers available with increased limits.

### Token Counting

```go
// Estimate tokens (approximate: 1 token ≈ 4 characters)
func estimateTokens(text string) int {
    return len(text) / 4
}

// Calculate cost
func calculateCost(usage types.Usage, model string) float64 {
    rates := map[string][2]float64{
        "claude-opus-4-5":   {15.00 / 1_000_000, 75.00 / 1_000_000},
        "claude-sonnet-4-5": {3.00 / 1_000_000, 15.00 / 1_000_000},
        "claude-haiku-4-5":  {0.80 / 1_000_000, 4.00 / 1_000_000},
    }

    rate := rates[model]
    inputCost := float64(usage.GetInputTokens()) * rate[0]
    outputCost := float64(usage.GetOutputTokens()) * rate[1]

    return inputCost + outputCost
}
```

### Batch API

Anthropic implements the batch API (Message Batches) used by
`ai.ExperimentalStartBatch` / `ExperimentalGetBatchStatus` /
`ExperimentalGetBatchResults` — process large volumes at a discount with
async turnaround instead of `ai.GenerateText` per request:

```go
started, err := ai.ExperimentalStartBatch(ctx, ai.StartBatchOptions{
    Provider: provider, // an *anthropic.Provider (implements provider.BatchProvider)
    Requests: []ai.BatchRequest{
        {Text: &ai.BatchTextRequest{
            ID:     "req-1",
            Model:  "claude-sonnet-4-6",
            Prompt: "Summarize the Go concurrency model.",
        }},
    },
})
if err != nil {
    log.Fatal(err)
}

status, err := ai.ExperimentalGetBatchStatus(ctx, ai.GetBatchStatusOptions{
    Provider: provider,
    Batch:    started.BatchReference,
})
```

## Forwarding Code-Execution Containers Across Steps

`anthropic.ForwardContainerIDFromLastStep(steps)` builds `ProviderOptions`
that reuse the code-execution container from the most recent step that had
one, so a multi-step agent loop doesn't pay to spin up a fresh sandbox on
every step. Use it from `PrepareStep`:

```go
// tools includes anthropicTools.CodeExecution20260120() — see
// docs/providers/anthropic-advanced-features.md#code-execution-tool-2026-01-20
// for the full code execution tool setup.
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
    Model:  model,
    Prompt: "Run some Python to analyze this data, then summarize.",
    Tools:  tools,
    StopWhen: []ai.StopCondition{ai.IsStepCount(5)},
    PrepareStep: func(ctx context.Context, step ai.PrepareStepOptions) ai.PrepareStepOptions {
        if opts := anthropic.ForwardContainerIDFromLastStep(step.Steps); opts != nil {
            step.ProviderOptions = opts
        }
        return step
    },
})
```

### Pruned Programmatic Tool History

If history is pruned between steps (e.g. to control context size) and that removes a code-execution source call (`code_execution_20250825`/`code_execution_20260120`) while a dependent tool call still references it via `caller`, the provider now omits that dangling `caller` metadata — rather than sending it as-is and having Anthropic reject the request with "source tool ... not found" — and returns one warning per affected tool call. The call, its result, ordering, and cache controls are otherwise unchanged; a caller that still resolves to an emitted `server_tool_use` block, or that is in an active continuation not yet followed by a new user message, is left untouched.

## Finish Reasons

`result.FinishReason` and `result.RawFinishReason` (and their `StreamChunk`
equivalents) report Anthropic's raw `stop_reason` alongside the normalized
value. Since this release, three additional raw reasons map to
non-`"other"` finish reasons, matching TS:

| Anthropic `stop_reason` | `FinishReason` |
|--------------------------|-----------------|
| `end_turn`, `stop_sequence` | `stop` |
| `pause_turn` | `stop` |
| `refusal` | `content-filter` |
| `model_context_window_exceeded` | `length` |
| `max_tokens` | `length` |
| `tool_use` | `tool-calls` |

`RawFinishReason` always carries Anthropic's original string (e.g.
`"pause_turn"`), so you can distinguish these cases from a plain `end_turn`
even though they share the same normalized `FinishReason`.

## Workflow Serialization

Anthropic language models can cross a workflow boundary with
`providerutils.SerializeModel` / `DeserializeModel`. See
[Provider Serialization](https://goaisdk.com/docs/agents/workflow-agent.md#provider-serialization)
for the mechanism. Anthropic has no embedding, image, speech, transcription,
or video models, so there is nothing else to serialize.

## See Also

- [API Reference: GenerateText](https://goaisdk.com/docs/reference/ai/generate-text.md)
- [Core Concepts: Language Models](https://goaisdk.com/docs/foundations/providers-and-models.md)
- [Anthropic API Documentation](https://docs.anthropic.com/claude/reference)
- [Prompt Engineering Guide](https://docs.anthropic.com/claude/docs/prompt-engineering)

## May 2026 parity updates

### Claude Opus 4.7

Use `anthropic.ClaudeOpus4_7` for Claude Opus 4.7. The provider maps top-level `Reasoning` to Anthropic thinking settings and supports x-high effort on the newest Opus models.

```go
p := anthropic.New(anthropic.Config{APIKey: os.Getenv("ANTHROPIC_API_KEY")})
model, err := p.LanguageModel(anthropic.ClaudeOpus4_7)
```

### Inference geography and task budget

`ModelOptions` includes `InferenceGeo` and `TaskBudget`, matching the TypeScript provider options.

```go
remaining := 12000
model, err := p.LanguageModelWithOptions(anthropic.ClaudeOpus4_7, &anthropic.ModelOptions{
    InferenceGeo: "us",
    TaskBudget: &anthropic.TaskBudget{
        Type:      "tokens",
        Total:     32000,
        Remaining: &remaining,
    },
})
```

### Schema and beta header behavior

The provider sanitizes unsupported JSON Schema validation properties before sending tools or structured output requests to Anthropic. The obsolete fine-grained tool streaming beta header is no longer injected.
