mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: [Breaking] removed pydantic from types and workflows (#917)
* removed pydantic from types * fix test * fix test * fix tests * fix assistants client * Remove Pydantic usage from workflow code. * updated pydantic removal * updated lock and test fixes * fix mypy * updated build system * updated chat client parsing * fix broken test --------- Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
647db9635a
commit
b4ebafa9b1
@@ -68,9 +68,6 @@ class A2AAgent(BaseAgent):
|
||||
Can be initialized with a URL, AgentCard, or existing A2A Client instance.
|
||||
"""
|
||||
|
||||
client: Client
|
||||
_http_client: httpx.AsyncClient | None = None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@@ -81,6 +78,7 @@ class A2AAgent(BaseAgent):
|
||||
url: str | None = None,
|
||||
client: Client | None = None,
|
||||
http_client: httpx.AsyncClient | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize the A2AAgent.
|
||||
|
||||
@@ -92,42 +90,40 @@ class A2AAgent(BaseAgent):
|
||||
url: The URL for the A2A server.
|
||||
client: The A2A client for the agent.
|
||||
http_client: Optional httpx.AsyncClient to use.
|
||||
kwargs: any additional properties, passed to BaseAgent.
|
||||
"""
|
||||
if client is None:
|
||||
if agent_card is None:
|
||||
if url is None:
|
||||
raise ValueError("Either agent_card or url must be provided")
|
||||
# Create minimal agent card from URL
|
||||
agent_card = minimal_agent_card(url, [TransportProtocol.jsonrpc])
|
||||
super().__init__(id=id, name=name, description=description, **kwargs)
|
||||
self._http_client: httpx.AsyncClient | None = http_client
|
||||
if client is not None:
|
||||
self.client = client
|
||||
self._close_http_client = True
|
||||
return
|
||||
if agent_card is None:
|
||||
if url is None:
|
||||
raise ValueError("Either agent_card or url must be provided")
|
||||
# Create minimal agent card from URL
|
||||
agent_card = minimal_agent_card(url, [TransportProtocol.jsonrpc])
|
||||
|
||||
# Create or use provided httpx client
|
||||
if http_client is None:
|
||||
timeout = httpx.Timeout(
|
||||
connect=10.0, # 10 seconds to establish connection
|
||||
read=60.0, # 60 seconds to read response (A2A operations can take time)
|
||||
write=10.0, # 10 seconds to send request
|
||||
pool=5.0, # 5 seconds to get connection from pool
|
||||
)
|
||||
headers = prepend_agent_framework_to_user_agent()
|
||||
http_client = httpx.AsyncClient(timeout=timeout, headers=headers)
|
||||
self._http_client = http_client # Store for cleanup
|
||||
|
||||
# Create A2A client using factory
|
||||
config = ClientConfig(
|
||||
httpx_client=http_client,
|
||||
supported_transports=[TransportProtocol.jsonrpc],
|
||||
# Create or use provided httpx client
|
||||
if http_client is None:
|
||||
timeout = httpx.Timeout(
|
||||
connect=10.0, # 10 seconds to establish connection
|
||||
read=60.0, # 60 seconds to read response (A2A operations can take time)
|
||||
write=10.0, # 10 seconds to send request
|
||||
pool=5.0, # 5 seconds to get connection from pool
|
||||
)
|
||||
factory = ClientFactory(config)
|
||||
client = factory.create(agent_card)
|
||||
headers = prepend_agent_framework_to_user_agent()
|
||||
http_client = httpx.AsyncClient(timeout=timeout, headers=headers)
|
||||
self._http_client = http_client # Store for cleanup
|
||||
self._close_http_client = True
|
||||
|
||||
args: dict[str, Any] = {"client": client}
|
||||
if name:
|
||||
args["name"] = name
|
||||
if id:
|
||||
args["id"] = id
|
||||
if description:
|
||||
args["description"] = description
|
||||
super().__init__(**args)
|
||||
# Create A2A client using factory
|
||||
config = ClientConfig(
|
||||
httpx_client=http_client,
|
||||
supported_transports=[TransportProtocol.jsonrpc],
|
||||
)
|
||||
factory = ClientFactory(config)
|
||||
self.client = factory.create(agent_card)
|
||||
|
||||
async def __aenter__(self) -> "A2AAgent":
|
||||
"""Async context manager entry."""
|
||||
@@ -141,7 +137,7 @@ class A2AAgent(BaseAgent):
|
||||
) -> None:
|
||||
"""Async context manager exit with httpx client cleanup."""
|
||||
# Close our httpx client if we created it
|
||||
if self._http_client is not None:
|
||||
if self._http_client is not None and self._close_http_client:
|
||||
await self._http_client.aclose()
|
||||
|
||||
async def run(
|
||||
|
||||
@@ -90,14 +90,14 @@ def mock_a2a_client() -> MockA2AClient:
|
||||
@fixture
|
||||
def a2a_agent(mock_a2a_client: MockA2AClient) -> A2AAgent:
|
||||
"""Fixture that provides an A2AAgent with a mock client."""
|
||||
return A2AAgent.model_construct(name="Test Agent", id="test-agent", client=mock_a2a_client, _http_client=None)
|
||||
return A2AAgent(name="Test Agent", id="test-agent", client=mock_a2a_client, http_client=None)
|
||||
|
||||
|
||||
def test_a2a_agent_initialization_with_client(mock_a2a_client: MockA2AClient) -> None:
|
||||
"""Test A2AAgent initialization with provided client."""
|
||||
# Use model_construct to bypass Pydantic validation for mock objects
|
||||
agent = A2AAgent.model_construct(
|
||||
name="Test Agent", id="test-agent-123", description="A test agent", client=mock_a2a_client, _http_client=None
|
||||
agent = A2AAgent(
|
||||
name="Test Agent", id="test-agent-123", description="A test agent", client=mock_a2a_client, http_client=None
|
||||
)
|
||||
|
||||
assert agent.name == "Test Agent"
|
||||
@@ -266,7 +266,7 @@ def test_get_uri_data_invalid_uri() -> None:
|
||||
def test_a2a_parts_to_contents_conversion(a2a_agent: A2AAgent) -> None:
|
||||
"""Test A2A parts to contents conversion."""
|
||||
|
||||
agent = A2AAgent.model_construct(name="Test Agent", client=MockA2AClient(), _http_client=None)
|
||||
agent = A2AAgent(name="Test Agent", client=MockA2AClient(), _http_client=None)
|
||||
|
||||
# Create A2A parts
|
||||
parts = [Part(root=TextPart(text="First part")), Part(root=TextPart(text="Second part"))]
|
||||
@@ -369,7 +369,8 @@ async def test_context_manager_cleanup() -> None:
|
||||
mock_http_client = AsyncMock()
|
||||
mock_a2a_client = MagicMock()
|
||||
|
||||
agent = A2AAgent.model_construct(client=mock_a2a_client, _http_client=mock_http_client)
|
||||
agent = A2AAgent(client=mock_a2a_client)
|
||||
agent._http_client = mock_http_client
|
||||
|
||||
# Test context manager cleanup
|
||||
async with agent:
|
||||
@@ -384,7 +385,7 @@ async def test_context_manager_no_cleanup_when_no_http_client() -> None:
|
||||
|
||||
mock_a2a_client = MagicMock()
|
||||
|
||||
agent = A2AAgent.model_construct(client=mock_a2a_client, _http_client=None)
|
||||
agent = A2AAgent(client=mock_a2a_client, _http_client=None)
|
||||
|
||||
# This should not raise any errors
|
||||
async with agent:
|
||||
@@ -394,7 +395,7 @@ async def test_context_manager_no_cleanup_when_no_http_client() -> None:
|
||||
def test_chat_message_to_a2a_message_with_multiple_contents() -> None:
|
||||
"""Test conversion of ChatMessage with multiple contents."""
|
||||
|
||||
agent = A2AAgent.model_construct(client=MagicMock(), _http_client=None)
|
||||
agent = A2AAgent(client=MagicMock(), _http_client=None)
|
||||
|
||||
# Create message with multiple content types
|
||||
message = ChatMessage(
|
||||
@@ -422,7 +423,7 @@ def test_chat_message_to_a2a_message_with_multiple_contents() -> None:
|
||||
def test_a2a_parts_to_contents_with_data_part() -> None:
|
||||
"""Test conversion of A2A DataPart."""
|
||||
|
||||
agent = A2AAgent.model_construct(client=MagicMock(), _http_client=None)
|
||||
agent = A2AAgent(client=MagicMock(), _http_client=None)
|
||||
|
||||
# Create DataPart
|
||||
data_part = Part(root=DataPart(data={"key": "value", "number": 42}, metadata={"source": "test"}))
|
||||
@@ -438,7 +439,7 @@ def test_a2a_parts_to_contents_with_data_part() -> None:
|
||||
|
||||
def test_a2a_parts_to_contents_unknown_part_kind() -> None:
|
||||
"""Test error handling for unknown A2A part kind."""
|
||||
agent = A2AAgent.model_construct(client=MagicMock(), _http_client=None)
|
||||
agent = A2AAgent(client=MagicMock(), _http_client=None)
|
||||
|
||||
# Create a mock part with unknown kind
|
||||
mock_part = MagicMock()
|
||||
@@ -451,7 +452,7 @@ def test_a2a_parts_to_contents_unknown_part_kind() -> None:
|
||||
def test_chat_message_to_a2a_message_with_hosted_file() -> None:
|
||||
"""Test conversion of ChatMessage with HostedFileContent to A2A message."""
|
||||
|
||||
agent = A2AAgent.model_construct(client=MagicMock(), _http_client=None)
|
||||
agent = A2AAgent(client=MagicMock(), _http_client=None)
|
||||
|
||||
# Create message with hosted file content
|
||||
message = ChatMessage(
|
||||
@@ -477,7 +478,7 @@ def test_chat_message_to_a2a_message_with_hosted_file() -> None:
|
||||
def test_a2a_parts_to_contents_with_hosted_file_uri() -> None:
|
||||
"""Test conversion of A2A FilePart with hosted file URI back to UriContent."""
|
||||
|
||||
agent = A2AAgent.model_construct(client=MagicMock(), _http_client=None)
|
||||
agent = A2AAgent(client=MagicMock(), _http_client=None)
|
||||
|
||||
# Create FilePart with hosted file URI (simulating what A2A would send back)
|
||||
file_part = Part(
|
||||
|
||||
Reference in New Issue
Block a user