perf(router): shallow-copy wildcard deployments instead of deepcopy in pattern router

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
ryan 2026-07-18 04:26:12 +00:00
parent 07e07e6e2b
commit a0efa6f26d
2 changed files with 68 additions and 11 deletions

View file

@ -2,7 +2,6 @@
Class to handle llm wildcard routing and regex pattern matching
"""
import copy
import re
from re import Match
from typing import Dict, List, Optional, Tuple
@ -98,16 +97,19 @@ class PatternMatchRouter:
return re.escape(pattern).replace(r"\*", "(.*)")
def _return_pattern_matched_deployments(self, matched_pattern: Match, deployments: List[Dict]) -> List[Dict]:
new_deployments = []
for deployment in deployments:
new_deployment = copy.deepcopy(deployment)
new_deployment["litellm_params"]["model"] = PatternMatchRouter.set_deployment_model_name(
matched_pattern=matched_pattern,
litellm_deployment_litellm_model=deployment["litellm_params"]["model"],
)
new_deployments.append(new_deployment)
return new_deployments
return [
{
**deployment,
"litellm_params": {
**deployment["litellm_params"],
"model": PatternMatchRouter.set_deployment_model_name(
matched_pattern=matched_pattern,
litellm_deployment_litellm_model=deployment["litellm_params"]["model"],
),
},
}
for deployment in deployments
]
def route(self, request: Optional[str], filtered_model_names: Optional[List[str]] = None) -> Optional[List[Dict]]:
"""

View file

@ -395,3 +395,58 @@ def test_wildcard_priority_over_deployment_names():
assert (
deployments[0]["litellm_params"]["api_base"] == "http://localhost:8081/openai"
), f"Expected '*' wildcard deployment (8081), got {deployments[0]['litellm_params']['api_base']}"
def test_route_does_not_mutate_stored_template():
"""
Regression: route() substitutes the requested wildcard segment into
litellm_params["model"], but it must never mutate the deployment template
stored in self.patterns.
A naive shallow copy of only the outer deployment dict shares the nested
litellm_params dict with the template, so writing litellm_params["model"]
would corrupt the stored pattern for every subsequent request.
"""
router = PatternMatchRouter()
deployment = Deployment(
model_name="openai/*",
litellm_params=LiteLLM_Params(model="openai/*"),
model_info=ModelInfo(),
)
router.add_pattern("openai/*", deployment.to_json(exclude_none=True))
first = router.route("openai/gpt-4o")
assert first is not None and first[0]["litellm_params"]["model"] == "openai/gpt-4o"
stored_template = list(router.patterns.values())[0][0]
assert stored_template["litellm_params"]["model"] == "openai/*"
second = router.route("openai/gpt-4o-mini")
assert second is not None and second[0]["litellm_params"]["model"] == "openai/gpt-4o-mini"
assert first[0]["litellm_params"]["model"] == "openai/gpt-4o"
def test_route_returns_shallow_copy_not_deepcopy():
"""
Regression: the per-listed-model /v1/models hot path deep-copied every
matched deployment, which blocked the event loop (issue tracked in PR
#33721). route() must isolate the mutated litellm_params.model without a
full deepcopy, so nested objects that are not rewritten stay shared by
reference with the template. Reverting to copy.deepcopy fails this.
"""
router = PatternMatchRouter()
shared_metadata = {"team": "core"}
deployment = Deployment(
model_name="openai/*",
litellm_params=LiteLLM_Params(model="openai/*", metadata=shared_metadata),
model_info=ModelInfo(),
)
router.add_pattern("openai/*", deployment.to_json(exclude_none=True))
stored_template = list(router.patterns.values())[0][0]
routed = router.route("openai/gpt-4o")
assert routed is not None
assert routed[0]["litellm_params"] is not stored_template["litellm_params"]
assert routed[0]["litellm_params"]["metadata"] is stored_template["litellm_params"]["metadata"]