agent-relay 3.2.2 → 3.2.4
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.
- package/bin/agent-relay-broker-darwin-arm64 +0 -0
- package/bin/agent-relay-broker-darwin-x64 +0 -0
- package/bin/agent-relay-broker-linux-arm64 +0 -0
- package/bin/agent-relay-broker-linux-x64 +0 -0
- package/dist/index.cjs +1358 -941
- package/dist/src/cli/commands/agent-management.d.ts +2 -2
- package/dist/src/cli/commands/agent-management.d.ts.map +1 -1
- package/dist/src/cli/commands/agent-management.js +41 -240
- package/dist/src/cli/commands/agent-management.js.map +1 -1
- package/dist/src/cli/commands/messaging.d.ts +1 -1
- package/dist/src/cli/commands/messaging.d.ts.map +1 -1
- package/dist/src/cli/commands/messaging.js +14 -5
- package/dist/src/cli/commands/messaging.js.map +1 -1
- package/dist/src/cli/lib/agent-management-listing.d.ts +4 -1
- package/dist/src/cli/lib/agent-management-listing.d.ts.map +1 -1
- package/dist/src/cli/lib/agent-management-listing.js +27 -2
- package/dist/src/cli/lib/agent-management-listing.js.map +1 -1
- package/package.json +11 -10
- package/packages/acp-bridge/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/memory/package.json +2 -2
- package/packages/openclaw/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/sdk/ADAPTER_REVIEW.md +109 -0
- package/packages/sdk/dist/client.d.ts +66 -0
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +230 -0
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/dist/communicate/a2a-bridge.d.ts +25 -0
- package/packages/sdk/dist/communicate/a2a-bridge.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/a2a-bridge.js +89 -0
- package/packages/sdk/dist/communicate/a2a-bridge.js.map +1 -0
- package/packages/sdk/dist/communicate/a2a-server.d.ts +31 -0
- package/packages/sdk/dist/communicate/a2a-server.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/a2a-server.js +220 -0
- package/packages/sdk/dist/communicate/a2a-server.js.map +1 -0
- package/packages/sdk/dist/communicate/a2a-transport.d.ts +48 -0
- package/packages/sdk/dist/communicate/a2a-transport.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/a2a-transport.js +302 -0
- package/packages/sdk/dist/communicate/a2a-transport.js.map +1 -0
- package/packages/sdk/dist/communicate/a2a-types.d.ts +107 -0
- package/packages/sdk/dist/communicate/a2a-types.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/a2a-types.js +209 -0
- package/packages/sdk/dist/communicate/a2a-types.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/claude-sdk.d.ts +28 -0
- package/packages/sdk/dist/communicate/adapters/claude-sdk.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/claude-sdk.js +47 -0
- package/packages/sdk/dist/communicate/adapters/claude-sdk.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/crewai.d.ts +42 -0
- package/packages/sdk/dist/communicate/adapters/crewai.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/crewai.js +95 -0
- package/packages/sdk/dist/communicate/adapters/crewai.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/google-adk.d.ts +53 -0
- package/packages/sdk/dist/communicate/adapters/google-adk.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/google-adk.js +77 -0
- package/packages/sdk/dist/communicate/adapters/google-adk.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/index.d.ts +7 -0
- package/packages/sdk/dist/communicate/adapters/index.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/index.js +7 -0
- package/packages/sdk/dist/communicate/adapters/index.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/langgraph.d.ts +40 -0
- package/packages/sdk/dist/communicate/adapters/langgraph.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/langgraph.js +77 -0
- package/packages/sdk/dist/communicate/adapters/langgraph.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/openai-agents.d.ts +25 -0
- package/packages/sdk/dist/communicate/adapters/openai-agents.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/openai-agents.js +70 -0
- package/packages/sdk/dist/communicate/adapters/openai-agents.js.map +1 -0
- package/packages/sdk/dist/communicate/adapters/pi.d.ts +45 -0
- package/packages/sdk/dist/communicate/adapters/pi.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/adapters/pi.js +59 -0
- package/packages/sdk/dist/communicate/adapters/pi.js.map +1 -0
- package/packages/sdk/dist/communicate/core.d.ts +58 -0
- package/packages/sdk/dist/communicate/core.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/core.js +128 -0
- package/packages/sdk/dist/communicate/core.js.map +1 -0
- package/packages/sdk/dist/communicate/index.d.ts +4 -0
- package/packages/sdk/dist/communicate/index.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/index.js +4 -0
- package/packages/sdk/dist/communicate/index.js.map +1 -0
- package/packages/sdk/dist/communicate/transport.d.ts +36 -0
- package/packages/sdk/dist/communicate/transport.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/transport.js +371 -0
- package/packages/sdk/dist/communicate/transport.js.map +1 -0
- package/packages/sdk/dist/communicate/types.d.ts +58 -0
- package/packages/sdk/dist/communicate/types.d.ts.map +1 -0
- package/packages/sdk/dist/communicate/types.js +66 -0
- package/packages/sdk/dist/communicate/types.js.map +1 -0
- package/packages/sdk/dist/workflows/builder.d.ts +35 -5
- package/packages/sdk/dist/workflows/builder.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/builder.js +81 -7
- package/packages/sdk/dist/workflows/builder.js.map +1 -1
- package/packages/sdk/dist/workflows/cli.js +14 -1
- package/packages/sdk/dist/workflows/cli.js.map +1 -1
- package/packages/sdk/dist/workflows/runner.d.ts +10 -2
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +95 -1
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/dist/workflows/types.d.ts +11 -0
- package/packages/sdk/dist/workflows/types.d.ts.map +1 -1
- package/packages/sdk/examples/communicate/claude_sdk_example.ts +5 -0
- package/packages/sdk/examples/communicate/pi_example.ts +8 -0
- package/packages/sdk/package.json +48 -2
- package/packages/sdk/src/__tests__/builder-deterministic.test.ts +132 -0
- package/packages/sdk/src/__tests__/communicate/a2a-bridge.test.ts +211 -0
- package/packages/sdk/src/__tests__/communicate/a2a-server.test.ts +359 -0
- package/packages/sdk/src/__tests__/communicate/a2a-transport.test.ts +537 -0
- package/packages/sdk/src/__tests__/communicate/a2a-types.test.ts +297 -0
- package/packages/sdk/src/__tests__/communicate/adapters/claude-sdk.test.ts +163 -0
- package/packages/sdk/src/__tests__/communicate/adapters/crewai.test.ts +219 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-crewai.test.ts +101 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-google-adk.test.ts +166 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-langgraph.test.ts +181 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-openai-agents.test.ts +137 -0
- package/packages/sdk/src/__tests__/communicate/adapters/e2e-pi.test.ts +140 -0
- package/packages/sdk/src/__tests__/communicate/adapters/google-adk.test.ts +200 -0
- package/packages/sdk/src/__tests__/communicate/adapters/langgraph.test.ts +162 -0
- package/packages/sdk/src/__tests__/communicate/adapters/openai-agents.test.ts +166 -0
- package/packages/sdk/src/__tests__/communicate/adapters/pi.test.ts +140 -0
- package/packages/sdk/src/__tests__/communicate/core.test.ts +574 -0
- package/packages/sdk/src/__tests__/communicate/integration/cross-framework.test.ts +353 -0
- package/packages/sdk/src/__tests__/communicate/transport.test.ts +613 -0
- package/packages/sdk/src/__tests__/start-from.test.ts +346 -0
- package/packages/sdk/src/client.ts +301 -0
- package/packages/sdk/src/communicate/a2a-bridge.ts +111 -0
- package/packages/sdk/src/communicate/a2a-server.ts +277 -0
- package/packages/sdk/src/communicate/a2a-transport.ts +395 -0
- package/packages/sdk/src/communicate/a2a-types.ts +338 -0
- package/packages/sdk/src/communicate/adapters/claude-sdk.ts +85 -0
- package/packages/sdk/src/communicate/adapters/crewai.ts +141 -0
- package/packages/sdk/src/communicate/adapters/google-adk.ts +139 -0
- package/packages/sdk/src/communicate/adapters/index.ts +6 -0
- package/packages/sdk/src/communicate/adapters/langgraph.ts +112 -0
- package/packages/sdk/src/communicate/adapters/openai-agents.ts +113 -0
- package/packages/sdk/src/communicate/adapters/pi.ts +105 -0
- package/packages/sdk/src/communicate/core.ts +157 -0
- package/packages/sdk/src/communicate/index.ts +3 -0
- package/packages/sdk/src/communicate/transport.ts +489 -0
- package/packages/sdk/src/communicate/types.ts +106 -0
- package/packages/sdk/src/examples/workflows/fix-dashboard-user-registration.yaml +182 -0
- package/packages/sdk/src/workflows/builder.ts +97 -9
- package/packages/sdk/src/workflows/cli.ts +16 -1
- package/packages/sdk/src/workflows/runner.ts +110 -1
- package/packages/sdk/src/workflows/types.ts +14 -0
- package/packages/sdk/tsconfig.build.json +1 -7
- package/packages/sdk/tsconfig.json +1 -7
- package/packages/sdk-py/README.md +67 -25
- package/packages/sdk-py/examples/communicate/agno_example.py +8 -0
- package/packages/sdk-py/examples/communicate/claude_sdk_example.py +6 -0
- package/packages/sdk-py/examples/communicate/crewai_example.py +7 -0
- package/packages/sdk-py/examples/communicate/google_adk_example.py +7 -0
- package/packages/sdk-py/examples/communicate/openai_agents_example.py +8 -0
- package/packages/sdk-py/examples/communicate/swarms_example.py +7 -0
- package/packages/sdk-py/pyproject.toml +12 -1
- package/packages/sdk-py/src/agent_relay/__init__.py +8 -0
- package/packages/sdk-py/src/agent_relay/builder.py +65 -26
- package/packages/sdk-py/src/agent_relay/communicate/__init__.py +6 -0
- package/packages/sdk-py/src/agent_relay/communicate/a2a_bridge.py +138 -0
- package/packages/sdk-py/src/agent_relay/communicate/a2a_server.py +242 -0
- package/packages/sdk-py/src/agent_relay/communicate/a2a_transport.py +366 -0
- package/packages/sdk-py/src/agent_relay/communicate/a2a_types.py +294 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/__init__.py +10 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/agno.py +74 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/claude_sdk.py +78 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/crewai.py +143 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/google_adk.py +69 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/openai_agents.py +86 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/pi.py +175 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/swarms.py +44 -0
- package/packages/sdk-py/src/agent_relay/communicate/core.py +293 -0
- package/packages/sdk-py/src/agent_relay/communicate/transport.py +502 -0
- package/packages/sdk-py/src/agent_relay/communicate/types.py +89 -0
- package/packages/sdk-py/src/agent_relay/types.py +2 -1
- package/packages/sdk-py/tests/communicate/__init__.py +0 -0
- package/packages/sdk-py/tests/communicate/adapters/__init__.py +0 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_agno.py +154 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_claude_sdk.py +428 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_crewai.py +234 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_google_adk.py +182 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_langgraph.py +262 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_openai_agents.py +88 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_pi.py +156 -0
- package/packages/sdk-py/tests/communicate/adapters/e2e_test_swarms.py +239 -0
- package/packages/sdk-py/tests/communicate/adapters/test_agno.py +140 -0
- package/packages/sdk-py/tests/communicate/adapters/test_claude_sdk.py +147 -0
- package/packages/sdk-py/tests/communicate/adapters/test_crewai.py +136 -0
- package/packages/sdk-py/tests/communicate/adapters/test_google_adk.py +125 -0
- package/packages/sdk-py/tests/communicate/adapters/test_openai_agents.py +99 -0
- package/packages/sdk-py/tests/communicate/adapters/test_pi.py +270 -0
- package/packages/sdk-py/tests/communicate/adapters/test_swarms.py +113 -0
- package/packages/sdk-py/tests/communicate/conftest.py +555 -0
- package/packages/sdk-py/tests/communicate/integration/__init__.py +1 -0
- package/packages/sdk-py/tests/communicate/integration/test_cross_framework.py +331 -0
- package/packages/sdk-py/tests/communicate/integration/test_end_to_end.py +151 -0
- package/packages/sdk-py/tests/communicate/test_a2a_bridge.py +363 -0
- package/packages/sdk-py/tests/communicate/test_a2a_server.py +346 -0
- package/packages/sdk-py/tests/communicate/test_a2a_transport.py +561 -0
- package/packages/sdk-py/tests/communicate/test_a2a_types.py +342 -0
- package/packages/sdk-py/tests/communicate/test_auto_detect.py +67 -0
- package/packages/sdk-py/tests/communicate/test_core.py +331 -0
- package/packages/sdk-py/tests/communicate/test_transport.py +373 -0
- package/packages/sdk-py/tests/communicate/test_types.py +285 -0
- package/packages/sdk-py/tests/test_builder_deterministic.py +118 -0
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/packages/sdk/dist/__tests__/completion-pipeline.test.d.ts +0 -14
- package/packages/sdk/dist/__tests__/completion-pipeline.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/completion-pipeline.test.js +0 -1476
- package/packages/sdk/dist/__tests__/completion-pipeline.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/contract-fixtures.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/contract-fixtures.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/contract-fixtures.test.js +0 -152
- package/packages/sdk/dist/__tests__/contract-fixtures.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/e2e-owner-review.test.d.ts +0 -16
- package/packages/sdk/dist/__tests__/e2e-owner-review.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/e2e-owner-review.test.js +0 -640
- package/packages/sdk/dist/__tests__/e2e-owner-review.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/facade.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/facade.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/facade.test.js +0 -305
- package/packages/sdk/dist/__tests__/facade.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/integration.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/integration.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/integration.test.js +0 -205
- package/packages/sdk/dist/__tests__/integration.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/pty.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/pty.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/pty.test.js +0 -20
- package/packages/sdk/dist/__tests__/pty.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/quickstart.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/quickstart.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/quickstart.test.js +0 -176
- package/packages/sdk/dist/__tests__/quickstart.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/spawn-from-env.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/spawn-from-env.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/spawn-from-env.test.js +0 -222
- package/packages/sdk/dist/__tests__/spawn-from-env.test.js.map +0 -1
- package/packages/sdk/dist/__tests__/unit.test.d.ts +0 -2
- package/packages/sdk/dist/__tests__/unit.test.d.ts.map +0 -1
- package/packages/sdk/dist/__tests__/unit.test.js +0 -357
- package/packages/sdk/dist/__tests__/unit.test.js.map +0 -1
- package/packages/sdk-py/agent_relay/__init__.py +0 -21
- package/packages/sdk-py/agent_relay/models.py +0 -398
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
# Agent Relay Python SDK
|
|
2
2
|
|
|
3
|
-
Python SDK for
|
|
3
|
+
Python SDK for two workflows:
|
|
4
|
+
|
|
5
|
+
- `Orchestrate` mode spawns and manages AI agents from Python.
|
|
6
|
+
- `Communicate` mode puts an existing agent framework on Relaycast with `Relay` + `on_relay()`.
|
|
4
7
|
|
|
5
8
|
## Installation
|
|
6
9
|
|
|
10
|
+
### Orchestrate
|
|
11
|
+
|
|
7
12
|
```bash
|
|
8
13
|
pip install agent-relay-sdk
|
|
9
14
|
```
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
### Communicate
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install "agent-relay-sdk[communicate]"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The SDK automatically downloads the broker binary on first use. Communicate mode also needs the framework package you want to wrap, such as `claude-agent-sdk`, `google-adk`, `agno`, `swarms`, or `crewai`.
|
|
12
23
|
|
|
13
24
|
## Requirements
|
|
14
25
|
|
|
15
26
|
- Python 3.10+
|
|
16
27
|
|
|
17
|
-
##
|
|
28
|
+
## Choose a Mode
|
|
29
|
+
|
|
30
|
+
### Orchestrate
|
|
31
|
+
|
|
32
|
+
Use `AgentRelay` when you want Python to spawn agents, wait for readiness, route messages, and shut everything down.
|
|
18
33
|
|
|
19
34
|
```python
|
|
20
35
|
import asyncio
|
|
@@ -22,45 +37,56 @@ from agent_relay import AgentRelay, Models
|
|
|
22
37
|
|
|
23
38
|
async def main():
|
|
24
39
|
relay = AgentRelay(channels=["dev"])
|
|
25
|
-
|
|
26
|
-
# Event hooks
|
|
27
40
|
relay.on_message_received = lambda msg: print(f"[{msg.from_name}]: {msg.text}")
|
|
28
|
-
relay.on_agent_ready = lambda agent: print(f" {agent.name} ready")
|
|
29
|
-
relay.on_agent_exited = lambda agent: print(f" {agent.name} exited")
|
|
30
41
|
|
|
31
|
-
# Spawn agents
|
|
32
42
|
await relay.claude.spawn(
|
|
33
43
|
name="Reviewer",
|
|
34
44
|
model=Models.Claude.OPUS,
|
|
35
45
|
channels=["dev"],
|
|
36
46
|
task="Review the PR and suggest improvements",
|
|
37
47
|
)
|
|
38
|
-
|
|
39
48
|
await relay.codex.spawn(
|
|
40
49
|
name="Builder",
|
|
41
50
|
model=Models.Codex.GPT_5_3_CODEX,
|
|
42
51
|
channels=["dev"],
|
|
43
|
-
task="Implement the
|
|
52
|
+
task="Implement the suggestions",
|
|
44
53
|
)
|
|
45
54
|
|
|
46
|
-
# Wait for both agents to be ready
|
|
47
55
|
await asyncio.gather(
|
|
48
56
|
relay.wait_for_agent_ready("Reviewer"),
|
|
49
57
|
relay.wait_for_agent_ready("Builder"),
|
|
50
58
|
)
|
|
51
|
-
|
|
52
|
-
# Let agents collaborate, then shut down
|
|
53
|
-
await asyncio.sleep(600)
|
|
54
59
|
await relay.shutdown()
|
|
55
60
|
|
|
56
61
|
asyncio.run(main())
|
|
57
62
|
```
|
|
58
63
|
|
|
64
|
+
### Communicate
|
|
65
|
+
|
|
66
|
+
Use `Relay` + `on_relay()` when your framework already owns the runtime and you only want Relaycast messaging.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
relay = Relay("Researcher")
|
|
70
|
+
agent = FrameworkAgent(...)
|
|
71
|
+
agent = on_relay(agent, relay)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Supported Python adapters:
|
|
75
|
+
|
|
76
|
+
- OpenAI Agents
|
|
77
|
+
- Claude Agent SDK
|
|
78
|
+
- Google ADK
|
|
79
|
+
- Agno
|
|
80
|
+
- Swarms
|
|
81
|
+
- CrewAI
|
|
82
|
+
|
|
83
|
+
The wrapped agent gets Relay tools for direct messages, channel posts, inbox reads, and agent discovery. Framework-specific receive hooks are added automatically.
|
|
84
|
+
|
|
59
85
|
## API
|
|
60
86
|
|
|
61
87
|
### AgentRelay
|
|
62
88
|
|
|
63
|
-
The main entry point. Pass `channels` to subscribe to message channels.
|
|
89
|
+
The main Orchestrate entry point. Pass `channels` to subscribe to message channels.
|
|
64
90
|
|
|
65
91
|
```python
|
|
66
92
|
relay = AgentRelay(channels=["dev", "planning"])
|
|
@@ -78,7 +104,6 @@ await relay.gemini.spawn(name="Agent3", model=Models.Gemini.GEMINI_2_5_PRO, chan
|
|
|
78
104
|
worker = await relay.claude.spawn(
|
|
79
105
|
name="HookedWorker",
|
|
80
106
|
channels=["dev"],
|
|
81
|
-
# Lifecycle hooks can be sync or async callables.
|
|
82
107
|
on_start=lambda ctx: print(f"spawning {ctx['name']}"),
|
|
83
108
|
on_success=lambda ctx: print(f"spawned {ctx['name']} ({ctx['runtime']})"),
|
|
84
109
|
on_error=lambda ctx: print(f"failed to spawn {ctx['name']}: {ctx['error']}"),
|
|
@@ -91,22 +116,39 @@ await worker.release(
|
|
|
91
116
|
)
|
|
92
117
|
```
|
|
93
118
|
|
|
94
|
-
###
|
|
119
|
+
### Relay
|
|
120
|
+
|
|
121
|
+
The Communicate-mode client. Configure it directly or via `RELAY_WORKSPACE`, `RELAY_API_KEY`, and `RELAY_BASE_URL`.
|
|
95
122
|
|
|
96
123
|
```python
|
|
97
|
-
|
|
98
|
-
|
|
124
|
+
from agent_relay import Relay
|
|
125
|
+
|
|
126
|
+
relay = Relay("Researcher")
|
|
127
|
+
await relay.send("Lead", "Status update")
|
|
128
|
+
await relay.post("docs", "Wave 5.1 complete")
|
|
129
|
+
messages = await relay.inbox()
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### `on_relay()`
|
|
133
|
+
|
|
134
|
+
Wrap a framework-owned agent or options object and keep the runtime you already use.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from agent_relay import Relay, on_relay
|
|
138
|
+
|
|
139
|
+
relay = Relay("Researcher")
|
|
140
|
+
wrapped = on_relay(framework_agent_or_options, relay)
|
|
99
141
|
```
|
|
100
142
|
|
|
101
143
|
### Event Hooks
|
|
102
144
|
|
|
103
145
|
```python
|
|
104
|
-
relay.on_message_received = lambda msg: ...
|
|
105
|
-
relay.on_agent_ready = lambda agent: ...
|
|
106
|
-
relay.on_agent_exited = lambda agent: ...
|
|
107
|
-
relay.on_agent_spawned = lambda agent: ...
|
|
108
|
-
relay.on_worker_output = lambda data: ...
|
|
109
|
-
relay.on_agent_idle = lambda agent: ...
|
|
146
|
+
relay.on_message_received = lambda msg: ... # New message
|
|
147
|
+
relay.on_agent_ready = lambda agent: ... # Agent connected
|
|
148
|
+
relay.on_agent_exited = lambda agent: ... # Agent exited
|
|
149
|
+
relay.on_agent_spawned = lambda agent: ... # Agent spawned
|
|
150
|
+
relay.on_worker_output = lambda data: ... # Agent output
|
|
151
|
+
relay.on_agent_idle = lambda agent: ... # Agent idle
|
|
110
152
|
```
|
|
111
153
|
|
|
112
154
|
### Models
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from agent_relay import Relay, on_relay
|
|
2
|
+
from agno.agent import Agent
|
|
3
|
+
from agno.models.openai import OpenAIChat
|
|
4
|
+
|
|
5
|
+
relay = Relay("AgnoWorker")
|
|
6
|
+
agent = Agent(model=OpenAIChat(id="gpt-5-mini"), instructions="Use relay tools when needed.")
|
|
7
|
+
agent = on_relay(agent, relay)
|
|
8
|
+
print(len(agent.tools))
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from agent_relay import Relay, on_relay
|
|
2
|
+
from crewai import Agent
|
|
3
|
+
|
|
4
|
+
relay = Relay("CrewWorker")
|
|
5
|
+
agent = Agent(role="Relay worker", goal="Coordinate with teammates", backstory="You check relay messages before acting.", llm="gpt-4o-mini")
|
|
6
|
+
agent = on_relay(agent, relay)
|
|
7
|
+
print(agent.backstory)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from agent_relay import Relay, on_relay
|
|
2
|
+
from google.adk.agents import Agent
|
|
3
|
+
|
|
4
|
+
relay = Relay("GoogleWorker")
|
|
5
|
+
agent = Agent(name="GoogleWorker", model="gemini-2.0-flash", instruction="Use relay tools when needed.")
|
|
6
|
+
agent = on_relay(agent, relay)
|
|
7
|
+
print([tool.__name__ for tool in agent.tools])
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
from agent_relay import Relay
|
|
2
|
+
from agent_relay.communicate.adapters.openai_agents import on_relay
|
|
3
|
+
from openai_agents import Agent
|
|
4
|
+
|
|
5
|
+
relay = Relay("OpenAIWorker")
|
|
6
|
+
agent = Agent(name="OpenAIWorker", instructions="Check relay_inbox before acting.")
|
|
7
|
+
agent = on_relay(agent, relay)
|
|
8
|
+
print(len(agent.tools))
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from agent_relay import Relay, on_relay
|
|
2
|
+
from swarms import Agent
|
|
3
|
+
|
|
4
|
+
relay = Relay("SwarmsWorker")
|
|
5
|
+
agent = Agent(agent_name="SwarmsWorker", system_prompt="Use relay tools when needed.", model_name="gpt-4o-mini", max_loops=1)
|
|
6
|
+
agent = on_relay(agent, relay)
|
|
7
|
+
print(agent.run("Check relay_inbox, then say hello to the team."))
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agent-relay-sdk"
|
|
7
|
-
version = "3.2.
|
|
7
|
+
version = "3.2.4"
|
|
8
8
|
description = "Python SDK for Agent Relay workflows"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -14,9 +14,20 @@ dependencies = [
|
|
|
14
14
|
]
|
|
15
15
|
|
|
16
16
|
[project.optional-dependencies]
|
|
17
|
+
communicate = [
|
|
18
|
+
"aiohttp>=3.9",
|
|
19
|
+
]
|
|
20
|
+
openai-agents = ["openai-agents>=0.1"]
|
|
21
|
+
claude-sdk = ["claude-agent-sdk>=0.1"]
|
|
22
|
+
google-adk = ["google-adk>=0.1"]
|
|
23
|
+
agno = ["agno>=0.1"]
|
|
24
|
+
swarms = ["swarms>=0.1"]
|
|
25
|
+
crewai = ["crewai>=0.1"]
|
|
17
26
|
dev = [
|
|
27
|
+
"aiohttp>=3.9",
|
|
18
28
|
"pytest>=8.0",
|
|
19
29
|
"pytest-asyncio>=0.23",
|
|
30
|
+
"pytest-cov>=5.0",
|
|
20
31
|
]
|
|
21
32
|
|
|
22
33
|
[tool.hatch.build.targets.wheel]
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
# ── Primary API: Direct spawn/message (matches TypeScript SDK) ────────────────
|
|
4
4
|
|
|
5
5
|
from .relay import AgentRelay, Agent, AgentSpawner, HumanHandle, Message, SpawnOptions
|
|
6
|
+
_has_communicate = False
|
|
7
|
+
try:
|
|
8
|
+
from .communicate import Relay, RelayConfig, on_relay
|
|
9
|
+
_has_communicate = True
|
|
10
|
+
except ImportError:
|
|
11
|
+
# communicate extras not installed (pip install agent-relay-sdk[communicate])
|
|
12
|
+
pass
|
|
6
13
|
from .models import Models
|
|
7
14
|
from .client import AgentRelayClient, AgentRelayProtocolError, AgentRelayProcessError
|
|
8
15
|
from .protocol import (
|
|
@@ -76,6 +83,7 @@ __all__ = [
|
|
|
76
83
|
"HumanHandle",
|
|
77
84
|
"Message",
|
|
78
85
|
"SpawnOptions",
|
|
86
|
+
*(["Relay", "RelayConfig", "on_relay"] if _has_communicate else []),
|
|
79
87
|
"Models",
|
|
80
88
|
"AgentRelayClient",
|
|
81
89
|
"AgentRelayProtocolError",
|
|
@@ -243,36 +243,71 @@ class WorkflowBuilder:
|
|
|
243
243
|
self,
|
|
244
244
|
name: str,
|
|
245
245
|
*,
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
type: str | None = None,
|
|
247
|
+
# Agent step fields
|
|
248
|
+
agent: str | None = None,
|
|
249
|
+
task: str | None = None,
|
|
250
|
+
# Common fields
|
|
248
251
|
depends_on: list[str] | None = None,
|
|
249
252
|
verification: VerificationCheck | None = None,
|
|
250
253
|
timeout_ms: int | None = None,
|
|
251
254
|
retries: int | None = None,
|
|
255
|
+
# Deterministic step fields
|
|
256
|
+
command: str | None = None,
|
|
257
|
+
capture_output: bool | None = None,
|
|
258
|
+
fail_on_error: bool | None = None,
|
|
259
|
+
# Worktree step fields
|
|
260
|
+
branch: str | None = None,
|
|
261
|
+
base_branch: str | None = None,
|
|
262
|
+
path: str | None = None,
|
|
263
|
+
create_branch: bool | None = None,
|
|
252
264
|
) -> WorkflowBuilder:
|
|
253
265
|
"""Add a workflow step."""
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
step_def
|
|
266
|
+
if type == "deterministic":
|
|
267
|
+
if not command:
|
|
268
|
+
raise ValueError("deterministic steps must have a command")
|
|
269
|
+
if agent is not None or task is not None:
|
|
270
|
+
raise ValueError("deterministic steps must not have agent or task")
|
|
271
|
+
step_def: dict[str, Any] = {"name": name, "type": "deterministic", "command": command}
|
|
272
|
+
if capture_output is not None:
|
|
273
|
+
step_def["captureOutput"] = capture_output
|
|
274
|
+
if fail_on_error is not None:
|
|
275
|
+
step_def["failOnError"] = fail_on_error
|
|
276
|
+
if depends_on is not None:
|
|
277
|
+
step_def["dependsOn"] = depends_on
|
|
278
|
+
if verification is not None:
|
|
279
|
+
step_def["verification"] = verification.to_dict()
|
|
280
|
+
if timeout_ms is not None:
|
|
281
|
+
step_def["timeoutMs"] = timeout_ms
|
|
282
|
+
elif type == "worktree":
|
|
283
|
+
if agent is not None or task is not None:
|
|
284
|
+
raise ValueError("worktree steps must not have agent or task")
|
|
285
|
+
if not branch:
|
|
286
|
+
raise ValueError("worktree steps must have a branch")
|
|
287
|
+
step_def = {"name": name, "type": "worktree", "branch": branch}
|
|
288
|
+
if base_branch is not None:
|
|
289
|
+
step_def["baseBranch"] = base_branch
|
|
290
|
+
if path is not None:
|
|
291
|
+
step_def["path"] = path
|
|
292
|
+
if create_branch is not None:
|
|
293
|
+
step_def["createBranch"] = create_branch
|
|
294
|
+
if depends_on is not None:
|
|
295
|
+
step_def["dependsOn"] = depends_on
|
|
296
|
+
if timeout_ms is not None:
|
|
297
|
+
step_def["timeoutMs"] = timeout_ms
|
|
298
|
+
else:
|
|
299
|
+
# Agent step
|
|
300
|
+
if not agent or not task:
|
|
301
|
+
raise ValueError("Agent steps must have both agent and task")
|
|
302
|
+
step_def = {"name": name, "agent": agent, "task": task}
|
|
303
|
+
if depends_on is not None:
|
|
304
|
+
step_def["dependsOn"] = depends_on
|
|
305
|
+
if verification is not None:
|
|
306
|
+
step_def["verification"] = verification.to_dict()
|
|
307
|
+
if timeout_ms is not None:
|
|
308
|
+
step_def["timeoutMs"] = timeout_ms
|
|
309
|
+
if retries is not None:
|
|
310
|
+
step_def["retries"] = retries
|
|
276
311
|
|
|
277
312
|
self._steps.append(step_def)
|
|
278
313
|
return self
|
|
@@ -302,11 +337,15 @@ class WorkflowBuilder:
|
|
|
302
337
|
|
|
303
338
|
def to_config(self) -> dict[str, Any]:
|
|
304
339
|
"""Build and return the config as a dictionary (RelayYamlConfig shape)."""
|
|
305
|
-
if not self._agents:
|
|
306
|
-
raise ValueError("Workflow must have at least one agent")
|
|
307
340
|
if not self._steps:
|
|
308
341
|
raise ValueError("Workflow must have at least one step")
|
|
309
342
|
|
|
343
|
+
has_agent_steps = any(
|
|
344
|
+
s.get("type") not in ("deterministic", "worktree") for s in self._steps
|
|
345
|
+
)
|
|
346
|
+
if has_agent_steps and not self._agents:
|
|
347
|
+
raise ValueError("Workflow must have at least one agent when using agent steps")
|
|
348
|
+
|
|
310
349
|
swarm: dict[str, Any] = {"pattern": self._pattern}
|
|
311
350
|
if self._max_concurrency is not None:
|
|
312
351
|
swarm["maxConcurrency"] = self._max_concurrency
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Bridge that connects an external A2A agent into a Relay workspace."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import aiohttp
|
|
9
|
+
|
|
10
|
+
from .a2a_types import A2AAgentCard, A2AConfig, A2AMessage, A2APart
|
|
11
|
+
from .core import Relay
|
|
12
|
+
from .types import Message, RelayConfig
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class A2ABridge:
|
|
16
|
+
"""Bridges an external A2A agent into a Relay workspace.
|
|
17
|
+
|
|
18
|
+
- Registers a proxy agent on the Relay workspace
|
|
19
|
+
- When Relay messages arrive for the proxy, forwards them as A2A JSON-RPC
|
|
20
|
+
message/send calls to the external agent
|
|
21
|
+
- When A2A responses come back, forwards them as Relay DMs
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
bridge = A2ABridge(
|
|
25
|
+
relay_config=RelayConfig(workspace="myworkspace", api_key="rk_..."),
|
|
26
|
+
a2a_agent_url="https://partner-billing-agent.example.com",
|
|
27
|
+
proxy_name="partner-billing",
|
|
28
|
+
)
|
|
29
|
+
await bridge.start()
|
|
30
|
+
# Now "partner-billing" appears as an agent in the Relay workspace
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
relay_config: RelayConfig,
|
|
36
|
+
a2a_agent_url: str,
|
|
37
|
+
proxy_name: str,
|
|
38
|
+
) -> None:
|
|
39
|
+
self.relay = Relay(proxy_name, relay_config)
|
|
40
|
+
self.a2a_agent_url = a2a_agent_url.rstrip("/")
|
|
41
|
+
self.proxy_name = proxy_name
|
|
42
|
+
self._session: aiohttp.ClientSession | None = None
|
|
43
|
+
self._agent_card: A2AAgentCard | None = None
|
|
44
|
+
self._started = False
|
|
45
|
+
|
|
46
|
+
async def start(self) -> None:
|
|
47
|
+
"""Register proxy on Relay, listen for messages, forward to A2A agent."""
|
|
48
|
+
await self.relay.__aenter__()
|
|
49
|
+
self.relay.on_message(self._handle_relay_message)
|
|
50
|
+
self._started = True
|
|
51
|
+
|
|
52
|
+
async def stop(self) -> None:
|
|
53
|
+
"""Disconnect from Relay and clean up."""
|
|
54
|
+
self._started = False
|
|
55
|
+
await self.relay.__aexit__(None, None, None)
|
|
56
|
+
if self._session is not None and not self._session.closed:
|
|
57
|
+
await self._session.close()
|
|
58
|
+
self._session = None
|
|
59
|
+
|
|
60
|
+
async def discover_agent(self) -> A2AAgentCard:
|
|
61
|
+
"""Fetch the external A2A agent's Agent Card."""
|
|
62
|
+
session = await self._ensure_session()
|
|
63
|
+
url = f"{self.a2a_agent_url}/.well-known/agent.json"
|
|
64
|
+
async with session.get(url) as resp:
|
|
65
|
+
data = await resp.json()
|
|
66
|
+
self._agent_card = A2AAgentCard(
|
|
67
|
+
name=data.get("name", ""),
|
|
68
|
+
description=data.get("description", ""),
|
|
69
|
+
url=data.get("url", self.a2a_agent_url),
|
|
70
|
+
version=data.get("version", "1.0.0"),
|
|
71
|
+
)
|
|
72
|
+
return self._agent_card
|
|
73
|
+
|
|
74
|
+
async def send_a2a_message(self, text: str) -> str | None:
|
|
75
|
+
"""Send a message/send JSON-RPC call to the external A2A agent.
|
|
76
|
+
|
|
77
|
+
Returns the response text if available.
|
|
78
|
+
"""
|
|
79
|
+
session = await self._ensure_session()
|
|
80
|
+
|
|
81
|
+
a2a_msg = {
|
|
82
|
+
"role": "user",
|
|
83
|
+
"parts": [{"text": text}],
|
|
84
|
+
"messageId": str(uuid.uuid4()),
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
jsonrpc_request = {
|
|
88
|
+
"jsonrpc": "2.0",
|
|
89
|
+
"method": "message/send",
|
|
90
|
+
"params": {"message": a2a_msg},
|
|
91
|
+
"id": str(uuid.uuid4()),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
target_url = self.a2a_agent_url
|
|
95
|
+
if self._agent_card and self._agent_card.url:
|
|
96
|
+
target_url = self._agent_card.url
|
|
97
|
+
|
|
98
|
+
async with session.post(target_url, json=jsonrpc_request) as resp:
|
|
99
|
+
data = await resp.json()
|
|
100
|
+
|
|
101
|
+
result = data.get("result", {})
|
|
102
|
+
# Extract response text from the task
|
|
103
|
+
status = result.get("status", {})
|
|
104
|
+
status_msg = status.get("message", {})
|
|
105
|
+
parts = status_msg.get("parts", [])
|
|
106
|
+
if parts:
|
|
107
|
+
return parts[0].get("text")
|
|
108
|
+
|
|
109
|
+
# Try from messages list
|
|
110
|
+
messages = result.get("messages", [])
|
|
111
|
+
for msg in reversed(messages):
|
|
112
|
+
if msg.get("role") == "agent":
|
|
113
|
+
msg_parts = msg.get("parts", [])
|
|
114
|
+
if msg_parts:
|
|
115
|
+
return msg_parts[0].get("text")
|
|
116
|
+
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
async def _handle_relay_message(self, msg: Message) -> None:
|
|
120
|
+
"""Forward Relay message to A2A agent, then forward response back."""
|
|
121
|
+
response_text = await self.send_a2a_message(msg.text)
|
|
122
|
+
if response_text:
|
|
123
|
+
await self.relay.send(msg.sender, response_text)
|
|
124
|
+
|
|
125
|
+
async def _ensure_session(self) -> aiohttp.ClientSession:
|
|
126
|
+
if self._session is None or self._session.closed:
|
|
127
|
+
self._session = aiohttp.ClientSession()
|
|
128
|
+
return self._session
|
|
129
|
+
|
|
130
|
+
async def __aenter__(self) -> "A2ABridge":
|
|
131
|
+
await self.start()
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
|
|
135
|
+
await self.stop()
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
__all__ = ["A2ABridge"]
|