distributed-a2a 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- distributed_a2a/__init__.py +3 -1
- distributed_a2a/client.py +110 -0
- {distributed_a2a-0.1.1.dist-info → distributed_a2a-0.1.3.dist-info}/METADATA +1 -1
- {distributed_a2a-0.1.1.dist-info → distributed_a2a-0.1.3.dist-info}/RECORD +7 -6
- {distributed_a2a-0.1.1.dist-info → distributed_a2a-0.1.3.dist-info}/WHEEL +0 -0
- {distributed_a2a-0.1.1.dist-info → distributed_a2a-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {distributed_a2a-0.1.1.dist-info → distributed_a2a-0.1.3.dist-info}/top_level.txt +0 -0
distributed_a2a/__init__.py
CHANGED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
from uuid import uuid4
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from a2a.client import ClientConfig, ClientFactory, A2ACardResolver, ClientEvent
|
|
7
|
+
from a2a.client import create_text_message_object
|
|
8
|
+
from a2a.types import (
|
|
9
|
+
AgentCard,
|
|
10
|
+
Message, TaskQueryParams, Task, Artifact, Part, TextPart
|
|
11
|
+
)
|
|
12
|
+
from a2a.types import TaskState
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RemoteAgentConnection:
|
|
16
|
+
"""A class to hold the connections to the remote agents."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, agent_card: AgentCard, client: httpx.AsyncClient):
|
|
19
|
+
client_config = ClientConfig(
|
|
20
|
+
httpx_client=client,
|
|
21
|
+
supported_transports=[agent_card.preferred_transport],
|
|
22
|
+
streaming=agent_card.capabilities.streaming,
|
|
23
|
+
polling=True
|
|
24
|
+
)
|
|
25
|
+
client_factory = ClientFactory(config=client_config)
|
|
26
|
+
self.agent_client = client_factory.create(agent_card)
|
|
27
|
+
|
|
28
|
+
async def _send_message_to_agent(self, message_request: Message) -> Task:
|
|
29
|
+
|
|
30
|
+
responses: list[ClientEvent] = []
|
|
31
|
+
async for response in self.agent_client.send_message(message_request):
|
|
32
|
+
responses.append(response)
|
|
33
|
+
|
|
34
|
+
task_response: Task | None = None
|
|
35
|
+
match responses:
|
|
36
|
+
case [(task, _)]:
|
|
37
|
+
task_response = task
|
|
38
|
+
case _:
|
|
39
|
+
raise Exception("Wrong response format")
|
|
40
|
+
return task_response
|
|
41
|
+
|
|
42
|
+
async def _get_task(self, task_id: str) -> Task:
|
|
43
|
+
query_params: TaskQueryParams = TaskQueryParams(id=task_id)
|
|
44
|
+
response: Task = await self.agent_client.get_task(query_params)
|
|
45
|
+
return response
|
|
46
|
+
|
|
47
|
+
async def send_message(self, message_to_send: str, context_id, task_id: None | str = None,
|
|
48
|
+
count=0) -> str | AgentCard:
|
|
49
|
+
message: Message = create_text_message_object(content=message_to_send)
|
|
50
|
+
message.message_id = str(uuid4())
|
|
51
|
+
message.context_id = context_id
|
|
52
|
+
|
|
53
|
+
response: Task
|
|
54
|
+
if task_id is None:
|
|
55
|
+
response = await self._send_message_to_agent(message)
|
|
56
|
+
else:
|
|
57
|
+
response = await self._get_task(task_id)
|
|
58
|
+
|
|
59
|
+
task_state = response.status.state
|
|
60
|
+
if task_state == TaskState.working or task_state == TaskState.submitted:
|
|
61
|
+
if count < 20:
|
|
62
|
+
time.sleep(1)
|
|
63
|
+
return await self.send_message(message_to_send, context_id, response.id, count + 1)
|
|
64
|
+
else:
|
|
65
|
+
raise Exception("Timeout waiting for agent to respond")
|
|
66
|
+
|
|
67
|
+
if task_state == TaskState.failed:
|
|
68
|
+
raise Exception("A2ATaskFailed")
|
|
69
|
+
elif task_state == TaskState.auth_required:
|
|
70
|
+
raise Exception("A2ATaskAuthRequired")
|
|
71
|
+
|
|
72
|
+
match response.artifacts:
|
|
73
|
+
case [Artifact(name='target_agent', parts=[Part(root=TextPart(text=agent_card))])]:
|
|
74
|
+
return AgentCard(**json.loads(agent_card))
|
|
75
|
+
case [Artifact(name='current_result', parts=[Part(root=TextPart(text=result))])]:
|
|
76
|
+
return result
|
|
77
|
+
case _:
|
|
78
|
+
raise Exception("Wrong response format")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
MAX_RECURSION_DEPTH = 10
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class RoutingA2AClient:
|
|
85
|
+
def __init__(self, initial_url: str):
|
|
86
|
+
self.url = initial_url
|
|
87
|
+
self.client = httpx.AsyncClient()
|
|
88
|
+
self.current_card: AgentCard | None = None
|
|
89
|
+
|
|
90
|
+
async def fetch_current_card(self):
|
|
91
|
+
card_resolver = A2ACardResolver(
|
|
92
|
+
self.client, self.url
|
|
93
|
+
)
|
|
94
|
+
self.current_card = (
|
|
95
|
+
await card_resolver.get_agent_card()
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
async def send_message(self, message: str, context_id: str, depth: int = 0) -> str:
|
|
99
|
+
if depth > MAX_RECURSION_DEPTH:
|
|
100
|
+
raise Exception("Maximum recursion depth exceeded. This is likely due to an infinite loop in your agent.")
|
|
101
|
+
if self.current_card is None:
|
|
102
|
+
await self.fetch_current_card()
|
|
103
|
+
|
|
104
|
+
agent_connection = RemoteAgentConnection(self.current_card, self.client)
|
|
105
|
+
|
|
106
|
+
agent_response: str | AgentCard = await agent_connection.send_message(message, context_id)
|
|
107
|
+
if isinstance(agent_response, AgentCard):
|
|
108
|
+
self.current_card = agent_response
|
|
109
|
+
return await self.send_message(message, context_id, depth + 1)
|
|
110
|
+
return agent_response
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
distributed_a2a/__init__.py,sha256=
|
|
1
|
+
distributed_a2a/__init__.py,sha256=1q_7gRfBCGe-7sF_9YKzevyS97XQhtuFnZiYHIqaU-0,120
|
|
2
2
|
distributed_a2a/agent.py,sha256=dv4EBN70E4PoW9XxErwUEievDB7X-gyz8bmbG0Z-05U,2376
|
|
3
|
+
distributed_a2a/client.py,sha256=2974Uw8YUuyBytwxxJJKYsWXCpEaIbGmMUHDraITxJ0,4149
|
|
3
4
|
distributed_a2a/executors.py,sha256=xXiJydbb4modTdn7XmDflJ4QZ6v_SBeiIrLlcNErc9M,3951
|
|
4
5
|
distributed_a2a/model.py,sha256=I-e0V1cqxbRNi2LdSZW8XPkgqncy9zX_dOXAusuqPrQ,331
|
|
5
6
|
distributed_a2a/registry.py,sha256=197eZVR6TW0isOUE0VjWSWs7aCqbWqnBzcV8EVXAxrI,677
|
|
6
7
|
distributed_a2a/server.py,sha256=C83rt-T3_EGQbDfPmncSvpa9PYbJxweHc5tKFmTOnvI,2601
|
|
7
|
-
distributed_a2a-0.1.
|
|
8
|
-
distributed_a2a-0.1.
|
|
9
|
-
distributed_a2a-0.1.
|
|
10
|
-
distributed_a2a-0.1.
|
|
11
|
-
distributed_a2a-0.1.
|
|
8
|
+
distributed_a2a-0.1.3.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
|
|
9
|
+
distributed_a2a-0.1.3.dist-info/METADATA,sha256=D0xe5WxwE8nfTlR8ZDoIP_b2FEFlkJDcqpKe6apr09c,3604
|
|
10
|
+
distributed_a2a-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
distributed_a2a-0.1.3.dist-info/top_level.txt,sha256=23qJ8n5k7796BHDK7a58uuO-X4GV0EgUWcGi8NIn-0k,16
|
|
12
|
+
distributed_a2a-0.1.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|