|
10 | 10 | from a2a.client import ( |
11 | 11 | A2ACardResolver, |
12 | 12 | Client, |
13 | | - ClientFactory, |
14 | 13 | create_text_message_object, |
15 | 14 | minimal_agent_card, |
16 | 15 | ) |
|
22 | 21 | from slima2a import setup_slim_client |
23 | 22 | from slima2a.client_transport import ( |
24 | 23 | ClientConfig, |
| 24 | + MultiAgentClientFactory, |
| 25 | + MulticastClient, |
25 | 26 | slimrpc_channel_factory, |
| 27 | + slimrpc_group_channel_factory, |
26 | 28 | ) |
27 | 29 |
|
28 | 30 | BASE_URL = "http://localhost:9999" |
@@ -65,39 +67,44 @@ async def main() -> None: |
65 | 67 | streaming=args.stream, |
66 | 68 | httpx_client=httpx_client, |
67 | 69 | slimrpc_channel_factory=slimrpc_channel_factory(slim_local_app, conn_id), |
| 70 | + slimrpc_group_channel_factory=slimrpc_group_channel_factory( |
| 71 | + slim_local_app, conn_id |
| 72 | + ), |
68 | 73 | ) |
69 | | - client_factory = ClientFactory(client_config) |
| 74 | + client_factory = MultiAgentClientFactory(client_config) |
70 | 75 |
|
71 | 76 | if args.a2a_version == "v0": |
72 | 77 | from slima2a.compat.v3_0.client_transport import SRPCCompatTransport |
73 | 78 |
|
74 | | - # mypy: the register API expects a different callable type; safe to ignore here. |
75 | | - client_factory.register("slimrpc", SRPCCompatTransport.create) # type: ignore |
76 | | - else: |
77 | | - from slima2a.client_transport import SRPCTransport |
| 79 | + client_factory.register("slimrpc", SRPCCompatTransport.create, multiagent=True) # type: ignore |
78 | 80 |
|
79 | | - # mypy: the register API expects a different callable type; safe to ignore here. |
80 | | - client_factory.register("slimrpc", SRPCTransport.create) # type: ignore |
| 81 | + agent_names = [f"agntcy/demo/{name.strip()}" for name in args.agents.split(",")] |
81 | 82 |
|
82 | 83 | match args.type: |
83 | 84 | case "slimrpc": |
84 | | - agent_card = minimal_agent_card("agntcy/demo/echo_agent", ["slimrpc"]) |
| 85 | + cards = [minimal_agent_card(name, ["slimrpc"]) for name in agent_names] |
85 | 86 | case "starlette": |
86 | | - agent_card = await fetch_agent_card( |
87 | | - resolver=A2ACardResolver( |
88 | | - httpx_client=httpx_client, |
89 | | - base_url=BASE_URL, |
| 87 | + cards = [ |
| 88 | + await fetch_agent_card( |
| 89 | + resolver=A2ACardResolver( |
| 90 | + httpx_client=httpx_client, |
| 91 | + base_url=BASE_URL, |
| 92 | + ) |
90 | 93 | ) |
91 | | - ) |
| 94 | + ] |
92 | 95 | case _: |
93 | 96 | raise ValueError(f"Invalid client type: {args.type}") |
94 | 97 |
|
95 | | - client = client_factory.create(card=agent_card) |
96 | | - logger.info("A2AClient initialized.") |
| 98 | + client = client_factory.create(card=cards) |
97 | 99 |
|
98 | | - response_text = await send_message(client, args.text) |
99 | | - print(f"> {args.text}") |
100 | | - print(response_text) |
| 100 | + if isinstance(client, MulticastClient): |
| 101 | + print(f"> {args.text} (multicast to {agent_names})") |
| 102 | + await send_message_multicast(client, args.text) |
| 103 | + else: |
| 104 | + logger.info("A2AClient initialized.") |
| 105 | + response_text = await send_message(client, args.text) |
| 106 | + print(f"> {args.text}") |
| 107 | + print(response_text) |
101 | 108 |
|
102 | 109 |
|
103 | 110 | def parse_arguments() -> argparse.Namespace: |
@@ -133,6 +140,13 @@ def parse_arguments() -> argparse.Namespace: |
133 | 140 | default="v1", |
134 | 141 | choices=["v0", "v1"], |
135 | 142 | ) |
| 143 | + parser.add_argument( |
| 144 | + "--agents", |
| 145 | + type=str, |
| 146 | + required=False, |
| 147 | + default="echo_agent", |
| 148 | + help="Comma-separated agent names (e.g. echo_agent_1,echo_agent_2). Multiple names triggers multicast.", |
| 149 | + ) |
136 | 150 |
|
137 | 151 | args = parser.parse_args() |
138 | 152 |
|
@@ -180,5 +194,41 @@ async def send_message( |
180 | 194 | return output |
181 | 195 |
|
182 | 196 |
|
| 197 | +async def send_message_multicast( |
| 198 | + client: MulticastClient, |
| 199 | + text: str, |
| 200 | +) -> None: |
| 201 | + message = create_text_message_object(content=text) |
| 202 | + request = SendMessageRequest(message=message) |
| 203 | + |
| 204 | + try: |
| 205 | + async for source, response in client.send_message(request): |
| 206 | + output = "" |
| 207 | + which = response.WhichOneof("payload") |
| 208 | + if which == "message": |
| 209 | + for part in response.message.parts: |
| 210 | + if part.WhichOneof("content") == "text": |
| 211 | + output += part.text |
| 212 | + elif which == "task": |
| 213 | + if response.task.artifacts: |
| 214 | + for artifact in response.task.artifacts: |
| 215 | + for part in artifact.parts: |
| 216 | + if part.WhichOneof("content") == "text": |
| 217 | + output += part.text |
| 218 | + elif which == "artifact_update": |
| 219 | + artifact = response.artifact_update.artifact |
| 220 | + for part in artifact.parts: |
| 221 | + if part.WhichOneof("content") == "text": |
| 222 | + output += part.text |
| 223 | + if output: |
| 224 | + print(f" [{source}] {output}") |
| 225 | + except Exception as e: |
| 226 | + logger.error( |
| 227 | + f"failed sending multicast message: {e}", |
| 228 | + exc_info=True, |
| 229 | + ) |
| 230 | + raise RuntimeError("failed sending multicast message") from e |
| 231 | + |
| 232 | + |
183 | 233 | if __name__ == "__main__": |
184 | 234 | asyncio.run(main()) |
0 commit comments