Repository navigation
fix(caching): write the response-cache SET to Redis at once instead of on the post-call batch - #43973
Conversation
…f on the post-call batch The post-call Redis batch only goes out after every success callback finishes or the 1s deadline, so an identical request sent right after the first response missed the cache and went to the provider again. Response-cache writes go straight to Redis again, the counters, rate limits, TPM and slot releases keep riding the post-call batch Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
|
… strict ruff gate Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
| from .base_cache import BaseCache | ||
| from .disk_cache import DiskCache | ||
| from .dual_cache import DualCache | ||
| from .dual_cache import DualCache # noqa: F401 # re-exported, callers import DualCache from litellm.caching.caching |
…ject_key_prefix * upstream/main: (62 commits) fix(guardrails): scan Responses API input in Azure Prompt Shield (BerriAI#43786) feat(lens): investigate sampled traces and retain batch results (BerriAI#43942) fix(proxy): restore pre-config-wins handling of pass-through endpoints (BerriAI#43962) fix(cost-map): raise baseten DeepSeek-V4.1-Flash max output to 262144 (BerriAI#43916) chore(cost-map): add deprecation date for anthropic claude-sonnet-4-5 (BerriAI#43898) chore(cost-map): add fireworks inkling priority prices from the prices api (BerriAI#43949) feat(guardrails): honor litellm_params.timeout in every HTTP guardrail (BerriAI#43134) test(e2e): typed per-test metadata for the e2e suite (BerriAI#42044) fix(caching): write the response-cache SET to Redis at once instead of on the post-call batch (BerriAI#43973) feat(ui): filter tags by name and description on the Tag Management page (BerriAI#42949) feat(providers): add Cortecs as an OpenAI-compatible provider (BerriAI#43872) feat(e2e): record each e2e test's steps, starting with ProxyClient (BerriAI#42393) test(ci): repair stale tests and move retired OpenAI text-completion fixtures (BerriAI#43958) feat(proxy): record in spend logs whether a request used a client-forwarded Anthropic OAuth token (BerriAI#43063) fix(azure_storage): keep the DataLakeServiceClient alive until its TTL elapses (BerriAI#43082) chore(deps): bump gitpython and tornado, extend diskcache osv ignore to Nov 1 (BerriAI#43961) fix(guardrails): treat an unknown straiker api_version as unset instead of skipping the guardrail (BerriAI#43956) fix(azure_storage): name Data Lake objects without base64 padding or slashes (BerriAI#43914) fix(grayswan): send request conversation and tool calls to post-call monitor (BerriAI#43770) chore(cost-map): sync openrouter prices from the models API (BerriAI#43950) ...
TLDR
Problem this solves:
/v1/messagesrequest sent right after the first misses the cachetest_cache_hit_post_call_reject_keeps_deployment_labels_anthropicin CircleCI integration-extensions on mainHow it solves it:
User Flow
Before: a developer behind a proxy with a Redis cache and a slow success callback pays for the same answer twice
msg_Abackmsg_B, billed againAfter: the second request is served from the cache
msg_Abackmsg_Acomes back from the cache, no second provider callAffected release
Regression in v1.105.0-dev.1 (introduced by #43779)
Pre-Submission checklist
uv run pytest tests/unit/<your_test_file>.py -vScreenshots / Proof of Fix
Live proxy, real Redis at 127.0.0.1:6379, real Anthropic and OpenAI calls, and a success callback that sleeps 0.5s (the shape of a slow logging integration)
Each run sends two back-to-back identical requests with a fresh random marker in the prompt, so every run starts with a cold key. A cache hit returns the first call's provider id, a miss returns a new one
Before (424bfd8, merge base)
/v1/messages
msg_011Cfaknn3vifvxKf2aDK6k9, call 2msg_011CfaknqY2AbmJFpM1ec3P6, a second provider callmsg_011CfakntqTc9jb75mu3EmmQ, call 2msg_011CfaknwFKLkKv5ATVtdxxP, a second provider callmsg_011CfaknypbuZtmC7MoFrCFv, call 2msg_011Cfako2Ndvbt4UJLJg1KAG, a second provider call/v1/chat/completions
chatcmpl-EU19GJrS2fJXeeQKFLrIyiG5SgMwA, call 2 carriesx-litellm-cache-key, cache hitchatcmpl-EU19HGgJQKro90iE8tcmyExQDnHkM, cache hitchatcmpl-EU19ILT3hqgtPB4f4n7fS5gQOc6S9, cache hitAfter (e791607)
/v1/messages
msg_011CfamqVedscTbbbtPjbQ1f, cache hitmsg_011CfamqYUnzFmpgVQwajbR3, cache hitmsg_011CfamqbLwaRxu5YBXZ2mhb, cache hit/v1/chat/completions
chatcmpl-EU1MVgHhyCzsckKQWHSm5GGSA62Wh, call 2 carriesx-litellm-cache-key, cache hitchatcmpl-EU1MVeK8gFd0x4E2xesvuqP6458RC, cache hitchatcmpl-EU1MW49WX0hjDM8UjvleUvG3SqoAR, cache hitIntegration suite
tests/integration/observability/test_cache_hit_guardrail_metrics.pyagainst a local proxy, Postgres, Redis and the scripted upstreamtest_cache_hit_post_call_reject_keeps_deployment_labels_anthropicfails withassert 2 == 1provider requestsUnit tests and mutation check
tests/unit/caching: 560 passed, 1 skippedcaching.pyanddual_cache.pycopied back in, both new tests intest_request_redis_batch_post_call.pyfail (response not in Redis before the post-call flush)Type
🐛 Bug Fix
Caveats (if any)
Low
/v1/messagesmissed/v1/responsescovered by the integration suite only, it passed on both sides_slot_stashline in the test file got reflowed byruff formatFinal Attestation
Link to Devin session: https://app.devin.ai/sessions/ca842b11eda64990a199ffbd982d8826
Open in Devin Desktop: https://app.devin.ai/desktop/session/ca842b11eda64990a199ffbd982d8826?variant=devin
Requested by: @yassin-berriai