fix(agents): hydrate identities on registry cache misses

This commit is contained in:
Joshua Valluru 2026-09-26 14:52:00 -07:00
parent 38c09a8d7f
commit cd55488ce6
4 changed files with 65 additions and 4 deletions

View file

@ -592,7 +592,7 @@ async def get_agent_by_id(
if agent is None:
agent_row: Final = await agents_table(prisma_client).find_unique(
where={"agent_id": agent_id},
include={"object_permission": True},
include={"object_permission": True, "identity": True},
)
if agent_row is not None:
agent_dict: Final = agent_row.model_dump()

View file

@ -174,7 +174,10 @@ async def _resync_agents(agent_id_or_name: str) -> bool:
table: Final = agents_table(prisma_client)
id_filter: Final[LiteLLM_AgentsTableWhereUniqueInput] = {"agent_id": agent_id_or_name}
name_filter: Final[LiteLLM_AgentsTableWhereUniqueInput] = {"agent_name": agent_id_or_name}
include_permission: Final[LiteLLM_AgentsTableInclude] = {"object_permission": True}
include_permission: Final[LiteLLM_AgentsTableInclude] = {
"object_permission": True,
"identity": True,
}
async with AGENT_RECONCILE_LOCK:
if _agent_from_registry(agent_id_or_name) is not None:
return True

View file

@ -1389,3 +1389,31 @@ def test_agent_identity_configuration_is_only_returned_to_admins(role, path, mon
payload = response.json()[0] if path == "/v1/agents" else response.json()
assert payload["identity"] == (binding.model_dump(mode="json") if role == LitellmUserRoles.PROXY_ADMIN else None)
assert agent.identity == binding
@pytest.mark.parametrize("role", [LitellmUserRoles.PROXY_ADMIN, LitellmUserRoles.INTERNAL_USER])
def test_agent_detail_cache_miss_preserves_admin_identity_visibility(role, monkeypatch):
binding = AgentIdentityBinding(
agent_id="agent-123", provider="microsoft_entra", tenant_id="tenant", client_id="client",
issuer="https://login.microsoftonline.com/tenant/v2.0", revision="revision",
)
agent = _sample_agent_response()
registry = MagicMock()
registry.get_agent_by_id.return_value = None
registry.ids_for_agent.return_value = frozenset({agent.agent_id})
monkeypatch.setattr(agent_endpoints, "AGENT_REGISTRY", registry)
monkeypatch.setattr(
"litellm.proxy.agent_endpoints.auth.agent_permission_handler.AgentRequestHandler.is_agent_allowed",
AsyncMock(return_value=True),
)
async def load_row(*, where, include):
assert where == {"agent_id": agent.agent_id}
return agent.model_copy(update={"identity": binding if include.get("identity") else None})
with patch("litellm.proxy.proxy_server.prisma_client") as prisma:
prisma.db.litellm_agentstable.find_unique = AsyncMock(side_effect=load_row)
prisma.db.litellm_verificationtoken.find_many = AsyncMock(return_value=[])
response = _make_app_with_role(role).get("/v1/agents/agent-123")
assert response.status_code == 200
assert response.json()["identity"] == (binding.model_dump(mode="json") if role == LitellmUserRoles.PROXY_ADMIN else None)

View file

@ -177,7 +177,7 @@ async def test_get_agent_with_read_through_recovers_agent_created_on_sibling_rep
assert agent.agent_id == agent_id
prisma_client.db.litellm_agentstable.find_unique.assert_awaited_once_with(
where={"agent_id": agent_id},
include={"object_permission": True},
include={"object_permission": True, "identity": True},
)
@ -202,7 +202,7 @@ async def test_get_agent_with_read_through_recovers_agent_by_name(clean_agent_re
assert agent.agent_name == agent_name
prisma_client.db.litellm_agentstable.find_unique.assert_awaited_with(
where={"agent_name": agent_name},
include={"object_permission": True},
include={"object_permission": True, "identity": True},
)
@ -521,3 +521,33 @@ async def test_resync_agents_waits_for_agent_reload_and_skips_duplicate_registra
assert await resync_task is True
assert len(clean_agent_registry.agent_list) == 1
@pytest.mark.asyncio
@pytest.mark.parametrize("lookup", ["agent-id", "Agent name"])
async def test_agent_read_through_hydrates_identity_binding(lookup, clean_agent_registry, fresh_agent_read_through, monkeypatch):
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock
from litellm.proxy.common_utils.registry_read_through import get_agent_with_read_through
binding = {
"agent_id": "agent-id", "provider": "microsoft_entra", "tenant_id": "tenant", "client_id": "client",
"issuer": "https://login.microsoftonline.com/tenant/v2.0", "revision": "revision",
}
async def load_row(*, where, include):
if where == {"agent_id": "Agent name"}:
return None
row = FakeAgentRow("agent-id", "Agent name").model_dump()
return SimpleNamespace(model_dump=lambda: {**row, "identity": binding if include.get("identity") else None})
prisma = MagicMock()
prisma.db.litellm_agentstable.find_unique = AsyncMock(side_effect=load_row)
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", prisma)
monkeypatch.setattr("litellm.proxy.proxy_server.store_model_in_db", True)
agent = await get_agent_with_read_through(lookup)
assert agent is not None
assert agent.identity is not None
assert agent.identity.model_dump(include=set(binding)) == binding
assert clean_agent_registry.get_agent_by_id(agent_id="agent-id").identity == agent.identity