mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-14 23:21:35 +00:00
fix(proxy): /config/update normalize success_callback on first write
Some checks are pending
Unit Tests: Caching (Redis) / caching-redis (push) Waiting to run
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / schema-migration (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Security / security (push) Waiting to run
Some checks are pending
Unit Tests: Caching (Redis) / caching-redis (push) Waiting to run
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / schema-migration (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Security / security (push) Waiting to run
Previously the normalize_callback_names call only ran when the existing litellm_settings DB row already had a success_callback key. On the very first write (no row yet, or row missing the key), incoming mixed-case values like ["SQS", "sQs"] persisted as-is. delete_callback (lowercase lookup) then could not find them, and a follow-up /config/update would union normalized incoming with mixed-case stored entries, producing duplicates. Always normalize incoming success_callback before merging, and dedupe both the standalone first-write case and the union-with-existing case. Adds test_success_callback_normalized_on_first_write covering the no-existing-row path; the existing union test still passes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
abbe5d7f85
commit
b6e4ccf876
2 changed files with 41 additions and 14 deletions
|
|
@ -12617,23 +12617,30 @@ async def update_config( # noqa: PLR0915
|
|||
await _upsert_section("environment_variables", existing)
|
||||
|
||||
# litellm_settings: existing-wins merge (preserving legacy behavior),
|
||||
# except success_callback is unioned with the request value.
|
||||
# except success_callback is always normalized + deduped, and unioned
|
||||
# with any existing list. Normalizing on every write — not only when
|
||||
# an existing entry is present — keeps the DB free of mixed-case
|
||||
# entries that delete_callback (lowercase lookup) cannot find.
|
||||
if config_info.litellm_settings is not None:
|
||||
existing = await _read_section("litellm_settings")
|
||||
updated_litellm_settings = config_info.litellm_settings
|
||||
updated_litellm_settings = dict(config_info.litellm_settings)
|
||||
|
||||
incoming_cb = updated_litellm_settings.get("success_callback")
|
||||
if isinstance(incoming_cb, list):
|
||||
updated_litellm_settings["success_callback"] = normalize_callback_names(
|
||||
incoming_cb
|
||||
)
|
||||
|
||||
merged = {**updated_litellm_settings, **existing}
|
||||
if (
|
||||
"success_callback" in updated_litellm_settings
|
||||
and "success_callback" in existing
|
||||
and isinstance(existing["success_callback"], list)
|
||||
and isinstance(updated_litellm_settings["success_callback"], list)
|
||||
):
|
||||
normalized = normalize_callback_names(
|
||||
updated_litellm_settings["success_callback"]
|
||||
)
|
||||
merged["success_callback"] = list(
|
||||
set(existing["success_callback"] + normalized)
|
||||
)
|
||||
|
||||
incoming_cb = updated_litellm_settings.get("success_callback")
|
||||
existing_cb = existing.get("success_callback")
|
||||
if isinstance(incoming_cb, list):
|
||||
if isinstance(existing_cb, list):
|
||||
merged["success_callback"] = list(set(existing_cb + incoming_cb))
|
||||
else:
|
||||
merged["success_callback"] = list(set(incoming_cb))
|
||||
|
||||
await _upsert_section("litellm_settings", merged)
|
||||
|
||||
# router_settings: merge existing + request, request wins.
|
||||
|
|
|
|||
|
|
@ -208,6 +208,26 @@ def test_success_callback_unioned_with_existing(admin_auth, patched_proxy):
|
|||
assert set(stored) == {"langfuse", "prometheus"}
|
||||
|
||||
|
||||
def test_success_callback_normalized_on_first_write(admin_auth, patched_proxy):
|
||||
"""
|
||||
Regression: when no litellm_settings row exists yet, incoming mixed-case
|
||||
callbacks must still be lowercased and deduped before write. delete_callback
|
||||
looks up by lowercase name, so a stored "SQS" would be unreachable, and a
|
||||
follow-up /config/update with ["sqs"] would union mixed-case stored entries
|
||||
with normalized incoming ones, producing duplicates.
|
||||
"""
|
||||
prisma = patched_proxy()
|
||||
client = TestClient(app)
|
||||
resp = client.post(
|
||||
"/config/update",
|
||||
json={"litellm_settings": {"success_callback": ["SQS", "sQs"]}},
|
||||
)
|
||||
|
||||
assert resp.status_code == 200
|
||||
stored = prisma.db.litellm_config.rows["litellm_settings"]["success_callback"]
|
||||
assert set(stored) == {"sqs"}
|
||||
|
||||
|
||||
def test_alert_to_webhook_url_enables_slack_alerting(admin_auth, patched_proxy):
|
||||
prisma = patched_proxy()
|
||||
client = TestClient(app)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue