Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions python/packages/devui/agent_framework_devui/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,22 @@ def extract_agent_metadata(entity_object: Any) -> dict[str, Any]:
if hasattr(entity_object, "client"):
metadata["chat_client_type"] = entity_object.client.__class__.__name__

# Try to get context providers
if (
hasattr(entity_object, "context_provider")
and entity_object.context_provider
and hasattr(entity_object.context_provider, "__class__")
):
metadata["context_provider"] = [entity_object.context_provider.__class__.__name__] # type: ignore
# Try to get context providers.
# ``Agent`` exposes a ``context_providers`` list; the singular
# ``context_provider`` attribute only exists on older/custom agent
# objects, so fall back to it when the list is absent.
context_provider_names: list[str] = []
providers = getattr(entity_object, "context_providers", None)
if isinstance(providers, (list, tuple)):
for provider in cast("list[Any]", providers):
if provider is not None:
context_provider_names.append(type(provider).__name__)
if not context_provider_names:
single_provider = getattr(entity_object, "context_provider", None)
if single_provider is not None:
context_provider_names = [type(single_provider).__name__]
if context_provider_names:
metadata["context_provider"] = context_provider_names # type: ignore

# Try to get middleware
if hasattr(entity_object, "middleware") and entity_object.middleware:
Expand Down
75 changes: 75 additions & 0 deletions python/packages/devui/tests/devui/test_agent_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Copyright (c) Microsoft. All rights reserved.

"""Tests for agent metadata extraction used by DevUI discovery."""

from unittest.mock import Mock

from agent_framework import Agent, ContextProvider, InMemoryHistoryProvider

from agent_framework_devui._utils import extract_agent_metadata


class _ZebraProvider(ContextProvider):
"""Provider whose class name sorts after the other one used in the order test."""


class _AardvarkProvider(ContextProvider):
"""Provider whose class name sorts before the other one used in the order test."""


def test_extract_agent_metadata_reports_configured_context_providers() -> None:
"""A configured context provider must show up in the agent metadata."""
agent = Agent(
client=Mock(model=None),
context_providers=[InMemoryHistoryProvider()],
)

metadata = extract_agent_metadata(agent)

assert metadata["context_provider"] == ["InMemoryHistoryProvider"]


def test_extract_agent_metadata_preserves_provider_order() -> None:
"""Multiple providers are listed in their configured order."""
agent = Agent(
client=Mock(model=None),
context_providers=[
_ZebraProvider(source_id="zebra"),
_AardvarkProvider(source_id="aardvark"),
],
)

metadata = extract_agent_metadata(agent)

assert metadata["context_provider"] == ["_ZebraProvider", "_AardvarkProvider"]


def test_extract_agent_metadata_without_context_providers() -> None:
"""An agent without providers reports no context provider."""
agent = Agent(client=Mock(model=None))

metadata = extract_agent_metadata(agent)

assert metadata["context_provider"] is None


def test_extract_agent_metadata_falls_back_to_single_context_provider() -> None:
"""Older agent objects exposing a single ``context_provider`` still work."""

class LegacyAgent:
context_provider = InMemoryHistoryProvider()

metadata = extract_agent_metadata(LegacyAgent())

assert metadata["context_provider"] == ["InMemoryHistoryProvider"]


def test_extract_agent_metadata_ignores_non_iterable_context_providers() -> None:
"""A truthy but non-iterable ``context_providers`` value must not break discovery."""

class OddAgent:
context_providers = Mock()

metadata = extract_agent_metadata(OddAgent())

assert metadata["context_provider"] is None
Loading