feat(ui): configure Anthropic automatic prompt caching from the Admin UI - #33581
Conversation
Greptile SummaryThis PR adds Admin UI controls for
Confidence Score: 5/5Safe to merge — the change is additive and well-tested, with no modifications to existing request-path logic. The core logic changes (validation dispatch, reset defaults, stored_in_db comparison) are all correct and covered by dedicated unit tests. Both new fields are enrolled in LITELLM_SETTINGS_SAFE_DB_OVERRIDES with a structural test that prevents the two registries from drifting. The UI properly narrows accessToken to non-null before reaching PromptCachingPanel, and the Prompt Caching tab correctly calls deleteConfigFieldSetting when the TTL is cleared. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/constants.py | Adds enable_anthropic_prompt_caching and anthropic_prompt_caching_ttl to LITELLM_SETTINGS_SAFE_DB_OVERRIDES with a clear comment explaining the invariant; no issues found. |
| litellm/proxy/_types.py | Adds field_options and field_tab optional fields to ConfigList; also reorders two noqa: E402 imports. Clean and non-breaking. |
| litellm/proxy/proxy_server.py | Extends the general-settings UI registry with typed specs (Float/Boolean/Select), fixes validation to dispatch on declared type, fixes reset to restore per-type defaults, and fixes stored_in_db to compare against per-type defaults. All logic is correct and well-tested. |
| tests/test_litellm/proxy/test_proxy_server.py | Adds comprehensive mock-only tests covering the config list, validation, persistence, propagation, and reset paths for both new caching fields; includes a structural invariant test that the UI-editable field set stays in sync with LITELLM_SETTINGS_SAFE_DB_OVERRIDES. |
| ui/litellm-dashboard/src/app/(dashboard)/router-settings/_components/general_settings.tsx | Adds PromptCachingPanel (immediate-save toggle + TTL dropdown), SettingValueEditor (shared typed editor), and hides caching fields from the General table. accessToken is correctly narrowed to non-null before PromptCachingPanel is rendered. |
| ui/litellm-dashboard/eslint-suppressions.json | Reduces no-nested-ternary suppression count from 3 to 1, reflecting the replacement of the ternary chain with SettingValueEditor. |
| ui/litellm-dashboard/src/lib/http/schema.d.ts | Adds field_options and field_tab optional fields to the TypeScript schema, matching the Pydantic model changes. |
Reviews (2): Last reviewed commit: "fix(proxy): propagate the caching flag a..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
a09f3fc to
786807d
Compare
This comment was marked as outdated.
This comment was marked as outdated.
786807d to
9245e84
Compare
Register enable_anthropic_prompt_caching and anthropic_prompt_caching_ttl on the General Settings table so caching can be turned on without hand-writing config. The registry could not express either field: validation was hardcoded to a float in (0, 1], reset set every field to None (not a bool for a boolean flag), and the listing reported any non-None value as 'In Config', which a False default would always trip. Validation now dispatches on the declared type and reset restores each field's own default. ConfigList carries field_options so the table can render a Select for enums instead of no editor at all.
The value cell was a ternary chain over field_type; adding Select made it a fourth level and tripped no-nested-ternary. Early returns read better than a deeper chain and let the suppression baseline ratchet down.
…credential The provider caches a prefix against the credentials that sent it, not per end user, so turning the flag on makes every caller's prompts cacheable on that shared account. Surface that where the toggle is, since it is the operator's call to make.
9245e84 to
16e3954
Compare
PR overviewAll previously flagged issues have been addressed. No open security concerns remain on this pull request. Security reviewNo open security issues remain on this pull request. Fixed/addressed: 1 · PR risk: 0/10 |
Rather than mixing the flag and its ttl into the generic General settings table (which also surfaced the confusing Not Set / In Config / In DB provenance badges), give prompt caching a dedicated tab with a purpose-built toggle and ttl dropdown. Each registry field gains an optional tab, surfaced as ConfigList.field_tab, so the General tab renders the ungrouped fields and the caching fields render on their own tab. The update, persist and reset endpoints are unchanged.
The toggle and ttl descriptions were a wall of text, with a panel intro that mostly repeated the toggle description. Drop the intro and cut both descriptions to one or two lines, keeping a one-clause note that the cache is shared across callers on the same upstream credentials.
|
bugbot run |
…erride allowlist enable_anthropic_prompt_caching and anthropic_prompt_caching_ttl are set as live litellm attributes on the worker that handles the UI save, exactly like budget_exceeded_throttle_percentage, but they were missing from LITELLM_SETTINGS_SAFE_DB_OVERRIDES, so a peer worker's config reload merged the DB value without applying it to the live attribute and stayed stale. Add both to the allowlist so they behave like the sibling field, and add test_general_settings_ui_fields_are_db_overridable so the UI registry and the override allowlist cannot drift again (the exact omission that caused this), plus a regression test that the flag flips on a simulated peer-worker reload.
20a64cc to
47ba9e7
Compare
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 47ba9e7. Configure here.
|
@greptileai rereview |
Proof of fix: real proxy, real Anthropic API, real $, and a cost comparison vs Claude Code's own cachingAll of the below runs against a live proxy on LITELLM_ENABLE_ANTHROPIC_PROMPT_CACHING=true \
python litellm/proxy/proxy_cli.py --config cache_test_config.yaml --port 4000The flag caches when the client sends no cache_controlA ~14k token prefix with no client # call 1
curl -s http://localhost:4000/v1/messages -H "x-api-key: sk-1234" -H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" -d @msg.json | jq -c '.usage | {input_tokens, cache_creation_input_tokens, cache_read_input_tokens}'
{"input_tokens":3,"cache_creation_input_tokens":14428,"cache_read_input_tokens":0}
# call 2, identical body
{"input_tokens":3,"cache_creation_input_tokens":0,"cache_read_input_tokens":14428}
Cost comparison: LiteLLM auto caching vs Claude Code's internal cachingThe same agentic coding task run both ways: implement six functions so a 15 case pytest suite passes. Claude Code finished it in 4 API calls with every test green. To compare the two caching strategies on identical inputs rather than on two different agent trajectories, I ran Claude Code against the proxy (it sends its own Claude Code invocation (pointed at the proxy): ANTHROPIC_BASE_URL=http://localhost:4001 ANTHROPIC_API_KEY=sk-1234 ANTHROPIC_MODEL=claude-sonnet-4-6 \
claude -p "Implement all the functions in stringutils.py so that every test in test_stringutils.py passes..." \
--permission-mode bypassPermissions --output-format jsonPer request cache tokens are near identical between the two strategies:
Costed at the
On an identical trajectory the flag lands within 0.2% of Claude Code, a $0.0003 gap that is just the extra nonce tokens plus output nondeterminism, not a caching difference. So for any client that does not roll its own prompt caching, flipping this flag on delivers the same ~56% savings Claude Code gets for free, and it safely stands down for clients that already cache themselves QA across more models and a more complex taskRepeated the same LiteLLM-auto-caching vs Claude-Code-caching methodology on a harder agentic task (build an arithmetic tokenizer + recursive-descent parser + evaluator against a 30 case pytest suite; Claude Code got all 30 green on every model) across four models. Caching only changes input-token pricing, so the numbers below are the input-side cost (the replay regenerates each final assistant turn, so output tokens carry regeneration noise that has nothing to do with caching and is excluded):
The cache write and read token counts line up between the two strategies on every model (for example sonnet-5 writes 53,560 vs 53,529 and reads 238,161 vs 235,796), so the caching itself behaves the same. Where LiteLLM comes out slightly ahead it is because its auto injection also caches the trailing turn, which Claude Code leaves as fresh input on some turns. Savings track how many turns reuse the prefix (roughly 59% at 4 requests, roughly 69% at 6), as expected So the flag holds up across sonnet-5, opus-4-8, opus-4-7 and fable-5 on a non-trivial task: it matches Claude Code's own caching within a few percent, occasionally a touch cheaper, and never worse |
Relevant issues
enable_anthropic_prompt_cachingand its ttl arelitellm_settingsglobals, so the only way to turn automatic Anthropic prompt caching on was to hand-write config, which is the recipe the flag set out to replace. Support has been filing tickets to flip caching settings on customer instances because there is no equivalent surface in the UI.litellm_settingsand onto the livelitellm.<attr>, so caching starts on the next request without a restart. Each field carries atabin the registry (surfaced asfield_tabonConfigList) so the General tab shows the ungrouped fields and the caching fields render on their own tab;field_optionsonConfigListgives the ttl dropdown its allowed valuesStacked on #33573, which adds the flag itself. Merge that first; this PR only exposes it
Linear ticket
Resolves LIT-4478
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
prompt_caching.mp4
Live proxy on localhost:4000 against the real Anthropic API, backed by a real DB so the config endpoints are live. The flag is set nowhere: not in the config file, not in the environment. Everything below drives the exact endpoints the Prompt Caching tab calls
Both settings arrive from the config list, the ttl carrying its allowed values so the tab can render its dropdown
Toggling the switch on caches immediately, in the same proxy process, with no restart. A ~13.6k token prefix goes from full price to cached
Picking
1hin the ttl Select moves the write onto Anthropic's 1 hour cache rather than the 5 minute one, which is the knob long agentic sessions actually needAn unsupported ttl is refused at the gateway instead of reaching Anthropic verbatim
Reset returns the flag to its real default, rather than leaving the boolean as
NoneIn the Admin UI these same endpoints are driven from a dedicated Prompt Caching tab under Router Settings (http://localhost:4000/ui/?page=router-settings): a toggle for the flag and a ttl dropdown that is disabled until the toggle is on. The click-through is in the QA runbook below.
(Earlier screenshots in this PR showed the first design, where these lived as rows on the General settings table with In DB / In Config / Not Set badges; that was replaced by the dedicated tab, so those images have been removed as stale.)
Type
🆕 New Feature
Changes
enable_anthropic_prompt_cachingand itsanthropic_prompt_caching_ttlarelitellm_settingsglobals, so today the only way to turn automatic Anthropic prompt caching on is to hand-write config, which is the recipe this whole feature set out to replace. Support has been filing tickets to flip caching settings on customer instances because there was no equivalent surface in the UIBoth are registered in
_GENERAL_SETTINGS_UI_LITELLM_FIELDS, tagged with atabso they render on a dedicated Prompt Caching tab (a purpose-built toggle and dropdown, not the generic settings table) rather than mixed in with the other global limits. The existing update, persist and reset plumbing carries them intolitellm_settingsand onto the livelitellm.<attr>, so caching starts on the next request, with no restart.ConfigListgains an optionalfield_tabthat the frontend uses to route each field to its tab, andfield_optionsthat gives the ttl dropdown its allowed valuesThe flag is a Boolean and the table already renders that as a switch. The ttl is an enum, and the table only knew Integer, Boolean and Float, falling through to no editor at all for anything else. Rather than special casing this one field,
ConfigListnow carries an optionalfield_optionsand the table renders aSelectforfield_type == "Select", so any future enum setting gets an editor for free. Clearing it sends an empty value, which resolves back to the provider defaultThree things the registry could not previously express, all of which this needed:
_validate_general_settings_ui_litellm_valuewas hardcoded to a float in (0, 1], the shape of the one field that existed. It now dispatches on the field's declared type, so a Boolean rejects"yes"and1, and a Select rejects any value outside its options. The Float path is unchanged and its existing tests still pass_reset_general_settings_ui_litellm_fieldset every field toNone. For a boolean flag that is not a bool and reads as neither on nor off, so reset now restores each field's own declared default,Falsefor a Boolean andNoneotherwiseThe listing reported
stored_in_db=False, rendered as "In Config", for any field whose value was notNone. A boolean that defaults toFalsewould therefore always claim an admin had configured it. It now compares against the field's default, which is equivalent to the old check for the existing Float field and correct for the new onesCross-worker propagation
These two settings are set as live
litellm.<attr>values on the worker that handles the UI save, the same waybudget_exceeded_throttle_percentageis. That sibling field is inLITELLM_SETTINGS_SAFE_DB_OVERRIDES, which is what makes a config reload apply the DB value to the live attribute on other workers; these two were missing from it, so a peer worker merged the DB value but stayed on its startup value. This PR adds both to that allowlist so they behave like the sibling, and addstest_general_settings_ui_fields_are_db_overridable, which asserts every UI-editable field is enrolled so the two lists cannot drift again (that drift is exactly what caused the bug).One pre-existing gap remains, shared with
budget_exceeded_throttle_percentageand not specific to caching: the safe-override reapply runs onget_config, not on the periodic worker poll, so allowlistedlitellm_settingsconverge when a worker next reloads config rather than on a timer. Config-file and env-var configuration are unaffected (applied to every worker at boot). Tracked in LIT-4567.QA runbook
enable_anthropic_prompt_cachingout of the config and the environment entirely, and start the proxy with a DB5mand1h(the dropdown is disabled until the toggle is on). Confirm these two settings no longer appear on the General tab/v1/messagesand confirmcache_creation_input_tokensis 0cache_creation_input_tokensis now greater than 0; a second identical call reportscache_read_input_tokens1hand confirmusage.cache_creation.ephemeral_1h_input_tokensis what moves. Clear it and confirm the write returns toephemeral_5m_input_tokensbudget_exceeded_throttle_percentagerow still accepts a value in (0, 1] and still rejects 0 and 1.5Final Attestation
Link to Devin session: https://app.devin.ai/sessions/58dc3285dc2b44bbbebffc4ecfc41362
Note
Medium Risk
Changes global
litellm_settingsand livelitellmattributes that affect Anthropic/Bedrock caching on all requests; multi-worker behavior depends on the safe-override allowlist staying in sync with UI fields.Overview
Adds Admin UI control for
enable_anthropic_prompt_cachingandanthropic_prompt_caching_ttl, wired through the same general-settings persist/reset flow so changes apply live onlitellm.<attr>and inlitellm_settingswithout a restart.The proxy general-settings registry grows typed specs (Boolean, Float, Select) with optional
tabandoptions;/config/listexposesfield_tabandfield_options, validation is type-aware, reset restores per-type defaults (e.g.Falsefor booleans), andstored_in_dbcompares against those defaults. Both caching fields are added toLITELLM_SETTINGS_SAFE_DB_OVERRIDESso peer workers pick up UI saves on config reload, with a test that UI fields stay enrolled in that allowlist.The dashboard gets a Prompt Caching tab under Router Settings (toggle + TTL select with immediate save), a shared
SettingValueEditorincluding Select support, and caching fields are hidden from the General table viafield_tab.Reviewed by Cursor Bugbot for commit 47ba9e7. Bugbot is set up for automated code reviews on this repo. Configure here.