From 5876b826a89c13367024d5b47cf9d9d95f3a7211 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 22 May 2026 16:46:29 +0000 Subject: [PATCH] fix(purview): use post-await timestamp for cache TTL Capture the timestamp after the network call completes when storing it as the cache freshness marker, so the effective TTL reflects when the response was actually received rather than when the request started. Under high network latency the previous behavior shortened the effective cache lifetime. Co-authored-by: Yassin Kortam --- .../guardrails/guardrail_hooks/microsoft_purview/base.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/base.py b/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/base.py index 0fe505ee18a..ea57cf1591a 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/base.py +++ b/litellm/proxy/guardrails/guardrail_hooks/microsoft_purview/base.py @@ -110,8 +110,10 @@ class PurviewGuardrailBase: token_data = response.json() access_token = token_data["access_token"] expires_in = int(token_data.get("expires_in", 3599)) + # Recompute ``now`` after the await so the expiry reflects when the + # token was actually received, not when the request started. with self._cache_lock: - self._token_cache = (access_token, now + expires_in) + self._token_cache = (access_token, time.time() + expires_in) verbose_proxy_logger.debug( "Purview: acquired new OAuth2 token (expires_in=%ds)", expires_in ) @@ -188,8 +190,11 @@ class PurviewGuardrailBase: response_json, response_headers = await self._graph_post(url, body) etag = response_headers.get("etag", response_headers.get("ETag", "")) + # Recompute ``now`` after the await so the TTL reflects when the + # scope response was actually received, not when the request started. + fetched_at = time.time() with self._cache_lock: - self._scope_cache[user_id] = (etag, response_json, now) + self._scope_cache[user_id] = (etag, response_json, fetched_at) # Move refreshed entry to the end so it is treated as most-recently-used. # OrderedDict.__setitem__ preserves existing insertion order for known # keys, so an explicit move_to_end() call is required.