"""Shared body for the `passthrough` × compat cells. Every other matrix row drives the proxy's `/v1/messages` translation layer: Claude Code speaks the first-party Anthropic wire and LiteLLM transforms the request per provider. This row instead exercises LiteLLM's *native passthrough* routes -- the "LLM gateway" configuration documented at https://code.claude.com/docs/en/gateway -- where Claude Code speaks each cloud's own wire format and the proxy forwards it, attaching provider credentials on the way out: anthropic ANTHROPIC_BASE_URL={proxy}/anthropic. The CLI's first-party wire, forwarded verbatim to api.anthropic.com, so the model ids are real Anthropic ids rather than proxy aliases. bedrock_invoke CLAUDE_CODE_USE_BEDROCK=1 + ANTHROPIC_BEDROCK_BASE_URL={proxy}/bedrock. The CLI POSTs /model/{model}/invoke-with-response-stream; the proxy recognizes a router alias in the model segment, rewrites it to the deployment's upstream model id, and SigV4-signs with its own AWS creds. vertex_ai CLAUDE_CODE_USE_VERTEX=1 + ANTHROPIC_VERTEX_BASE_URL={proxy}/vertex_ai/v1. The CLI POSTs .../models/{model}:streamRawPredict; the proxy resolves a router alias in the model segment and takes project, location, and credentials from the deployment (which is why the deployment must set `use_in_pass_through: true` -- see test_config.yaml). azure CLAUDE_CODE_USE_FOUNDRY=1 + ANTHROPIC_FOUNDRY_BASE_URL={proxy}/azure. Foundry mode sends the model in the JSON body, not the URL, so the proxy's /azure route cannot resolve a router alias and falls back to the env-configured AZURE_API_BASE / AZURE_API_KEY target. bedrock_converse not applicable -- Claude Code's bedrock mode is InvokeModel-only; no Converse-wire client exists. Auth is the same in every mode: the CLI's provider-native signing is disabled via CLAUDE_CODE_SKIP__AUTH, and the LiteLLM virtual key travels as `Authorization: Bearer` (ANTHROPIC_AUTH_TOKEN), exactly like the translation rows. The proxy holds the real provider credentials. The per-mode env vars and URL shapes above were captured from a real `claude` CLI (2.1.210) run against a request-logging sink, not from docs; if a CLI release changes them, the cells fail with the CLI's own diagnostic rather than silently testing the wrong wire. `run_models` and `env` are injection seams for `_driver_unit_tests/test_passthrough.py`; production callers leave them unset. """ from __future__ import annotations from typing import Any, Callable, Dict, Mapping, Optional, Sequence import pytest from claude_code._env import require_proxy from claude_code.cli_driver import ( ClaudeCLIError, failure_diagnostic, run_claude_models_parallel, ) ANTHROPIC_PASSTHROUGH_BASE_PATH = "/anthropic" CLIENT_SIDE_AWS_REGION = "us-east-1" """Satisfies the CLI's embedded AWS SDK, which refuses to construct a client without a region. The value never influences routing: the proxy signs the upstream request with its own credentials and region.""" VERTEX_PLACEHOLDER_PROJECT = "proxy-resolved-project" VERTEX_PLACEHOLDER_REGION = "us-east5" """The CLI refuses to build a Vertex URL without a project id and region, but the proxy replaces both path segments with the resolved deployment's `vertex_project` / `vertex_location` before forwarding, so deliberately-fake values prove the resolution actually happened.""" def bedrock_extra_env(proxy_base_url: str) -> Dict[str, str]: return { "CLAUDE_CODE_USE_BEDROCK": "1", "CLAUDE_CODE_SKIP_BEDROCK_AUTH": "1", "ANTHROPIC_BEDROCK_BASE_URL": f"{proxy_base_url}/bedrock", "AWS_REGION": CLIENT_SIDE_AWS_REGION, } def vertex_extra_env(proxy_base_url: str) -> Dict[str, str]: """Vertex-mode CLI env pointed at the proxy's /vertex_ai route. The `/v1` suffix on ANTHROPIC_VERTEX_BASE_URL is load-bearing: the CLI's Vertex SDK ships its API version inside its *default* base URL (`https://{region}-aiplatform.googleapis.com/v1`), so overriding the base drops the version from the request path unless the override carries it. LiteLLM's /vertex_ai route reuses the incoming path verbatim when it contains `/projects/.../locations/...`, so a version-less path would reach Google as `aiplatform.googleapis.com/projects/...` and 404. """ return { "CLAUDE_CODE_USE_VERTEX": "1", "CLAUDE_CODE_SKIP_VERTEX_AUTH": "1", "ANTHROPIC_VERTEX_BASE_URL": f"{proxy_base_url}/vertex_ai/v1", "ANTHROPIC_VERTEX_PROJECT_ID": VERTEX_PLACEHOLDER_PROJECT, "CLOUD_ML_REGION": VERTEX_PLACEHOLDER_REGION, } def foundry_extra_env(proxy_base_url: str) -> Dict[str, str]: return { "CLAUDE_CODE_USE_FOUNDRY": "1", "CLAUDE_CODE_SKIP_FOUNDRY_AUTH": "1", "ANTHROPIC_FOUNDRY_BASE_URL": f"{proxy_base_url}/azure", } def run_passthrough_cell( *, compat_result, models: Sequence[str], prompt: str, passthrough_base_path: str = "", build_extra_env: Optional[Callable[[str], Mapping[str, str]]] = None, run_models: Callable[..., Mapping[str, Any]] = run_claude_models_parallel, env: Optional[Mapping[str, str]] = None, ) -> None: """Run the shared `passthrough` × cell body. `passthrough_base_path` is appended to the proxy base URL and becomes the CLI's ANTHROPIC_BASE_URL (only the anthropic column uses it; the cloud columns ignore ANTHROPIC_BASE_URL entirely once their CLAUDE_CODE_USE_* flag is set). `build_extra_env` receives the trailing-slash-normalized proxy base URL and returns the provider-mode env for the CLI subprocess. """ proxy = require_proxy(compat_result, env=env) proxy_base = proxy.base_url.rstrip("/") extra_env = dict(build_extra_env(proxy_base)) if build_extra_env else None outcomes = run_models( models=models, prompt=prompt, base_url=proxy_base + passthrough_base_path, api_key=proxy.api_key, extra_env=extra_env, ) failures = [] for model in models: outcome = outcomes[model] if isinstance(outcome, ClaudeCLIError): error = f"[{model}] {outcome}" compat_result.add({"status": "fail", "error": error}) failures.append(error) continue if outcome.exit_code != 0: error = f"[{model}] claude CLI failed: {failure_diagnostic(outcome)}" compat_result.add({"status": "fail", "error": error}) failures.append(error) continue if not outcome.text.strip(): error = f"[{model}] claude returned empty assistant text" compat_result.add({"status": "fail", "error": error}) failures.append(error) continue compat_result.add({"status": "pass"}) if failures: pytest.fail("; ".join(failures), pytrace=False)