Skip to content

Fall back to Claude Code subscription when ANTHROPIC_API_KEY is unset#629

Open
aarshvir wants to merge 2 commits into
virattt:mainfrom
aarshvir:claude-code-subscription-fallback
Open

Fall back to Claude Code subscription when ANTHROPIC_API_KEY is unset#629
aarshvir wants to merge 2 commits into
virattt:mainfrom
aarshvir:claude-code-subscription-fallback

Conversation

@aarshvir

Copy link
Copy Markdown

Summary

If a user has no ANTHROPIC_API_KEY but is already logged into the Claude Code CLI locally, this PR routes Anthropic model calls through claude-agent-sdk so the run uses their Claude Code subscription instead of erroring out with ValueError: Anthropic API key not found.

Today the only way to use a Claude model is to pay for the Anthropic API on top of any existing Claude Code / Pro / Max subscription. After this change, a Claude Code login is enough.

What changed

  • New src/llm/claude_code.pyChatClaudeCode, a minimal LangChain BaseChatModel that wraps claude_agent_sdk.query(). Concatenates messages, pulls SystemMessage into the SDK's system_prompt, runs the async call via asyncio.run (or a thread if a loop is already running), and returns the assistant text as an AIMessage. Prints a one-time stderr notice on first use.
  • src/llm/models.py — the Anthropic branch of get_model() no longer raises on missing key. It lazy-imports ChatClaudeCode and returns it; if claude-agent-sdk isn't installed, the error message now tells the user to either set the key or install the SDK.
  • src/llm/models.pyLLMModel.has_json_mode() returns False for Anthropic when ANTHROPIC_API_KEY is unset, so call_llm skips with_structured_output and falls into the existing markdown/brace JSON-extraction path that already serves Gemini and DeepSeek. No changes to call_llm itself.
  • pyproject.toml — adds claude-agent-sdk = "^0.1.0".
  • .env.example + README.md — short notes documenting the fallback.

Design notes

  • Scoped to Anthropic. Other provider branches still raise on missing keys — we don't claim a Claude Code login can substitute for OpenAI, Groq, etc.
  • Lazy import. Users with ANTHROPIC_API_KEY set never import the SDK, so it's effectively a soft dependency.
  • No changes to call_llm. Reuses the existing text-parsing fallback path.
  • CLI scope. The check in has_json_mode() reads os.environ rather than the web-app's per-request api_keys dict. If a web caller passes the key only via api_keys (not env), structured output is skipped and JSON extraction kicks in — still produces correct output, just slightly less efficient. Happy to thread api_keys into the check if you'd prefer.

Test plan

  • poetry lock && poetry install cleanly resolves claude-agent-sdk
  • With key (regression): ANTHROPIC_API_KEY set → poetry run python src/main.py --ticker AAPL picking Claude Opus 4.7 runs via ChatAnthropic as before
  • Without key (the new behavior): ANTHROPIC_API_KEY unset, claude CLI logged in → same command prints the one-time notice and produces normal trading signals
  • SDK not installed: pip uninstall claude-agent-sdk and unset the key → fails fast with the updated error message
  • Non-Anthropic untouched: OpenAI/Groq paths unchanged with or without keys
  • Backtester: poetry run python src/backtester.py --ticker AAPL under the fallback path survives the LangGraph loop

🤖 Generated with Claude Code

If a user has no Anthropic API key but is logged into the Claude Code CLI,
route Anthropic model calls through `claude-agent-sdk` so the run uses
their Claude Code subscription instead of erroring out.

- Add `ChatClaudeCode`, a minimal LangChain `BaseChatModel` that wraps
  `claude_agent_sdk.query()`.
- `get_model()` returns it for the Anthropic branch when no key is found
  (lazy import; if the SDK isn't installed, raise an upgraded error).
- `has_json_mode()` returns False for Anthropic-without-key so call_llm's
  existing JSON-extraction path handles structured output.
- Other providers unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ion-fallback

# Conflicts:
#	src/llm/models.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant