Skip to content

Add anthropic_base_url so every model request can use one Anthropic-compatible endpoint - #484

Open
alex-clickhouse wants to merge 1 commit into
mainfrom
alex/anthropic-base-url
Open

alex-clickhouse wants to merge 1 commit into
mainfrom
alex/anthropic-base-url

Conversation

@alex-clickhouse

Copy link
Copy Markdown
Collaborator

Problem

Nerve sends model requests from two places:

  • The Claude CLI. It reads ANTHROPIC_BASE_URL from its environment.
  • Nerve's own calls. These are session titles, prompt rewrite, source condensing, image descriptions, model discovery, nerve doctor, and memU. Each one gives the SDK https://api.anthropic.com explicitly. ANTHROPIC_BASE_URL has no effect on them, and no config key can change it.

When Nerve runs behind an LLM gateway (for credentials, budgets or usage records), only the CLI's requests go through the gateway. All other model calls go to api.anthropic.com. If the network allows only the gateway, those calls time out instead.

The proxy: settings cannot do this job, because proxy.enabled downloads and starts CLIProxyAPI at startup.

Change

  • New top-level key anthropic_base_url. It is empty by default.
  • When set:
    • anthropic_api_base_url returns it. The two SDK client factories and memU use that property, so all of Nerve's own calls follow it. memU reaches <url>/v1/chat/completions.
    • _build_env passes it to the CLI as ANTHROPIC_BASE_URL, so the config is the only source.
    • A trailing / or /v1 is removed.
  • Rejected values: a value that is not an http:// or https:// URL with a host, and a URL with credentials, a query or a fragment. The key goes in anthropic_api_key.
  • Conflicts: an error if set together with proxy.enabled or provider.type: bedrock. Each of those already decides the endpoint.
  • Restart-only, because memU builds its clients once at startup.
  • Docs: docs/config.md (a new section and a restart-table row) and config.example.yaml.

Default behavior

  • Without the key, nothing changes. The direct Anthropic API, CLIProxyAPI, Bedrock and Codex keep their current behavior.
  • Nerve does not read ANTHROPIC_BASE_URL from its own environment for this. A user who set that variable only for the CLI would otherwise see memU, titles and condensing move to that endpoint. The endpoint may not serve /v1/chat/completions or accept their key.

Model names: temporary and later

The endpoint must serve every model that Nerve names. A gateway with its own catalog, with no aliases, refuses an unknown model.

  • For now, set the model keys in config. Set agent.model, agent.title_model, agent.model_aliases and memory.* to IDs that the endpoint serves.
    • The composer's picker already comes from the endpoint: agent.model_discovery asks its GET /v1/models.
    • The default models do not: Nerve takes them from config and does not check them against the catalog.
  • Later, an external configuration source could supply the default models. This PR does not do that.

Tests

  • tests/test_anthropic_base_url.py covers these cases:
    • Default: the default config and proxy mode keep their URLs and CLI environment.
    • Endpoint set: both SDK clients and the CLI environment use it.
    • Normalization and validation: trailing / and /v1 are removed; malformed URLs and URLs with credentials are rejected; the key conflicts with the proxy and with Bedrock.
    • Config layers: the key is read from config.local.yaml.
    • Real requests: an SDK request and a memU chat request against a loopback server reach <url>/v1/messages and <url>/v1/chat/completions under a path prefix.
  • The full suite passes locally (3950 tests).

🤖 Generated with Claude Code

The Claude CLI follows ANTHROPIC_BASE_URL from its environment, but Nerve's
own model calls (session titles, prompt rewrite, source condensing, image
descriptions, model discovery, nerve doctor) and memU give the SDK
api.anthropic.com explicitly. Behind an LLM gateway, only part of the model
traffic went through the gateway.

anthropic_base_url sets one endpoint for all of them, and Nerve passes it
to the CLI. The key is empty by default, which keeps api.anthropic.com. It
cannot be combined with proxy.enabled or Bedrock, and a change needs a
restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alex-clickhouse
alex-clickhouse marked this pull request as ready for review October 5, 2026 07:31
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