provider-comparison.md

reference

← Back to skill

Content hash: da90e70ea21cb1e88103796cc48a6c7a547079ae01f83b7d0cbf5ff473728b6e
## Provider Cache Pricing/Behavior Comparison

> Verify current numbers against provider docs before shipping - this changes.

| Feature | OpenAI | Anthropic | Google (varies) |
|---------|--------|-----------|-----------------|
| Mechanism | Automatic | Manual (`cache_control`) | Mixed / auto flags |
| Cache hit discount | ~50% off input | ~90% off input | Check docs |
| Guaranteed hits? | Best-effort | Yes (matching prefix) | Check docs |
| Min prefix length | ~1024 tokens | ~2048 tokens (model dep.) | Check docs |
| TTL | ~5-10 min (auto) | 5 min (1h option) | Check docs |
| Write cost | None | Per breakpoint | Check docs |

### Anthropic cache_control placement rules
- Put a breakpoint AFTER each long stable block (system prompt, tool definitions)
- Do NOT put breakpoints on the tiny dynamic tail
- Maximum breakpoints per request (typically 4)

### How to monitor
```python
# Anthropic usage object
usage.cache_creation_input_tokens  # tokens WRITTEN to cache (pay write)
usage.cache_read_input_tokens      # tokens READ from cache (discounted)

# OpenAI usage object
usage.prompt_tokens_details.cached_tokens  # cached input tokens
```

### Hit-rate diagnostics
A hit rate below ~70% on a "static" prefix usually means:
1. Hidden timestamp / request id in the shared block
2. Nondeterministic ordering in a serialized list
3. Per-user metadata injected into the shared system prompt
4. Different apps/layouts fragmenting the cache

### The one rule that matters
```
[STATIC: system + tools + examples]  <- identical across requests
[DYNAMIC: user query]                <- varies, always last
```
A single varying byte in the prefix invalidates every token after it.