chore(release): cherry-pick #28284 and #28683 into patch/1.86.0 - #28731
Conversation
…8683) * fix(team): refresh team cache on team_model_add/delete (LIT-3244) team_model_add and team_model_delete wrote to the DB but did not invalidate the in-memory LiteLLM_TeamTableCachedObj used by common_checks. After the v1.83.14 common_checks centralization made team.models authoritative on /v1/files and /v1/vector_stores/*, adding a Team-BYOK model silently failed to grant the new public model name to team members until the cache TTL expired (and a removed model kept working until then on the symmetric path). Extract the cache-refresh snippet from update_team into a small helper and apply it consistently at all three team-write sites. * test: also assert updated models in team-cache-refresh pin Strengthens the LIT-3244 regression test to also assert `call_kwargs["team_table"].models` matches the updated row, not just `team_id`. Both `existing_team` and `updated_team` share `team_id` in the test setup, so the previous assertion would have passed even if the implementation accidentally cached the pre-mutation row. Greptile review feedback. * fix(team): hydrate object_permission on cache-refreshing team updates The Prisma update calls in update_team, team_model_add, and team_model_delete returned a team row with object_permission_id set but object_permission=None (the relation was not requested via include=). _refresh_cached_team then wrote that to the in-memory LiteLLM_TeamTableCachedObj, and the cache-hit path in get_team_object returns the cached object without re-hydrating. Downstream consumers (validate_key_search_tools_against_team, the MCP/agent authz paths) treat a missing object_permission as no team-level restriction, so a team-write op silently dropped object-permission enforcement until the cache TTL expired or a DB-fetch path re-hydrated it. Add include={"object_permission": True} to all three updates so the refresh writes a complete cached team. Extend the LIT-3244 regression test to pin both the cached object_permission and the include shape on the Prisma call. Surfaced in PR review of LIT-3244. (cherry picked from commit 5f73ad4)
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Greptile SummaryThis cherry-pick combines two bug fixes for the 1.86.0 patch release: wildcard credential hydration for BYOK discovery routes, and team cache staleness after
Confidence Score: 4/5The core production changes are correct: credentials are hydrated before wildcard discovery and the team cache is refreshed consistently after DB writes. Both fixes are well-motivated and the new code paths are covered by focused mock tests. The The parametrized test in
|
| Filename | Overview |
|---|---|
| litellm/proxy/auth/model_checks.py | Adds _hydrate_litellm_credential_name to resolve credential-name references before wildcard model discovery, and threads team_id through the wildcard expansion path so team-scoped BYOK deployments are found correctly. |
| litellm/proxy/management_endpoints/team_endpoints.py | Introduces _refresh_cached_team helper and wires it into team_model_add, team_model_delete, and update_team so the in-memory team cache stays in sync after every DB write. |
| litellm/proxy/utils.py | Computes effective_team_id (query param or key's own team_id) and passes it to get_complete_model_list so the wildcard expansion uses the correct team-scoped deployment. |
| tests/test_litellm/proxy/auth/test_model_checks.py | Adds four focused mock-only tests covering credential hydration for wildcard discovery and end-to-end get_available_models_for_user flow. |
| tests/test_litellm/proxy/management_endpoints/test_team_endpoints.py | Adds a parametrized regression pin for LIT-3244 cache refresh and updates two existing tests to patch the re-exported _cache_team_object binding correctly. |
Reviews (1): Last reviewed commit: "fix(team): refresh team cache on team_mo..." | Re-trigger Greptile
| updated_team = MagicMock() | ||
| updated_team.team_id = "team-1234" | ||
| updated_team.model_dump.return_value = { | ||
| "team_id": "team-1234", | ||
| "models": ["bedrock-claude-sonnet-4", "openai/*", "team-byok-1"], | ||
| # The Prisma update must come back with `object_permission` populated | ||
| # (via `include={"object_permission": True}`), otherwise the cache | ||
| # write below would null it out — see LIT-3244 follow-up. | ||
| "object_permission_id": "op-1234", | ||
| "object_permission": { | ||
| "object_permission_id": "op-1234", | ||
| "search_tools": ["allowed-tool-A"], | ||
| }, | ||
| } |
There was a problem hiding this comment.
The
updated_team mock is shared between both the team_model_add and team_model_delete parametrized cases, and its models list (["bedrock-claude-sonnet-4", "openai/*", "team-byok-1"]) only makes sense for the add case. The delete case asserts that the cached models still include "openai/*" and "team-byok-1", which is logically backwards. The regression pin still holds because the key check is that the mock's return value (not existing_team) was cached, but the model-list assertion passes for reasons unrelated to the delete path's correctness.
| updated_team = MagicMock() | |
| updated_team.team_id = "team-1234" | |
| updated_team.model_dump.return_value = { | |
| "team_id": "team-1234", | |
| "models": ["bedrock-claude-sonnet-4", "openai/*", "team-byok-1"], | |
| # The Prisma update must come back with `object_permission` populated | |
| # (via `include={"object_permission": True}`), otherwise the cache | |
| # write below would null it out — see LIT-3244 follow-up. | |
| "object_permission_id": "op-1234", | |
| "object_permission": { | |
| "object_permission_id": "op-1234", | |
| "search_tools": ["allowed-tool-A"], | |
| }, | |
| } | |
| add_updated_models = ["bedrock-claude-sonnet-4", "openai/*", "team-byok-1"] | |
| delete_updated_models = ["bedrock-claude-sonnet-4"] | |
| updated_team = MagicMock() | |
| updated_team.team_id = "team-1234" | |
| updated_team.model_dump.return_value = { | |
| "team_id": "team-1234", | |
| "models": add_updated_models if endpoint_name == "team_model_add" else delete_updated_models, | |
| # The Prisma update must come back with `object_permission` populated | |
| # (via `include={"object_permission": True}`), otherwise the cache | |
| # write below would null it out — see LIT-3244 follow-up. | |
| "object_permission_id": "op-1234", | |
| "object_permission": { | |
| "object_permission_id": "op-1234", | |
| "search_tools": ["allowed-tool-A"], | |
| }, | |
| } |
Summary
Cherry-picks onto
patch/1.86.0for the 1.86.0 stable release.37ef8d9059)5f73ad4fe7)Test plan
patch/1.86.0