a2a-lite 0.1.0__tar.gz → 0.2.0__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.
- a2a_lite-0.2.0/.claude/settings.local.json +12 -0
- a2a_lite-0.2.0/PKG-INFO +526 -0
- a2a_lite-0.2.0/README.md +495 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/09_testing.py +8 -8
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/pyproject.toml +1 -1
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/__init__.py +7 -6
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/agent.py +70 -13
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/auth.py +12 -4
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/cli.py +1 -1
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/decorators.py +3 -1
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/discovery.py +4 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/executor.py +15 -8
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/tasks.py +38 -29
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/testing.py +56 -14
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/utils.py +16 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_pydantic.py +7 -7
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_tasks.py +36 -28
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_testing.py +7 -7
- a2a_lite-0.1.0/PKG-INFO +0 -383
- a2a_lite-0.1.0/README.md +0 -352
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/.gitignore +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/01_hello_world.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/02_calculator.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/03_async_agent.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/04_multi_agent/finance_agent.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/04_multi_agent/reporter_agent.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/04_multi_agent/run_demo.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/05_with_llm.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/06_pydantic_models.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/07_middleware.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/08_streaming.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/10_webhooks.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/11_human_in_the_loop.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/12_file_handling.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/13_task_tracking.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/examples/14_with_auth.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/human_loop.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/middleware.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/parts.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/streaming.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/src/a2a_lite/webhooks.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/__init__.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_agent.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_auth.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_decorators.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_discovery.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_human_loop.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_integration.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_middleware.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_parts.py +0 -0
- {a2a_lite-0.1.0 → a2a_lite-0.2.0}/tests/test_utils.py +0 -0
a2a_lite-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: a2a-lite
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Simplified wrapper for Google's A2A Protocol SDK
|
|
5
|
+
Author: A2A Lite Contributors
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Keywords: a2a,agents,ai,protocol,sdk
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Requires-Dist: a2a-sdk[http-server]>=0.2.6
|
|
18
|
+
Requires-Dist: httpx>=0.25.0
|
|
19
|
+
Requires-Dist: pydantic>=2.0
|
|
20
|
+
Requires-Dist: rich>=13.0
|
|
21
|
+
Requires-Dist: starlette>=0.40.0
|
|
22
|
+
Requires-Dist: typer>=0.9.0
|
|
23
|
+
Requires-Dist: uvicorn>=0.30.0
|
|
24
|
+
Requires-Dist: watchfiles>=0.20.0
|
|
25
|
+
Requires-Dist: zeroconf>=0.80.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: httpx>=0.25; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# A2A Lite - Python
|
|
33
|
+
|
|
34
|
+
**Build A2A agents in 8 lines. Add enterprise features when you need them.**
|
|
35
|
+
|
|
36
|
+
Wraps the official [A2A Python SDK](https://github.com/a2aproject/a2a-python) with a simple, intuitive API.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from a2a_lite import Agent
|
|
40
|
+
|
|
41
|
+
agent = Agent(name="Bot", description="My bot")
|
|
42
|
+
|
|
43
|
+
@agent.skill("greet")
|
|
44
|
+
async def greet(name: str) -> str:
|
|
45
|
+
return f"Hello, {name}!"
|
|
46
|
+
|
|
47
|
+
agent.run()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install a2a-lite
|
|
56
|
+
# or
|
|
57
|
+
uv add a2a-lite
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Requirements:** Python 3.10+
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
### 1. Create an agent
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from a2a_lite import Agent
|
|
70
|
+
|
|
71
|
+
agent = Agent(name="Calculator", description="Does math")
|
|
72
|
+
|
|
73
|
+
@agent.skill("add")
|
|
74
|
+
async def add(a: int, b: int) -> int:
|
|
75
|
+
return a + b
|
|
76
|
+
|
|
77
|
+
@agent.skill("multiply")
|
|
78
|
+
async def multiply(a: int, b: int) -> int:
|
|
79
|
+
return a * b
|
|
80
|
+
|
|
81
|
+
agent.run(port=8787)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 2. Test it
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from a2a_lite import Agent, AgentTestClient
|
|
88
|
+
|
|
89
|
+
agent = Agent(name="Calculator", description="Does math")
|
|
90
|
+
|
|
91
|
+
@agent.skill("add")
|
|
92
|
+
async def add(a: int, b: int) -> int:
|
|
93
|
+
return a + b
|
|
94
|
+
|
|
95
|
+
client = AgentTestClient(agent)
|
|
96
|
+
result = client.call("add", a=2, b=3)
|
|
97
|
+
assert result == 5
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 3. Call it
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
curl -X POST http://localhost:8787/ \
|
|
104
|
+
-H "Content-Type: application/json" \
|
|
105
|
+
-d '{
|
|
106
|
+
"jsonrpc": "2.0",
|
|
107
|
+
"method": "message/send",
|
|
108
|
+
"id": "1",
|
|
109
|
+
"params": {
|
|
110
|
+
"message": {
|
|
111
|
+
"role": "user",
|
|
112
|
+
"parts": [{"type": "text", "text": "{\"skill\": \"add\", \"params\": {\"a\": 2, \"b\": 3}}"}],
|
|
113
|
+
"messageId": "msg-1"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}'
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Progressive Complexity
|
|
122
|
+
|
|
123
|
+
### Level 1: Basic Skills
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from a2a_lite import Agent
|
|
127
|
+
|
|
128
|
+
agent = Agent(name="Bot", description="A bot")
|
|
129
|
+
|
|
130
|
+
@agent.skill("greet")
|
|
131
|
+
async def greet(name: str) -> str:
|
|
132
|
+
return f"Hello, {name}!"
|
|
133
|
+
|
|
134
|
+
agent.run()
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Level 2: Pydantic Models (Just Works)
|
|
138
|
+
|
|
139
|
+
Pass dicts from callers — they're auto-converted to Pydantic models:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from pydantic import BaseModel
|
|
143
|
+
|
|
144
|
+
class User(BaseModel):
|
|
145
|
+
name: str
|
|
146
|
+
email: str
|
|
147
|
+
|
|
148
|
+
@agent.skill("create_user")
|
|
149
|
+
async def create_user(user: User) -> dict:
|
|
150
|
+
# 'user' is already a User instance — auto-converted from dict!
|
|
151
|
+
return {"id": 1, "name": user.name}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Lists of models work too:
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from typing import List
|
|
158
|
+
|
|
159
|
+
@agent.skill("count_users")
|
|
160
|
+
async def count_users(users: List[User]) -> int:
|
|
161
|
+
return len(users)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Level 3: Streaming (Just Yield)
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
@agent.skill("chat", streaming=True)
|
|
168
|
+
async def chat(message: str):
|
|
169
|
+
for word in message.split():
|
|
170
|
+
yield word + " "
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Level 4: Middleware
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
@agent.middleware
|
|
177
|
+
async def log_requests(ctx, next):
|
|
178
|
+
print(f"Calling: {ctx.skill}")
|
|
179
|
+
result = await next()
|
|
180
|
+
print(f"Result: {result}")
|
|
181
|
+
return result
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Built-in middleware:
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
from a2a_lite import logging_middleware, timing_middleware, retry_middleware, rate_limit_middleware
|
|
188
|
+
|
|
189
|
+
agent.use(logging_middleware)
|
|
190
|
+
agent.use(timing_middleware)
|
|
191
|
+
agent.use(rate_limit_middleware(max_per_minute=60))
|
|
192
|
+
agent.use(retry_middleware(max_retries=3))
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Level 5: Human-in-the-Loop
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
from a2a_lite import InteractionContext
|
|
199
|
+
|
|
200
|
+
@agent.skill("wizard")
|
|
201
|
+
async def wizard(ctx: InteractionContext) -> dict:
|
|
202
|
+
name = await ctx.ask("What's your name?")
|
|
203
|
+
role = await ctx.ask("Role?", options=["Dev", "Manager"])
|
|
204
|
+
|
|
205
|
+
if await ctx.confirm(f"Create {name} as {role}?"):
|
|
206
|
+
return {"created": name}
|
|
207
|
+
return {"cancelled": True}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Level 6: File Handling
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from a2a_lite import FilePart
|
|
214
|
+
|
|
215
|
+
@agent.skill("summarize")
|
|
216
|
+
async def summarize(doc: FilePart) -> str:
|
|
217
|
+
content = await doc.read_text()
|
|
218
|
+
return f"Summary: {content[:100]}..."
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Level 7: Task Tracking
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from a2a_lite import TaskContext
|
|
225
|
+
|
|
226
|
+
agent = Agent(name="Bot", description="A bot", task_store="memory")
|
|
227
|
+
|
|
228
|
+
@agent.skill("process")
|
|
229
|
+
async def process(data: str, task: TaskContext) -> str:
|
|
230
|
+
await task.update("working", "Starting...", progress=0.0)
|
|
231
|
+
|
|
232
|
+
for i in range(10):
|
|
233
|
+
await task.update("working", f"Step {i}/10", progress=i/10)
|
|
234
|
+
|
|
235
|
+
return "Done!"
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Level 8: Authentication
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from a2a_lite import Agent, APIKeyAuth
|
|
242
|
+
|
|
243
|
+
agent = Agent(
|
|
244
|
+
name="SecureBot",
|
|
245
|
+
description="A secure bot",
|
|
246
|
+
auth=APIKeyAuth(keys=["secret-key-1", "secret-key-2"]),
|
|
247
|
+
)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
API keys are hashed in memory using SHA-256 — plaintext keys are never stored.
|
|
251
|
+
|
|
252
|
+
Other auth providers:
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from a2a_lite.auth import BearerAuth, OAuth2Auth
|
|
256
|
+
|
|
257
|
+
# Bearer/JWT
|
|
258
|
+
agent = Agent(
|
|
259
|
+
name="Bot", description="A bot",
|
|
260
|
+
auth=BearerAuth(secret="your-jwt-secret"),
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# OAuth2
|
|
264
|
+
agent = Agent(
|
|
265
|
+
name="Bot", description="A bot",
|
|
266
|
+
auth=OAuth2Auth(issuer="https://auth.example.com", audience="my-api"),
|
|
267
|
+
)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Level 9: CORS and Production Mode
|
|
271
|
+
|
|
272
|
+
```python
|
|
273
|
+
agent = Agent(
|
|
274
|
+
name="Bot",
|
|
275
|
+
description="A bot",
|
|
276
|
+
cors_origins=["https://myapp.com", "https://admin.myapp.com"],
|
|
277
|
+
production=True, # Warns if running over HTTP
|
|
278
|
+
)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Level 10: Webhooks
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
@agent.on_complete
|
|
285
|
+
async def notify(skill_name, result):
|
|
286
|
+
print(f"Skill {skill_name} completed with: {result}")
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Testing
|
|
292
|
+
|
|
293
|
+
### AgentTestClient
|
|
294
|
+
|
|
295
|
+
The synchronous test client for use with pytest:
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
from a2a_lite import Agent, AgentTestClient
|
|
299
|
+
|
|
300
|
+
agent = Agent(name="Bot", description="Test")
|
|
301
|
+
|
|
302
|
+
@agent.skill("greet")
|
|
303
|
+
async def greet(name: str) -> str:
|
|
304
|
+
return f"Hello, {name}!"
|
|
305
|
+
|
|
306
|
+
@agent.skill("info")
|
|
307
|
+
async def info(name: str, age: int) -> dict:
|
|
308
|
+
return {"name": name, "age": age}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def test_simple_result():
|
|
312
|
+
client = AgentTestClient(agent)
|
|
313
|
+
result = client.call("greet", name="World")
|
|
314
|
+
# Simple values support direct equality
|
|
315
|
+
assert result == "Hello, World!"
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def test_dict_result():
|
|
319
|
+
client = AgentTestClient(agent)
|
|
320
|
+
result = client.call("info", name="Alice", age=30)
|
|
321
|
+
# Access dict results via .data
|
|
322
|
+
assert result.data["name"] == "Alice"
|
|
323
|
+
assert result.data["age"] == 30
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def test_text_access():
|
|
327
|
+
client = AgentTestClient(agent)
|
|
328
|
+
result = client.call("greet", name="World")
|
|
329
|
+
# .text gives the raw string
|
|
330
|
+
assert result.text == '"Hello, World!"'
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def test_list_skills():
|
|
334
|
+
client = AgentTestClient(agent)
|
|
335
|
+
skills = client.list_skills()
|
|
336
|
+
assert "greet" in skills
|
|
337
|
+
assert "info" in skills
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### TestResult
|
|
341
|
+
|
|
342
|
+
Every `client.call()` returns a `TestResult` with:
|
|
343
|
+
|
|
344
|
+
| Property | Description |
|
|
345
|
+
|----------|-------------|
|
|
346
|
+
| `.data` | Parsed Python object (dict, list, int, str, etc.) |
|
|
347
|
+
| `.text` | Raw text string from the response |
|
|
348
|
+
| `.json()` | Parse text as JSON (raises on invalid JSON) |
|
|
349
|
+
| `.raw_response` | Full A2A response dict |
|
|
350
|
+
|
|
351
|
+
`TestResult` supports direct equality comparison for simple values (`result == 5`), but use `.data` for subscripting (`result.data["key"]`).
|
|
352
|
+
|
|
353
|
+
### AsyncAgentTestClient
|
|
354
|
+
|
|
355
|
+
For async test frameworks:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
import pytest
|
|
359
|
+
from a2a_lite import AsyncAgentTestClient
|
|
360
|
+
|
|
361
|
+
@pytest.mark.asyncio
|
|
362
|
+
async def test_async():
|
|
363
|
+
client = AsyncAgentTestClient(agent)
|
|
364
|
+
result = await client.call("greet", name="World")
|
|
365
|
+
assert result == "Hello, World!"
|
|
366
|
+
await client.close()
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Streaming Tests
|
|
370
|
+
|
|
371
|
+
```python
|
|
372
|
+
def test_streaming():
|
|
373
|
+
client = AgentTestClient(agent)
|
|
374
|
+
results = client.stream("chat", message="hello world")
|
|
375
|
+
assert len(results) == 2
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Task Store
|
|
381
|
+
|
|
382
|
+
The `TaskStore` provides async-safe task lifecycle management:
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
from a2a_lite.tasks import TaskStore, TaskStatus
|
|
386
|
+
|
|
387
|
+
store = TaskStore()
|
|
388
|
+
|
|
389
|
+
# All operations are async and thread-safe
|
|
390
|
+
task = await store.create(task_id="task-1", skill="process")
|
|
391
|
+
task = await store.get("task-1")
|
|
392
|
+
await store.update("task-1", status=TaskStatus.WORKING, progress=0.5)
|
|
393
|
+
tasks = await store.list()
|
|
394
|
+
await store.delete("task-1")
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Agent Discovery
|
|
400
|
+
|
|
401
|
+
Find agents on your local network via mDNS:
|
|
402
|
+
|
|
403
|
+
```python
|
|
404
|
+
from a2a_lite import AgentDiscovery
|
|
405
|
+
|
|
406
|
+
# Advertise your agent
|
|
407
|
+
agent.run(port=8787, enable_discovery=True)
|
|
408
|
+
|
|
409
|
+
# Discover other agents
|
|
410
|
+
discovery = AgentDiscovery()
|
|
411
|
+
agents = await discovery.discover(timeout=5.0)
|
|
412
|
+
for a in agents:
|
|
413
|
+
print(f"{a.name} at {a.url}")
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## CLI
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
a2a-lite init my-agent # Create new project
|
|
422
|
+
a2a-lite serve agent.py # Run agent from file
|
|
423
|
+
a2a-lite serve agent.py -r # Run with hot reload
|
|
424
|
+
a2a-lite inspect http://... # View agent capabilities
|
|
425
|
+
a2a-lite test http://... skill # Test a skill
|
|
426
|
+
a2a-lite discover # Find local agents
|
|
427
|
+
a2a-lite version # Show version
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## Full API Reference
|
|
433
|
+
|
|
434
|
+
### Agent
|
|
435
|
+
|
|
436
|
+
```python
|
|
437
|
+
Agent(
|
|
438
|
+
name: str, # Required
|
|
439
|
+
description: str, # Required
|
|
440
|
+
version: str = "1.0.0",
|
|
441
|
+
url: str = None, # Override auto-detected URL
|
|
442
|
+
auth: AuthProvider = None, # Authentication provider
|
|
443
|
+
task_store: str | TaskStore = None, # "memory" or custom TaskStore
|
|
444
|
+
cors_origins: List[str] = None, # CORS allowed origins
|
|
445
|
+
production: bool = False, # Enable production warnings
|
|
446
|
+
enable_discovery: bool = False, # mDNS discovery
|
|
447
|
+
)
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Methods:**
|
|
451
|
+
|
|
452
|
+
| Method | Description |
|
|
453
|
+
|--------|-------------|
|
|
454
|
+
| `@agent.skill(name, **config)` | Register a skill via decorator |
|
|
455
|
+
| `@agent.middleware` | Register middleware via decorator |
|
|
456
|
+
| `agent.use(middleware)` | Register middleware function |
|
|
457
|
+
| `@agent.on_complete` | Register completion hook |
|
|
458
|
+
| `agent.run(port=8787)` | Start the server |
|
|
459
|
+
| `agent.get_app()` | Get the ASGI app (for custom deployment) |
|
|
460
|
+
|
|
461
|
+
### Skill Decorator
|
|
462
|
+
|
|
463
|
+
```python
|
|
464
|
+
@agent.skill(
|
|
465
|
+
name: str, # Skill name (required)
|
|
466
|
+
description: str = None, # Human-readable description
|
|
467
|
+
tags: List[str] = None, # Categorization tags
|
|
468
|
+
streaming: bool = False, # Enable streaming
|
|
469
|
+
)
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Auth Providers
|
|
473
|
+
|
|
474
|
+
| Provider | Usage |
|
|
475
|
+
|----------|-------|
|
|
476
|
+
| `APIKeyAuth(keys=[...])` | API key auth (keys hashed with SHA-256) |
|
|
477
|
+
| `BearerAuth(secret=...)` | JWT/Bearer token auth |
|
|
478
|
+
| `OAuth2Auth(issuer=..., audience=...)` | OAuth2 auth |
|
|
479
|
+
| `NoAuth()` | No auth (default) |
|
|
480
|
+
|
|
481
|
+
### Special Parameter Types
|
|
482
|
+
|
|
483
|
+
These are auto-injected when detected in skill signatures:
|
|
484
|
+
|
|
485
|
+
| Type | Description |
|
|
486
|
+
|------|-------------|
|
|
487
|
+
| `TaskContext` | Task lifecycle management (requires `task_store`) |
|
|
488
|
+
| `InteractionContext` | Human-in-the-loop interactions |
|
|
489
|
+
| `FilePart` | File upload handling |
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## Examples
|
|
494
|
+
|
|
495
|
+
| Example | What it shows |
|
|
496
|
+
|---------|---------------|
|
|
497
|
+
| [01_hello_world.py](examples/01_hello_world.py) | Simplest agent (8 lines) |
|
|
498
|
+
| [02_calculator.py](examples/02_calculator.py) | Multiple skills |
|
|
499
|
+
| [06_pydantic_models.py](examples/06_pydantic_models.py) | Auto Pydantic conversion |
|
|
500
|
+
| [08_streaming.py](examples/08_streaming.py) | Streaming responses |
|
|
501
|
+
| [09_testing.py](examples/09_testing.py) | Testing your agents |
|
|
502
|
+
| [11_human_in_the_loop.py](examples/11_human_in_the_loop.py) | Ask user questions |
|
|
503
|
+
| [12_file_handling.py](examples/12_file_handling.py) | Handle files |
|
|
504
|
+
| [13_task_tracking.py](examples/13_task_tracking.py) | Progress updates |
|
|
505
|
+
| [14_with_auth.py](examples/14_with_auth.py) | Authentication |
|
|
506
|
+
|
|
507
|
+
---
|
|
508
|
+
|
|
509
|
+
## 100% A2A Protocol Compatible
|
|
510
|
+
|
|
511
|
+
A2A Lite wraps the official A2A Python SDK. Every feature maps to real A2A protocol concepts:
|
|
512
|
+
|
|
513
|
+
| A2A Lite | A2A Protocol |
|
|
514
|
+
|----------|--------------|
|
|
515
|
+
| `@agent.skill()` | Agent Skills |
|
|
516
|
+
| `streaming=True` | SSE Streaming |
|
|
517
|
+
| `InteractionContext.ask()` | `input-required` state |
|
|
518
|
+
| `TaskContext.update()` | Task lifecycle states |
|
|
519
|
+
| `FilePart` | A2A File parts |
|
|
520
|
+
| `APIKeyAuth` / `BearerAuth` | Security schemes |
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## License
|
|
525
|
+
|
|
526
|
+
Apache 2.0
|