distributed-a2a 0.1.9rc4__py3-none-any.whl → 0.1.9rc6__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.
@@ -64,11 +64,21 @@ class RoutingAgentExecutor(AgentExecutor):
64
64
  if agent_response.status == TaskState.rejected:
65
65
  routing_agent_response: RoutingResponse = await self.routing_agent(message=context.get_user_input(),
66
66
  context_id=context.context_id)
67
- agent_name: str = json.loads(routing_agent_response.agent_card)["name"]
67
+ agent_card = routing_agent_response.agent_card
68
+ if isinstance(agent_card, str):
69
+ try:
70
+ agent_card_dict = json.loads(agent_card)
71
+ except json.JSONDecodeError:
72
+ logger.error(f"Failed to parse agent_card as JSON: {agent_card}")
73
+ raise
74
+ else:
75
+ agent_card_dict = agent_card
76
+
77
+ agent_name: str = agent_card_dict["name"]
68
78
  logger.info(f"Request with id {context.context_id} got rejected and will be rerouted to a '{agent_name}'.",
69
79
  extra={"card": routing_agent_response.agent_card})
70
80
  artifact = new_text_artifact(name='target_agent', description='New target agent for request.',
71
- text=routing_agent_response.agent_card)
81
+ text=json.dumps(agent_card_dict) if isinstance(agent_card_dict, dict) else str(agent_card))
72
82
  else:
73
83
  logger.info(f"Request with id {context.context_id} was successfully processed by agent.")
74
84
  artifact = new_text_artifact(name='current_result', description='Result of request to agent.',
@@ -119,11 +129,23 @@ class RoutingExecutor(AgentExecutor):
119
129
  agent_response: RoutingResponse = await self.routing_agent(message=context.get_user_input(),
120
130
  context_id=context.context_id)
121
131
  logger.info(f"Routing agent response for request with id {context.context_id}: {agent_response}")
122
- agent_name: str = json.loads(agent_response.agent_card)["name"]
132
+ agent_card = agent_response.agent_card
133
+ if isinstance(agent_card, str):
134
+ try:
135
+ agent_card_dict = json.loads(agent_card)
136
+ except json.JSONDecodeError:
137
+ # If it's not JSON, maybe it's already the name or something else?
138
+ # But AgentCard expects a dict if we use model_validate
139
+ logger.error(f"Failed to parse agent_card as JSON: {agent_card}")
140
+ raise
141
+ else:
142
+ agent_card_dict = agent_card
143
+
144
+ agent_name: str = agent_card_dict["name"]
123
145
  logger.info(f"Request with id {context.context_id} got rejected and will be rerouted to a '{agent_name}'.",
124
- extra={"card": agent_response.agent_card})
146
+ extra={"card": agent_card})
125
147
  artifact = new_text_artifact(name='target_agent', description='New target agent for request.',
126
- text=agent_response.agent_card)
148
+ text=json.dumps(agent_card_dict) if isinstance(agent_card_dict, dict) else str(agent_card))
127
149
 
128
150
  # publish actual result
129
151
  await event_queue.enqueue_event(TaskArtifactUpdateEvent(append=False,
@@ -33,7 +33,13 @@ class AgentRegistryLookup:
33
33
  json=agent_card,
34
34
  timeout=30
35
35
  )
36
- response.raise_for_status()
36
+ try:
37
+ response.raise_for_status()
38
+ except requests.exceptions.HTTPError as e:
39
+ print(f"HTTP error during put_agent_card: {e}")
40
+ if response.text:
41
+ print(f"Response content: {response.text}")
42
+ raise
37
43
 
38
44
  def as_tool(self) -> StructuredTool:
39
45
  return StructuredTool.from_function(func=lambda: self.get_agent_cards(), name="agent_card_lookup",
distributed_a2a/router.py CHANGED
@@ -1,4 +1,5 @@
1
- from typing import Any
1
+ from contextlib import asynccontextmanager
2
+ from typing import Any, AsyncGenerator
2
3
 
3
4
  from a2a.server.apps import A2ARESTFastAPIApplication
4
5
  from a2a.server.request_handlers import DefaultRequestHandler
@@ -8,8 +9,8 @@ from fastapi import FastAPI
8
9
 
9
10
  from .executors import RoutingExecutor
10
11
  from .model import RouterConfig
11
- from .registry import DynamoDbRegistryLookup, AgentRegistryLookup
12
- from .server import CAPABILITIES, AGENT_CARD_TABLE
12
+ from .registry import AgentRegistryLookup, registry_heart_beat
13
+ from .server import CAPABILITIES, HEART_BEAT_INTERVAL_SEC, get_expire_at
13
14
 
14
15
 
15
16
  def load_router(router_dic: dict[str, Any]) -> FastAPI:
@@ -36,6 +37,19 @@ def load_router(router_dic: dict[str, Any]) -> FastAPI:
36
37
  routing_tool=AgentRegistryLookup(router_config.router.registry.agent.url).as_tool()
37
38
  )
38
39
 
40
+ @asynccontextmanager
41
+ async def lifespan(_: FastAPI) -> AsyncGenerator[None, Any]:
42
+ if router_config.router.registry:
43
+ import asyncio
44
+ asyncio.create_task(registry_heart_beat(
45
+ name=agent_card.name,
46
+ registry_url=router_config.router.registry.agent.url,
47
+ agent_card=agent_card,
48
+ interval_sec=HEART_BEAT_INTERVAL_SEC,
49
+ get_expire_at=get_expire_at
50
+ ))
51
+ yield
52
+
39
53
  return A2ARESTFastAPIApplication(
40
54
  agent_card=agent_card,
41
55
  http_handler=DefaultRequestHandler(
@@ -43,4 +57,5 @@ def load_router(router_dic: dict[str, Any]) -> FastAPI:
43
57
  task_store=InMemoryTaskStore() # TODO replace with dynamodb store
44
58
 
45
59
  )).build(title=agent_card.name,
60
+ lifespan=lifespan,
46
61
  root_path=f"/{router_config.router.card.name}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: distributed_a2a
3
- Version: 0.1.9rc4
3
+ Version: 0.1.9rc6
4
4
  Summary: A library for building A2A agents with routing capabilities
5
5
  Home-page: https://github.com/Barra-Technologies/distributed-a2a
6
6
  Author: Fabian Bell
@@ -0,0 +1,13 @@
1
+ distributed_a2a/__init__.py,sha256=gO8sTT20O6XvDRzj7B322O9v6HxV-_eIlJD1wMlPYoI,171
2
+ distributed_a2a/agent.py,sha256=qY775N3Br2YMuaHLALU4t6MO47syuEoTtFLLPRr8TyA,2910
3
+ distributed_a2a/client.py,sha256=3tEBqu3HKEQyOFk5vsO1YiuKP2pZx-n6SCzsJUYNcyc,4601
4
+ distributed_a2a/executors.py,sha256=gu3MdnUldOt-9yUJA2xkmJNoDyhwH7qyznRY4lwlTys,8226
5
+ distributed_a2a/model.py,sha256=x-Es1WIm-MMBMizD3CePXf04mpWLiqRCYbz61KGDLRc,3535
6
+ distributed_a2a/registry.py,sha256=4OK_Zbsq38UNKliyagifKfDG6nUIhflxq0VAoqeLKgM,2368
7
+ distributed_a2a/router.py,sha256=jBk_tR8Qbq-PDTYm0-R9OHCCwuEYm5qv1r5QTopJ0D4,2404
8
+ distributed_a2a/server.py,sha256=JVIcdjyjtVhP-YTeoaOb0R7d0honC_JPR0aQfJ4P_SQ,3434
9
+ distributed_a2a-0.1.9rc6.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
10
+ distributed_a2a-0.1.9rc6.dist-info/METADATA,sha256=Llaxl5djnyFtqrwbyLpCRJVhZ0e3wyGb38pamDx3xkY,3198
11
+ distributed_a2a-0.1.9rc6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
+ distributed_a2a-0.1.9rc6.dist-info/top_level.txt,sha256=23qJ8n5k7796BHDK7a58uuO-X4GV0EgUWcGi8NIn-0k,16
13
+ distributed_a2a-0.1.9rc6.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- distributed_a2a/__init__.py,sha256=gO8sTT20O6XvDRzj7B322O9v6HxV-_eIlJD1wMlPYoI,171
2
- distributed_a2a/agent.py,sha256=qY775N3Br2YMuaHLALU4t6MO47syuEoTtFLLPRr8TyA,2910
3
- distributed_a2a/client.py,sha256=3tEBqu3HKEQyOFk5vsO1YiuKP2pZx-n6SCzsJUYNcyc,4601
4
- distributed_a2a/executors.py,sha256=xinzle76ZpIpbAs1RqolaDKwABCQkz6mmrfE7gRqnFY,7258
5
- distributed_a2a/model.py,sha256=x-Es1WIm-MMBMizD3CePXf04mpWLiqRCYbz61KGDLRc,3535
6
- distributed_a2a/registry.py,sha256=2QDxCY02tkI8MsLA4JPYEzFSV4LuuKPtCVVx-u8BniU,2132
7
- distributed_a2a/router.py,sha256=SRYBgLiN4j7JDIKb_9D8ypJxqVYs_fFlG5dgWYIbF7o,1803
8
- distributed_a2a/server.py,sha256=JVIcdjyjtVhP-YTeoaOb0R7d0honC_JPR0aQfJ4P_SQ,3434
9
- distributed_a2a-0.1.9rc4.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
10
- distributed_a2a-0.1.9rc4.dist-info/METADATA,sha256=6scdu3Gkw42NX4EljgCrsyXp44Jy-Lninn2ZSQr01Kw,3198
11
- distributed_a2a-0.1.9rc4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
- distributed_a2a-0.1.9rc4.dist-info/top_level.txt,sha256=23qJ8n5k7796BHDK7a58uuO-X4GV0EgUWcGi8NIn-0k,16
13
- distributed_a2a-0.1.9rc4.dist-info/RECORD,,