@@ -8,3 +8,170 @@ Verification:
88- uv run ruff check src/rotator_library/providers/anthropic_provider.py --select F401 — passed
99
1010Notes: Removed unused imports (asyncio, re, Path, UsageManager and TYPE_CHECKING).
11+
12+ ## 2026-07-01 — Add newer Claude models to OAuth whitelist and max output tokens
13+
14+ Target: ` feat(anthropic): add OAuth support and handle streaming nulls `
15+ Files:
16+ - ` src/rotator_library/providers/anthropic_provider.py `
17+
18+ Changes:
19+ - Added ` claude-fable-5 ` , ` claude-opus-4-8 ` , ` claude-opus-4-7 ` , ` claude-sonnet-4-6 `
20+ to ` OAUTH_MODELS ` — these are current active Anthropic models available via
21+ Claude Pro/Max OAuth subscription.
22+ - Added corresponding entries to ` _MODEL_MAX_OUTPUT_TOKENS ` :
23+ - ` claude-fable-5 ` : 128,000
24+ - ` claude-opus-4-8 ` : 128,000
25+ - ` claude-opus-4-7 ` : 128,000
26+ - ` claude-sonnet-4-6 ` : 64,000
27+ - ` claude-mythos-5 ` intentionally excluded (restricted to Project Glasswing participants).
28+
29+ Model IDs sourced from Anthropic's official skills catalog
30+ (anthropics/skills/skills/claude-api/shared/models.md).
31+
32+ Verification:
33+ - ` uv run python3 -m py_compile src/rotator_library/providers/anthropic_provider.py ` — passed
34+ - ` uv run ruff check src/rotator_library/providers/anthropic_provider.py --select F401,F811,F821,E9 ` — passed
35+
36+ Notes:
37+ - Existing models (opus-4-6, opus-4-5, sonnet-4-5, haiku-4-5) remain in the list.
38+ - The ` model_quota_groups ` (5h-limit, weekly-limit, anthropic-global) automatically
39+ include the new models since they use ` list(OAUTH_MODELS) ` .
40+ - The max output token prefix-matching loop uses ` startswith() ` with ` break ` on
41+ first match. No prefix collisions exist between the new entries and existing ones.
42+ - Ref: b3nw/LLM-API-Key-Proxy #97
43+
44+ ## 2026-07-01 — Dynamic model discovery via models.dev
45+
46+ Target: ` feat(anthropic): add OAuth support and handle streaming nulls `
47+ Files:
48+ - ` src/rotator_library/providers/anthropic_provider.py `
49+ - ` tests/test_anthropic_models_dev.py `
50+ - ` .gitignore `
51+
52+ Changes:
53+ - Added ` _fetch_anthropic_models_from_models_dev() ` — fetches the Anthropic model
54+ catalog from ` https://models.dev/api.json ` (community-maintained, no auth required).
55+ Filters out retired 3.x models and restricted mythos models. Only includes models
56+ with ` tool_call: true ` (required by Claude Code).
57+ - Added ` _get_dynamic_models() ` — module-level cache with 1-hour TTL and 3-tier
58+ fallback: fresh cache → fetch → stale cache → None (caller falls back to hardcoded
59+ ` OAUTH_MODELS ` ). Pattern follows the Codex provider's GitHub JSON catalog fetch.
60+ - Modified ` get_models() ` to use dynamic list, falling back to ` OAUTH_MODELS ` .
61+ - Modified max output tokens lookup in ` handle_oauth_completion() ` to check dynamic
62+ data first (exact match), then fall back to hardcoded ` _MODEL_MAX_OUTPUT_TOKENS ` .
63+ - Moved ` model_quota_groups ` from class attribute to ` __init__ ` , populated from
64+ dynamic model list. Override ` get_model_quota_group() ` to always return
65+ ` "anthropic-global" ` for any Anthropic model (matches Codex pattern).
66+ - Added 11 tests: fetch parsing, filtering (3x, mythos, no-tool-call), network/JSON
67+ errors, cache behavior, stale fallback, quota group override.
68+
69+ Rationale:
70+ - OAuth tokens (` sk-ant-oat-* ` ) cannot call Anthropic's ` GET /v1/models ` endpoint.
71+ models.dev provides the same data (model IDs, context windows, max output tokens)
72+ without auth. This is the approach used by pi (earendil-works/pi) and opencode
73+ (sst/opencode).
74+ - Builds on PR #99 's hardcoded fallback list. Dynamic discovery augments the
75+ fallback — when models.dev is reachable, new models appear automatically.
76+
77+ Verification:
78+ - ` uv run python3 -m py_compile src/rotator_library/providers/anthropic_provider.py ` — passed
79+ - ` uv run ruff check src/rotator_library/providers/anthropic_provider.py --select F401,F811,F821,E9 ` — passed
80+ - ` pytest tests/test_anthropic_models_dev.py tests/test_model_alias.py -v ` — 23 passed
81+
82+ Notes:
83+ - ` MODELS_DEV_URL ` env var allows overriding the catalog URL (e.g., for testing or
84+ self-hosting). ` ANTHROPIC_MODELS_CACHE_TTL ` controls the cache TTL (default 3600s).
85+ - models.dev includes ` claude-sonnet-5 ` which was NOT in PR #99 's hardcoded list —
86+ this demonstrates the value of dynamic discovery.
87+ - Ref: b3nw/LLM-API-Key-Proxy #97
88+
89+ ## 2026-07-01 — Mirror pi-agent OAuth headers and tool naming
90+
91+ Target: ` feat(anthropic): add OAuth support and handle streaming nulls `
92+ Files:
93+ - ` src/rotator_library/providers/anthropic_provider.py `
94+ - ` tests/test_anthropic_oauth_headers.py `
95+ - ` .gitignore `
96+
97+ Changes:
98+ - Added ` _compute_beta_header(model) ` — dynamically computes the ` anthropic-beta `
99+ header based on the model. Base betas now include ` claude-code-20250219 `
100+ (critical: tells Anthropic this is a Claude Code session), ` prompt-caching-scope-2026-01-05 ` ,
101+ and ` context-management-2025-06-27 ` . Long-context models (opus-4-6+, sonnet-4-6+,
102+ fable-5, sonnet-5) get ` context-1m-2025-08-07 ` and ` effort-2025-11-24 ` . Haiku
103+ models exclude ` interleaved-thinking-2025-05-14 ` .
104+ - Added ` x-app: cli ` header to OAuth request headers.
105+ - Added ` _prefix_tool_name() ` helper — capitalizes first letter before prefixing
106+ (e.g., ` read ` → ` mcp_Read ` instead of ` mcp_read ` ). Mirrors Claude Code's
107+ PascalCase tool naming convention.
108+ - Kept ` ANTHROPIC_BETA_HEADER ` constant for backward compatibility (token refresh
109+ requests that don't have a model context).
110+ - 11 new tests covering beta computation (base, long-context, haiku exclusion)
111+ and tool name prefixing (lowercase, capitalized, empty, single char).
112+
113+ Rationale:
114+ - Research into pi-agent (earendil-works/pi) and @cgaravitoq/pi-claude-code-auth
115+ revealed that the proxy was missing critical protocol signals:
116+ - ` claude-code-20250219 ` beta (identifies as Claude Code session)
117+ - ` x-app: cli ` header (present in both pi implementations)
118+ - PascalCase tool names (Anthropic expects ` mcp_Read ` , not ` mcp_read ` )
119+ - Skipped for now: billing header (cch), Claude Code identity system prompt
120+ injection, system prompt relocation — these are protocol emulation, not
121+ safe header additions.
122+
123+ Verification:
124+ - ` uv run python3 -m py_compile ` — passed
125+ - ` uv run ruff check --select F401,F811,F821,E9 ` — passed
126+ - ` pytest tests/test_anthropic_oauth_headers.py tests/test_anthropic_models_dev.py tests/test_anthropic_translator.py -v ` — 63 passed
127+
128+ Notes:
129+ - ` _strip_tool_prefix() ` not modified — may need case-insensitive matching
130+ in a follow-up if tool result routing breaks.
131+ - The ` ANTHROPIC_BETA_HEADER ` constant is kept for token refresh requests
132+ that don't have model context. It uses the base betas only.
133+ - Ref: b3nw/LLM-API-Key-Proxy #97
134+
135+ ## 2026-07-01 — Full Claude Code protocol emulation (billing header + identity)
136+
137+ Target: ` feat(anthropic): add OAuth support and handle streaming nulls `
138+ Files:
139+ - ` src/rotator_library/providers/anthropic_provider.py `
140+ - ` tests/test_anthropic_oauth_headers.py `
141+
142+ Changes:
143+ - Added ` _compute_billing_header(messages) ` — computes the client attestation
144+ hash (cch) from the first user message text, mirroring @cgaravitoq :
145+ - cch = SHA256(first_user_message_text)[ :5]
146+ - suffix = SHA256(salt + chars_at[ 4,7,20] + version)[ :3]
147+ - salt = "59cf53e54c78"
148+ - Added ` _build_claude_code_system(messages, original_system_prompt) ` — builds
149+ the system prompt array with:
150+ 1 . Billing header as first system entry
151+ 2 . Claude Code identity ("You are Claude Code, Anthropic's official CLI for Claude.")
152+ as second system entry
153+ 3 . Original system prompt relocated to first user message (prevents 400 rejections
154+ from non-Claude Code identity in system[ ] )
155+ - Modified ` handle_oauth_completion() ` to use ` _build_claude_code_system() ` for
156+ all OAuth requests, replacing the plain ` payload["system"] = system_prompt ` .
157+ - Added 11 new tests: billing header format, determinism, hash correctness,
158+ list content extraction, empty messages, assistant message skipping, system
159+ array structure, prompt relocation, no-prompt case, no-user-message case.
160+ - Configurable: ` ANTHROPIC_CLI_VERSION ` and ` CLAUDE_CODE_ENTRYPOINT ` env vars.
161+
162+ Rationale:
163+ - Safe headers alone (PR #101 initial commit) produced 429 errors in testing.
164+ - The billing header and identity prompt are required for Anthropic to treat
165+ OAuth requests as genuine Claude Code sessions with standard rate limits.
166+ - Confirmed by pi-agent (earendil-works/pi) and @cgaravitoq/pi-claude-code-auth .
167+
168+ Verification:
169+ - ` uv run python3 -m py_compile ` — passed
170+ - ` uv run ruff check --select F401,F811,F821,E9 ` — passed
171+ - ` pytest tests/test_anthropic_oauth_headers.py tests/test_anthropic_models_dev.py tests/test_anthropic_translator.py tests/test_model_alias.py -v ` — 74 passed
172+
173+ Notes:
174+ - The billing header salt (59cf53e54c78) is hardcoded from @cgaravitoq 's reverse-
175+ engineered code. If Anthropic changes the algorithm, this will need updating.
176+ - ` ANTHROPIC_CLI_VERSION ` should be kept in sync with the user agent version.
177+ - Ref: b3nw/LLM-API-Key-Proxy #97
0 commit comments