# Anthropic Thinking Support — Implementation Plan ## Overview Add `Thinking`, `Effort`, `thinking_budget_tokens`, and `thinking_interleaved` fields to `AnthropicMessagesConfig` and wire them through to the Anthropic Messages API. OpenAI config is unchanged. ## Design decisions - `Thinking = .enabled` by default (compatible with all current models incl. Haiku 4.5) - `Effort = .medium` by default (only used when `thinking = .adaptive`) - `thinking_budget_tokens: ?u32 = 32_000` (quality/cost shoulder per Anthropic docs; ignored when adaptive) - `thinking_interleaved: bool = false` (requires explicit opt-in; beta header 400s on unsupported models) - Always send `display: "summarized"` to get visible thinking text in all modes - `effort` is only emitted when `thinking == .adaptive` - `budget_tokens` uses `thinking_budget_tokens orelse (max_tokens - 1)` when `.enabled` without interleaving - `interleaved-thinking-2025-05-14` beta header only sent when `thinking == .enabled && thinking_interleaved == true` - `WireIdentity` for Anthropic extended with thinking/effort/budget/interleaved so sessions with different thinking configs are distinct --- ## Phase 1 — Config types and `AnthropicMessagesConfig` fields ✅ COMPLETE **Files:** `libpanto/src/config.zig` - Add `Thinking` enum: `disabled`, `enabled`, `adaptive` - Add `Effort` enum: `low`, `medium`, `high`, `xhigh`, `max` - Add fields to `AnthropicMessagesConfig`: - `thinking: Thinking = .enabled` - `effort: Effort = .medium` - `thinking_budget_tokens: ?u32 = 32_000` - `thinking_interleaved: bool = false` - Update `WireIdentity` to include `thinking`, `effort`, `thinking_budget_tokens`, `thinking_interleaved` for Anthropic; `wireIdentity()` on Anthropic variant fills them in - Update tests in `config.zig` --- ## Phase 2 — Wire serialization (`anthropic_messages_json.zig`) ✅ COMPLETE **Files:** `libpanto/src/anthropic_messages_json.zig` - In `serializeRequest`, after the system prompt block: - `thinking == .disabled` → nothing - `thinking == .enabled` → emit `thinking: { type: "enabled", budget_tokens: B, display: "summarized" }` where `B = thinking_budget_tokens orelse (max_tokens - 1)`, clamped so `B < max_tokens` - `thinking == .adaptive` → emit `thinking: { type: "adaptive", display: "summarized" }` + top-level `effort: ""` - Add unit tests covering all three modes, budget fallback/clamp, effort gating --- ## Phase 3 — Beta header in provider (`provider_anthropic_messages.zig`) ✅ COMPLETE **Files:** `libpanto/src/provider_anthropic_messages.zig` - In `AnthropicMessagesRequest.open`, add `anthropic-beta` header only when `config.thinking == .enabled && config.thinking_interleaved == true` - Value: `"interleaved-thinking-2025-05-14"` - Add unit test (or update existing open/serialize path test) confirming header presence/absence --- ## Phase 4 — Session persistence ✅ COMPLETE **Files:** `libpanto/src/session.zig`, `libpanto/src/session_store.zig`, `libpanto/src/file_system_jsonl_store.zig`, `libpanto/src/null_store.zig` - Extend the stamp serialization / deserialization in `session.zig` to read/write `thinking`, `effort`, `thinking_budget_tokens`, `thinking_interleaved` from/to JSONL - Update `WireIdentity` in `session_store.zig` (distinct from config's) to carry the same fields - Update `null_store.zig` and `file_system_jsonl_store.zig` default stamps - Add round-trip tests --- ## Phase 5 — `models.toml` parsing and `buildProviderConfig` ✅ COMPLETE **Files:** `src/models_toml.zig`, `src/config_file.zig` - Add Anthropic fields to `ModelDef`: `thinking`, `effort`, `thinking_budget_tokens`, `thinking_interleaved` - Hand-parse all four from TOML in `ingestModel` (string→enum for thinking/effort, int for budget, bool for interleaved) - Thread them through `buildProviderConfig` into `AnthropicMessagesConfig` - Update schema doc comment in `models_toml.zig` and sample config in `src/luarocks_runtime.zig` - Add parse and buildProviderConfig tests --- ## Phase 6 — Build verification and test run ✅ COMPLETE - `zig build` clean - `zig build test` passes (all existing + new tests) - Smoke-check that the TOML sample in `luarocks_runtime.zig` is valid ---