feat(copilot): add use_openrouter flag to bypass OpenRouter proxy

Adds CHAT_USE_OPENROUTER config flag (default: true) that controls
whether the SDK routes API calls through OpenRouter or connects to
Anthropic directly. When false, the subprocess inherits ANTHROPIC_API_KEY
from the environment and skips the proxy hop, reducing connection
drop surface area.
This commit is contained in:
Zamil Majdy
2026-03-17 13:09:36 +07:00
parent a31fc008e8
commit 94af0b264c
2 changed files with 28 additions and 10 deletions

View File

@@ -94,6 +94,11 @@ class ChatConfig(BaseSettings):
description="Use --resume for multi-turn conversations instead of "
"history compression. Falls back to compression when unavailable.",
)
use_openrouter: bool = Field(
default=True,
description="Route API calls through OpenRouter proxy. When False, the SDK "
"uses ANTHROPIC_API_KEY from the environment directly (no proxy hop).",
)
use_claude_code_subscription: bool = Field(
default=False,
description="For personal/dev use: use Claude Code CLI subscription auth instead of API keys. Requires `claude login` on the host. Only works with SDK mode.",
@@ -209,6 +214,15 @@ class ChatConfig(BaseSettings):
# Default to True (SDK enabled by default)
return True if v is None else v
@field_validator("use_openrouter", mode="before")
@classmethod
def get_use_openrouter(cls, v):
"""Get use_openrouter from environment if not provided."""
env_val = os.getenv("CHAT_USE_OPENROUTER", "").lower()
if env_val:
return env_val in ("true", "1", "yes", "on")
return True if v is None else v
@field_validator("use_claude_code_subscription", mode="before")
@classmethod
def get_use_claude_code_subscription(cls, v):

View File

@@ -214,18 +214,16 @@ def _build_sdk_env(
) -> dict[str, str]:
"""Build env vars for the SDK CLI process.
Routes API calls through OpenRouter (or a custom base_url) using
the same ``config.api_key`` / ``config.base_url`` as the non-SDK path.
This gives per-call token and cost tracking on the OpenRouter dashboard.
When ``config.use_openrouter`` is True (default), routes API calls
through OpenRouter using ``config.api_key`` / ``config.base_url``.
When *session_id* is provided, an ``x-session-id`` custom header is
injected via ``ANTHROPIC_CUSTOM_HEADERS`` so that OpenRouter Broadcast
forwards traces (including cost/usage) to Langfuse for the
``/api/v1/messages`` endpoint.
When ``config.use_openrouter`` is False, returns an empty dict so the
subprocess inherits ``ANTHROPIC_API_KEY`` from the parent environment
and connects to Anthropic directly (no proxy hop).
Only overrides ``ANTHROPIC_API_KEY`` when a valid proxy URL and auth
token are both present — otherwise returns an empty dict so the SDK
falls back to its default credentials.
When *session_id* is provided and OpenRouter is active, an
``x-session-id`` custom header is injected so that OpenRouter Broadcast
forwards traces to Langfuse.
"""
env: dict[str, str] = {}
@@ -237,6 +235,12 @@ def _build_sdk_env(
env["ANTHROPIC_API_KEY"] = ""
env["ANTHROPIC_AUTH_TOKEN"] = ""
env["ANTHROPIC_BASE_URL"] = ""
elif not config.use_openrouter:
# Direct Anthropic: skip OpenRouter proxy. The subprocess inherits
# ANTHROPIC_API_KEY from the parent environment (set via shared
# secrets). No base_url override needed — the SDK defaults to
# https://api.anthropic.com.
pass
elif config.api_key and config.base_url:
# Strip /v1 suffix — SDK expects the base URL without a version path
base = config.base_url.rstrip("/")