fix(proxy): release the health check save window lock on failure or cancel

This commit is contained in:
michelligabriele 2026-09-03 12:36:04 +02:00
parent eddb29d90e
commit f980b94923
No known key found for this signature in database
4 changed files with 278 additions and 136 deletions

View file

@ -7,7 +7,7 @@ import secrets
import time
import traceback
from collections.abc import Iterable, Mapping
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
from typing import Any, Final, Literal, TypedDict, cast
import fastapi
@ -41,6 +41,7 @@ from litellm.proxy.auth.auth_utils import (
)
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
from litellm.proxy.db.exception_handler import PrismaDBExceptionHandler
from litellm.proxy.db.health_check_latest import LatestHealthCheckRow
from litellm.proxy.db.proxy_worker_heartbeat import count_live_proxy_workers
from litellm.proxy.health_check import (
ADMIN_ONLY_HEALTH_DISPLAY_PARAMS,
@ -697,13 +698,42 @@ def _aggregate_health_check_results(
return model_results
class _AggregatedHealthResult(TypedDict):
"""One entry of ``_aggregate_health_check_results``: a model's counts for this cycle."""
model_name: ReadOnly[str]
model_id: ReadOnly[str | None]
healthy_count: ReadOnly[int]
unhealthy_count: ReadOnly[int]
error_message: ReadOnly[str | None]
def _new_health_status(result: _AggregatedHealthResult) -> str:
return "healthy" if result["healthy_count"] > 0 else "unhealthy"
def _should_persist_health_check_result(
result: _AggregatedHealthResult, latest_checks_map: Mapping[str, LatestHealthCheckRow]
) -> bool:
"""
True when this result has to be written: no previous row, the status changed, or the
previous row is older than one hour (periodic refresh while the status is stable).
"""
lookup_key: Final = result["model_id"] if result["model_id"] else result["model_name"]
last_check: Final = latest_checks_map.get(lookup_key)
if last_check is None or last_check.status != _new_health_status(result):
return True
time_since_last_check: Final = (datetime.now(timezone.utc) - last_check.checked_at).total_seconds()
return time_since_last_check >= 3600 # 1 hour threshold
async def _save_health_check_results_if_changed(
prisma_client,
model_results: dict,
latest_checks_map: dict,
start_time: float,
checked_by: str | None = None,
):
) -> bool:
"""
Save health check results to database, but only if status changed or >1 hour since last save.
@ -714,47 +744,39 @@ async def _save_health_check_results_if_changed(
- Status changes: Immediate write (no delay)
- Result: ~92% reduction in DB writes for stable systems, while maintaining real-time updates on changes
The writes are awaited rather than detached so the caller learns whether this cycle's
persistence completed.
Args:
prisma_client: Database client
model_results: Dictionary of aggregated health check results per model
latest_checks_map: Dictionary mapping model_id/model_name to latest health check
start_time: Start time of health check for calculating response time
checked_by: Identifier for who/what performed the check
Returns:
True when every row that needed writing was written (including when nothing needed
writing); False when any write failed.
"""
for result in model_results.values():
new_status = "healthy" if result["healthy_count"] > 0 else "unhealthy"
# Check if we should save this result
should_save = True
lookup_key = result["model_id"] if result["model_id"] else result["model_name"]
if lookup_key in latest_checks_map:
last_check = latest_checks_map[lookup_key]
# Only save if status changed or if it's been a while since last check
if last_check.status == new_status:
# Check if last check was recent (within 1 hour)
if last_check.checked_at:
from datetime import datetime, timezone
time_since_last_check = (datetime.now(timezone.utc) - last_check.checked_at).total_seconds()
# Only skip if status unchanged AND checked recently (within 1 hour)
# This ensures we still get periodic updates even if status is stable
if time_since_last_check < 3600: # 1 hour threshold
should_save = False
if should_save:
asyncio.create_task(
prisma_client.save_health_check_result(
model_name=result["model_name"],
model_id=result["model_id"],
status=new_status,
healthy_count=result["healthy_count"],
unhealthy_count=result["unhealthy_count"],
error_message=result["error_message"],
response_time_ms=(time.time() - start_time) * 1000,
details=None,
checked_by=checked_by,
)
)
to_write: Final = tuple(
result for result in model_results.values() if _should_persist_health_check_result(result, latest_checks_map)
)
writes: Final = tuple(
prisma_client.save_health_check_result(
model_name=result["model_name"],
model_id=result["model_id"],
status=_new_health_status(result),
healthy_count=result["healthy_count"],
unhealthy_count=result["unhealthy_count"],
error_message=result["error_message"],
response_time_ms=(time.time() - start_time) * 1000,
details=None,
checked_by=checked_by,
)
for result in to_write
)
rows: Final = await asyncio.gather(*writes)
return all(row is not None for row in rows)
async def _save_background_health_checks_to_db(
@ -764,7 +786,7 @@ async def _save_background_health_checks_to_db(
unhealthy_endpoints: list,
start_time: float,
checked_by: str | None = None,
):
) -> bool:
"""
Save background health check results to database for each model.
@ -773,9 +795,13 @@ async def _save_background_health_checks_to_db(
OPTIMIZATION: Only saves to database if the status has changed from the last saved check.
This dramatically reduces database writes when health status remains stable.
Returns:
True when this cycle's persistence completed; False when it was skipped or any step
failed. Never raises: a database failure must not break the health check loop.
"""
if prisma_client is None:
return
return False
try:
# Step 1: Build mapping from model parameter to model info
@ -798,7 +824,7 @@ async def _save_background_health_checks_to_db(
latest_checks_map[key] = check
# Step 4: Save aggregated results, but only if status changed
await _save_health_check_results_if_changed(
return await _save_health_check_results_if_changed(
prisma_client,
model_results,
latest_checks_map,
@ -808,6 +834,7 @@ async def _save_background_health_checks_to_db(
except Exception as db_error:
verbose_proxy_logger.warning("Failed to save background health checks to database: %s", db_error)
# Continue execution - don't let database save failure break health checks
return False
_PROXY_ADMIN_ROLES: Final = frozenset(

View file

@ -152,6 +152,7 @@ if TYPE_CHECKING:
from prisma import models as prisma_models
from litellm.integrations.opentelemetry import OpenTelemetry
from litellm.proxy.health_check_utils.shared_health_check_manager import SharedHealthCheckManager
Span = _Span | Any
else:
@ -3592,35 +3593,49 @@ async def _run_direct_health_check_with_instrumentation(
async def _window_gated_health_check_db_save(
save: Callable[[], Awaitable[None]],
save: Callable[[], Awaitable[bool]],
pod_lock_manager: PodLockManager | None,
lock_ttl: int | None,
) -> None:
"""
Persist at most once per window fleet-wide: the lock is the "this window's save is
done" marker, so it is deliberately never released and expires with the interval.
Persist at most once per window fleet-wide. A completed save keeps the lock as the
"this window's save is done" marker, so it is deliberately never released and expires
with the interval. A save that reports failure or is cancelled releases the lock so
another pod's cycle in the same window can retry, instead of the fleet going a whole
window without a write.
"""
if pod_lock_manager is not None and pod_lock_manager.redis_cache is not None:
acquired: Final = await pod_lock_manager.acquire_lock(
cronjob_id=BACKGROUND_HEALTH_CHECK_DB_SAVE_JOB_NAME,
ttl=lock_ttl,
allow_reentrant=False,
if pod_lock_manager is None or pod_lock_manager.redis_cache is None:
await save()
return
acquired: Final = await pod_lock_manager.acquire_lock(
cronjob_id=BACKGROUND_HEALTH_CHECK_DB_SAVE_JOB_NAME,
ttl=lock_ttl,
allow_reentrant=False,
)
if not acquired:
verbose_proxy_logger.debug("background_health_check_db_save_skipped another pod persisted this window")
return
try:
persisted: Final = await save()
except BaseException:
await pod_lock_manager.release_lock(cronjob_id=BACKGROUND_HEALTH_CHECK_DB_SAVE_JOB_NAME)
raise
if not persisted:
verbose_proxy_logger.warning(
"background_health_check_db_save_incomplete released the window lock so another pod can retry"
)
if not acquired:
verbose_proxy_logger.debug("background_health_check_db_save_skipped another pod persisted this window")
return
await save()
await pod_lock_manager.release_lock(cronjob_id=BACKGROUND_HEALTH_CHECK_DB_SAVE_JOB_NAME)
def _schedule_background_health_check_db_save(
prisma_client,
shared_health_manager,
prisma_client: PrismaClient | None,
shared_health_manager: "SharedHealthCheckManager | None",
model_list: list,
healthy_endpoints: list,
unhealthy_endpoints: list,
pod_lock_manager: PodLockManager | None = None,
lock_ttl: int | None = None,
):
) -> None:
"""Fire-and-forget: persist health check results to DB if prisma is available."""
if prisma_client is None:
return

View file

@ -112,13 +112,11 @@ async def test_run_direct_health_check_with_instrumentation_returns_results(
lambda _gs: {},
)
healthy, unhealthy, exceptions = (
await _run_direct_health_check_with_instrumentation(
model_list=[{"model_name": "gpt-4"}],
details=False,
max_concurrency=1,
instrumentation_context={"source": "test"},
)
healthy, unhealthy, exceptions = await _run_direct_health_check_with_instrumentation(
model_list=[{"model_name": "gpt-4"}],
details=False,
max_concurrency=1,
instrumentation_context={"source": "test"},
)
assert normalize(
@ -254,11 +252,12 @@ def _lock_manager(redis_cache, acquired):
return manager
def _capture_saves(monkeypatch):
def _capture_saves(monkeypatch, persisted=True):
saves = []
async def _fake_save(*_args, **kwargs):
saves.append(kwargs)
return persisted
import litellm.proxy.health_endpoints._health_endpoints as he
@ -266,6 +265,15 @@ def _capture_saves(monkeypatch):
return saves
def _cancel_during_save(monkeypatch):
async def _fake_save(*_args, **_kwargs):
raise asyncio.CancelledError()
import litellm.proxy.health_endpoints._health_endpoints as he
monkeypatch.setattr(he, "_save_background_health_checks_to_db", _fake_save)
def _schedule_with(lock_manager):
_schedule_background_health_check_db_save(
prisma_client=MagicMock(),
@ -315,15 +323,56 @@ async def test_schedule_background_health_check_db_save_holds_the_window_lock_fo
}
@pytest.mark.asyncio
async def test_schedule_background_health_check_db_save_releases_the_window_lock_when_the_save_reports_failure(
monkeypatch,
):
"""A failed save must not burn the window: release the lock so another pod's cycle can retry."""
saves = _capture_saves(monkeypatch, persisted=False)
lock_manager = _lock_manager(redis_cache=MagicMock(), acquired=True)
_schedule_with(lock_manager)
await asyncio.sleep(0)
assert normalize(
{
"saves": len(saves),
"release_request": lock_manager.release_lock.await_args.kwargs,
"release_count": lock_manager.release_lock.await_count,
}
) == {
"saves": 1,
"release_request": {"cronjob_id": BACKGROUND_HEALTH_CHECK_DB_SAVE_JOB_NAME},
"release_count": 1,
}
@pytest.mark.asyncio
async def test_schedule_background_health_check_db_save_releases_the_window_lock_when_the_save_is_cancelled(
monkeypatch,
):
"""A pod shutting down mid-save releases the lock instead of holding it until the TTL."""
_cancel_during_save(monkeypatch)
lock_manager = _lock_manager(redis_cache=MagicMock(), acquired=True)
_schedule_with(lock_manager)
await asyncio.sleep(0)
assert (
lock_manager.release_lock.await_args.kwargs,
lock_manager.release_lock.await_count,
) == ({"cronjob_id": BACKGROUND_HEALTH_CHECK_DB_SAVE_JOB_NAME}, 1)
@pytest.mark.asyncio
async def test_schedule_background_health_check_db_save_runs_ungated_without_redis(monkeypatch):
saves = _capture_saves(monkeypatch)
saves = _capture_saves(monkeypatch, persisted=False)
lock_manager = _lock_manager(redis_cache=None, acquired=True)
_schedule_with(lock_manager)
await asyncio.sleep(0)
assert (len(saves), lock_manager.acquire_lock.await_count) == (1, 0)
assert (len(saves), lock_manager.acquire_lock.await_count, lock_manager.release_lock.await_count) == (1, 0, 0)
# ---------------------------------------------------------------------------
@ -400,13 +449,9 @@ def test_write_health_state_to_router_cache_sets_states(monkeypatch):
_write_health_state_to_router_cache(healthy, unhealthy, exceptions)
fake_router.health_state_cache.set_deployment_health_states.assert_called_once_with(
fake_states
)
fake_router.health_state_cache.set_deployment_health_states.assert_called_once_with(fake_states)
call_args = fake_router.health_state_cache.set_deployment_health_states.call_args[
0
][0]
call_args = fake_router.health_state_cache.set_deployment_health_states.call_args[0][0]
assert normalize(
{
"states_keys": sorted(call_args.keys()),
@ -448,9 +493,7 @@ def test_write_health_state_to_router_cache_populates_for_listing_filter(monkeyp
fake_router.cooldown_time = 30
monkeypatch.setattr(proxy_server, "llm_router", fake_router)
monkeypatch.setattr(
proxy_server, "general_settings", {"model_list_healthy_only": True}
)
monkeypatch.setattr(proxy_server, "general_settings", {"model_list_healthy_only": True})
fake_states = {"m1": {"is_healthy": True}, "m2": {"is_healthy": False}}
@ -484,9 +527,7 @@ def test_write_health_state_to_router_cache_populates_for_listing_filter(monkeyp
{"m2": SimpleNamespace(status_code=500)},
)
fake_router.health_state_cache.set_deployment_health_states.assert_called_once_with(
fake_states
)
fake_router.health_state_cache.set_deployment_health_states.assert_called_once_with(fake_states)
assert cooldowns == []
assert failures == []
@ -496,9 +537,7 @@ def test_write_health_state_to_router_cache_swallows_internal_failures(monkeypat
fake_router = MagicMock()
fake_router.enable_health_check_routing = True
fake_router.health_check_ignore_transient_errors = False
fake_router.health_state_cache.set_deployment_health_states.side_effect = (
RuntimeError("cache exploded")
)
fake_router.health_state_cache.set_deployment_health_states.side_effect = RuntimeError("cache exploded")
monkeypatch.setattr(proxy_server, "llm_router", fake_router)
@ -528,9 +567,7 @@ async def test_adaptive_router_flusher_loop_flushes_each_router(monkeypatch):
from litellm.types.router import TaggedPreRoutingStrategy
fake_router = MagicMock()
fake_router.adaptive_routers = {
"alpha": [TaggedPreRoutingStrategy(tags=(), strategy=fake_ar)]
}
fake_router.adaptive_routers = {"alpha": [TaggedPreRoutingStrategy(tags=(), strategy=fake_ar)]}
monkeypatch.setattr(proxy_server, "llm_router", fake_router)
monkeypatch.setattr(proxy_server, "prisma_client", MagicMock())
@ -628,12 +665,8 @@ async def test_run_background_health_check_runs_one_cycle_then_cancels(monkeypat
"_run_direct_health_check_with_instrumentation",
_fake_direct,
)
monkeypatch.setattr(
proxy_server, "_schedule_background_health_check_db_save", lambda *a, **kw: None
)
monkeypatch.setattr(
proxy_server, "_write_health_state_to_router_cache", lambda *a, **kw: None
)
monkeypatch.setattr(proxy_server, "_schedule_background_health_check_db_save", lambda *a, **kw: None)
monkeypatch.setattr(proxy_server, "_write_health_state_to_router_cache", lambda *a, **kw: None)
monkeypatch.setattr(
proxy_server,
"health_check_filter_kwargs_from_general_settings",
@ -711,12 +744,8 @@ async def test_run_background_health_check_probes_only_listed_model_groups(monke
"_run_direct_health_check_with_instrumentation",
_fake_direct,
)
monkeypatch.setattr(
proxy_server, "_schedule_background_health_check_db_save", lambda *a, **kw: None
)
monkeypatch.setattr(
proxy_server, "_write_health_state_to_router_cache", lambda *a, **kw: None
)
monkeypatch.setattr(proxy_server, "_schedule_background_health_check_db_save", lambda *a, **kw: None)
monkeypatch.setattr(proxy_server, "_write_health_state_to_router_cache", lambda *a, **kw: None)
monkeypatch.setattr(
proxy_server,
"health_check_filter_kwargs_from_general_settings",

View file

@ -24,12 +24,8 @@ from litellm.proxy.utils import PrismaClient
def mock_prisma():
"""Simplified mock PrismaClient with bound methods"""
client = MagicMock()
client.db.litellm_healthchecktable.create = AsyncMock(
return_value={"id": "test-id"}
)
client.db.litellm_healthchecktable.find_many = AsyncMock(
return_value=[{"id": "1", "model_name": "test"}]
)
client.db.litellm_healthchecktable.create = AsyncMock(return_value={"id": "test-id"})
client.db.litellm_healthchecktable.find_many = AsyncMock(return_value=[{"id": "1", "model_name": "test"}])
# Bind actual methods
import types
@ -55,14 +51,10 @@ def mock_prisma():
("healthy", 1, 0, False), # Database error case
],
)
async def test_save_health_check_result(
mock_prisma, status, healthy, unhealthy, should_succeed
):
async def test_save_health_check_result(mock_prisma, status, healthy, unhealthy, should_succeed):
"""Test health check result saving with various scenarios"""
if not should_succeed:
mock_prisma.db.litellm_healthchecktable.create.side_effect = Exception(
"DB Error"
)
mock_prisma.db.litellm_healthchecktable.create.side_effect = Exception("DB Error")
result = await mock_prisma.save_health_check_result(
model_name="test-model",
@ -190,9 +182,7 @@ def test_aggregate_health_check_results():
{"model": "gpt-4", "error": "Rate limit exceeded"},
]
result = _aggregate_health_check_results(
model_param_to_info, healthy_endpoints, unhealthy_endpoints
)
result = _aggregate_health_check_results(model_param_to_info, healthy_endpoints, unhealthy_endpoints)
# Check gpt-3.5-turbo is healthy
gpt35_key = ("model-123", "gpt-3.5-turbo")
@ -223,9 +213,7 @@ def test_aggregate_health_check_results_multiple_endpoints():
]
unhealthy_endpoints = []
result = _aggregate_health_check_results(
model_param_to_info, healthy_endpoints, unhealthy_endpoints
)
result = _aggregate_health_check_results(model_param_to_info, healthy_endpoints, unhealthy_endpoints)
key = ("model-123", "gpt-3.5-turbo")
assert result[key]["healthy_count"] == 2
@ -401,7 +389,7 @@ async def test_save_background_health_checks_to_db():
start_time = 1234567890.0
await _save_background_health_checks_to_db(
persisted = await _save_background_health_checks_to_db(
mock_prisma,
model_list,
healthy_endpoints,
@ -410,7 +398,8 @@ async def test_save_background_health_checks_to_db():
"background_health_check",
)
# Should call get_all_latest_health_checks and save_health_check_result
# Should call get_all_latest_health_checks and save_health_check_result, and report completion
assert persisted is True
mock_prisma.get_all_latest_health_checks.assert_called_once()
mock_prisma.save_health_check_result.assert_called_once()
@ -421,22 +410,112 @@ async def test_save_background_health_checks_to_db():
assert call_kwargs["checked_by"] == "background_health_check"
def _two_model_results():
return {
("model-1", "gpt-4"): {
"model_name": "gpt-4",
"model_id": "model-1",
"healthy_count": 1,
"unhealthy_count": 0,
"error_message": None,
},
("model-2", "gpt-4o"): {
"model_name": "gpt-4o",
"model_id": "model-2",
"healthy_count": 0,
"unhealthy_count": 1,
"error_message": "boom",
},
}
@pytest.mark.asyncio
async def test_save_health_check_results_if_changed_awaits_every_write_and_reports_success():
"""Writes are awaited, not detached, so the caller can tell the cycle's persistence completed."""
mock_prisma = MagicMock()
mock_prisma.save_health_check_result = AsyncMock(return_value={"id": "row"})
persisted = await _save_health_check_results_if_changed(
mock_prisma, _two_model_results(), {}, 1234567890.0, "background_health_check"
)
assert (persisted, mock_prisma.save_health_check_result.await_count) == (True, 2)
@pytest.mark.asyncio
async def test_save_health_check_results_if_changed_reports_failure_when_a_write_returns_none():
"""save_health_check_result swallows DB errors and returns None; that must surface as False."""
mock_prisma = MagicMock()
mock_prisma.save_health_check_result = AsyncMock(side_effect=[{"id": "row"}, None])
persisted = await _save_health_check_results_if_changed(
mock_prisma, _two_model_results(), {}, 1234567890.0, "background_health_check"
)
assert (persisted, mock_prisma.save_health_check_result.await_count) == (False, 2)
@pytest.mark.asyncio
async def test_save_health_check_results_if_changed_reports_success_when_nothing_needed_writing():
mock_prisma = MagicMock()
mock_prisma.save_health_check_result = AsyncMock()
model_results = {
("model-1", "gpt-4"): {
"model_name": "gpt-4",
"model_id": "model-1",
"healthy_count": 1,
"unhealthy_count": 0,
"error_message": None,
},
}
latest_checks_map = {
"model-1": MagicMock(status="healthy", checked_at=datetime.now(timezone.utc) - timedelta(minutes=5)),
}
persisted = await _save_health_check_results_if_changed(
mock_prisma, model_results, latest_checks_map, 1234567890.0, "background_health_check"
)
assert (persisted, mock_prisma.save_health_check_result.await_count) == (True, 0)
def _one_model_setup():
model_list = [
{
"model_name": "gpt-3.5-turbo",
"model_info": {"id": "model-123"},
"litellm_params": {"model": "gpt-3.5-turbo"},
},
]
return model_list, [{"model": "gpt-3.5-turbo"}], []
@pytest.mark.asyncio
async def test_save_background_health_checks_to_db_returns_false_when_a_write_fails():
mock_prisma = MagicMock()
mock_prisma.get_all_latest_health_checks = AsyncMock(return_value=[])
mock_prisma.save_health_check_result = AsyncMock(return_value=None)
model_list, healthy_endpoints, unhealthy_endpoints = _one_model_setup()
persisted = await _save_background_health_checks_to_db(
mock_prisma, model_list, healthy_endpoints, unhealthy_endpoints, 1234567890.0, "background_health_check"
)
assert (persisted, mock_prisma.save_health_check_result.await_count) == (False, 1)
@pytest.mark.asyncio
async def test_save_background_health_checks_to_db_no_prisma():
"""Test graceful handling when no prisma client"""
result = await _save_background_health_checks_to_db(
None, [], [], [], 0.0, "background_health_check"
)
assert result is None
result = await _save_background_health_checks_to_db(None, [], [], [], 0.0, "background_health_check")
assert result is False
@pytest.mark.asyncio
async def test_save_background_health_checks_to_db_exception_handling():
"""Test exception handling in background health check save"""
mock_prisma = MagicMock()
mock_prisma.get_all_latest_health_checks = AsyncMock(
side_effect=Exception("DB Error")
)
mock_prisma.get_all_latest_health_checks = AsyncMock(side_effect=Exception("DB Error"))
model_list = [
{
@ -446,12 +525,13 @@ async def test_save_background_health_checks_to_db_exception_handling():
},
]
# Should not raise exception, should handle gracefully
await _save_background_health_checks_to_db(
# Must not raise (the health check loop has to survive a DB outage) but must report
# the failure, so the window lock can be released for another pod to retry
persisted = await _save_background_health_checks_to_db(
mock_prisma, model_list, [], [], 0.0, "background_health_check"
)
# Function should complete without raising
assert persisted is False
def _raw_latest_row(model_name: str, model_id, checked_at: datetime) -> dict:
@ -660,12 +740,7 @@ def test_parse_background_health_check_model_groups_unset_returns_none():
assert parse_background_health_check_model_groups(None) is None
assert parse_background_health_check_model_groups({}) is None
assert (
parse_background_health_check_model_groups(
{"background_health_check_model_groups": None}
)
is None
)
assert parse_background_health_check_model_groups({"background_health_check_model_groups": None}) is None
def test_parse_background_health_check_model_groups_list_returns_frozenset():
@ -682,9 +757,7 @@ def test_parse_background_health_check_model_groups_malformed_raises(bad_value):
from litellm.proxy.health_check import parse_background_health_check_model_groups
with pytest.raises(ValueError, match="must be a list of model group names"):
parse_background_health_check_model_groups(
{"background_health_check_model_groups": bad_value}
)
parse_background_health_check_model_groups({"background_health_check_model_groups": bad_value})
def test_filter_deployments_to_model_groups():
@ -697,9 +770,7 @@ def test_filter_deployments_to_model_groups():
]
assert filter_deployments_to_model_groups(model_list, None) == tuple(model_list)
assert filter_deployments_to_model_groups(
model_list, frozenset({"prod-openai"})
) == (model_list[0], model_list[2])
assert filter_deployments_to_model_groups(model_list, frozenset({"prod-openai"})) == (model_list[0], model_list[2])
assert filter_deployments_to_model_groups(model_list, frozenset()) == ()