Route chat, responses, anthropic, and interactions through the shared
buildMcpToolBlocks helper so MCP server_url/label resolution is correct
for any model. Chat was labeling every server as "litellm" and preferring
aliases; responses used absolute /mcp URLs that broke the all-servers path
Anchor MultiSelect popup to chips, forward refs on ComboboxChips, harden
MCP list response parsing, and load servers with the active key so
existing MCP servers appear in the dropdown
Drop the custom cancel/queue/dual-buffer state machine and the duplicate
OPEN_AI_REALTIME_VOICES list. Reuse OPEN_AI_VOICE_SELECT_OPTIONS and the
original thin WebSocket event handler (append deltas, response.done
fallback) with the shared ChatComposer shell
Text turns use text-only modalities with VAD off, and voice re-enables
audio+VAD only while recording so residual VAD cannot race a second
response. Stream text and transcript into one draft, always update the
last assistant bubble (even past status lines), and unlock with a cancel
timeout so the UI cannot stick mid-response forever
Track in-progress responses so text/mic turns cannot race response.create,
prefer a single transcript stream per turn, and queue sends until the active
response finishes or is cancelled
Move RealtimePlayground off Ant Design onto shadcn controls and the
shared ChatComposer, use the realtime-safe voice list, and reuse the
same composer for Compare message input
Throw when both model endpoints fail so ChatUI can show an error instead of
an empty list. Use the same resolved key for model listing and chat requests,
and drop the fetch_models JSDoc
Prefer the key-scoped OpenAI models list when the Virtual Key source is
selected, enrich with mode from model_group/info, and debounce custom key
input so models appear for the key's access set
Replace the compact single-line input with a PromptInput-style composer:
taller auto-growing textarea, rounded card shell, footer tools, and
stop button while a request is in flight
Bring back the prior Chat model dropdown filter (including chat models
on responses/anthropic/interactions and image models on image_edits), and
map mode realtime so the realtime endpoint only lists compatible models
Cancel in-flight model fetches when the key or source changes so an older
response cannot overwrite modelInfo. Drop the inverted endpoint-filter
assertion; filtering coverage lands in the next stack PR
Replace ChatUI cards, inputs, dialogs, popovers, MCP selects, uploads,
tooltips, and icons with shadcn/Base UI and Lucide. Update ChatUI tests
to drive searchable combobox controls instead of Ant Design selectors
Continue the Playground Chat Ant Design/Tremor migration: shared MultiSelect,
upload validation with semantic file inputs, collapsible message widgets, and
AdditionalModelSettings on Base UI controls
When store_model_in_db is true, general_settings are persisted to the
LiteLLM_Config DB table. On subsequent startups and periodic reloads,
_add_general_settings_from_db_config() unconditionally overwrites the
in-memory general_settings with DB-cached values, including
store_prompts_in_spend_logs.
This means a YAML config change (e.g. store_prompts_in_spend_logs: false)
deployed via CI/CD has no effect because the stale DB value (true) always
wins. The admin must manually update via /config/update API after every
deploy, defeating config-as-code.
Fix: track which general_settings keys were explicitly set in YAML at
startup (_yaml_general_settings_keys). During DB config merge, prefer the
YAML value for tracked keys. The DB value is only used as fallback when
YAML does not set the key, preserving the admin UI's ability to change
settings at runtime.
Steps to reproduce:
1. Start proxy with store_model_in_db: true, store_prompts_in_spend_logs: true
2. Change YAML to store_prompts_in_spend_logs: false, restart
3. Send a request, query LiteLLM_SpendLogs - prompts still stored
4. Check LiteLLM_Config table - DB still has true, overriding YAML
Slack thread: https://dataset-jsonhackathon.slack.com/archives/C0ACUS7LM29/p1785835131860139
The base branch ratcheted the same limits in 28a277e9, so the conflicting
files were reset to base and the ratchet re-run against the new merge-base
rather than resolved by hand. Each limit is now the base value minus this
branch's own delta, so both ratchets survive: basedpyright -653 across 48
rules, strict ruff -80, LIT -85.
Seven wide-reaching but semantically neutral commits landed since the
last entry, together rewriting roughly 162k lines across ~4,700 file
touches. Blame on any line they reflowed points at the sweep instead of
the commit that wrote the logic.
They cover the safe ruff autofix pass, the collections.abc import move,
the f-string !s cleanup, lazy log message construction, the LIT010 and
LIT011 Final and frozen-parameter rollout, ruff coverage for
litellm/types, and the inert type: ignore strip.
Smaller ratchet commits are left out on purpose: each touches a few
hundred lines at most, so listing them would grow the file faster than
it buys back blame accuracy
Revert the LIT001 build-clause inserts, phrase the LIT002 menu as
'or (if it really must be dynamic) a MappingProxyType wrapping a dict
literal or comprehension', and fold the two freezing-wrapper exemption
sentences into one that names MappingProxyType beside tuple/frozenset.
LIT001/LIT002 and the typing.Dict ban all steered dict-shaped values to
frozen dataclasses or suppression even though the checker already accepts
MappingProxyType as a freezing wrapper; the messages now name it so the
dict-shaped freeze path is actually discoverable at fix time.
Storing a baseline used to prune every other cache entry, so gate runs in
concurrent worktrees kept evicting each other's baselines and forcing full
recomputes: this bit six times across two nights of benchmarking. The store
now writes alongside existing entries and evicts only the oldest beyond
eight, keyed as before by merge-base and environment fingerprint, so
parallel worktrees' baselines simply coexist