distributed-a2a 0.1.6rc5__py3-none-any.whl → 0.1.6rc7__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/agent.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Literal
1
+ from typing import Literal, Optional
2
2
 
3
3
  from a2a.types import TaskState
4
4
  from langchain.agents import create_agent
@@ -7,7 +7,7 @@ from langchain_core.tools import BaseTool
7
7
  from langgraph_dynamodb_checkpoint import DynamoDBSaver
8
8
  from pydantic import BaseModel, Field
9
9
 
10
- from .model import get_model, AgentConfig, LLMConfig
10
+ from .model import get_model, LLMConfig
11
11
 
12
12
 
13
13
  class AgentResponse(BaseModel):
@@ -58,10 +58,10 @@ class StatusAgent[ResponseT: AgentResponse]:
58
58
  name=name
59
59
  )
60
60
 
61
- async def __call__(self, message: str, context_id: str = None) -> ResponseT:
61
+ async def __call__(self, message: str, context_id: Optional[str] = None) -> ResponseT:
62
62
  config: RunnableConfig = RunnableConfig(configurable={'thread_id': context_id})
63
- response = await self.agent.ainvoke(LangGraphMessage(message), config)
64
- return response['structured_response']
63
+ response = await self.agent.ainvoke({"messages": [("user", message)]}, config) # type: ignore[arg-type]
64
+ return response['structured_response'] # type: ignore[no-any-return]
65
65
 
66
66
 
67
67
  class LangGraphMessage(BaseModel):
distributed_a2a/client.py CHANGED
@@ -16,6 +16,11 @@ class RemoteAgentConnection:
16
16
  """A class to hold the connections to the remote agents."""
17
17
 
18
18
  def __init__(self, agent_card: AgentCard, client: httpx.AsyncClient):
19
+ if agent_card.preferred_transport is None:
20
+ raise ValueError("Agent card preferred transport must be provided.")
21
+ if agent_card.capabilities.streaming is None:
22
+ raise ValueError("Agent card streaming capability must be provided.")
23
+
19
24
  client_config = ClientConfig(
20
25
  httpx_client=client,
21
26
  supported_transports=[agent_card.preferred_transport],
@@ -29,7 +34,8 @@ class RemoteAgentConnection:
29
34
 
30
35
  responses: list[ClientEvent] = []
31
36
  async for response in self.agent_client.send_message(message_request):
32
- responses.append(response)
37
+ if isinstance(response, tuple):
38
+ responses.append(response)
33
39
 
34
40
  task_response: Task | None = None
35
41
  match responses:
@@ -44,8 +50,8 @@ class RemoteAgentConnection:
44
50
  response: Task = await self.agent_client.get_task(query_params)
45
51
  return response
46
52
 
47
- async def send_message(self, message_to_send: str, context_id, task_id: None | str = None,
48
- count=0) -> str | AgentCard:
53
+ async def send_message(self, message_to_send: str, context_id: str, task_id: None | str = None,
54
+ count: int = 0) -> str | AgentCard:
49
55
  message: Message = create_text_message_object(content=message_to_send)
50
56
  message.message_id = str(uuid4())
51
57
  message.context_id = context_id
@@ -87,7 +93,7 @@ class RoutingA2AClient:
87
93
  self.client = httpx.AsyncClient()
88
94
  self.current_card: AgentCard | None = None
89
95
 
90
- async def fetch_current_card(self):
96
+ async def fetch_current_card(self) -> None:
91
97
  card_resolver = A2ACardResolver(
92
98
  self.client, self.url
93
99
  )
@@ -100,6 +106,9 @@ class RoutingA2AClient:
100
106
  raise Exception("Maximum recursion depth exceeded. This is likely due to an infinite loop in your agent.")
101
107
  if self.current_card is None:
102
108
  await self.fetch_current_card()
109
+
110
+ if self.current_card is None:
111
+ raise ValueError("Failed to fetch current agent card.")
103
112
 
104
113
  agent_connection = RemoteAgentConnection(self.current_card, self.client)
105
114
 
@@ -25,6 +25,9 @@ class RoutingAgentExecutor(AgentExecutor):
25
25
  def __init__(self, agent_config: AgentConfig, routing_tool: BaseTool, tools: list[BaseTool] | None = None):
26
26
  super().__init__()
27
27
  api_key = os.environ.get(agent_config.agent.llm.api_key_env)
28
+ if api_key is None:
29
+ raise ValueError("No API key found for LLM.")
30
+
28
31
  self.agent = StatusAgent[StringResponse](
29
32
  llm_config=agent_config.agent.llm,
30
33
  system_prompt=agent_config.agent.system_prompt,
@@ -46,6 +49,9 @@ class RoutingAgentExecutor(AgentExecutor):
46
49
  raise NotImplementedError
47
50
 
48
51
  async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
52
+ if context.context_id is None or context.task_id is None:
53
+ raise ValueError("Context ID and Task ID must be provided.")
54
+
49
55
  # set status to processing
50
56
  await event_queue.enqueue_event(TaskStatusUpdateEvent(status=TaskStatus(state=TaskState.working),
51
57
  final=False,
@@ -56,13 +62,13 @@ class RoutingAgentExecutor(AgentExecutor):
56
62
 
57
63
  artifact: Artifact
58
64
  if agent_response.status == TaskState.rejected:
59
- agent_response: RoutingResponse = await self.routing_agent(message=context.get_user_input(),
65
+ routing_agent_response: RoutingResponse = await self.routing_agent(message=context.get_user_input(),
60
66
  context_id=context.context_id)
61
- agent_name: str = json.loads(agent_response.agent_card)["name"]
67
+ agent_name: str = json.loads(routing_agent_response.agent_card)["name"]
62
68
  logger.info(f"Request with id {context.context_id} got rejected and will be rerouted to a '{agent_name}'.",
63
- extra={"card": agent_response.agent_card})
69
+ extra={"card": routing_agent_response.agent_card})
64
70
  artifact = new_text_artifact(name='target_agent', description='New target agent for request.',
65
- text=agent_response.agent_card)
71
+ text=routing_agent_response.agent_card)
66
72
  else:
67
73
  logger.info(f"Request with id {context.context_id} was successfully processed by agent.")
68
74
  artifact = new_text_artifact(name='current_result', description='Result of request to agent.',
@@ -86,6 +92,9 @@ class RoutingExecutor(AgentExecutor):
86
92
  def __init__(self, router_config: RouterConfig, routing_tool: BaseTool) -> None:
87
93
  super().__init__()
88
94
  api_key = os.environ.get(router_config.router.llm.api_key_env)
95
+ if api_key is None:
96
+ raise ValueError("No API key found for LLM.")
97
+
89
98
  self.routing_agent = StatusAgent[RoutingResponse](
90
99
  llm_config=router_config.router.llm,
91
100
  system_prompt=ROUTING_SYSTEM_PROMPT,
@@ -99,6 +108,9 @@ class RoutingExecutor(AgentExecutor):
99
108
  raise NotImplementedError
100
109
 
101
110
  async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
111
+ if context.context_id is None or context.task_id is None:
112
+ raise ValueError("Context ID and Task ID must be provided.")
113
+
102
114
  await event_queue.enqueue_event(TaskStatusUpdateEvent(status=TaskStatus(state=TaskState.working),
103
115
  final=False,
104
116
  context_id=context.context_id,
distributed_a2a/model.py CHANGED
@@ -59,8 +59,8 @@ class RouterConfig(BaseModel):
59
59
 
60
60
  def get_model(api_key: str, model: str, base_url: str, reasoning_effort: str) -> BaseChatModel:
61
61
  return ChatOpenAI(
62
- model=model,
63
- base_url=base_url,
64
- api_key=lambda: api_key,
62
+ model_name=model,
63
+ openai_api_base=base_url,
64
+ openai_api_key=lambda: api_key,
65
65
  reasoning_effort=reasoning_effort
66
66
  )
distributed_a2a/router.py CHANGED
@@ -1,4 +1,3 @@
1
- import os
2
1
  from typing import Any
3
2
 
4
3
  from a2a.server.apps import A2ARESTFastAPIApplication
@@ -45,4 +44,4 @@ def load_router(router_dic: dict[str, Any]) -> FastAPI:
45
44
  task_store=InMemoryTaskStore() # TODO replace with dynamodb store
46
45
 
47
46
  )).build(title=agent_card.name,
48
- root_path=f"/{router_config.agent.card.name}")
47
+ root_path=f"/{router_config.router.card.name}")
distributed_a2a/server.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  import time
3
3
  from contextlib import asynccontextmanager
4
- from typing import Any
4
+ from typing import Any, AsyncGenerator
5
5
 
6
6
  import boto3
7
7
  from a2a.server.apps import A2ARESTFastAPIApplication
@@ -25,7 +25,7 @@ AGENT_CARD_TABLE = "agent-cards"
25
25
  def get_expire_at() -> int:
26
26
  return int(time.time() + MAX_HEART_BEAT_MISSES * HEART_BEAT_INTERVAL_SEC)
27
27
 
28
- async def heart_beat(name: str, agent_card_table: str, agent_card: AgentCard):
28
+ async def heart_beat(name: str, agent_card_table: str, agent_card: AgentCard) -> None:
29
29
  table = boto3.resource("dynamodb", region_name="eu-central-1").Table(agent_card_table)
30
30
  table.put_item(Item={"id": name, "card": agent_card.model_dump_json(), "expireAt": get_expire_at()})
31
31
  while True:
@@ -39,9 +39,9 @@ async def heart_beat(name: str, agent_card_table: str, agent_card: AgentCard):
39
39
 
40
40
 
41
41
 
42
- def load_app(agent_config: dict[str, Any]) -> FastAPI:
42
+ def load_app(agent_config: Any) -> FastAPI:
43
43
 
44
- agent_config= AgentConfig.model_validate(agent_config)
44
+ agent_config= AgentConfig.model_validate(obj=agent_config)
45
45
 
46
46
  skills = [AgentSkill(
47
47
  id=skill.id,
@@ -73,7 +73,7 @@ def load_app(agent_config: dict[str, Any]) -> FastAPI:
73
73
  routing_tool=DynamoDbRegistryLookup(agent_card_tabel=AGENT_CARD_TABLE).as_tool())
74
74
 
75
75
  @asynccontextmanager
76
- async def lifespan(_: FastAPI):
76
+ async def lifespan(_: FastAPI) -> AsyncGenerator[None, Any]:
77
77
  asyncio.create_task(heart_beat(name=agent_card.name, agent_card_table=AGENT_CARD_TABLE, agent_card=agent_card))
78
78
  yield
79
79
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: distributed_a2a
3
- Version: 0.1.6rc5
3
+ Version: 0.1.6rc7
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=c0ykzMDSizgDn2njXoCXePHMaPzy2YIA22WLURLYs-0,2930
3
+ distributed_a2a/client.py,sha256=3tEBqu3HKEQyOFk5vsO1YiuKP2pZx-n6SCzsJUYNcyc,4601
4
+ distributed_a2a/executors.py,sha256=1toayqLUan0f1INyMo5v02L9uCHMq6hp1gf0jVdjX6I,7152
5
+ distributed_a2a/model.py,sha256=K4ltuBHTm_r5I-viITDt4GrxntwcUj1sOjLenHYB79k,2981
6
+ distributed_a2a/registry.py,sha256=197eZVR6TW0isOUE0VjWSWs7aCqbWqnBzcV8EVXAxrI,677
7
+ distributed_a2a/router.py,sha256=NV8aKraFNrI53PxJAGXI5NtG3-Oy7x5ofHfgqeqsnsI,1780
8
+ distributed_a2a/server.py,sha256=6DWLHc4ju5WK5IdmPteetn7V8dnCttAYEf5W8bryTpk,3131
9
+ distributed_a2a-0.1.6rc7.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
10
+ distributed_a2a-0.1.6rc7.dist-info/METADATA,sha256=eVRPZpS4ZzD1nXITqedwOryspHKlA7ThISaD2lbIt2M,3160
11
+ distributed_a2a-0.1.6rc7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
12
+ distributed_a2a-0.1.6rc7.dist-info/top_level.txt,sha256=23qJ8n5k7796BHDK7a58uuO-X4GV0EgUWcGi8NIn-0k,16
13
+ distributed_a2a-0.1.6rc7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,13 +0,0 @@
1
- distributed_a2a/__init__.py,sha256=gO8sTT20O6XvDRzj7B322O9v6HxV-_eIlJD1wMlPYoI,171
2
- distributed_a2a/agent.py,sha256=ZI6sFISU5nELEqzVXbZaf7osqVH095raxjmrFzipoDA,2858
3
- distributed_a2a/client.py,sha256=2974Uw8YUuyBytwxxJJKYsWXCpEaIbGmMUHDraITxJ0,4149
4
- distributed_a2a/executors.py,sha256=PV9WvbicBjAENhAK4yRGjNWg8Y9O7rFAjxzdUCIabWs,6666
5
- distributed_a2a/model.py,sha256=S6o56cQysSp1Nq3-gbawa2xsvMWd4hHYUj6aW3yWwTA,2962
6
- distributed_a2a/registry.py,sha256=197eZVR6TW0isOUE0VjWSWs7aCqbWqnBzcV8EVXAxrI,677
7
- distributed_a2a/router.py,sha256=UlYMJ4BT9xAo452eBfYn9U10_1QuEk_3yZagiJ18hlg,1789
8
- distributed_a2a/server.py,sha256=eVccqBos4nMQqe9feyglGlMmUYIGz2t5jmBbMVt-NGE,3085
9
- distributed_a2a-0.1.6rc5.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
10
- distributed_a2a-0.1.6rc5.dist-info/METADATA,sha256=rg1qaBK6HhF4PC1EXKBw62qIjzS46IjfWG4RRYU1RrM,3160
11
- distributed_a2a-0.1.6rc5.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
12
- distributed_a2a-0.1.6rc5.dist-info/top_level.txt,sha256=23qJ8n5k7796BHDK7a58uuO-X4GV0EgUWcGi8NIn-0k,16
13
- distributed_a2a-0.1.6rc5.dist-info/RECORD,,