distributed-a2a 0.1.8__tar.gz → 0.1.9rc1__tar.gz

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.
Files changed (22) hide show
  1. {distributed_a2a-0.1.8/distributed_a2a.egg-info → distributed_a2a-0.1.9rc1}/PKG-INFO +1 -1
  2. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a/model.py +12 -1
  3. distributed_a2a-0.1.9rc1/distributed_a2a/registry.py +55 -0
  4. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a/router.py +2 -3
  5. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a/server.py +11 -5
  6. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1/distributed_a2a.egg-info}/PKG-INFO +1 -1
  7. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/pyproject.toml +1 -1
  8. distributed_a2a-0.1.8/distributed_a2a/registry.py +0 -18
  9. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/LICENSE +0 -0
  10. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/MANIFEST.in +0 -0
  11. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/README.md +0 -0
  12. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a/__init__.py +0 -0
  13. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a/agent.py +0 -0
  14. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a/client.py +0 -0
  15. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a/executors.py +0 -0
  16. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a.egg-info/SOURCES.txt +0 -0
  17. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a.egg-info/dependency_links.txt +0 -0
  18. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a.egg-info/requires.txt +0 -0
  19. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/distributed_a2a.egg-info/top_level.txt +0 -0
  20. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/requirements.txt +0 -0
  21. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/setup.cfg +0 -0
  22. {distributed_a2a-0.1.8 → distributed_a2a-0.1.9rc1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: distributed_a2a
3
- Version: 0.1.8
3
+ Version: 0.1.9rc1
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
@@ -1,5 +1,5 @@
1
1
  import os
2
- from typing import List, Any
2
+ from typing import List, Any, Optional
3
3
 
4
4
  from langchain_core.language_models import BaseChatModel
5
5
  from langchain_openai import ChatOpenAI
@@ -13,6 +13,15 @@ class SkillConfig(BaseModel):
13
13
  tags: List[str] = Field(description="The tags associated with the skill")
14
14
 
15
15
 
16
+ class RegistryItemConfig(BaseModel):
17
+ url: str = Field(description="The url of the registry")
18
+
19
+
20
+ class RegistryConfig(BaseModel):
21
+ agent: RegistryItemConfig = Field(description="The agent registry configuration")
22
+ mcp: RegistryItemConfig = Field(description="The mcp registry configuration")
23
+
24
+
16
25
  class LLMConfig(BaseModel):
17
26
  base_url: str = Field(description="The base url of the LLM provider")
18
27
  model: str = Field(description="The model to use for the LLM e.g. gpt-3.5-turbo")
@@ -32,6 +41,7 @@ class CardConfig(BaseModel):
32
41
 
33
42
 
34
43
  class AgentItem(BaseModel):
44
+ registry: Optional[RegistryConfig] = Field(description="The registry configuration node", default=None)
35
45
  card: CardConfig = Field(description="The agent card configuration node")
36
46
  llm: LLMConfig = Field(description="The LLM configuration node")
37
47
  system_prompt: str = Field(description="The system prompt to use for the LLM or a path to a file containing the system prompt")
@@ -50,6 +60,7 @@ class AgentConfig(BaseModel):
50
60
 
51
61
 
52
62
  class RouterItem(BaseModel):
63
+ registry: Optional[RegistryConfig] = Field(description="The registry configuration node", default=None)
53
64
  card: CardConfig = Field(description="The router card configuration node")
54
65
  llm: LLMConfig = Field(description="The LLM configuration node")
55
66
 
@@ -0,0 +1,55 @@
1
+ import json
2
+ import asyncio
3
+ import boto3
4
+ import requests
5
+ from langchain_core.tools import StructuredTool
6
+ from a2a.types import AgentCard
7
+
8
+
9
+ async def registry_heart_beat(name: str, registry_url: str, agent_card: AgentCard, interval_sec: int,
10
+ get_expire_at: callable) -> None:
11
+ registry = AgentRegistryLookup(registry_url=registry_url)
12
+ while True:
13
+ try:
14
+ registry.put_agent_card(name=name, agent_card=agent_card.model_dump(), expire_at=get_expire_at())
15
+ except Exception as e:
16
+ print(f"Failed to send heart beat to registry: {e}")
17
+ await asyncio.sleep(interval_sec)
18
+
19
+
20
+ class AgentRegistryLookup:
21
+ def __init__(self, registry_url: str):
22
+ self.registry_url = registry_url
23
+
24
+ def get_agent_cards(self) -> list[dict]:
25
+ response = requests.get(url=f"{self.registry_url}/agent-cards", timeout=30)
26
+ response.raise_for_status()
27
+ return response.json()
28
+
29
+ def put_agent_card(self, name: str, agent_card: dict, expire_at: int) -> None:
30
+ response = requests.put(
31
+ url=f"{self.registry_url}/agent-card/{name}",
32
+ params={"expire_at": str(expire_at)},
33
+ json=agent_card,
34
+ timeout=30
35
+ )
36
+ response.raise_for_status()
37
+
38
+ def as_tool(self) -> StructuredTool:
39
+ return StructuredTool.from_function(func=lambda: self.get_agent_cards(), name="agent_card_lookup",
40
+ description="Gets all available agent cards")
41
+
42
+ class DynamoDbRegistryLookup:
43
+ def __init__(self, agent_card_tabel: str):
44
+ dynamo = boto3.resource("dynamodb", region_name="eu-central-1")
45
+ self.table = dynamo.Table(agent_card_tabel)
46
+
47
+ def get_agent_cards(self) -> list[dict]:
48
+
49
+ items = self.table.scan().get("Items", [])
50
+ cards: list[dict] = [json.loads(it["card"]) for it in items]
51
+ return cards
52
+
53
+ def as_tool(self) -> StructuredTool:
54
+ return StructuredTool.from_function(func=lambda: self.get_agent_cards(), name="agent_card_lookup",
55
+ description="Gets all available agent cards")
@@ -8,7 +8,7 @@ from fastapi import FastAPI
8
8
 
9
9
  from .executors import RoutingExecutor
10
10
  from .model import RouterConfig
11
- from .registry import DynamoDbRegistryLookup
11
+ from .registry import DynamoDbRegistryLookup, AgentRegistryLookup
12
12
  from .server import CAPABILITIES, AGENT_CARD_TABLE
13
13
 
14
14
 
@@ -33,8 +33,7 @@ def load_router(router_dic: dict[str, Any]) -> FastAPI:
33
33
 
34
34
  executor = RoutingExecutor(
35
35
  router_config=router_config,
36
- routing_tool=DynamoDbRegistryLookup(agent_card_tabel=AGENT_CARD_TABLE).as_tool()
37
-
36
+ routing_tool=AgentRegistryLookup(router_config.router.registry.agent.url).as_tool()
38
37
  )
39
38
 
40
39
  return A2ARESTFastAPIApplication(
@@ -1,3 +1,4 @@
1
+ import os
1
2
  import asyncio
2
3
  import time
3
4
  from contextlib import asynccontextmanager
@@ -13,7 +14,7 @@ from fastapi import FastAPI
13
14
 
14
15
  from .executors import RoutingAgentExecutor
15
16
  from .model import AgentConfig
16
- from .registry import DynamoDbRegistryLookup
17
+ from .registry import DynamoDbRegistryLookup, registry_heart_beat, AgentRegistryLookup
17
18
 
18
19
  CAPABILITIES = AgentCapabilities(streaming=False, push_notifications=False)
19
20
 
@@ -21,10 +22,13 @@ HEART_BEAT_INTERVAL_SEC = 5
21
22
  MAX_HEART_BEAT_MISSES = 3
22
23
 
23
24
  AGENT_CARD_TABLE = "agent-cards"
25
+ AGENT_REGISTRY_URL = ""
24
26
 
25
27
  def get_expire_at() -> int:
26
28
  return int(time.time() + MAX_HEART_BEAT_MISSES * HEART_BEAT_INTERVAL_SEC)
27
29
 
30
+
31
+
28
32
  async def heart_beat(name: str, agent_card_table: str, agent_card: AgentCard) -> None:
29
33
  table = boto3.resource("dynamodb", region_name="eu-central-1").Table(agent_card_table)
30
34
  table.put_item(Item={"id": name, "card": agent_card.model_dump_json(), "expireAt": get_expire_at()})
@@ -67,14 +71,16 @@ def load_app(agent_config: Any) -> FastAPI:
67
71
  preferred_transport=agent_config.agent.card.preferred_transport_protocol,
68
72
  capabilities=CAPABILITIES
69
73
  )
70
-
71
-
74
+ agent_registry = AgentRegistryLookup(agent_config.agent.registry.agent.url)
72
75
  executor = RoutingAgentExecutor(agent_config=agent_config,
73
- routing_tool=DynamoDbRegistryLookup(agent_card_tabel=AGENT_CARD_TABLE).as_tool())
76
+ routing_tool=agent_registry.as_tool())
74
77
 
75
78
  @asynccontextmanager
76
79
  async def lifespan(_: FastAPI) -> AsyncGenerator[None, Any]:
77
- asyncio.create_task(heart_beat(name=agent_card.name, agent_card_table=AGENT_CARD_TABLE, agent_card=agent_card))
80
+ asyncio.create_task(registry_heart_beat(name=agent_card.name, registry_url=agent_config.agent.registry.agent.url,
81
+ agent_card=agent_card, interval_sec=HEART_BEAT_INTERVAL_SEC,
82
+ get_expire_at=get_expire_at))
83
+
78
84
  yield
79
85
 
80
86
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: distributed_a2a
3
- Version: 0.1.8
3
+ Version: 0.1.9rc1
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "distributed_a2a"
7
- version = "0.1.8"
7
+ version = "0.1.9rc1"
8
8
  description = "A library for building A2A agents with routing capabilities"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14"
@@ -1,18 +0,0 @@
1
- import boto3
2
- from langchain_core.tools import StructuredTool
3
-
4
-
5
- class DynamoDbRegistryLookup:
6
- def __init__(self, agent_card_tabel: str):
7
- dynamo = boto3.resource("dynamodb", region_name="eu-central-1")
8
- self.table = dynamo.Table(agent_card_tabel)
9
-
10
- def get_agent_cards(self) -> list[str]:
11
-
12
- items = self.table.scan().get("Items", [])
13
- cards: list[str] = [it["card"] for it in items]
14
- return cards
15
-
16
- def as_tool(self) -> StructuredTool:
17
- return StructuredTool.from_function(func=lambda: self.get_agent_cards(), name="agent_card_lookup",
18
- description="Gets all available agent cards")