Changelog
v3.13.0 — 2026-07-29
Added
-
Per-task model preferences API. New authenticated endpoints
GET /v1/model-preferencesandPUT /v1/model-preferenceslet a user read and replace theirauto-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,autoitself is rejected, and restricted models (Fable, GPT-5.6 Sol) require entitlement (403 otherwise). Preferences are stored in the existing FirestoremodelPreferencefield (same one the telemetry attest hook writes andselect_modelreads), 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'sMODEL_PREFERENCEenv telemetry could set it. -
autofooter moved into the gateway. When a request comes in withmodel="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 beforedata: [DONE]). The footer is skipped for explicit model selections, for completions that produced no text, and for intermediate agent-loop steps ending infinish_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-solfunction tools fixed on/chat/completions. The upstream rejects function tools unlessreasoning_effortis explicitly"none". When a client sends tools without any explicit effort (OpenCode's default request shape), the gateway now injectsreasoning_effort="none"; an explicit effort in the body orreasoningheader is always respected. -
bonsailisted in model discovery. The synthetic PwC Agent entries in/modelsand/v1/modelsnow includebonsai(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
autoclassifier now ignoresrole=usermessages made exclusively of Anthropictool_resultblocks 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 tosimple_text/easy → qwen-fastafter 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-solfailed with 400integer_below_min_value. Claude Code validates a model switch with a one-token/v1/messagesprobe; GPT-5.6 Sol, like the Azure Foundry models, requiresmax_output_tokens >= 16. The gateway now clamps the probe to 16 foropenai.eu.gpt-5.6-solas well as DeepSeek and Kimi. -
/modelsslash command failed withContains 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 invokescurlthrough 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 400integer_below_min_value. Claude Code probes the target model with a 1-token/v1/messagesrequest when switching; Azure Foundry requiresmax_output_tokens >= 16. The gateway now clampsmax_tokensto 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_FABLEin quota-management-lib andmodels_costs; the registry internal name (SELFHOSTED_FABLE_5) is renamed to match.claude-fable-5now 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. -
/modelsslash command for Claude Code.GET /downloads/models-command.mdserves a ready-made Claude Code slash command: install withcurl -fsSL <gateway>/downloads/models-command.md -o ~/.claude/commands/models.md, then type/modelsin Claude Code to get a table of the models available to your key.
Changed
- Restricted models are hidden from non-entitled users.
claude-fable-5andgpt-5.6-sol(and their upstream aliases, e.g.openai.eu.gpt-5.6-sol) appear in/modelsand/v1/modelsonly when the caller's API key is in the entitlement allowlist (fable-keyssecret, 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 viaRESTRICTED_MODELS/RESTRICTED_KEYS_SECRETenv 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
QuotaMetadatarejected an unknown internal model ID — the call proceeded with unlimited, unattributed spend. The preflight now raises403 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 Firestoremodels_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 realmodels_costsdocuments. Note:claude-fable-5(SELFHOSTED_FABLE_5) has no whitelist entry yet — it returns 403model_not_quota_enableduntil 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_starttwice — the Anthropic SDK treats the duplicate as a protocol violation and hangs. Fixed by deduplicatingmessage_startevents instream_anthropic_response: after the firstmessage_startis 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 addingextra_body: {chat_template_kwargs: {enable_thinking: false}}to the GLM entry inlitellm-orchestrator/server/config.yaml(mirrors the existing qwen-fast setting). -
Default
temperature: 0.15broke reasoning models on/chat/completions.ChatCompletionRequest.temperaturedefaulted to0.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 nowNone: withmodel_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-solrejects function tools on/chat/completionsunlessreasoning_effortis"none", but the gateway dropped the value twice: thereasoningheader only acceptedlow|medium|high, and a body-levelreasoning_effortwas silently discarded by Pydantic (field not declared). Both paths fixed: the header now acceptsnone, andChatCompletionRequestdeclaresreasoning_effortso body values are forwarded (header still wins when present). This unblocks OpenCode'svariant=nonefor GPT-5.6 Sol. -
Claude Code + DeepSeek-V4-Pro / Kimi-K2.6 returned 400 (
reasoning.effort not supported). Claude Code always sendsthinking: {type: enabled}on/v1/messages; litellm translates it toreasoning.effortfor 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 stripsthinkingfor models in the newTHINKING_UNSUPPORTEDregistry set before forwarding. -
Non-UUID
x-request-idcaused 500.quota-management-librequiresQuotaMetadata.requestIdto be a valid UUID v4, but the middleware accepted any client-providedx-request-idverbatim — a value liketest-123blew up quota preflight with an unhandledValidationError. The middleware now validates the header and regenerates a freshuuid4when 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).
QuotaMetadatafromquota-management-libvalidates 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 PydanticValidationErroratcheck_quota_preflightbefore the request reaches the model. Fixed by wrappingQuotaMetadata(...)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: updatequota-management-libto 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:
SafeLoggingMiddlewareextended Starlette'sBaseHTTPMiddleware, whosecall_next()runs the inner app in an anyio task group. Exceptions raised inside that task group (includingUpstreamErrorfrom the litellm-router's FABLE_KEYS check) were wrapped inExceptionGroupby anyio before reaching FastAPI'sExceptionMiddleware. Theexcept Exception:block inSafeLoggingMiddleware.dispatch()then caught theExceptionGroupand returned a generic 500 — bypassing bothupstream_error_handlerandall_exception_handler. RewrittenSafeLoggingMiddlewareas a pure ASGI middleware (noBaseHTTPMiddleware, 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 byExceptionMiddlewarebefore reaching this middleware.
v3.12.32 — 2026-07-23
Fixed
-
UpstreamErrorpropagated as 500 instead of the router's original status code.UpstreamError(raised byCloudRunClient.forward_requestwhen the upstream returns 4xx/5xx) was being caught by the genericExceptionhandler (all_exception_handler) which always returned 500. Added a dedicatedupstream_error_handlerregistered 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_responsecaughtUpstreamErroron 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 fromresponse_content.error.messageand 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, theauth_override(apikey=USER_KEY&tenantid=TID) is no longer applied to theAuthorizationheader — the staticUPSTREAM_API_KEYfrom 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). WhenUSE_UPSTREAM_BRIDGE=False(direct EMEA access),auth_overrideis 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
modelname (e.g."modello-inesistente"), the model registry performs a pass-through (intentional, for unknown variants) and setsModelEntry.is_passthrough=True. The three route handlers (/v1/messages,/chat/completions,/responses) now check this flag and raiseHTTPException(422, code="unknown_model")with a message pointing toGET /models. Previously the pass-through reached the cost calculator which raised an unhandled exception → 500.pick_auto_modelis unaffected: it already skips unknown candidates silently.
v3.12.29 — 2026-07-23
Fixed
CloudRunClient.forward_request/streammissingauth_overrideparameter. The completion modules callforward_request(..., auth_override=auth_override)on whatever client is active (EMEA or self-hosted).UpstreamClientaccepts the kwarg (and uses it for per-user billing), butCloudRunClientdid not declare it, causingTypeErroron every self-hosted model request (GLM, Qwen, DS4, Fable). Addedauth_override: str | None = Noneto bothforward_requestandforward_streamonCloudRunClient; the parameter is accepted and ignored (CR auth is always via GCP identity token).
v3.12.28 — 2026-07-23
Fixed
openaimissing from dependencies (CrashLoopBackOff in DEV).utils/task_classifier.pyimportsopenai.AsyncOpenAIfor the Gemini and GLM classifier clients, butopenaiwas not declared inpyproject.toml. Addedopenai>=1.0.0to fix theModuleNotFoundErroron startup.
v3.12.27 — 2026-07-21
Changed
- Architecture: sonnet/opus served directly by the gateway (not via litellm-router).
sonnet-4-6andopus-4-7are removed from the litellm-router'sconfig.yaml. The gateway now serves them directly viaupstream_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_ALIASESto their EMEA vertex_ai upstream IDs;ModelEntryobjects andPWC_AGENT_UPSTREAM_IDSentries 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 viaawait try_compress(body)before everyforward_request/forward_streamin all three completion modules. No-op ifheadroom-ai[ml]is not installed orHEADROOM_ENABLED=false. Removesheadroom_wrapper.pyfrom litellm-router and theheadroom_wrapper.headroom_callbackentry fromconfig.yaml. UpstreamClient.forward_request/streamacceptauth_override: str | None. When set, the per-requestAuthorizationheader overrides the client-level static key for that single call (per-user EMEA billing). Ignored byCloudRunClient(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 fromcustom_auth.py.sonnet-4-6andopus-4-7model entries, their fallbacks (opus↔sonnet), andcontext_window_fallbacksfromconfig.yaml.
Tests
- Gateway: 63 tests pass (was 62). Updated
TestModelRegistryAutoRouting:sonnet-4-6/opus-4-7short names now assert EMEA routing (not pwc_agent). Addedtest_auto_model_self_hosted_routes_via_pwc_agentandtest_auto_model_emea_routes_via_upstream_clientreplacing the oldtest_all_auto_model_outputs_route_via_pwc_agent. - litellm-router: 33 tests pass (was 42). Removed
TestPerUserCredentials,TestRequestTenantId, and_GENAI_SHARED_MODELSassertions fromTestModelSets.
v3.12.26 — 2026-07-21
Fixed
- Model registry routing for
sonnet-4-6/opus-4-7(Bug 1). Whenmodel="auto"resolved to"sonnet-4-6"or"opus-4-7", these requests were incorrectly routed directly to GenAI Shared EMEA viaupstream_client(static team key) instead of going throughpwc_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 correctpwc_agentModelEntryregistered 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"butcustom_auth.pyin the litellm-router reads"x-api-key"and"x-tenant-id". Per-user EMEA credentials forsonnet-4-6/opus-4-7were silently dropped, causing all requests to use the teamGENAI_SHARED_API_KEYinstead 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_idheader 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, anduser_api_key_authlane resolution.
v3.12.25 — 2026-07-21
Fixed
model="auto"classification for Anthropic Messages API. Theclassify()call in/v1/messageswas passing raw Pydantic objects instead of dicts, causing the task classifier to always fall back tosimple_text/easy → qwen-fast. All messages are now serialised with.model_dump()before classification.
Added
AUTO_MODEL_ENABLEDguard. When the env varAUTO_MODEL_ENABLED=False, requests withmodel="auto"are rejected with HTTP 400 across all three routes (/v1/messages,/chat/completions,/responses). Defaults toTrue.- 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'spublicUrl(bypassing the bridge), so it always hits the real EMEA endpoint.CLASSIFIER_GLM_URL/CLASSIFIER_GLM_KEYcan still be set via env to configure the GLM fallback. routing_decisionpropagated to litellm-router. Formodel="auto"calls routed viapwc_agent_client, the classifier decision is now embedded in the request body'smetadata.routing_decisionfield before forwarding. This lets the litellm-router'scustom_auth._log_usagebuild the correct BQ note (auto:task/difficulty->model cls=… cache=…)./responsesroute:pwc_agent_clientrouting +model="auto"support. The OpenAI Responses API endpoint now routes self-hosted models (glm,qwen-fast, etc.) viapwc_agent_clientand supportsmodel="auto"(classifier reads from the Responses APIinputfield).- Deployment config: classifier env vars. Added
CLASSIFIER_GLM_URL,CLASSIFIER_GLM_KEY,AUTO_MODEL_ENABLEDtoConfig/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 sendsmodel: 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 dedicatedCloudRunClientwith 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-solOpenAI model to the registry and models table, alongside the existinggpt-5.6-lunaandgpt-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.shnow installs the status line automatically (macOS/Linux). Many users lost the status line after it silently disappeared (script never installed it, or a stale/missingstatusline.shbroke silently). The installer now downloadsstatusline.shto~/.claude/statusline.shand registers it viastatusLineinsettings.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 providingbash/curl/jqonPATHat 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, onceREQUIRE_TELEMETRYis enabled, every request is rejected withhook_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.ps1now installs and registers this hook instead of the bash one, and no longer needsjq. install-claude.ps1could hang onwinget upgrade. When Git or Claude Code was already installed, the upgrade check ran inline with no timeout — on some machineswinget upgradestalls 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.ps1could fail to parse on Windows with aTerminatorExpectedAtEndOfStringerror. Both scripts contained em-dash characters (—) in comments/strings. Windows PowerShell 5.1 (powershell.exe) reads a.ps1file 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 themodelfield is rewritten); no quota/cost impact, matching Anthropic's own free semantics for this endpoint.
v3.12.20 — 2026-07-14
Added
blockableexemption flag for API keys. A new optionalblockablefield onAPI_KEYSFirestore 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 ortrue→ unchanged, normal enforcement.
v3.12.19 — 2026-07-13
Added
- GPT-5.6 Luna & Terra support (EU). Added the new
gpt-5.6-lunaandgpt-5.6-terraOpenAI 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 leftutils/auth.pyimporting the removedget_telemetry_last_seensymbol and using the old heartbeat-based telemetry check. Replaced withenforce_attestation(attestation pattern). Also restored the missingtelemetryrouter inapp.pyso/v1/telemetry/attestis 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 realTIMESTAMP created_ts, pre-filtered tosdlc_llms) instead of the raw, unpartitioned, STRING-timestampusage_monitoring_revenuestable. 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.shandtelemetry-attest-hook.share now served dynamically: the gateway injects the correct base URL at request time from theHostheader, 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_ENABLEDmaster switch. New environment variable to enable/disable L2 telemetry reconciliation via the release. Whenfalse, the/v1/telemetry/reconcileendpoint 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 totrue.
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
telemetryBlockedAtwas 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 detectsfloor >= win_endexplicitly, 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] startline (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] BLOCKEDline, adry-run/enforcement-off … NOT blockedline when applicable, and a[RECONCILE] donesummary (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-idresponse header for OTEL reconciliation. The gateway now sets arequest-idresponse header on every successful LLM call, equal to therequestIdwritten tousage_monitoring_revenues. This enables a 1:1 join betweenotel_events.attributes.request_idandusage_monitoring_revenues.requestIdfor Claude Code telemetry reconciliation.
v3.11.2 — 2026-06-26
Fixed
- NameError in non-streaming path (
total_input_tokensundefined). The v3.11.1 cleanup of theextended_contextblock accidentally removed thetotal_input_tokensvariable while a log statement still referenced it, causing HTTP 500 on all non-streaming requests (including the Claude Code gateway probe withclaude-3-haiku).
v3.11.1 — 2026-06-26
Added
- Sonnet 4.6 1M context support.
claude-sonnet-4-6now supports the full 1M context window at standard pricing. The gateway automatically injects theanthropic-beta: context-1m-2025-08-07header for Sonnet 4.6 requests, and/v1/modelsnow exposes aclaude-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_contextvariable) 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-6→bedrock.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 helperget_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
CostManagerprice cache was loaded once from Firestoremodels_costsat application startup and never reloaded. Any model added tomodels_costsafter 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 continuousModel '…CLAUDE_48_OPUS' not found in cachewarning.
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 to0to disable. - On-demand fetch-through on cache-miss. When a model isn't in the cache,
CostManagernow 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 downstreamQuotaMetadataPydantic 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 ofhttps://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-styleGET /modelsis 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) andbedrock.anthropic.claude-opus-4-8(GENAI_SHARED_BEDROCK_ANTHROPIC_CLAUDE_48_OPUS) to the model registry with 1M context support. Bare nameclaude-opus-4-8defaults 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) andinstall-claude.ps1(Windows) scripts that install Git, Claude Code, and any prerequisites in a single command. Replaces the previous manualwinget/ native-installer steps.
v3.7.0 — 2026-05-26
Added
suggestedfield on/models— each entry returned byGET /modelsnow carries an optionalsuggestedfield whose value is one ofOPUS,SONNET,HAIKU,CODEX,DEFAULT, ornull. Populated manually from the new Firestore collectionmodel_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 asnulland/modelscontinues 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.5now defaults to Azure (GENAI_SHARED_AZURE_OPENAI_GPT_55) instead of OpenAI. Users can still explicitly target OpenAI viaopenai.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_BRIDGEandUPSTREAM_BRIDGE_URLenv vars allow routing all upstream requests through the internal Azure bridge (LiteLLM proxy) instead of the public GenAI Shared Service endpoint. WhenUSE_UPSTREAM_BRIDGE=true, the gateway readsbridgeUrlfrom Secret Manager or usesUPSTREAM_BRIDGE_URLif set.
v3.5.1 — 2026-05-19
Changed
- Default Opus 4.7 routing to Vertex AI — bare name
claude-opus-4-7now 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-07for 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.tomlonly,auth.jsonno longer needed). Authentication now useshttp_headersin 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-7to the model registry with 1M context support. Bare nameclaude-opus-4-7resolves 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, andgpt-5.4-proto 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
/gpt4olegacy route — backwards-compatible alias for/chat/completionsfor 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_BETASfrom yoursettings.jsonand select "Opus (1M context)" in the model picker (/model). The gateway whitelists thecontext-1m-2025-08-07beta 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_BETASis now optional — this variable was previously required in thesettings.jsonexample. 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/statusendpoint was including quota rules from other organisations in a user'smatching_entitieswhen 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/statusendpoint (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/completionsendpoint now accepts themodelfield in the request body as a fallback when themodel-nameheader is not provided, aligning with OpenAI API conventions. - Default max_tokens — when clients omit
max_tokensor send 0, the gateway now defaults to 8192 tokens, preventing upstream validation errors.
Important: Enforcement Starting Monday 9th 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
| Condition | Result |
|---|---|
Request made with a personal @pwc.com API key | 403 Forbidden — blocked immediately |
Claude Code without telemetry configured (missing OTEL_RESOURCE_ATTRIBUTES) | 403 Telemetry not configured — blocked immediately |
What you need to do
- Get an API key from the Get Access page — personal
@pwc.comkeys are no longer accepted. - Configure telemetry — add your PwC email to
OTEL_RESOURCE_ATTRIBUTESin~/.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_ATTRIBUTESis 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.comAPI 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 configuration —
OTEL_RESOURCE_ATTRIBUTESnow only requiresuser.email=name.surname@pwc.com. Team and department are resolved automatically; no manualteam.idordepartmentvalues 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-idheaders) 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/modelsendpoint now returns an accurate, up-to-date list of available models.