Skip to main content

Changelog

v3.13.0 — 2026-07-29

Added

  • Per-task model preferences API. New authenticated endpoints GET /v1/model-preferences and PUT /v1/model-preferences let a user read and replace their auto-routing overrides (planning, simple_text, coding, subagent; coding/subagent also accept a per-difficulty map). Values are validated against the model registry — unknown models return 400, auto itself is rejected, and restricted models (Fable, GPT-5.6 Sol) require entitlement (403 otherwise). Preferences are stored in the existing Firestore modelPreference field (same one the telemetry attest hook writes and select_model reads), and the user cache is evicted on update so changes take effect immediately. This gives pwc-agent/OpenCode users a first override path — previously only Claude Code's MODEL_PREFERENCE env telemetry could set it.

  • auto footer moved into the gateway. When a request comes in with model="auto", the gateway now appends a one-line markdown footer (> _via <model> (auto)_) to the final assistant text on /chat/completions, both non-streaming and streaming (synthetic delta chunk injected before data: [DONE]). The footer is skipped for explicit model selections, for completions that produced no text, and for intermediate agent-loop steps ending in finish_reason=tool_calls — so a tool-looping turn shows the footer exactly once, on the final textual completion. Because it lives in the gateway it now also covers the direct-EMEA path (auto → sonnet/opus), which the litellm-router marker could never see. A transitional dedup suppresses the gateway footer if an older router build already injected its own.

  • Auto footers stripped from request history. Assistant messages sent back by the client keep the visible footer; the model would see it in context and imitate it in the next answer (a second, model-generated footer). The gateway now strips every > _via … (auto|selected)_ line from assistant history before forwarding on /chat/completions.

  • Double auto footer fixed. The transitional dedup now matches ANY > _via <model> (auto)_ footer (regex), not just the one naming the gateway-resolved model: an old router build can fall back internally (glm → qwen-fast) and inject a footer naming a different model, which previously produced two footers in the same response.

  • gpt-5.6-sol function tools fixed on /chat/completions. The upstream rejects function tools unless reasoning_effort is explicitly "none". When a client sends tools without any explicit effort (OpenCode's default request shape), the gateway now injects reasoning_effort="none"; an explicit effort in the body or reasoning header is always respected.

  • bonsai listed in model discovery. The synthetic PwC Agent entries in /models and /v1/models now include bonsai (Ternary-Bonsai-27B), aligning discovery with what the router actually serves.

v3.12.40 — 2026-07-27

Changed

  • Stable model routing inside Anthropic tool loops. The auto classifier now ignores role=user messages made exclusively of Anthropic tool_result blocks and continues scanning back to the latest real human text. All completions in the same agentic turn therefore reuse the original classification instead of falling back to simple_text/easy → qwen-fast after every tool call. Mixed messages containing both text and tool results still use the new text, so a new human instruction can trigger a new route.

  • Classifier decision cache extended from 60 seconds to one hour. The cache remains bounded to 1,024 entries and keyed by SHA-256 of the latest human text. Cache hits are in-memory and add no network latency; in tool loops they avoid repeated classifier calls and generally reduce latency. The cache is still pod-local, so this is best-effort rather than a cross-pod session pin.

v3.12.39 — 2026-07-27

Fixed

  • Claude Code /model gpt-5.6-sol failed with 400 integer_below_min_value. Claude Code validates a model switch with a one-token /v1/messages probe; GPT-5.6 Sol, like the Azure Foundry models, requires max_output_tokens >= 16. The gateway now clamps the probe to 16 for openai.eu.gpt-5.6-sol as well as DeepSeek and Kimi.

  • /models slash command failed with Contains simple_expansion. The deployed command still used Claude Code's inline !`curl ... $ANTHROPIC_AUTH_TOKEN` expansion, which the shell permission checker rejects before execution. The command now invokes curl through the Bash tool instead; it never prints the token and uses /v1/models, whose response already includes all entitled Claude, PwC self-hosted, Azure, and restricted models.

v3.12.38 — 2026-07-24

Fixed

  • Claude Code /model Kimi-K2.6 (and DeepSeek) failed with 400 integer_below_min_value. Claude Code probes the target model with a 1-token /v1/messages request when switching; Azure Foundry requires max_output_tokens >= 16. The gateway now clamps max_tokens to 16 for the Azure Foundry models when the client sends a lower positive value.

  • Claude Fable 5 enabled. The quota team registered the model as SELFHOSTED_CLAUDE_5_FABLE in quota-management-lib and models_costs; the registry internal name (SELFHOSTED_FABLE_5) is renamed to match. claude-fable-5 now passes quota preflight, is tracked and priced from the real cost document (entitled keys only — FABLE_KEYS in the router still gates access).

v3.12.37 — 2026-07-24

Added

  • PwC Agent models in the discovery endpoints. /models (OpenAI shape) and /v1/models (Anthropic shape) now also list the PwC Agent models — auto, glm, qwen-fast, qwen-thinking, DeepSeek-V4-Pro, Kimi-K2.6 — so Claude Code's model discovery and any OpenAI client can see the full catalogue, not just the EMEA upstream list.

  • /models slash command for Claude Code. GET /downloads/models-command.md serves a ready-made Claude Code slash command: install with curl -fsSL <gateway>/downloads/models-command.md -o ~/.claude/commands/models.md, then type /models in Claude Code to get a table of the models available to your key.

Changed

  • Restricted models are hidden from non-entitled users. claude-fable-5 and gpt-5.6-sol (and their upstream aliases, e.g. openai.eu.gpt-5.6-sol) appear in /models and /v1/models only when the caller's API key is in the entitlement allowlist (fable-keys secret, cached 5 min, fails closed on read errors). Visibility only: the hard access gate stays in the litellm-router (FABLE_KEYS) and the quota system. Configurable via RESTRICTED_MODELS / RESTRICTED_KEYS_SECRET env vars.

v3.12.36 — 2026-07-24

Changed

  • Models not registered in the quota-management-lib whitelist are now BLOCKED (403), not silently untracked. v3.12.34 skipped quota tracking when QuotaMetadata rejected an unknown internal model ID — the call proceeded with unlimited, unattributed spend. The preflight now raises 403 model_not_quota_enabled ("Modello non abilitato…"): a model is usable only once its internal ID is registered in quota-management-lib.

Fixed

  • DeepSeek-V4-Pro and Kimi-K2.6 quota tracking restored. The registry internal names (SELFHOSTED_DEEPSEEK_V4_PRO, SELFHOSTED_KIMI_K2_6) did not match the IDs registered in the quota-lib whitelist and in Firestore models_costs (SELFHOSTED_DEEPSEEK_V4_PRO_GLOBAL, SELFHOSTED_KIMI_K26_THINKING_GLOBAL), so quota was never tracked and costs fell back to defaults. Renamed to match: spend is now tracked and priced from the real models_costs documents. Note: claude-fable-5 (SELFHOSTED_FABLE_5) has no whitelist entry yet — it returns 403 model_not_quota_enabled until the ID is registered in quota-management-lib.

v3.12.35 — 2026-07-24

Fixed

  • GLM and Qwen streaming hang when called via Claude Code CLI (~190s timeout). Two root causes: (1) LiteLLM proxy emits message_start twice — the Anthropic SDK treats the duplicate as a protocol violation and hangs. Fixed by deduplicating message_start events in stream_anthropic_response: after the first message_start is forwarded, subsequent ones are dropped. (2) GLM-5.2 on the b200-vm has extended thinking enabled by default; without text content blocks, the SDK waits indefinitely. Fixed by adding extra_body: {chat_template_kwargs: {enable_thinking: false}} to the GLM entry in litellm-orchestrator/server/config.yaml (mirrors the existing qwen-fast setting).

  • Default temperature: 0.15 broke reasoning models on /chat/completions. ChatCompletionRequest.temperature defaulted to 0.15, so the gateway injected it whenever the client omitted temperature. OpenAI reasoning models (e.g. gpt-5.6-sol) only accept their default (1) and returned 400 (Unsupported value: 'temperature' does not support 0.15 with this model). The default is now None: with model_dump(exclude_none=True) the field is simply omitted and the upstream default applies. Clients that explicitly send a temperature are unaffected.

  • reasoning_effort: "none" now supported on /chat/completions. gpt-5.6-sol rejects function tools on /chat/completions unless reasoning_effort is "none", but the gateway dropped the value twice: the reasoning header only accepted low|medium|high, and a body-level reasoning_effort was silently discarded by Pydantic (field not declared). Both paths fixed: the header now accepts none, and ChatCompletionRequest declares reasoning_effort so body values are forwarded (header still wins when present). This unblocks OpenCode's variant=none for GPT-5.6 Sol.

  • Claude Code + DeepSeek-V4-Pro / Kimi-K2.6 returned 400 (reasoning.effort not supported). Claude Code always sends thinking: {type: enabled} on /v1/messages; litellm translates it to reasoning.effort for OpenAI-compatible backends, but the two Azure Foundry models reject the parameter (Kimi reasons natively, DeepSeek-V4-Pro exposes no reasoning control). The gateway now strips thinking for models in the new THINKING_UNSUPPORTED registry set before forwarding.

  • Non-UUID x-request-id caused 500. quota-management-lib requires QuotaMetadata.requestId to be a valid UUID v4, but the middleware accepted any client-provided x-request-id verbatim — a value like test-123 blew up quota preflight with an unhandled ValidationError. The middleware now validates the header and regenerates a fresh uuid4 when the value is not a valid UUID v4.

v3.12.34 — 2026-07-24

Fixed

  • DeepSeek-V4-Pro and Kimi-K2.6 return 500 (quota-lib ValidationError). QuotaMetadata from quota-management-lib validates model IDs against a hardcoded whitelist. Self-hosted models added after the last lib release (SELFHOSTED_DEEPSEEK_V4_PRO, SELFHOSTED_KIMI_K2_6, SELFHOSTED_DS4_FLASH, SELFHOSTED_BONSAI_27B, SELFHOSTED_FABLE_5) are not in that whitelist, causing a Pydantic ValidationError at check_quota_preflight before the request reaches the model. Fixed by wrapping QuotaMetadata(...) in a try/except: on failure, logs a warning and returns (None, None) — quota tracking is skipped for this request (no spend attributed to a wrong model ID). The model call proceeds normally. Long-term fix: update quota-management-lib to include all self-hosted model IDs.

v3.12.33 — 2026-07-23

Fixed

  • Restricted-model errors (403) were still returned as 500. The root cause: SafeLoggingMiddleware extended Starlette's BaseHTTPMiddleware, whose call_next() runs the inner app in an anyio task group. Exceptions raised inside that task group (including UpstreamError from the litellm-router's FABLE_KEYS check) were wrapped in ExceptionGroup by anyio before reaching FastAPI's ExceptionMiddleware. The except Exception: block in SafeLoggingMiddleware.dispatch() then caught the ExceptionGroup and returned a generic 500 — bypassing both upstream_error_handler and all_exception_handler. Rewritten SafeLoggingMiddleware as a pure ASGI middleware (no BaseHTTPMiddleware, no task groups): await self.app(scope, receive, send) calls the inner chain synchronously in the same stack frame, so exceptions are handled in-order by ExceptionMiddleware before reaching this middleware.

v3.12.32 — 2026-07-23

Fixed

  • UpstreamError propagated as 500 instead of the router's original status code. UpstreamError (raised by CloudRunClient.forward_request when the upstream returns 4xx/5xx) was being caught by the generic Exception handler (all_exception_handler) which always returned 500. Added a dedicated upstream_error_handler registered before the generic handler that returns the router's original HTTP status code (e.g. 403 when FABLE_KEYS denies access) and body.

  • Streaming path swallowed router error message. When stream_anthropic_response caught UpstreamError on a non-transient error (e.g. 403), it emitted a generic "An error occurred during streaming." SSE event instead of the router's actual message. The error message is now extracted from response_content.error.message and included in the emitted event as [{status_code}] {message}.

v3.12.31 — 2026-07-23

Fixed

  • EMEA calls through the bridge now use the static team API key. When USE_UPSTREAM_BRIDGE=True, the auth_override (apikey=USER_KEY&tenantid=TID) is no longer applied to the Authorization header — the static UPSTREAM_API_KEY from Secret Manager is used instead. The bridge (wezgfompadwa001.azurewebsites.net) only accepts this team-level key; the per-user format caused 401 errors. Attribution to the individual user's API key is preserved at the gateway level (structured logging, Firestore quota tracking). When USE_UPSTREAM_BRIDGE=False (direct EMEA access), auth_override is still applied for per-user billing. Fix applied to all three completion paths: anthropic_completions.py, chat_completions.py, responses_completions.py.

v3.12.30 — 2026-07-23

Fixed

  • Unknown model name returns 422 instead of 500. When a client sends an unrecognised model name (e.g. "modello-inesistente"), the model registry performs a pass-through (intentional, for unknown variants) and sets ModelEntry.is_passthrough=True. The three route handlers (/v1/messages, /chat/completions, /responses) now check this flag and raise HTTPException(422, code="unknown_model") with a message pointing to GET /models. Previously the pass-through reached the cost calculator which raised an unhandled exception → 500. pick_auto_model is unaffected: it already skips unknown candidates silently.

v3.12.29 — 2026-07-23

Fixed

  • CloudRunClient.forward_request/stream missing auth_override parameter. The completion modules call forward_request(..., auth_override=auth_override) on whatever client is active (EMEA or self-hosted). UpstreamClient accepts the kwarg (and uses it for per-user billing), but CloudRunClient did not declare it, causing TypeError on every self-hosted model request (GLM, Qwen, DS4, Fable). Added auth_override: str | None = None to both forward_request and forward_stream on CloudRunClient; the parameter is accepted and ignored (CR auth is always via GCP identity token).

v3.12.28 — 2026-07-23

Fixed

  • openai missing from dependencies (CrashLoopBackOff in DEV). utils/task_classifier.py imports openai.AsyncOpenAI for the Gemini and GLM classifier clients, but openai was not declared in pyproject.toml. Added openai>=1.0.0 to fix the ModuleNotFoundError on startup.

v3.12.27 — 2026-07-21

Changed

  • Architecture: sonnet/opus served directly by the gateway (not via litellm-router). sonnet-4-6 and opus-4-7 are removed from the litellm-router's config.yaml. The gateway now serves them directly via upstream_client (httpx → GenAI Shared EMEA) with per-user billing: auth_override = f"apikey={api_key}&tenantid={tenant_id}" injected from context vars in all three completion modules (chat_completions.py, anthropic_completions.py, responses_completions.py). Model registry: both short names are re-mapped in _NO_PREFIX_ALIASES to their EMEA vertex_ai upstream IDs; ModelEntry objects and PWC_AGENT_UPSTREAM_IDS entries for them are removed.
  • Headroom context compression moved to gateway (core/headroom.py). Applies to ALL traffic (EMEA + self-hosted) via a lazy-init singleton. Called via await try_compress(body) before every forward_request/forward_stream in all three completion modules. No-op if headroom-ai[ml] is not installed or HEADROOM_ENABLED=false. Removes headroom_wrapper.py from litellm-router and the headroom_wrapper.headroom_callback entry from config.yaml.
  • UpstreamClient.forward_request/stream accept auth_override: str | None. When set, the per-request Authorization header overrides the client-level static key for that single call (per-user EMEA billing). Ignored by CloudRunClient (does not declare the parameter — never reached for pwc_agent models).

Removed (litellm-router)

  • server/headroom_wrapper.py — deleted.
  • _GENAI_SHARED_MODELS, _request_tenant_id, per-user EMEA creds block from custom_auth.py.
  • sonnet-4-6 and opus-4-7 model entries, their fallbacks (opus↔sonnet), and context_window_fallbacks from config.yaml.

Tests

  • Gateway: 63 tests pass (was 62). Updated TestModelRegistryAutoRouting: sonnet-4-6/opus-4-7 short names now assert EMEA routing (not pwc_agent). Added test_auto_model_self_hosted_routes_via_pwc_agent and test_auto_model_emea_routes_via_upstream_client replacing the old test_all_auto_model_outputs_route_via_pwc_agent.
  • litellm-router: 33 tests pass (was 42). Removed TestPerUserCredentials, TestRequestTenantId, and _GENAI_SHARED_MODELS assertions from TestModelSets.

v3.12.26 — 2026-07-21

Fixed

  • Model registry routing for sonnet-4-6 / opus-4-7 (Bug 1). When model="auto" resolved to "sonnet-4-6" or "opus-4-7", these requests were incorrectly routed directly to GenAI Shared EMEA via upstream_client (static team key) instead of going through pwc_agent_client → litellm-router (per-user billing). Root cause: both model names existed in _NO_PREFIX_ALIASES (step 4 of _build_lookup) which overwrote the correct pwc_agent ModelEntry registered at step 1. Removed them from _NO_PREFIX_ALIASES; step 1 already handles them correctly.
  • CloudRunClient header names for per-user billing (Bug 2). _request_headers() was sending "api-key" and "tenant-id" but custom_auth.py in the litellm-router reads "x-api-key" and "x-tenant-id". Per-user EMEA credentials for sonnet-4-6 / opus-4-7 were silently dropped, causing all requests to use the team GENAI_SHARED_API_KEY instead of the originating user's key. Renamed headers to match the litellm-router contract.

Added

  • Unit tests — gateway (tests/unit/test_orchestrator.py): 62 tests covering task classifier (extraction, cache, circuit breaker, gemini/glm mocks), model registry routing (Bug 1 regression), CloudRunClient headers (Bug 2 regression), routing decision injection, and _responses_input_as_messages.
  • Unit tests — litellm-router (litellm-orchestrator/tests/test_custom_auth.py): 42 tests covering _request_api_key/_request_tenant_id header parsing, restricted model access control, per-user credential injection for sonnet/opus, identity token caching, _resolve_api_base, response annotation (auto marker), streaming marker injection, and user_api_key_auth lane resolution.

v3.12.25 — 2026-07-21

Fixed

  • model="auto" classification for Anthropic Messages API. The classify() call in /v1/messages was passing raw Pydantic objects instead of dicts, causing the task classifier to always fall back to simple_text/easy → qwen-fast. All messages are now serialised with .model_dump() before classification.

Added

  • AUTO_MODEL_ENABLED guard. When the env var AUTO_MODEL_ENABLED=False, requests with model="auto" are rejected with HTTP 400 across all three routes (/v1/messages, /chat/completions, /responses). Defaults to True.
  • Classifier eager initialisation at startup. init_classifier() is called from lifespan after upstream credentials are resolved. The Gemini classifier derives its URL and key from the upstream secret's publicUrl (bypassing the bridge), so it always hits the real EMEA endpoint. CLASSIFIER_GLM_URL / CLASSIFIER_GLM_KEY can still be set via env to configure the GLM fallback.
  • routing_decision propagated to litellm-router. For model="auto" calls routed via pwc_agent_client, the classifier decision is now embedded in the request body's metadata.routing_decision field before forwarding. This lets the litellm-router's custom_auth._log_usage build the correct BQ note (auto:task/difficulty->model cls=… cache=…).
  • /responses route: pwc_agent_client routing + model="auto" support. The OpenAI Responses API endpoint now routes self-hosted models (glm, qwen-fast, etc.) via pwc_agent_client and supports model="auto" (classifier reads from the Responses API input field).
  • Deployment config: classifier env vars. Added CLASSIFIER_GLM_URL, CLASSIFIER_GLM_KEY, AUTO_MODEL_ENABLED to Config/DEV/SERVICES/APIs/coding-agents-gateway/deployment.yaml.

v3.12.24 — 2026-07-21

Added

  • Gateway orchestrator (model=auto). The task classifier previously embedded in the PwC Agent litellm-router is now part of the gateway itself. When a client sends model: auto, the gateway classifies the last user message (Gemini 3.1 Flash Lite → GLM fallback) and rewrites the model name to a concrete backend before forwarding. This gives the gateway full visibility into which model is used for quota, telemetry, and policy checks.
  • PwC Agent model routing via litellm-router. Self-hosted models (glm, qwen-thinking, qwen-fast, ds4, ds4-fast, bonsai, claude-fable-5, sonnet-4-6, opus-4-7, DeepSeek-V4-Pro, Kimi-K2.6) are now routed through the PwC Agent litellm-router Cloud Run service via a dedicated CloudRunClient with GCP identity token injection. New env vars: PWC_AGENT_BASE_URL, PWC_AGENT_AUDIENCE, CLASSIFIER_GEMINI_URL, CLASSIFIER_GEMINI_KEY, CLASSIFIER_GLM_URL, CLASSIFIER_GLM_KEY, AUTO_MODEL_ENABLED.

v3.12.23 — 2026-07-20

Added

  • GPT-5.6 Sol support (EU). Added the new gpt-5.6-sol OpenAI model to the registry and models table, alongside the existing gpt-5.6-luna and gpt-5.6-terra. Only the EU-hosted deployment is exposed; the global endpoint is intentionally not registered.

v3.12.22 — 2026-07-16

Added

  • install-claude.sh now installs the status line automatically (macOS/Linux). Many users lost the status line after it silently disappeared (script never installed it, or a stale/missing statusline.sh broke silently). The installer now downloads statusline.sh to ~/.claude/statusline.sh and registers it via statusLine in settings.json, same as it already does for the telemetry hook. Non-fatal if the download fails (network/VPN issue) — setup continues without it. Windows is not affected (no status line script for PowerShell yet), and L1 telemetry enforcement is unchanged — the status line was never required for attestation.

Fixed

  • Windows users blocked by L1 telemetry enforcement. The SessionStart telemetry attestation hook was bash-only (telemetry-attest-hook.sh), which on Windows depends on Git for Windows providing bash/curl/jq on PATH at the moment Claude Code launches the hook — fragile in practice (winget PATH updates don't reach already-open shells/shortcuts). If the hook never runs, attestation is never recorded and, once REQUIRE_TELEMETRY is enabled, every request is rejected with hook_not_configured/attestation_expired, regardless of OS. Added a native PowerShell hook (telemetry-attest-hook.ps1, served at /downloads/telemetry-attest-hook.ps1) with no external dependencies — only built-in PowerShell cmdlets (Invoke-RestMethod, ConvertTo/From-Json). install-claude.ps1 now installs and registers this hook instead of the bash one, and no longer needs jq.
  • install-claude.ps1 could hang on winget upgrade. When Git or Claude Code was already installed, the upgrade check ran inline with no timeout — on some machines winget upgrade stalls for minutes syncing source indexes. It now runs in a background job with a 300s timeout (-UpgradeTimeoutSec), and falls back to the currently installed version instead of blocking the rest of the setup.
  • telemetry-attest-hook.ps1 / install-claude.ps1 could fail to parse on Windows with a TerminatorExpectedAtEndOfString error. Both scripts contained em-dash characters () in comments/strings. Windows PowerShell 5.1 (powershell.exe) reads a .ps1 file without a byte-order mark using the system's ANSI codepage, not UTF-8 — so the em-dash's multi-byte UTF-8 encoding got misread as garbled characters, breaking the string literal and crashing the parser before the hook could run (silently defeating L1 attestation on affected machines). Replaced all em-dashes with plain hyphens and added a UTF-8 BOM to both files so PowerShell 5.1 reads them correctly regardless of codepage.

v3.12.21 — 2026-07-14

Added

  • POST /v1/messages/count_tokens. New Anthropic-compatible endpoint that counts the input tokens a request would use, without creating a message. Pure passthrough to the upstream (only the model field is rewritten); no quota/cost impact, matching Anthropic's own free semantics for this endpoint.

v3.12.20 — 2026-07-14

Added

  • blockable exemption flag for API keys. A new optional blockable field on API_KEYS Firestore documents lets an API key be permanently exempt from both L1 (telemetry attestation) and L2 (BigQuery reconciliation) enforcement. blockable=false → the key is never rejected for telemetry reasons and never disabled by L2, regardless of attestation state or OTEL match rate. Field absent or true → unchanged, normal enforcement.

v3.12.19 — 2026-07-13

Added

  • GPT-5.6 Luna & Terra support (EU). Added the new gpt-5.6-luna and gpt-5.6-terra OpenAI models to the registry and models table. Only the EU-hosted deployment is exposed.

v3.12.18 — 2026-07-09

Fixed

  • Hotfix: startup ImportError + missing telemetry router. A bad squash merge left utils/auth.py importing the removed get_telemetry_last_seen symbol and using the old heartbeat-based telemetry check. Replaced with enforce_attestation (attestation pattern). Also restored the missing telemetry router in app.py so /v1/telemetry/attest is correctly registered.

v3.12.17 — 2026-07-08

Changed

  • Internal (L2 cost). The reconciliation now reads its billing data from a configurable source and, in dev/prod, from a pruning-friendly incremental materialized view (coding_agents_reconcile.mv_sdlc_billing, exposing a real TIMESTAMP created_ts, pre-filtered to sdlc_llms) instead of the raw, unpartitioned, STRING-timestamp usage_monitoring_revenues table. The two per-run billing scans (candidate query + active-user count) are also merged into a single scan — the active-user count is now derived from the same rows. Net effect: per-run BigQuery scan drops from ~1.5 GB to a small fraction, and cost stays flat as history grows (the MV is maintained incrementally). New env: BQ_BILLING_TABLE, BQ_BILLING_TIME_SQL, BQ_BILLING_METRIC_FILTER (defaults preserve the old raw-table behaviour). No functional change to who gets blocked.

v3.12.16 — 2026-07-08

Changed

  • install-claude.sh and telemetry-attest-hook.sh are now served dynamically: the gateway injects the correct base URL at request time from the Host header, so the scripts downloaded from DEV point to the DEV endpoint and those from PROD point to PROD — no manual override needed.

v3.12.15 — 2026-07-06

Added

  • L2_RECONCILE_ENABLED master switch. New environment variable to enable/disable L2 telemetry reconciliation via the release. When false, the /v1/telemetry/reconcile endpoint is a no-op (the CronJob may still fire but does nothing), so L2 can be turned on/off without changing the schedule or deleting the job. Defaults to true.

v3.12.14 — 2026-07-06

Added

  • "API Key Disabled" guide page. New documentation page explaining what to do if your API key was disabled by the telemetry reconciliation: fix your telemetry configuration and contact the PwC Help Desk to have it re-activated — and note that it will be automatically disabled again if telemetry still isn't received. It is the page linked from the "your API key has been disabled" message.

v3.12.13 — 2026-07-03

Fixed

  • Internal (L2 block-floor). The per-user block floor no longer issues an inverted-range BigQuery query on the first reconcile run after a block. Previously, when a user's telemetryBlockedAt was newer than the window end (win_end = now − OTEL buffer), the post-block slice (floor, win_end] was inverted (lower bound after upper bound), always returned 0 rows, and was reported as "no messages after last block" — a misleading skip. The run now detects floor >= win_end explicitly, logs that post-block messages are still inside the OTEL buffer, and skips that user for one run (no wasted query, honest reason). No outcome change — the user is still correctly not re-blocked until mature post-block messages exist; only the degenerate query and its misleading log are removed.

v3.12.12 — 2026-07-03

Changed

  • Internal (L2 observability). The reconciliation run now emits run-level logs so it is auditable who gets blocked and why: a [RECONCILE] start line (window + thresholds + enforcement/dry-run), a [RECONCILE] candidate <id> — M/N OTEL matched (X% < threshold) line per flagged user, the match rate on the [RECONCILE] BLOCKED line, a dry-run/enforcement-off … NOT blocked line when applicable, and a [RECONCILE] done summary (scanned/active_users/candidates/blocked). The endpoint also logs when it is invoked/skipped. No behaviour change.

v3.12.11 — 2026-07-03

Changed

  • Internal (L2). The reconciliation window is now configured in minutes (L2_OTEL_BUFFER_MINUTES, L2_LOOKBACK_MINUTES) instead of fractional hours, so short per-message telemetry deadlines (e.g. 5 minutes) are expressed cleanly. No user impact.

v3.12.10 — 2026-07-03

Changed

  • Internal (L2 reconciliation). A user who was blocked for missing telemetry and then unblocked is no longer re-blocked on the old (pre-block) messages: L2 now re-evaluates only messages sent after the last block, using the block timestamp as a per-user floor. Only genuinely new missing telemetry can re-block. No user impact.

v3.12.9 — 2026-07-01

Changed

  • Internal. The telemetry reconciliation windows now support fractional hours (e.g. 0.5 = 30 minutes), enabling a short reconciliation schedule (run via a Kubernetes CronJob). No user impact.

v3.12.8 — 2026-07-01

Changed

  • Account and telemetry status changes take effect faster. The gateway now refreshes each user's cached status every minute (previously every 5 minutes), so telemetry attestation and access changes are picked up more quickly.
  • Claude Code guide. The setup section is now titled "Installation and Telemetry Setting" and highlights that the downloadable script can also configure telemetry even if Claude Code is already installed.
  • Internal. Simplified the setup of the behind-the-scenes telemetry reconciliation job (the endpoint stays hidden and internal). No user impact.

v3.12.7 — 2026-07-01

Changed

  • Secured the internal telemetry reconciliation job. The behind-the-scenes job that cross-checks telemetry can now only be triggered by a trusted, scheduled caller. This is internal hardening — no impact on how you use Claude Code.

v3.12.6 — 2026-07-01

Changed

  • One installer that does everything. The Claude Code setup is now a single downloadable script per platform (macOS/Linux and Windows). It installs Claude Code if you don't already have it, configures the connection to the gateway, and sets up telemetry — all in one step, installing anything it needs automatically. It no longer pins specific model versions, because the gateway now provides the correct default models for you.
  • Refreshed the Claude Code guide. Clearer download buttons, an updated settings example, and the removal of outdated sections.

v3.12.5 — 2026-07-01

Fixed

  • Setup scripts always download the latest telemetry file. Previously a cached (out-of-date) copy could be fetched after an update.

v3.12.4 — 2026-07-01

Fixed

  • Clearer telemetry warnings at startup. When your telemetry configuration isn't valid, Claude Code now tells you exactly what to fix instead of showing a generic message.

v3.12.3 — 2026-07-01

Added

  • One-command telemetry setup. Added downloadable scripts (macOS/Linux and Windows) that set up the telemetry check for you automatically.

v3.12.2 — 2026-07-01

Changed

  • Friendlier telemetry error messages. If your telemetry setup is wrong, you now get a clear explanation naming the exact setting to correct.
  • Fewer, simpler requirements. Removed some overly strict checks, and your PwC email is no longer something you need to set (it's resolved automatically).

Added

  • Extra safety checks. Telemetry is now verified to point at the correct environment and to belong to your own account.

v3.12.1 — 2026-06-30

Fixed

  • Reliability fixes for telemetry enforcement. Resolved several issues found in testing so the telemetry checks work correctly for everyone — including older API keys and users returning after a break — and so an account confirmed to be sending no telemetry is properly blocked.

v3.12.0 — 2026-06-30

Added

  • New telemetry checks for Claude Code. Your telemetry configuration is now validated when Claude Code starts, so problems are caught early with a helpful message. In addition, a background job periodically confirms that telemetry is actually being received — catching setups that look correct but silently send nothing (for example, blocked by a firewall).

Removed

  • Retired the previous, less reliable telemetry check.

v3.11.3 — 2026-06-29

Added

  • Echo request-id response header for OTEL reconciliation. The gateway now sets a request-id response header on every successful LLM call, equal to the requestId written to usage_monitoring_revenues. This enables a 1:1 join between otel_events.attributes.request_id and usage_monitoring_revenues.requestId for Claude Code telemetry reconciliation.

v3.11.2 — 2026-06-26

Fixed

  • NameError in non-streaming path (total_input_tokens undefined). The v3.11.1 cleanup of the extended_context block accidentally removed the total_input_tokens variable while a log statement still referenced it, causing HTTP 500 on all non-streaming requests (including the Claude Code gateway probe with claude-3-haiku).

v3.11.1 — 2026-06-26

Added

  • Sonnet 4.6 1M context support. claude-sonnet-4-6 now supports the full 1M context window at standard pricing. The gateway automatically injects the anthropic-beta: context-1m-2025-08-07 header for Sonnet 4.6 requests, and /v1/models now exposes a claude-sonnet-4-6[1m] variant.

Removed

  • Extended-context 2x pricing multiplier removed. Current Anthropic pricing includes the full 1M context window at standard rates for all 1M-capable models (Opus 4.6/4.7/4.8, Sonnet 4, Sonnet 4.6) — no per-token premium applies beyond 200K tokens. The 2x detection and related dead code (EXTENDED_CONTEXT_THRESHOLD, extended_context variable) has been removed from the completions pipeline.

v3.11.0 — 2026-06-18

Added

  • Cross-provider failover on 429 (Vertex AI ↔ Bedrock). When the primary provider returns HTTP 429 (RESOURCE_EXHAUSTED) for an Anthropic model, the gateway now automatically retries the same model on the sibling provider (e.g. vertex_ai.anthropic.claude-sonnet-4-6bedrock.anthropic.claude-sonnet-4-6, and vice-versa). Works for both streaming and non-streaming /v1/messages. For streaming, failover only happens before the first SSE chunk is sent (the upstream returns 429 before any body), so the client never sees duplicated output. Cost/quota and [REQUEST COMPLETED] logs are attributed to the provider that actually served the request. Failover is limited to 429 (capacity exhaustion) and to models both providers serve; non-Anthropic models are unaffected. New helper get_failover_entry() in the model registry.

v3.10.0 — 2026-06-18

Fixed

  • Model prices were never refreshed after startup → new models silently billed at the 2€/10€ defaults. The CostManager price cache was loaded once from Firestore models_costs at application startup and never reloaded. Any model added to models_costs after the pods started (most recently Opus 4.8, added 2026-06-08 ~3.5h after the running pods had cached) fell through to the hardcoded defaults (Input 2€/1M, Output 10€/1M), under-counting its cost by ~2.5× on every request and emitting a continuous Model '…CLAUDE_48_OPUS' not found in cache warning.

Added

  • Periodic price-cache refresh. The price cache now reloads from Firestore every MODEL_COSTS_REFRESH_SECONDS (default 900s / 15 min) via a background task started in the lifespan, so newly-added models are picked up without a redeploy. The reload builds a fresh dict and swaps it atomically. Set the interval to 0 to disable.
  • On-demand fetch-through on cache-miss. When a model isn't in the cache, CostManager now does a one-off direct Firestore lookup for that model before falling back to the defaults, so an expensive model is never silently billed at 2€/10€. Genuinely-unknown models are negative-cached to avoid per-request Firestore queries.

v3.9.1 — 2026-06-08

Fixed

  • HTTP 500 on legacy / no-prefix model names — the model registry now recognises (a) newer Claude Code 2.x / Agent SDK model ids that drop the claude- prefix (sonnet-4-5, opus-4-7, haiku-4-5, with or without an 8-digit release-date suffix like -20250929) and (b) legacy Claude 3 family names hardcoded by Claude Desktop's connection probe and various Anthropic-compatible clients (claude-3-haiku, claude-3-5-sonnet, claude-3-7-sonnet, claude-3-opus, etc.). Previously, these names fell through to the resolver's pass-through branch and the downstream QuotaMetadata Pydantic validator rejected the unknown internal name, surfacing to clients as an opaque HTTP 500 from /v1/messages. They are now silently routed to the nearest current 4.x equivalent (Vertex AI variants per the existing bare-name defaults).

v3.9.0 — 2026-06-08

Added

  • GET /v1/models — new Anthropic-compatible model discovery endpoint that mirrors the shape of https://api.anthropic.com/v1/models. Returns only Anthropic-family models, each as a single entry using its bare vendor id (e.g. claude-opus-4-7, claude-sonnet-4-6, claude-haiku-4-5) — Bedrock vs Vertex variants are collapsed via the registry's default. Models supporting the 1M context window are also listed with a [1m] suffix (e.g. claude-opus-4-7[1m]). Authentication is required (same auth as /v1/messages). This unblocks Claude Desktop's "Check connection" flow and lets the Code/Cowork tabs auto-discover available Claude models. The existing OpenAI-style GET /models is unchanged and still lists every provider.

v3.8.0 — 2026-06-05

Added

  • Claude Opus 4.8 support — added vertex_ai.anthropic.claude-opus-4-8 (GENAI_SHARED_VERTEXAI_ANTHROPIC_CLAUDE_48_OPUS) and bedrock.anthropic.claude-opus-4-8 (GENAI_SHARED_BEDROCK_ANTHROPIC_CLAUDE_48_OPUS) to the model registry with 1M context support. Bare name claude-opus-4-8 defaults to Vertex AI, consistent with all other Anthropic models.

v3.7.2 — 2026-06-04

Changed

  • Claude code manual configuration — the Claude Code setup guide now marks manual configuration as not mandatory since the installation scripts automatically configure claude with all the needed variables

v3.7.1 — 2026-05-29

Added

  • One-shot Claude Code installation scripts — the Claude Code setup guide now links to downloadable install-claude.sh (macOS) and install-claude.ps1 (Windows) scripts that install Git, Claude Code, and any prerequisites in a single command. Replaces the previous manual winget / native-installer steps.

v3.7.0 — 2026-05-26

Added

  • suggested field on /models — each entry returned by GET /models now carries an optional suggested field whose value is one of OPUS, SONNET, HAIKU, CODEX, DEFAULT, or null. Populated manually from the new Firestore collection model_suggestions (document id = upstream model id, e.g. bedrock.anthropic.claude-opus-4-7). Refresh is piggybacked on the existing 1-hour models cache TTL; invalid or missing values are surfaced as null and /models continues to serve even if Firestore is unreachable.

v3.6.2 — 2026-05-22

Changed

  • Default GPT-5.5 routing to Azure — bare name gpt-5.5 now defaults to Azure (GENAI_SHARED_AZURE_OPENAI_GPT_55) instead of OpenAI. Users can still explicitly target OpenAI via openai.gpt-5.5.

v3.6.1 — 2026-05-21

Changed

  • Cline setup guide rewritten — authentication now uses the API Key field (apikey=<key>&tenantid=<id> format) instead of custom headers, avoiding a known Cline bug where custom headers are intermittently dropped.

v3.6.0 — 2026-05-20

Added

  • Internal bridge support — new USE_UPSTREAM_BRIDGE and UPSTREAM_BRIDGE_URL env vars allow routing all upstream requests through the internal Azure bridge (LiteLLM proxy) instead of the public GenAI Shared Service endpoint. When USE_UPSTREAM_BRIDGE=true, the gateway reads bridgeUrl from Secret Manager or uses UPSTREAM_BRIDGE_URL if set.

v3.5.1 — 2026-05-19

Changed

  • Default Opus 4.7 routing to Vertex AI — bare name claude-opus-4-7 now defaults to Vertex AI instead of Bedrock, consistent with all other Anthropic models.
  • [QUOTA] log downgraded to DEBUG — remaining [QUOTA] tagged message moved from INFO to DEBUG for cleaner production logs.

v3.4.12 — 2026-05-19

Added

  • Streaming diagnostics — added explicit ERROR logs for downstream cancellations, abnormal stream closure, upstream 5xx responses, and incomplete chunked upstream streams, including redacted response headers and request summaries.
  • Claude Opus 4.7 Vertex AI support — added the Vertex AI Opus 4.7 model entry and 1M-context handling.

Changed

  • Long-running stream handling — disabled per-read upstream timeouts for streaming requests while keeping normal request timeouts for non-streaming calls.
  • Default output token ceiling — increased default max token handling to support long coding-agent requests.

v3.5.0 — 2026-05-19

Added

  • Vertex AI Opus 4.7 support — added vertex_ai.anthropic.claude-opus-4-7 (GENAI_SHARED_VERTEXAI_ANTHROPIC_CLAUDE_47_OPUS) to the model registry with 1M context support. Both Bedrock and Vertex variants of Opus 4.7 are now available.
  • Auto-inject 1M context beta — the gateway now automatically injects anthropic-beta: context-1m-2025-08-07 for all models that support 1M context (Opus 4.7, Opus 4.6, Sonnet 4 — both Bedrock and Vertex). No opt-in from the client is required.
  • [1m] suffix stripping — model names with a [1m]/[1M] suffix (e.g., claude-opus-4-7[1m], bedrock.anthropic.claude-opus-4-7[1m], GENAI_SHARED_BEDROCK_ANTHROPIC_CLAUDE_47_OPUS[1m]) are now correctly resolved. The suffix is stripped before lookup so all three naming conventions work.

Changed

  • Log noise reduction[1M-CONTEXT], [TELEMETRY], and [CACHE TOKENS] tagged messages downgraded from INFO to DEBUG. Only [REQUEST COMPLETED] and [REQUEST FAILED] remain at INFO level for production observability.

v3.4.10 — 2026-05-08

Changed

  • Codex setup guide rewritten — simplified to a single-file configuration (config.toml only, auth.json no longer needed). Authentication now uses http_headers in the provider config. Updated recommended model to GPT-5.5.

v3.4.9 — 2026-05-07

Added

  • GPT-5.5 documentation — added GPT-5.5 to the supported models table with OpenAI and Azure provider rows.

v3.4.6 — 2026-04-23

Added

  • Claude Opus 4.7 documentation — added Opus 4.7 to the supported models table and updated the Claude Code setup guide to recommend it as the default pinned Opus model.
  • GPT-5.4 model family pricing update — corrected pricing and context windows for GPT-5.4, GPT-5.4 Mini, and GPT-5.4 Nano based on the latest PwC GenAI Shared Service EMEA model list.

v3.4.5 — 2026-04-21

Added

  • Claude Opus 4.7 support — added bedrock.anthropic.claude-opus-4-7 to the model registry with 1M context support. Bare name claude-opus-4-7 resolves to the Bedrock endpoint.

v3.4.4 — 2026-04-10

Added

  • GPT-5.4 model family support — added gpt-5.4, gpt-5.4-mini, gpt-5.4-nano, and gpt-5.4-pro to the model registry. These models can now be used with any endpoint including /responses (Codex).

v3.4.2 — 2026-03-21

Added

  • Observability logs for 1M context and telemetry enforcement — tagged [1M-CONTEXT] and [TELEMETRY] INFO logs at every decision point for easier debugging in GCP.

Fixed

  • Telemetry no longer blocks returning users — users coming back after idle periods (minutes, hours, or days) now get a grace period instead of being immediately blocked with a stale-heartbeat error.
  • Increased telemetry staleness window from 30 seconds to 5 minutes to reduce false positives.

v3.4.1 — 2026-03-18

Fixed

  • Re-added /gpt4o legacy route — backwards-compatible alias for /chat/completions for reverse proxies that still reference the old path.

v3.4.0 — 2026-03-17

Added

  • Telemetry grace period for new sessions — new API keys now get a 120-second grace period before telemetry enforcement kicks in. This allows Claude Code's OTEL exporter time to send its first heartbeat batch, eliminating false 403 errors on session startup. The grace period is configurable via TELEMETRY_GRACE_PERIOD_SECONDS.

v3.3.0 — 2026-03-16

Added

  • 1M context window support — Claude Opus 4.6 and Claude Sonnet 4 now support a 1M-token context window (5x the standard 200K limit). To enable it, remove CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS from your settings.json and select "Opus (1M context)" in the model picker (/model). The gateway whitelists the context-1m-2025-08-07 beta header and forwards it to the upstream provider; all other beta headers are silently dropped.
  • Extended context pricing — requests exceeding 200K input tokens are automatically billed at 2x the standard input rate, consistent with Anthropic's pricing for 1M context. The cost multiplier applies to both regular and cache-aware cost calculations.

Changed

  • CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS is now optional — this variable was previously required in the settings.json example. It is no longer needed for most users. The gateway selectively whitelists supported beta features instead of requiring clients to suppress all betas.

v3.2.4 — 2026-03-05

Fixed

  • Budget display showing 300€ instead of 200€ — the /quota/status endpoint was including quota rules from other organisations in a user's matching_entities when calculating the monthly limit. The previous fix (filter=False) was too broad: it stopped filtering out the global base rule (correct) but also stopped filtering out cross-org rules (incorrect). The aggregation now correctly includes only base rules (orgId=null), global rules (orgId="*"), and rules scoped to the user's current organisation.

v3.2.3 — 2026-03-05

Changed

  • Default Opus/Sonnet routing to Vertex AI — bare model names (e.g. claude-opus-4-6) now default to Vertex AI instead of Bedrock. Bedrock often lags behind in supporting new Anthropic features (e.g. deferred tool loading). Users can still explicitly request a Bedrock model via the full upstream ID.

v3.2.2 — 2026-03-04

Fixed

  • Budget display showing 100€ instead of 200€ — the /quota/status endpoint (used by the status line and Budget MCP server) was incorrectly filtering out the global base rule when calculating the limit, showing half the actual cap. Quota enforcement was already correct; this was a display-only bug.

v3.2.1 — 2026-03-04

Fixed

  • Model resolution fallback — the /chat/completions endpoint now accepts the model field in the request body as a fallback when the model-name header is not provided, aligning with OpenAI API conventions.
  • Default max_tokens — when clients omit max_tokens or send 0, the gateway now defaults to 8192 tokens, preventing upstream validation errors.

Important: Enforcement Starting Monday 9th March 2026

Action Required — Access Will Be Blocked from Monday 9 March 2026

Starting Monday 9 March 2026, the PROD environment enforces the following policies. If you do not act before this date, your access will be blocked.

What changes

ConditionResult
Request made with a personal @pwc.com API key403 Forbidden — blocked immediately
Claude Code without telemetry configured (missing OTEL_RESOURCE_ATTRIBUTES)403 Telemetry not configured — blocked immediately

What you need to do

  1. Get an API key from the Get Access page — personal @pwc.com keys are no longer accepted.
  2. Configure telemetry — add your PwC email to OTEL_RESOURCE_ATTRIBUTES in ~/.claude/settings.json:
"OTEL_RESOURCE_ATTRIBUTES": "user.email=name.surname@pwc.com"

See the Claude Code guide for the full telemetry setup.

Already configured telemetry but still getting errors? Restart Claude Code — it may take a few seconds for the new session to register. If errors persist after restarting, double-check that OTEL_RESOURCE_ATTRIBUTES is correctly set and that you are using an API key from the Get Access page.


v3.2.0 — 2026-03-02

Added

  • Personal API-key blocking — the gateway can now reject requests made with personal @pwc.com API keys. Use an API key from the Get Access page to avoid disruption.
  • Telemetry enforcement for Claude Code — Claude Code users can be required to have an active telemetry configuration before accessing the gateway, helping ensure usage visibility and compliance.

Both features are opt-in and disabled by default; existing users are not affected.

Improved

  • Three model naming formats accepted — the gateway now documents and cross-references all three accepted model ID formats: vendor name (e.g., claude-sonnet-4-6), PwC GenAI Shared Service convention (e.g., bedrock.anthropic.claude-opus-4-6), and Coding Agents Gateway convention (e.g., GENAI_SHARED_BEDROCK_ANTHROPIC_CLAUDE_46_OPUS). See the Available Models page for the full list.
  • Simplified telemetry configurationOTEL_RESOURCE_ATTRIBUTES now only requires user.email=name.surname@pwc.com. Team and department are resolved automatically; no manual team.id or department values needed.
  • Actionable telemetry error messages — when telemetry is missing or stale, the 403 response now tells you exactly what to set and that access will be restored within ~1 minute of restarting Claude Code.
  • Faster telemetry recovery — after configuring telemetry and restarting Claude Code, access is restored in approximately 1 minute (reduced from up to 5 minutes in previous versions).

v3.1.0 — 2026-02-27

Added

  • Interactive API reference — browse all endpoints, view request/response schemas, and try calls directly in your browser at /docs (Swagger UI) or /redoc (ReDoc).
  • The Swagger "Authorize" dialog accepts both auth formats (Bearer token or separate api-key + tenant-id headers) so you can test calls without leaving the browser.

v3.0.2 — 2026-02-26

Improved

  • Model name resolution is now more flexible: the gateway correctly handles model IDs regardless of which naming convention your client sends.

v3.0.1 — 2026-02-25

Fixed

  • Some model IDs that were previously unrecognised by the gateway are now resolved correctly.

v3.0.0 — 2026-02-25

Changed

  • Reduced latency and improved reliability — internal routing optimisations eliminate a class of startup failures that affected some users.
  • The /v1/models endpoint now returns an accurate, up-to-date list of available models.