diff --git a/litellm/proxy/anthropic_endpoints/gateway_endpoints.py b/litellm/proxy/anthropic_endpoints/gateway_endpoints.py index 991dc67ab82..cc3106fce53 100644 --- a/litellm/proxy/anthropic_endpoints/gateway_endpoints.py +++ b/litellm/proxy/anthropic_endpoints/gateway_endpoints.py @@ -80,6 +80,12 @@ class _AccessTokenBody(BaseModel): token_type: str = "Bearer" +class _ManagedSettingsBody(BaseModel): + uuid: str + checksum: str + settings: dict[str, object] + + def _general_settings() -> Mapping[str, object]: from litellm.proxy.proxy_server import general_settings @@ -333,12 +339,14 @@ async def managed_settings(request: Request) -> Response: if settings is None: return Response(status_code=404) - body: Final = json.dumps(settings, sort_keys=True, separators=(",", ":")) - etag: Final = '"' + hashlib.sha256(body.encode("utf-8")).hexdigest() + '"' + canonical: Final = json.dumps(settings, sort_keys=True, separators=(",", ":")) + checksum: Final = "sha256:" + hashlib.sha256(canonical.encode("utf-8")).hexdigest() + etag: Final = f'"{checksum}"' headers: Final = MappingProxyType({"ETag": etag}) if request.headers.get("If-None-Match") == etag: return Response(status_code=304, headers=headers) - return Response(content=body, media_type="application/json", headers=headers) + body: Final = _ManagedSettingsBody(uuid=checksum, checksum=checksum, settings=settings) + return Response(content=body.model_dump_json(), media_type="application/json", headers=headers) def _accept_otlp() -> Response: diff --git a/tests/test_litellm/proxy/anthropic_endpoints/test_gateway_endpoints.py b/tests/test_litellm/proxy/anthropic_endpoints/test_gateway_endpoints.py index c0a39b95c40..158fe253796 100644 --- a/tests/test_litellm/proxy/anthropic_endpoints/test_gateway_endpoints.py +++ b/tests/test_litellm/proxy/anthropic_endpoints/test_gateway_endpoints.py @@ -327,18 +327,35 @@ def test_managed_settings_404_when_unset(): assert resp.status_code == 404 -def test_managed_settings_returns_json_with_etag_and_304(): +def test_managed_settings_returns_client_envelope_and_304_on_cached_checksum(): settings = {"permissions": {"defaultMode": "acceptEdits"}, "env": {"FOO": "bar"}} with _gateway_env(managed_settings=settings) as (client, _): resp = client.get("/claude_code_gateway/managed/settings") assert resp.status_code == 200 - assert resp.json() == settings - etag = resp.headers["ETag"] - assert etag + body = resp.json() + assert body["settings"] == settings + checksum = body["checksum"] + assert checksum.startswith("sha256:") + assert body["uuid"] == checksum + assert resp.headers["ETag"] == f'"{checksum}"' - not_modified = client.get("/claude_code_gateway/managed/settings", headers={"If-None-Match": etag}) - assert not_modified.status_code == 304 - assert not_modified.headers["ETag"] == etag + not_modified = client.get( + "/claude_code_gateway/managed/settings", headers={"If-None-Match": f'"{checksum}"'} + ) + assert not_modified.status_code == 304 + assert not_modified.headers["ETag"] == f'"{checksum}"' + + stale = client.get("/claude_code_gateway/managed/settings", headers={"If-None-Match": '"sha256:stale"'}) + assert stale.status_code == 200 + assert stale.json()["checksum"] == checksum + + +def test_managed_settings_checksum_tracks_policy_content(): + with _gateway_env(managed_settings={"env": {"FOO": "bar"}}) as (client, _): + first = client.get("/claude_code_gateway/managed/settings").json()["checksum"] + with _gateway_env(managed_settings={"env": {"FOO": "baz"}}) as (client, _): + second = client.get("/claude_code_gateway/managed/settings").json()["checksum"] + assert first != second def test_managed_settings_404_when_gateway_disabled():