fix(proxy): honor explicit null to clear cache_control_injection_points on model update

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
shivam 2026-09-10 23:24:53 +00:00
parent 511b133b0b
commit bcfc3d5ea9
2 changed files with 55 additions and 1 deletions

View file

@ -129,6 +129,7 @@ if TYPE_CHECKING:
from prisma import models as prisma_models
router: Final = APIRouter()
CLEARABLE_LITELLM_PARAMS: Final = frozenset({"cache_control_injection_points"})
async def update_team(*args, **kwargs):
@ -758,11 +759,16 @@ def update_db_model(db_model: Deployment, updated_patch: updateDeployment) -> Pr
# model_info fields like team_id or access groups. SPECIAL_MODEL_INFO_PARAMS are
# mirrored between litellm_params and model_info by Deployment.__init__, so the
# clear propagates to both blobs.
# CLEARABLE_LITELLM_PARAMS lists non-mirrored litellm_params that explicit null may clear.
if updated_patch.litellm_params:
for field in updated_patch.litellm_params.model_fields_set:
if field in SPECIAL_MODEL_INFO_PARAMS and getattr(updated_patch.litellm_params, field) is None:
if getattr(updated_patch.litellm_params, field) is not None:
continue
if field in SPECIAL_MODEL_INFO_PARAMS:
merged_litellm_params.pop(field, None)
merged_model_info.pop(field, None)
elif field in CLEARABLE_LITELLM_PARAMS:
merged_litellm_params.pop(field, None)
if updated_patch.model_info:
for field in updated_patch.model_info.model_fields_set:
if field in SPECIAL_MODEL_INFO_PARAMS and getattr(updated_patch.model_info, field) is None:

View file

@ -3573,6 +3573,54 @@ class TestUpdateDBModelClearPricing:
assert info["cache_creation_input_token_cost"] == 0.000003
class TestUpdateDBModelClearCacheControlInjectionPoints:
def test_explicit_null_removes_stored_injection_points(self):
from litellm.proxy.management_endpoints.model_management_endpoints import (
update_db_model,
)
from litellm.types.router import LiteLLM_Params, ModelInfo, updateLiteLLMParams
db_model = Deployment(
model_name="haiku-cached",
litellm_params=LiteLLM_Params(
model="anthropic/claude-haiku-4-5",
cache_control_injection_points=[{"location": "message", "role": "system"}],
),
model_info=ModelInfo(id="dep-cache-0"),
)
patch = updateDeployment(
litellm_params=updateLiteLLMParams(cache_control_injection_points=None)
)
result = update_db_model(db_model=db_model, updated_patch=patch)
params = json.loads(result["litellm_params"])
assert "cache_control_injection_points" not in params
assert params["model"] == "anthropic/claude-haiku-4-5"
def test_omitted_key_keeps_stored_injection_points(self):
from litellm.proxy.management_endpoints.model_management_endpoints import (
update_db_model,
)
from litellm.types.router import LiteLLM_Params, ModelInfo, updateLiteLLMParams
db_model = Deployment(
model_name="haiku-cached",
litellm_params=LiteLLM_Params(
model="anthropic/claude-haiku-4-5",
cache_control_injection_points=[{"location": "message", "role": "system"}],
),
model_info=ModelInfo(id="dep-cache-0"),
)
patch = updateDeployment(litellm_params=updateLiteLLMParams(tpm=10))
result = update_db_model(db_model=db_model, updated_patch=patch)
params = json.loads(result["litellm_params"])
assert params["cache_control_injection_points"] == [{"location": "message", "role": "system"}]
assert params["tpm"] == 10
class TestGetModelInfoWithIdBlocked:
"""`ProxyConfig.get_model_info_with_id` must propagate the DB-level `blocked`
column into the in-memory `model_info` dict so the router filter can read it."""