aigency 0.0.1rc238211992__tar.gz → 0.1.0rc67928564__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.
- aigency-0.1.0rc67928564/PKG-INFO +171 -0
- aigency-0.1.0rc67928564/README.md +159 -0
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/aigency/agents/client.py +22 -3
- aigency-0.1.0rc67928564/aigency/agents/communicator.py +157 -0
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/aigency/agents/executor.py +58 -2
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/aigency/agents/generator.py +64 -7
- aigency-0.1.0rc67928564/aigency/schemas/agent/agent.py +50 -0
- aigency-0.1.0rc67928564/aigency/schemas/agent/model.py +46 -0
- aigency-0.1.0rc67928564/aigency/schemas/agent/remote_agent.py +41 -0
- aigency-0.1.0rc67928564/aigency/schemas/agent/skills.py +42 -0
- aigency-0.1.0rc67928564/aigency/schemas/agent/tools.py +126 -0
- aigency-0.1.0rc67928564/aigency/schemas/aigency_config.py +48 -0
- aigency-0.1.0rc67928564/aigency/schemas/metadata/metadata.py +38 -0
- aigency-0.1.0rc67928564/aigency/schemas/observability/observability.py +45 -0
- aigency-0.1.0rc67928564/aigency/schemas/observability/phoenix.py +36 -0
- aigency-0.1.0rc67928564/aigency/schemas/service/capabilities.py +32 -0
- aigency-0.1.0rc67928564/aigency/schemas/service/interface.py +37 -0
- aigency-0.1.0rc67928564/aigency/schemas/service/service.py +42 -0
- aigency-0.1.0rc67928564/aigency/tools/generator.py +130 -0
- aigency-0.1.0rc67928564/aigency/utils/config_service.py +161 -0
- aigency-0.1.0rc67928564/aigency/utils/logger.py +190 -0
- aigency-0.1.0rc67928564/aigency/utils/singleton.py +73 -0
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/aigency/utils/utils.py +45 -6
- aigency-0.1.0rc67928564/aigency.egg-info/PKG-INFO +171 -0
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/aigency.egg-info/requires.txt +1 -1
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/pyproject.toml +2 -2
- aigency-0.0.1rc238211992/PKG-INFO +0 -267
- aigency-0.0.1rc238211992/README.md +0 -255
- aigency-0.0.1rc238211992/aigency/agents/communicator.py +0 -121
- aigency-0.0.1rc238211992/aigency/schemas/agent/agent.py +0 -16
- aigency-0.0.1rc238211992/aigency/schemas/agent/model.py +0 -12
- aigency-0.0.1rc238211992/aigency/schemas/agent/remote_agent.py +0 -9
- aigency-0.0.1rc238211992/aigency/schemas/agent/skills.py +0 -10
- aigency-0.0.1rc238211992/aigency/schemas/agent/tools.py +0 -53
- aigency-0.0.1rc238211992/aigency/schemas/aigency_config.py +0 -14
- aigency-0.0.1rc238211992/aigency/schemas/metadata/metadata.py +0 -8
- aigency-0.0.1rc238211992/aigency/schemas/observability/observability.py +0 -10
- aigency-0.0.1rc238211992/aigency/schemas/observability/phoenix.py +0 -6
- aigency-0.0.1rc238211992/aigency/schemas/service/capabilities.py +0 -5
- aigency-0.0.1rc238211992/aigency/schemas/service/interface.py +0 -7
- aigency-0.0.1rc238211992/aigency/schemas/service/service.py +0 -9
- aigency-0.0.1rc238211992/aigency/tools/generator.py +0 -79
- aigency-0.0.1rc238211992/aigency/utils/config_service.py +0 -89
- aigency-0.0.1rc238211992/aigency/utils/logger.py +0 -101
- aigency-0.0.1rc238211992/aigency/utils/singleton.py +0 -12
- aigency-0.0.1rc238211992/aigency.egg-info/PKG-INFO +0 -267
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/aigency/__init__.py +0 -0
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/aigency.egg-info/SOURCES.txt +0 -0
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/aigency.egg-info/dependency_links.txt +0 -0
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/aigency.egg-info/top_level.txt +0 -0
- {aigency-0.0.1rc238211992 → aigency-0.1.0rc67928564}/setup.cfg +0 -0
@@ -0,0 +1,171 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: aigency
|
3
|
+
Version: 0.1.0rc67928564
|
4
|
+
Summary: Add your description here
|
5
|
+
Requires-Python: >=3.12
|
6
|
+
Description-Content-Type: text/markdown
|
7
|
+
Requires-Dist: google-adk>=1.11.0
|
8
|
+
Requires-Dist: a2a-sdk>=0.3.0
|
9
|
+
Requires-Dist: litellm<1.73.0,>=1.72.6
|
10
|
+
Requires-Dist: pyyaml==6.0.2
|
11
|
+
Requires-Dist: PyJWT==2.10.1
|
12
|
+
|
13
|
+
# Aigency
|
14
|
+
|
15
|
+
AI Agent Development Acceleration Kit — build, run, and orchestrate intelligent agents with a production‑ready Agent‑to‑Agent (A2A) runtime.
|
16
|
+
|
17
|
+
Aigency provides primitives and utilities to define agents via simple YAML, instantiate them programmatically, and serve them over HTTP using the A2A server. It is designed to be modular, observable, and extensible.
|
18
|
+
|
19
|
+
- Python: >= 3.12
|
20
|
+
- PyPI package: `aigency`
|
21
|
+
- Core deps: `a2a-sdk`, `pyyaml`, `litellm`, `PyJWT`, `google-adk`
|
22
|
+
|
23
|
+
## Features
|
24
|
+
- Config‑first agents: define agent behavior, skills, tools, and model in YAML
|
25
|
+
- Agent generator: instantiate agents, build agent cards, and executors programmatically
|
26
|
+
- A2A integration: serve agents over HTTP with Starlette‑based A2A server
|
27
|
+
- MCP‑friendly: integrate external tools/services via Model Context Protocol (optional)
|
28
|
+
- Observability: compatible with Phoenix and A2A Inspector for tracing and debugging
|
29
|
+
- Docker‑friendly: used across example demos and containers
|
30
|
+
|
31
|
+
## Installation
|
32
|
+
```bash
|
33
|
+
pip install aigency
|
34
|
+
```
|
35
|
+
Requires Python 3.12+.
|
36
|
+
|
37
|
+
## Quickstart
|
38
|
+
Minimal example for a single agent (no MCP) that responds in the user’s language.
|
39
|
+
|
40
|
+
1) Create an agent config file (e.g., `agent_config.yaml`):
|
41
|
+
```yaml
|
42
|
+
metadata:
|
43
|
+
name: hello_agent
|
44
|
+
description: A simple example agent that greets and answers briefly.
|
45
|
+
version: 1.0.0
|
46
|
+
|
47
|
+
service:
|
48
|
+
url: http://hello-agent:8080
|
49
|
+
capabilities:
|
50
|
+
streaming: true
|
51
|
+
interface:
|
52
|
+
default_input_modes: [text, text/plain]
|
53
|
+
default_output_modes: [text, text/plain]
|
54
|
+
|
55
|
+
agent:
|
56
|
+
model:
|
57
|
+
name: gemini-2.0-flash
|
58
|
+
|
59
|
+
instruction: |
|
60
|
+
"""
|
61
|
+
You are a friendly, concise assistant. Always reply in the same language as the user.
|
62
|
+
Keep responses short and helpful.
|
63
|
+
"""
|
64
|
+
|
65
|
+
skills:
|
66
|
+
- id: greet
|
67
|
+
name: Greet
|
68
|
+
description: Greets users and offers help
|
69
|
+
examples:
|
70
|
+
- "Hello! How can I help you today?"
|
71
|
+
```
|
72
|
+
|
73
|
+
2) Run a tiny A2A app (e.g., `app.py`):
|
74
|
+
```python
|
75
|
+
import os
|
76
|
+
import uvicorn
|
77
|
+
from a2a.server.apps import A2AStarletteApplication
|
78
|
+
from a2a.server.request_handlers import DefaultRequestHandler
|
79
|
+
from a2a.server.tasks import InMemoryTaskStore
|
80
|
+
from aigency.agents.generator import AgentA2AGenerator
|
81
|
+
from aigency.utils.config_service import ConfigService
|
82
|
+
|
83
|
+
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "agent_config.yaml")
|
84
|
+
|
85
|
+
config_service = ConfigService(config_file=CONFIG_PATH)
|
86
|
+
agent_config = config_service.config
|
87
|
+
|
88
|
+
agent = AgentA2AGenerator.create_agent(agent_config=agent_config)
|
89
|
+
agent_card = AgentA2AGenerator.build_agent_card(agent_config=agent_config)
|
90
|
+
executor = AgentA2AGenerator.build_executor(agent=agent, agent_card=agent_card)
|
91
|
+
|
92
|
+
request_handler = DefaultRequestHandler(
|
93
|
+
agent_executor=executor,
|
94
|
+
task_store=InMemoryTaskStore(),
|
95
|
+
)
|
96
|
+
app = A2AStarletteApplication(
|
97
|
+
agent_card=agent_card,
|
98
|
+
http_handler=request_handler,
|
99
|
+
).build()
|
100
|
+
|
101
|
+
if __name__ == "__main__":
|
102
|
+
uvicorn.run(app, host="0.0.0.0", port=8080)
|
103
|
+
```
|
104
|
+
|
105
|
+
3) Start the server:
|
106
|
+
```bash
|
107
|
+
python app.py
|
108
|
+
```
|
109
|
+
Then open http://localhost:8080 to interact via the A2A HTTP interface or connect a compatible client.
|
110
|
+
|
111
|
+
## Using Models & Providers
|
112
|
+
Aigency integrates with LLM providers via its dependencies. For Google Gemini models:
|
113
|
+
|
114
|
+
- Use API key (Google AI Studio):
|
115
|
+
- `GEMINI_API_KEY=your_gemini_api_key`
|
116
|
+
- `GOOGLE_GENAI_USE_VERTEXAI=FALSE`
|
117
|
+
- Or use Vertex AI (requires additional env like project/region and credentials):
|
118
|
+
- `GOOGLE_GENAI_USE_VERTEXAI=TRUE`
|
119
|
+
|
120
|
+
Set these environment variables before running your app if you use Gemini‑based models.
|
121
|
+
|
122
|
+
## Configuration Reference (YAML)
|
123
|
+
Common top‑level sections:
|
124
|
+
|
125
|
+
- `metadata`: name, description, version
|
126
|
+
- `service`: url, capabilities, interface defaults
|
127
|
+
- `agent`:
|
128
|
+
- `model`: model name (e.g., `gemini-2.0-flash`)
|
129
|
+
- `instruction`: system prompt/persona
|
130
|
+
- `skills`: list of skills with `id`, `name`, `description`, and `examples`
|
131
|
+
- `tools`: optional integrations (e.g., MCP tools)
|
132
|
+
- `observability`: optional Phoenix/A2A Inspector configuration
|
133
|
+
|
134
|
+
Example of adding an MCP tool:
|
135
|
+
```yaml
|
136
|
+
tools:
|
137
|
+
- type: mcp
|
138
|
+
name: sample_mcp
|
139
|
+
description: Example MCP tool
|
140
|
+
mcp_config:
|
141
|
+
url: sample-mcp-service
|
142
|
+
port: 8080
|
143
|
+
path: /mcp/
|
144
|
+
```
|
145
|
+
|
146
|
+
## Examples & Demos
|
147
|
+
Explore ready‑to‑run demos built with Aigency:
|
148
|
+
|
149
|
+
- Reception Agent (single agent, no MCP):
|
150
|
+
https://aigency-project.github.io/get_started/demos/reception_aigent
|
151
|
+
- Gossip Agent (single agent + MCP tools):
|
152
|
+
https://aigency-project.github.io/get_started/demos/gossip_agent
|
153
|
+
- Detective Aigency (multi‑agent system):
|
154
|
+
https://aigency-project.github.io/get_started/demos/detective_aigency/
|
155
|
+
|
156
|
+
Documentation site:
|
157
|
+
- https://aigency-project.github.io/
|
158
|
+
|
159
|
+
## Observability
|
160
|
+
Aigency‑based apps can be observed with:
|
161
|
+
- Phoenix dashboard (tracing/metrics)
|
162
|
+
- A2A Inspector (agent/task introspection)
|
163
|
+
|
164
|
+
Refer to the demo repositories for docker‑compose setups that launch these services.
|
165
|
+
|
166
|
+
## Development
|
167
|
+
- Python 3.12+
|
168
|
+
- Install dev deps and run tests as usual; for versioning helpers, see `scripts/version_manager.py` in this repo.
|
169
|
+
|
170
|
+
## License
|
171
|
+
This project’s license is provided in the `LICENSE` file.
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# Aigency
|
2
|
+
|
3
|
+
AI Agent Development Acceleration Kit — build, run, and orchestrate intelligent agents with a production‑ready Agent‑to‑Agent (A2A) runtime.
|
4
|
+
|
5
|
+
Aigency provides primitives and utilities to define agents via simple YAML, instantiate them programmatically, and serve them over HTTP using the A2A server. It is designed to be modular, observable, and extensible.
|
6
|
+
|
7
|
+
- Python: >= 3.12
|
8
|
+
- PyPI package: `aigency`
|
9
|
+
- Core deps: `a2a-sdk`, `pyyaml`, `litellm`, `PyJWT`, `google-adk`
|
10
|
+
|
11
|
+
## Features
|
12
|
+
- Config‑first agents: define agent behavior, skills, tools, and model in YAML
|
13
|
+
- Agent generator: instantiate agents, build agent cards, and executors programmatically
|
14
|
+
- A2A integration: serve agents over HTTP with Starlette‑based A2A server
|
15
|
+
- MCP‑friendly: integrate external tools/services via Model Context Protocol (optional)
|
16
|
+
- Observability: compatible with Phoenix and A2A Inspector for tracing and debugging
|
17
|
+
- Docker‑friendly: used across example demos and containers
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
```bash
|
21
|
+
pip install aigency
|
22
|
+
```
|
23
|
+
Requires Python 3.12+.
|
24
|
+
|
25
|
+
## Quickstart
|
26
|
+
Minimal example for a single agent (no MCP) that responds in the user’s language.
|
27
|
+
|
28
|
+
1) Create an agent config file (e.g., `agent_config.yaml`):
|
29
|
+
```yaml
|
30
|
+
metadata:
|
31
|
+
name: hello_agent
|
32
|
+
description: A simple example agent that greets and answers briefly.
|
33
|
+
version: 1.0.0
|
34
|
+
|
35
|
+
service:
|
36
|
+
url: http://hello-agent:8080
|
37
|
+
capabilities:
|
38
|
+
streaming: true
|
39
|
+
interface:
|
40
|
+
default_input_modes: [text, text/plain]
|
41
|
+
default_output_modes: [text, text/plain]
|
42
|
+
|
43
|
+
agent:
|
44
|
+
model:
|
45
|
+
name: gemini-2.0-flash
|
46
|
+
|
47
|
+
instruction: |
|
48
|
+
"""
|
49
|
+
You are a friendly, concise assistant. Always reply in the same language as the user.
|
50
|
+
Keep responses short and helpful.
|
51
|
+
"""
|
52
|
+
|
53
|
+
skills:
|
54
|
+
- id: greet
|
55
|
+
name: Greet
|
56
|
+
description: Greets users and offers help
|
57
|
+
examples:
|
58
|
+
- "Hello! How can I help you today?"
|
59
|
+
```
|
60
|
+
|
61
|
+
2) Run a tiny A2A app (e.g., `app.py`):
|
62
|
+
```python
|
63
|
+
import os
|
64
|
+
import uvicorn
|
65
|
+
from a2a.server.apps import A2AStarletteApplication
|
66
|
+
from a2a.server.request_handlers import DefaultRequestHandler
|
67
|
+
from a2a.server.tasks import InMemoryTaskStore
|
68
|
+
from aigency.agents.generator import AgentA2AGenerator
|
69
|
+
from aigency.utils.config_service import ConfigService
|
70
|
+
|
71
|
+
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "agent_config.yaml")
|
72
|
+
|
73
|
+
config_service = ConfigService(config_file=CONFIG_PATH)
|
74
|
+
agent_config = config_service.config
|
75
|
+
|
76
|
+
agent = AgentA2AGenerator.create_agent(agent_config=agent_config)
|
77
|
+
agent_card = AgentA2AGenerator.build_agent_card(agent_config=agent_config)
|
78
|
+
executor = AgentA2AGenerator.build_executor(agent=agent, agent_card=agent_card)
|
79
|
+
|
80
|
+
request_handler = DefaultRequestHandler(
|
81
|
+
agent_executor=executor,
|
82
|
+
task_store=InMemoryTaskStore(),
|
83
|
+
)
|
84
|
+
app = A2AStarletteApplication(
|
85
|
+
agent_card=agent_card,
|
86
|
+
http_handler=request_handler,
|
87
|
+
).build()
|
88
|
+
|
89
|
+
if __name__ == "__main__":
|
90
|
+
uvicorn.run(app, host="0.0.0.0", port=8080)
|
91
|
+
```
|
92
|
+
|
93
|
+
3) Start the server:
|
94
|
+
```bash
|
95
|
+
python app.py
|
96
|
+
```
|
97
|
+
Then open http://localhost:8080 to interact via the A2A HTTP interface or connect a compatible client.
|
98
|
+
|
99
|
+
## Using Models & Providers
|
100
|
+
Aigency integrates with LLM providers via its dependencies. For Google Gemini models:
|
101
|
+
|
102
|
+
- Use API key (Google AI Studio):
|
103
|
+
- `GEMINI_API_KEY=your_gemini_api_key`
|
104
|
+
- `GOOGLE_GENAI_USE_VERTEXAI=FALSE`
|
105
|
+
- Or use Vertex AI (requires additional env like project/region and credentials):
|
106
|
+
- `GOOGLE_GENAI_USE_VERTEXAI=TRUE`
|
107
|
+
|
108
|
+
Set these environment variables before running your app if you use Gemini‑based models.
|
109
|
+
|
110
|
+
## Configuration Reference (YAML)
|
111
|
+
Common top‑level sections:
|
112
|
+
|
113
|
+
- `metadata`: name, description, version
|
114
|
+
- `service`: url, capabilities, interface defaults
|
115
|
+
- `agent`:
|
116
|
+
- `model`: model name (e.g., `gemini-2.0-flash`)
|
117
|
+
- `instruction`: system prompt/persona
|
118
|
+
- `skills`: list of skills with `id`, `name`, `description`, and `examples`
|
119
|
+
- `tools`: optional integrations (e.g., MCP tools)
|
120
|
+
- `observability`: optional Phoenix/A2A Inspector configuration
|
121
|
+
|
122
|
+
Example of adding an MCP tool:
|
123
|
+
```yaml
|
124
|
+
tools:
|
125
|
+
- type: mcp
|
126
|
+
name: sample_mcp
|
127
|
+
description: Example MCP tool
|
128
|
+
mcp_config:
|
129
|
+
url: sample-mcp-service
|
130
|
+
port: 8080
|
131
|
+
path: /mcp/
|
132
|
+
```
|
133
|
+
|
134
|
+
## Examples & Demos
|
135
|
+
Explore ready‑to‑run demos built with Aigency:
|
136
|
+
|
137
|
+
- Reception Agent (single agent, no MCP):
|
138
|
+
https://aigency-project.github.io/get_started/demos/reception_aigent
|
139
|
+
- Gossip Agent (single agent + MCP tools):
|
140
|
+
https://aigency-project.github.io/get_started/demos/gossip_agent
|
141
|
+
- Detective Aigency (multi‑agent system):
|
142
|
+
https://aigency-project.github.io/get_started/demos/detective_aigency/
|
143
|
+
|
144
|
+
Documentation site:
|
145
|
+
- https://aigency-project.github.io/
|
146
|
+
|
147
|
+
## Observability
|
148
|
+
Aigency‑based apps can be observed with:
|
149
|
+
- Phoenix dashboard (tracing/metrics)
|
150
|
+
- A2A Inspector (agent/task introspection)
|
151
|
+
|
152
|
+
Refer to the demo repositories for docker‑compose setups that launch these services.
|
153
|
+
|
154
|
+
## Development
|
155
|
+
- Python 3.12+
|
156
|
+
- Install dev deps and run tests as usual; for versioning helpers, see `scripts/version_manager.py` in this repo.
|
157
|
+
|
158
|
+
## License
|
159
|
+
This project’s license is provided in the `LICENSE` file.
|
@@ -1,10 +1,29 @@
|
|
1
|
+
"""Remote agent client module.
|
2
|
+
|
3
|
+
This module provides client functionality for connecting to and communicating with
|
4
|
+
remote agents in the A2A (Agent-to-Agent) ecosystem. It encapsulates the HTTP
|
5
|
+
client setup and agent card management required for inter-agent communication.
|
6
|
+
|
7
|
+
The AgentClient class serves as a wrapper around the A2A client factory and
|
8
|
+
provides a simplified interface for sending messages to remote agents while
|
9
|
+
handling connection management and protocol details.
|
10
|
+
|
11
|
+
Example:
|
12
|
+
Creating and using an agent client:
|
13
|
+
|
14
|
+
>>> agent_card = AgentCard(name="remote_agent", url="http://localhost:8080")
|
15
|
+
>>> client = AgentClient(agent_card)
|
16
|
+
>>> response = await client.send_message(message_request)
|
17
|
+
|
18
|
+
Attributes:
|
19
|
+
None: This module contains only class definitions.
|
20
|
+
"""
|
21
|
+
|
1
22
|
import httpx
|
2
|
-
|
23
|
+
|
3
24
|
from a2a.client.client import ClientConfig
|
4
25
|
from a2a.client.client_factory import ClientFactory
|
5
26
|
from a2a.types import AgentCard, Message, SendMessageResponse
|
6
|
-
#TODO: Enable when auth is implemented
|
7
|
-
#from a2a.client.auth.interceptor import AuthInterceptor
|
8
27
|
|
9
28
|
|
10
29
|
class AgentClient:
|
@@ -0,0 +1,157 @@
|
|
1
|
+
"""Agent-to-Agent communication module.
|
2
|
+
|
3
|
+
This module provides the core communication infrastructure for agents to interact
|
4
|
+
with each other using the A2A (Agent-to-Agent) protocol. It handles message
|
5
|
+
creation, payload formatting, and remote agent connection management.
|
6
|
+
|
7
|
+
The main component is the Communicator class which manages connections to remote
|
8
|
+
agents and provides methods for delegating tasks and sending messages between
|
9
|
+
agents in a distributed system.
|
10
|
+
|
11
|
+
Example:
|
12
|
+
Basic usage for agent communication:
|
13
|
+
|
14
|
+
>>> connections = {"agent1": client1, "agent2": client2}
|
15
|
+
>>> communicator = Communicator(connections)
|
16
|
+
>>> task_result = await communicator.send_message("agent1", "task description", context)
|
17
|
+
|
18
|
+
Attributes:
|
19
|
+
logger: Module-level logger instance for communication events.
|
20
|
+
"""
|
21
|
+
|
22
|
+
import uuid
|
23
|
+
from typing import Any, Awaitable
|
24
|
+
|
25
|
+
from a2a.types import Message, Task
|
26
|
+
from google.adk.tools.tool_context import ToolContext
|
27
|
+
|
28
|
+
from aigency.utils.logger import get_logger
|
29
|
+
|
30
|
+
logger = get_logger()
|
31
|
+
|
32
|
+
|
33
|
+
class Communicator:
|
34
|
+
"""Base class for agent-to-agent communication.
|
35
|
+
|
36
|
+
This class manages connections to remote agents and provides methods for
|
37
|
+
sending messages and delegating tasks to them.
|
38
|
+
|
39
|
+
Attributes:
|
40
|
+
remote_agent_connections (dict[str, Any]): Dictionary mapping agent names
|
41
|
+
to their client connection objects.
|
42
|
+
"""
|
43
|
+
|
44
|
+
def __init__(self, remote_agent_connections: dict[str, Any] | None = None):
|
45
|
+
"""Initialize the communicator with remote agent connections.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
remote_agent_connections (dict[str, Any] | None, optional): A dictionary
|
49
|
+
that maps agent names to their client connection objects.
|
50
|
+
Defaults to None.
|
51
|
+
"""
|
52
|
+
self.remote_agent_connections: dict[str, Any] = remote_agent_connections or {}
|
53
|
+
|
54
|
+
async def send_message(
|
55
|
+
self, agent_name: str, task: str, tool_context: ToolContext
|
56
|
+
) -> Awaitable[Task | None]:
|
57
|
+
"""Delegate a task to a specific remote agent.
|
58
|
+
|
59
|
+
This method sends a message to a remote agent, requesting it to perform a
|
60
|
+
task. It handles message payload creation and communication.
|
61
|
+
|
62
|
+
Args:
|
63
|
+
agent_name (str): Name of the remote agent to send the task to.
|
64
|
+
task (str): Detailed description of the task for the remote agent.
|
65
|
+
tool_context (ToolContext): Context object containing state and other
|
66
|
+
information.
|
67
|
+
|
68
|
+
Returns:
|
69
|
+
Task | None: A Task object if communication is successful, or None
|
70
|
+
otherwise.
|
71
|
+
|
72
|
+
Raises:
|
73
|
+
ValueError: If the specified agent is not found in connections.
|
74
|
+
"""
|
75
|
+
logger.info(
|
76
|
+
f"`send_message` started for agent: '{agent_name}' with task: '{task}'"
|
77
|
+
)
|
78
|
+
client = self.remote_agent_connections.get(agent_name)
|
79
|
+
if not client:
|
80
|
+
available_agents = list(self.remote_agent_connections.keys())
|
81
|
+
logger.error(
|
82
|
+
f"The LLM tried to call '{agent_name}', but it was not found. "
|
83
|
+
f"Available agents: {available_agents}"
|
84
|
+
)
|
85
|
+
raise ValueError(
|
86
|
+
f"Agent '{agent_name}' not found. Available agents: {available_agents}"
|
87
|
+
)
|
88
|
+
|
89
|
+
state = tool_context.state
|
90
|
+
|
91
|
+
contexts = state.setdefault("remote_agent_contexts", {})
|
92
|
+
agent_context = contexts.setdefault(
|
93
|
+
agent_name, {"context_id": str(uuid.uuid4())}
|
94
|
+
)
|
95
|
+
context_id = agent_context["context_id"]
|
96
|
+
|
97
|
+
task_id = state.get("task_id")
|
98
|
+
input_metadata = state.get("input_message_metadata", {})
|
99
|
+
message_id = input_metadata.get("message_id")
|
100
|
+
|
101
|
+
payload = self.create_send_message_payload(
|
102
|
+
text=task, task_id=task_id, context_id=context_id, message_id=message_id
|
103
|
+
)
|
104
|
+
logger.debug("`send_message` with the following payload: %s", payload)
|
105
|
+
|
106
|
+
send_response = None
|
107
|
+
|
108
|
+
async for resp in client.send_message(
|
109
|
+
message_request=Message(**payload["message"])
|
110
|
+
):
|
111
|
+
send_response = resp
|
112
|
+
|
113
|
+
if isinstance(send_response, tuple):
|
114
|
+
send_response, _ = send_response
|
115
|
+
|
116
|
+
if not isinstance(send_response, Task):
|
117
|
+
logger.warning(
|
118
|
+
f"The response received from agent '{agent_name}' is not a Task object. "
|
119
|
+
f"Received type: {type(send_response)}"
|
120
|
+
)
|
121
|
+
return None
|
122
|
+
|
123
|
+
return send_response
|
124
|
+
|
125
|
+
@staticmethod
|
126
|
+
def create_send_message_payload(
|
127
|
+
text: str,
|
128
|
+
task_id: str | None = None,
|
129
|
+
context_id: str | None = None,
|
130
|
+
message_id: str | None = None,
|
131
|
+
) -> dict[str, Any]:
|
132
|
+
"""Create a message payload to send to a remote agent.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
text (str): The text content of the message.
|
136
|
+
task_id (str | None, optional): Task ID to associate with the message.
|
137
|
+
Defaults to None.
|
138
|
+
context_id (str | None, optional): Context ID to associate with the
|
139
|
+
message. Defaults to None.
|
140
|
+
message_id (str | None, optional): Message ID. If None, a new one will
|
141
|
+
be generated. Defaults to None.
|
142
|
+
|
143
|
+
Returns:
|
144
|
+
dict[str, Any]: A dictionary containing the formatted message payload.
|
145
|
+
"""
|
146
|
+
payload: dict[str, Any] = {
|
147
|
+
"message": {
|
148
|
+
"role": "user",
|
149
|
+
"parts": [{"type": "text", "text": text}],
|
150
|
+
"message_id": message_id or uuid.uuid4().hex,
|
151
|
+
},
|
152
|
+
}
|
153
|
+
if task_id:
|
154
|
+
payload["message"]["task_id"] = task_id
|
155
|
+
if context_id:
|
156
|
+
payload["message"]["context_id"] = context_id
|
157
|
+
return payload
|
@@ -1,4 +1,27 @@
|
|
1
|
-
"""Agent executor module for A2A integration.
|
1
|
+
"""Agent executor module for A2A integration.
|
2
|
+
|
3
|
+
This module provides the execution engine for agents within the A2A (Agent-to-Agent)
|
4
|
+
protocol framework. It handles the lifecycle of agent tasks, session management,
|
5
|
+
and integration with Google ADK runners for processing agent requests.
|
6
|
+
|
7
|
+
The AgentA2AExecutor class manages the execution flow from task submission through
|
8
|
+
completion, handling streaming responses, function calls, and error conditions
|
9
|
+
while maintaining compatibility with the A2A protocol specifications.
|
10
|
+
|
11
|
+
Example:
|
12
|
+
Creating and using an agent executor:
|
13
|
+
|
14
|
+
>>> runner = Runner(app_name="my_agent", agent=agent)
|
15
|
+
>>> executor = AgentA2AExecutor(runner, agent_card)
|
16
|
+
>>> await executor.execute(context, event_queue)
|
17
|
+
|
18
|
+
Attributes:
|
19
|
+
logger: Module-level logger instance for execution events.
|
20
|
+
DEFAULT_USER_ID (str): Default user identifier for session management.
|
21
|
+
|
22
|
+
Todo:
|
23
|
+
* Replace DEFAULT_USER_ID with proper user management system.
|
24
|
+
"""
|
2
25
|
|
3
26
|
from a2a.server.agent_execution import AgentExecutor
|
4
27
|
from a2a.server.agent_execution.context import RequestContext
|
@@ -20,13 +43,24 @@ logger = get_logger()
|
|
20
43
|
# TODO: This needs to be changed
|
21
44
|
DEFAULT_USER_ID = "self"
|
22
45
|
|
46
|
+
|
23
47
|
class AgentA2AExecutor(AgentExecutor):
|
24
|
-
"""Agent executor for A2A integration with Google ADK runners.
|
48
|
+
"""Agent executor for A2A integration with Google ADK runners.
|
49
|
+
|
50
|
+
This class handles the execution of agent tasks within the A2A protocol,
|
51
|
+
managing sessions, processing requests, and handling task lifecycle.
|
52
|
+
|
53
|
+
Attributes:
|
54
|
+
_card (AgentCard): The agent card containing metadata about the agent.
|
55
|
+
_active_sessions (set[str]): Set of active session IDs for tracking.
|
56
|
+
runner (Runner): The Google ADK runner instance for executing agent logic.
|
57
|
+
"""
|
25
58
|
|
26
59
|
def __init__(self, runner: Runner, card: AgentCard):
|
27
60
|
"""Initialize the BaseAgentA2AExecutor.
|
28
61
|
|
29
62
|
Args:
|
63
|
+
runner (Runner): The Google ADK runner instance.
|
30
64
|
card (AgentCard): The agent card containing metadata about the agent.
|
31
65
|
"""
|
32
66
|
self._card = card
|
@@ -65,6 +99,13 @@ class AgentA2AExecutor(AgentExecutor):
|
|
65
99
|
session_id: str,
|
66
100
|
task_updater: TaskUpdater,
|
67
101
|
) -> None:
|
102
|
+
"""Process a request through the agent runner.
|
103
|
+
|
104
|
+
Args:
|
105
|
+
new_message (types.Content): The message content to process.
|
106
|
+
session_id (str): The session ID for this request.
|
107
|
+
task_updater (TaskUpdater): Task updater for reporting progress.
|
108
|
+
"""
|
68
109
|
session_obj = await self._upsert_session(session_id)
|
69
110
|
session_id = session_obj.id
|
70
111
|
|
@@ -112,6 +153,12 @@ class AgentA2AExecutor(AgentExecutor):
|
|
112
153
|
context: RequestContext,
|
113
154
|
event_queue: EventQueue,
|
114
155
|
):
|
156
|
+
"""Execute an agent task.
|
157
|
+
|
158
|
+
Args:
|
159
|
+
context (RequestContext): The request context containing task information.
|
160
|
+
event_queue (EventQueue): Event queue for task updates.
|
161
|
+
"""
|
115
162
|
# Run the agent until either complete or the task is suspended.
|
116
163
|
updater = TaskUpdater(event_queue, context.task_id, context.context_id)
|
117
164
|
# Immediately notify that the task is submitted.
|
@@ -131,6 +178,15 @@ class AgentA2AExecutor(AgentExecutor):
|
|
131
178
|
logger.debug("[ADKAgentA2AExecutor] execute exiting")
|
132
179
|
|
133
180
|
async def cancel(self, context: RequestContext, event_queue: EventQueue):
|
181
|
+
"""Cancel an active agent task.
|
182
|
+
|
183
|
+
Args:
|
184
|
+
context (RequestContext): The request context for the task to cancel.
|
185
|
+
event_queue (EventQueue): Event queue for task updates.
|
186
|
+
|
187
|
+
Raises:
|
188
|
+
ServerError: Always raised as cancellation is not currently supported.
|
189
|
+
"""
|
134
190
|
session_id = context.context_id
|
135
191
|
if session_id in self._active_sessions:
|
136
192
|
logger.info("Cancellation requested for active session: %s", session_id)
|