mirror of
https://github.com/BerriAI/litellm.git
synced 2026-08-28 05:25:59 +00:00
* fix(proxy): persist periodic reload schedule state so status survives restarts and fires without store_model_in_db The model cost map and Anthropic beta headers reload schedules kept their last-run time in a per-pod module global, so GET /schedule/*/status reported last_run null after any restart and the Admin UI showed the reload as never having run. The reload check also only ran from the add_deployment job, which is registered only when store_model_in_db is true, so config-file deployments stored a schedule that never fired. Persist last_run_at and reload_requested_at as dedicated columns on LiteLLM_Config, owned by the reload job and manual reload endpoints, while the schedule endpoints own the param_value JSON (interval_hours); no writer can clobber another's fields. Serve status entirely from the row. Register the check as its own periodic_reload_job outside the store_model_in_db gate. Replace the force_reload boolean with a reload_requested_at timestamp each pod compares against its own in-memory last reload, so a manual reload reaches every pod exactly once instead of being cleared by the first poller. Run the blocking fetches via asyncio.to_thread, and stamp last_run_at with update_many so a schedule cancelled mid-poll is not resurrected. * fix(proxy): compare reload requests against pod data age seeded at boot A pod that had never reloaded kept its in-memory clock at None, and with no interval configured nothing ever set it, so every manual reload request was ignored by every pod except the one serving the click (Greptile P1 on the previous commit). Seed the per-pod timestamp at boot as the time its data was loaded and reload whenever a request or the interval is older than that, which also removes both None special cases from the due predicate. A schedule whose row has no last_run_at fires on the next tick so the first run does not wait a full interval. * fix(proxy): scope reload persistence to the model cost map and seed the pod clock from the actual load time Revert the Anthropic beta headers reload path to its previous JSON-flag implementation so this PR only changes the price data reload; the beta headers path keeps working exactly as before and can migrate to the shared module in a follow-up. The unused columns on its config row are inert. Seed model_cost_map_loaded_at from the timestamp get_model_cost_map records at the actual import-time fetch instead of ProxyConfig construction time, closing the startup window where a manual reload request stamped between the fetch and the constructor compared as older than the pod's data and was skipped (Greptile P1 on the previous commit). * refactor(proxy): drop the legacy force_reload backfill from the reload tracking migration The backfill only carried over a manual reload clicked in the seconds before an upgrade, and every upgrade restarts the pods, which re-fetch the cost map at import and so already deliver what that request asked for. Removing it makes the migration schema-only, so prisma db push and prisma migrate deploy leave the database in the same state instead of diverging on a data statement that only one of them runs. * fix(proxy): stamp reload timestamps at the precision they are stored at Postgres stores these columns as TIMESTAMP(3) while Python stamps microseconds, so a pod comparing its in-memory clock against the persisted copy of the same instant read as newer and skipped the reload request it had just recorded. Truncate every stamp to milliseconds at the source, and floor the boot seed the same way, so the in-memory value and its persisted copy compare exactly. * fix(proxy): identify manual reloads by revision instead of comparing timestamps Comparing a request timestamp against each pod's data age made correctness depend on clock resolution: Postgres stores TIMESTAMP(3) while Python stamps microseconds, and two events inside the same millisecond are indistinguishable no matter how the comparison is written. Replace reload_requested_at with a reload_revision counter the manual reload endpoint increments atomically in the database. Each pod records the revision it last applied and reloads whenever the row's differs, so a request reaches every pod exactly once regardless of clock skew or precision, and concurrent requests publish distinct revisions instead of overwriting one another. A pod adopts the current revision on its first poll, since data it loaded at boot already satisfies any earlier request. Interval reloads still key off the pod's own data age, where hour scale comparisons make precision irrelevant. * fix(proxy): seed the applied reload revision at startup A pod adopted whatever revision it found on its first poll, so a manual reload published while the pod was starting was marked applied without ever being served and the pod kept the prices it fetched at import. Read the row once at startup instead, right after that fetch, and treat a missing row as revision 0 * style(tests): revert incidental reformatting of test_proxy_server.py An earlier ruff format run reflowed the whole file from its 88-column formatting, adding ~1150 lines of churn unrelated to this PR. Replay only the real test changes onto the original formatting * fix(proxy): serve an outstanding reload request on a booting pod Seeding the applied revision at startup left a window: a manual reload published after the import-time cost map fetch but before startup read the row was marked applied without ever being fetched, stranding that pod on stale prices when no interval was configured. A pod now starts unapplied and serves any outstanding request on its first poll, which costs one redundant fetch per boot and removes the window along with the seeding step * fix(proxy): accept a reload interval still encoded as JSON text param_value is written with safe_dumps, and a raw row read can return it decoded or as a string depending on the driver. Strict validation rejected the string, so the schedule read as disabled and an admin's configured reloads silently stopped. Mirrors the guard ConfigRepository.get_param already carries for the same column * fix(proxy): cancel a reload schedule without resetting the revision * fix(proxy): null the interval in JSON so cancelling keeps the revision prisma rejects a null literal for a Json? column, so update_many writes an interval-less object instead. The fake config table now rejects the same input the database does, which is what the live run caught and the mock did not. Also records the run before adopting the revision, so a failed status write leaves the request unserved for the next poll rather than reporting a run that never landed. * fix(ui): match the CI-generated user_role union order in schema.d.ts |
||
|---|---|---|
| .. | ||
| dist | ||
| litellm_proxy_extras | ||
| tests | ||
| build_and_publish.md | ||
| LICENSE | ||
| migration_runbook.md | ||
| pyproject.toml | ||
| README.md | ||
Additional files for the proxy. Reduces the size of the main litellm package.
Currently, only stores the migration.sql files for litellm-proxy.
To install, run:
uv add litellm-proxy-extras
OR
uv tool install 'litellm[proxy]' # installs litellm-proxy-extras and other proxy dependencies
To use the migrations, run:
litellm --use_prisma_migrate