a2a-lite 0.2.1__tar.gz → 0.2.3__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.1 → a2a_lite-0.2.3}/PKG-INFO +39 -92
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/README.md +36 -89
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/06_pydantic_models.py +1 -1
- a2a_lite-0.2.1/examples/12_file_handling.py → a2a_lite-0.2.3/examples/10_file_handling.py +1 -1
- a2a_lite-0.2.1/examples/13_task_tracking.py → a2a_lite-0.2.3/examples/11_task_tracking.py +1 -1
- a2a_lite-0.2.1/examples/14_with_auth.py → a2a_lite-0.2.3/examples/12_with_auth.py +1 -1
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/pyproject.toml +4 -3
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/__init__.py +7 -36
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/agent.py +31 -74
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/auth.py +2 -2
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/cli.py +2 -43
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/decorators.py +2 -3
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/executor.py +22 -23
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/streaming.py +0 -29
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/testing.py +10 -1
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/utils.py +5 -1
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_auth.py +86 -0
- a2a_lite-0.2.1/examples/10_webhooks.py +0 -91
- a2a_lite-0.2.1/examples/11_human_in_the_loop.py +0 -78
- a2a_lite-0.2.1/src/a2a_lite/discovery.py +0 -152
- a2a_lite-0.2.1/src/a2a_lite/human_loop.py +0 -284
- a2a_lite-0.2.1/src/a2a_lite/webhooks.py +0 -232
- a2a_lite-0.2.1/tests/test_discovery.py +0 -55
- a2a_lite-0.2.1/tests/test_human_loop.py +0 -134
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/.claude/settings.local.json +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/.gitignore +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/01_hello_world.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/02_calculator.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/03_async_agent.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/04_multi_agent/finance_agent.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/04_multi_agent/reporter_agent.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/04_multi_agent/run_demo.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/05_with_llm.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/07_middleware.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/08_streaming.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/examples/09_testing.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/middleware.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/parts.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/src/a2a_lite/tasks.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/__init__.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_agent.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_decorators.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_integration.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_middleware.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_parts.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_pydantic.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_tasks.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_testing.py +0 -0
- {a2a_lite-0.2.1 → a2a_lite-0.2.3}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: a2a-lite
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Simplified wrapper for Google's A2A Protocol SDK
|
|
5
5
|
Author: A2A Lite Contributors
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -21,17 +21,17 @@ Requires-Dist: rich>=13.0
|
|
|
21
21
|
Requires-Dist: starlette>=0.40.0
|
|
22
22
|
Requires-Dist: typer>=0.9.0
|
|
23
23
|
Requires-Dist: uvicorn>=0.30.0
|
|
24
|
-
Requires-Dist: watchfiles>=0.20.0
|
|
25
|
-
Requires-Dist: zeroconf>=0.80.0
|
|
26
24
|
Provides-Extra: dev
|
|
27
25
|
Requires-Dist: httpx>=0.25; extra == 'dev'
|
|
28
26
|
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
29
27
|
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
28
|
+
Provides-Extra: oauth
|
|
29
|
+
Requires-Dist: pyjwt[crypto]>=2.0; extra == 'oauth'
|
|
30
30
|
Description-Content-Type: text/markdown
|
|
31
31
|
|
|
32
32
|
# A2A Lite - Python
|
|
33
33
|
|
|
34
|
-
**Build A2A agents in 8 lines. Add
|
|
34
|
+
**Build A2A agents in 8 lines. Add features when you need them.**
|
|
35
35
|
|
|
36
36
|
Wraps the official [A2A Python SDK](https://github.com/a2aproject/a2a-python) with a simple, intuitive API.
|
|
37
37
|
|
|
@@ -147,7 +147,6 @@ class User(BaseModel):
|
|
|
147
147
|
|
|
148
148
|
@agent.skill("create_user")
|
|
149
149
|
async def create_user(user: User) -> dict:
|
|
150
|
-
# 'user' is already a User instance — auto-converted from dict!
|
|
151
150
|
return {"id": 1, "name": user.name}
|
|
152
151
|
```
|
|
153
152
|
|
|
@@ -186,28 +185,13 @@ Built-in middleware:
|
|
|
186
185
|
```python
|
|
187
186
|
from a2a_lite import logging_middleware, timing_middleware, retry_middleware, rate_limit_middleware
|
|
188
187
|
|
|
189
|
-
agent.
|
|
190
|
-
agent.
|
|
191
|
-
agent.
|
|
192
|
-
agent.
|
|
188
|
+
agent.add_middleware(logging_middleware)
|
|
189
|
+
agent.add_middleware(timing_middleware)
|
|
190
|
+
agent.add_middleware(rate_limit_middleware(max_per_minute=60))
|
|
191
|
+
agent.add_middleware(retry_middleware(max_retries=3))
|
|
193
192
|
```
|
|
194
193
|
|
|
195
|
-
### Level 5:
|
|
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
|
|
194
|
+
### Level 5: File Handling
|
|
211
195
|
|
|
212
196
|
```python
|
|
213
197
|
from a2a_lite import FilePart
|
|
@@ -218,7 +202,7 @@ async def summarize(doc: FilePart) -> str:
|
|
|
218
202
|
return f"Summary: {content[:100]}..."
|
|
219
203
|
```
|
|
220
204
|
|
|
221
|
-
### Level
|
|
205
|
+
### Level 6: Task Tracking
|
|
222
206
|
|
|
223
207
|
```python
|
|
224
208
|
from a2a_lite import TaskContext
|
|
@@ -235,7 +219,7 @@ async def process(data: str, task: TaskContext) -> str:
|
|
|
235
219
|
return "Done!"
|
|
236
220
|
```
|
|
237
221
|
|
|
238
|
-
### Level
|
|
222
|
+
### Level 7: Authentication
|
|
239
223
|
|
|
240
224
|
```python
|
|
241
225
|
from a2a_lite import Agent, APIKeyAuth
|
|
@@ -260,14 +244,24 @@ agent = Agent(
|
|
|
260
244
|
auth=BearerAuth(secret="your-jwt-secret"),
|
|
261
245
|
)
|
|
262
246
|
|
|
263
|
-
# OAuth2
|
|
247
|
+
# OAuth2 (requires: pip install a2a-lite[oauth])
|
|
264
248
|
agent = Agent(
|
|
265
249
|
name="Bot", description="A bot",
|
|
266
250
|
auth=OAuth2Auth(issuer="https://auth.example.com", audience="my-api"),
|
|
267
251
|
)
|
|
268
252
|
```
|
|
269
253
|
|
|
270
|
-
|
|
254
|
+
Skills can receive auth results by type-hinting a parameter as `AuthResult`:
|
|
255
|
+
|
|
256
|
+
```python
|
|
257
|
+
from a2a_lite.auth import AuthResult
|
|
258
|
+
|
|
259
|
+
@agent.skill("whoami")
|
|
260
|
+
async def whoami(auth: AuthResult) -> dict:
|
|
261
|
+
return {"user": auth.identity, "scheme": auth.scheme}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Level 8: CORS and Production Mode
|
|
271
265
|
|
|
272
266
|
```python
|
|
273
267
|
agent = Agent(
|
|
@@ -278,11 +272,11 @@ agent = Agent(
|
|
|
278
272
|
)
|
|
279
273
|
```
|
|
280
274
|
|
|
281
|
-
### Level
|
|
275
|
+
### Level 9: Completion Hooks
|
|
282
276
|
|
|
283
277
|
```python
|
|
284
278
|
@agent.on_complete
|
|
285
|
-
async def notify(skill_name, result):
|
|
279
|
+
async def notify(skill_name, result, ctx):
|
|
286
280
|
print(f"Skill {skill_name} completed with: {result}")
|
|
287
281
|
```
|
|
288
282
|
|
|
@@ -311,25 +305,16 @@ async def info(name: str, age: int) -> dict:
|
|
|
311
305
|
def test_simple_result():
|
|
312
306
|
client = AgentTestClient(agent)
|
|
313
307
|
result = client.call("greet", name="World")
|
|
314
|
-
# Simple values support direct equality
|
|
315
308
|
assert result == "Hello, World!"
|
|
316
309
|
|
|
317
310
|
|
|
318
311
|
def test_dict_result():
|
|
319
312
|
client = AgentTestClient(agent)
|
|
320
313
|
result = client.call("info", name="Alice", age=30)
|
|
321
|
-
# Access dict results via .data
|
|
322
314
|
assert result.data["name"] == "Alice"
|
|
323
315
|
assert result.data["age"] == 30
|
|
324
316
|
|
|
325
317
|
|
|
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
318
|
def test_list_skills():
|
|
334
319
|
client = AgentTestClient(agent)
|
|
335
320
|
skills = client.list_skills()
|
|
@@ -377,59 +362,19 @@ def test_streaming():
|
|
|
377
362
|
|
|
378
363
|
---
|
|
379
364
|
|
|
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
365
|
## CLI
|
|
419
366
|
|
|
420
367
|
```bash
|
|
421
368
|
a2a-lite init my-agent # Create new project
|
|
422
369
|
a2a-lite serve agent.py # Run agent from file
|
|
423
|
-
a2a-lite serve agent.py -r # Run with hot reload
|
|
424
370
|
a2a-lite inspect http://... # View agent capabilities
|
|
425
371
|
a2a-lite test http://... skill # Test a skill
|
|
426
|
-
a2a-lite discover # Find local agents
|
|
427
372
|
a2a-lite version # Show version
|
|
428
373
|
```
|
|
429
374
|
|
|
430
375
|
---
|
|
431
376
|
|
|
432
|
-
##
|
|
377
|
+
## API Reference
|
|
433
378
|
|
|
434
379
|
### Agent
|
|
435
380
|
|
|
@@ -443,7 +388,6 @@ Agent(
|
|
|
443
388
|
task_store: str | TaskStore = None, # "memory" or custom TaskStore
|
|
444
389
|
cors_origins: List[str] = None, # CORS allowed origins
|
|
445
390
|
production: bool = False, # Enable production warnings
|
|
446
|
-
enable_discovery: bool = False, # mDNS discovery
|
|
447
391
|
)
|
|
448
392
|
```
|
|
449
393
|
|
|
@@ -453,8 +397,11 @@ Agent(
|
|
|
453
397
|
|--------|-------------|
|
|
454
398
|
| `@agent.skill(name, **config)` | Register a skill via decorator |
|
|
455
399
|
| `@agent.middleware` | Register middleware via decorator |
|
|
456
|
-
| `agent.
|
|
400
|
+
| `agent.add_middleware(fn)` | Register middleware function |
|
|
457
401
|
| `@agent.on_complete` | Register completion hook |
|
|
402
|
+
| `@agent.on_startup` | Register startup hook |
|
|
403
|
+
| `@agent.on_shutdown` | Register shutdown hook |
|
|
404
|
+
| `@agent.on_error` | Register error handler |
|
|
458
405
|
| `agent.run(port=8787)` | Start the server |
|
|
459
406
|
| `agent.get_app()` | Get the ASGI app (for custom deployment) |
|
|
460
407
|
|
|
@@ -462,7 +409,7 @@ Agent(
|
|
|
462
409
|
|
|
463
410
|
```python
|
|
464
411
|
@agent.skill(
|
|
465
|
-
name: str,
|
|
412
|
+
name: str = None, # Skill name (defaults to function name)
|
|
466
413
|
description: str = None, # Human-readable description
|
|
467
414
|
tags: List[str] = None, # Categorization tags
|
|
468
415
|
streaming: bool = False, # Enable streaming
|
|
@@ -475,18 +422,19 @@ Agent(
|
|
|
475
422
|
|----------|-------|
|
|
476
423
|
| `APIKeyAuth(keys=[...])` | API key auth (keys hashed with SHA-256) |
|
|
477
424
|
| `BearerAuth(secret=...)` | JWT/Bearer token auth |
|
|
478
|
-
| `OAuth2Auth(issuer=..., audience=...)` | OAuth2 auth |
|
|
425
|
+
| `OAuth2Auth(issuer=..., audience=...)` | OAuth2 auth (requires `a2a-lite[oauth]`) |
|
|
479
426
|
| `NoAuth()` | No auth (default) |
|
|
480
427
|
|
|
481
428
|
### Special Parameter Types
|
|
482
429
|
|
|
483
|
-
These are auto-injected when detected in skill signatures:
|
|
430
|
+
These are auto-injected when detected in skill function signatures:
|
|
484
431
|
|
|
485
432
|
| Type | Description |
|
|
486
433
|
|------|-------------|
|
|
487
434
|
| `TaskContext` | Task lifecycle management (requires `task_store`) |
|
|
488
|
-
| `
|
|
435
|
+
| `AuthResult` | Authentication result injection |
|
|
489
436
|
| `FilePart` | File upload handling |
|
|
437
|
+
| `DataPart` | Structured data handling |
|
|
490
438
|
|
|
491
439
|
---
|
|
492
440
|
|
|
@@ -499,10 +447,9 @@ These are auto-injected when detected in skill signatures:
|
|
|
499
447
|
| [06_pydantic_models.py](examples/06_pydantic_models.py) | Auto Pydantic conversion |
|
|
500
448
|
| [08_streaming.py](examples/08_streaming.py) | Streaming responses |
|
|
501
449
|
| [09_testing.py](examples/09_testing.py) | Testing your agents |
|
|
502
|
-
| [
|
|
503
|
-
| [
|
|
504
|
-
| [
|
|
505
|
-
| [14_with_auth.py](examples/14_with_auth.py) | Authentication |
|
|
450
|
+
| [10_file_handling.py](examples/10_file_handling.py) | Handle files |
|
|
451
|
+
| [11_task_tracking.py](examples/11_task_tracking.py) | Progress updates |
|
|
452
|
+
| [12_with_auth.py](examples/12_with_auth.py) | Authentication |
|
|
506
453
|
|
|
507
454
|
---
|
|
508
455
|
|
|
@@ -514,9 +461,9 @@ A2A Lite wraps the official A2A Python SDK. Every feature maps to real A2A proto
|
|
|
514
461
|
|----------|--------------|
|
|
515
462
|
| `@agent.skill()` | Agent Skills |
|
|
516
463
|
| `streaming=True` | SSE Streaming |
|
|
517
|
-
| `InteractionContext.ask()` | `input-required` state |
|
|
518
464
|
| `TaskContext.update()` | Task lifecycle states |
|
|
519
465
|
| `FilePart` | A2A File parts |
|
|
466
|
+
| `DataPart` | A2A Data parts |
|
|
520
467
|
| `APIKeyAuth` / `BearerAuth` | Security schemes |
|
|
521
468
|
|
|
522
469
|
---
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# A2A Lite - Python
|
|
2
2
|
|
|
3
|
-
**Build A2A agents in 8 lines. Add
|
|
3
|
+
**Build A2A agents in 8 lines. Add features when you need them.**
|
|
4
4
|
|
|
5
5
|
Wraps the official [A2A Python SDK](https://github.com/a2aproject/a2a-python) with a simple, intuitive API.
|
|
6
6
|
|
|
@@ -116,7 +116,6 @@ class User(BaseModel):
|
|
|
116
116
|
|
|
117
117
|
@agent.skill("create_user")
|
|
118
118
|
async def create_user(user: User) -> dict:
|
|
119
|
-
# 'user' is already a User instance — auto-converted from dict!
|
|
120
119
|
return {"id": 1, "name": user.name}
|
|
121
120
|
```
|
|
122
121
|
|
|
@@ -155,28 +154,13 @@ Built-in middleware:
|
|
|
155
154
|
```python
|
|
156
155
|
from a2a_lite import logging_middleware, timing_middleware, retry_middleware, rate_limit_middleware
|
|
157
156
|
|
|
158
|
-
agent.
|
|
159
|
-
agent.
|
|
160
|
-
agent.
|
|
161
|
-
agent.
|
|
157
|
+
agent.add_middleware(logging_middleware)
|
|
158
|
+
agent.add_middleware(timing_middleware)
|
|
159
|
+
agent.add_middleware(rate_limit_middleware(max_per_minute=60))
|
|
160
|
+
agent.add_middleware(retry_middleware(max_retries=3))
|
|
162
161
|
```
|
|
163
162
|
|
|
164
|
-
### Level 5:
|
|
165
|
-
|
|
166
|
-
```python
|
|
167
|
-
from a2a_lite import InteractionContext
|
|
168
|
-
|
|
169
|
-
@agent.skill("wizard")
|
|
170
|
-
async def wizard(ctx: InteractionContext) -> dict:
|
|
171
|
-
name = await ctx.ask("What's your name?")
|
|
172
|
-
role = await ctx.ask("Role?", options=["Dev", "Manager"])
|
|
173
|
-
|
|
174
|
-
if await ctx.confirm(f"Create {name} as {role}?"):
|
|
175
|
-
return {"created": name}
|
|
176
|
-
return {"cancelled": True}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
### Level 6: File Handling
|
|
163
|
+
### Level 5: File Handling
|
|
180
164
|
|
|
181
165
|
```python
|
|
182
166
|
from a2a_lite import FilePart
|
|
@@ -187,7 +171,7 @@ async def summarize(doc: FilePart) -> str:
|
|
|
187
171
|
return f"Summary: {content[:100]}..."
|
|
188
172
|
```
|
|
189
173
|
|
|
190
|
-
### Level
|
|
174
|
+
### Level 6: Task Tracking
|
|
191
175
|
|
|
192
176
|
```python
|
|
193
177
|
from a2a_lite import TaskContext
|
|
@@ -204,7 +188,7 @@ async def process(data: str, task: TaskContext) -> str:
|
|
|
204
188
|
return "Done!"
|
|
205
189
|
```
|
|
206
190
|
|
|
207
|
-
### Level
|
|
191
|
+
### Level 7: Authentication
|
|
208
192
|
|
|
209
193
|
```python
|
|
210
194
|
from a2a_lite import Agent, APIKeyAuth
|
|
@@ -229,14 +213,24 @@ agent = Agent(
|
|
|
229
213
|
auth=BearerAuth(secret="your-jwt-secret"),
|
|
230
214
|
)
|
|
231
215
|
|
|
232
|
-
# OAuth2
|
|
216
|
+
# OAuth2 (requires: pip install a2a-lite[oauth])
|
|
233
217
|
agent = Agent(
|
|
234
218
|
name="Bot", description="A bot",
|
|
235
219
|
auth=OAuth2Auth(issuer="https://auth.example.com", audience="my-api"),
|
|
236
220
|
)
|
|
237
221
|
```
|
|
238
222
|
|
|
239
|
-
|
|
223
|
+
Skills can receive auth results by type-hinting a parameter as `AuthResult`:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from a2a_lite.auth import AuthResult
|
|
227
|
+
|
|
228
|
+
@agent.skill("whoami")
|
|
229
|
+
async def whoami(auth: AuthResult) -> dict:
|
|
230
|
+
return {"user": auth.identity, "scheme": auth.scheme}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Level 8: CORS and Production Mode
|
|
240
234
|
|
|
241
235
|
```python
|
|
242
236
|
agent = Agent(
|
|
@@ -247,11 +241,11 @@ agent = Agent(
|
|
|
247
241
|
)
|
|
248
242
|
```
|
|
249
243
|
|
|
250
|
-
### Level
|
|
244
|
+
### Level 9: Completion Hooks
|
|
251
245
|
|
|
252
246
|
```python
|
|
253
247
|
@agent.on_complete
|
|
254
|
-
async def notify(skill_name, result):
|
|
248
|
+
async def notify(skill_name, result, ctx):
|
|
255
249
|
print(f"Skill {skill_name} completed with: {result}")
|
|
256
250
|
```
|
|
257
251
|
|
|
@@ -280,25 +274,16 @@ async def info(name: str, age: int) -> dict:
|
|
|
280
274
|
def test_simple_result():
|
|
281
275
|
client = AgentTestClient(agent)
|
|
282
276
|
result = client.call("greet", name="World")
|
|
283
|
-
# Simple values support direct equality
|
|
284
277
|
assert result == "Hello, World!"
|
|
285
278
|
|
|
286
279
|
|
|
287
280
|
def test_dict_result():
|
|
288
281
|
client = AgentTestClient(agent)
|
|
289
282
|
result = client.call("info", name="Alice", age=30)
|
|
290
|
-
# Access dict results via .data
|
|
291
283
|
assert result.data["name"] == "Alice"
|
|
292
284
|
assert result.data["age"] == 30
|
|
293
285
|
|
|
294
286
|
|
|
295
|
-
def test_text_access():
|
|
296
|
-
client = AgentTestClient(agent)
|
|
297
|
-
result = client.call("greet", name="World")
|
|
298
|
-
# .text gives the raw string
|
|
299
|
-
assert result.text == '"Hello, World!"'
|
|
300
|
-
|
|
301
|
-
|
|
302
287
|
def test_list_skills():
|
|
303
288
|
client = AgentTestClient(agent)
|
|
304
289
|
skills = client.list_skills()
|
|
@@ -346,59 +331,19 @@ def test_streaming():
|
|
|
346
331
|
|
|
347
332
|
---
|
|
348
333
|
|
|
349
|
-
## Task Store
|
|
350
|
-
|
|
351
|
-
The `TaskStore` provides async-safe task lifecycle management:
|
|
352
|
-
|
|
353
|
-
```python
|
|
354
|
-
from a2a_lite.tasks import TaskStore, TaskStatus
|
|
355
|
-
|
|
356
|
-
store = TaskStore()
|
|
357
|
-
|
|
358
|
-
# All operations are async and thread-safe
|
|
359
|
-
task = await store.create(task_id="task-1", skill="process")
|
|
360
|
-
task = await store.get("task-1")
|
|
361
|
-
await store.update("task-1", status=TaskStatus.WORKING, progress=0.5)
|
|
362
|
-
tasks = await store.list()
|
|
363
|
-
await store.delete("task-1")
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
---
|
|
367
|
-
|
|
368
|
-
## Agent Discovery
|
|
369
|
-
|
|
370
|
-
Find agents on your local network via mDNS:
|
|
371
|
-
|
|
372
|
-
```python
|
|
373
|
-
from a2a_lite import AgentDiscovery
|
|
374
|
-
|
|
375
|
-
# Advertise your agent
|
|
376
|
-
agent.run(port=8787, enable_discovery=True)
|
|
377
|
-
|
|
378
|
-
# Discover other agents
|
|
379
|
-
discovery = AgentDiscovery()
|
|
380
|
-
agents = await discovery.discover(timeout=5.0)
|
|
381
|
-
for a in agents:
|
|
382
|
-
print(f"{a.name} at {a.url}")
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
---
|
|
386
|
-
|
|
387
334
|
## CLI
|
|
388
335
|
|
|
389
336
|
```bash
|
|
390
337
|
a2a-lite init my-agent # Create new project
|
|
391
338
|
a2a-lite serve agent.py # Run agent from file
|
|
392
|
-
a2a-lite serve agent.py -r # Run with hot reload
|
|
393
339
|
a2a-lite inspect http://... # View agent capabilities
|
|
394
340
|
a2a-lite test http://... skill # Test a skill
|
|
395
|
-
a2a-lite discover # Find local agents
|
|
396
341
|
a2a-lite version # Show version
|
|
397
342
|
```
|
|
398
343
|
|
|
399
344
|
---
|
|
400
345
|
|
|
401
|
-
##
|
|
346
|
+
## API Reference
|
|
402
347
|
|
|
403
348
|
### Agent
|
|
404
349
|
|
|
@@ -412,7 +357,6 @@ Agent(
|
|
|
412
357
|
task_store: str | TaskStore = None, # "memory" or custom TaskStore
|
|
413
358
|
cors_origins: List[str] = None, # CORS allowed origins
|
|
414
359
|
production: bool = False, # Enable production warnings
|
|
415
|
-
enable_discovery: bool = False, # mDNS discovery
|
|
416
360
|
)
|
|
417
361
|
```
|
|
418
362
|
|
|
@@ -422,8 +366,11 @@ Agent(
|
|
|
422
366
|
|--------|-------------|
|
|
423
367
|
| `@agent.skill(name, **config)` | Register a skill via decorator |
|
|
424
368
|
| `@agent.middleware` | Register middleware via decorator |
|
|
425
|
-
| `agent.
|
|
369
|
+
| `agent.add_middleware(fn)` | Register middleware function |
|
|
426
370
|
| `@agent.on_complete` | Register completion hook |
|
|
371
|
+
| `@agent.on_startup` | Register startup hook |
|
|
372
|
+
| `@agent.on_shutdown` | Register shutdown hook |
|
|
373
|
+
| `@agent.on_error` | Register error handler |
|
|
427
374
|
| `agent.run(port=8787)` | Start the server |
|
|
428
375
|
| `agent.get_app()` | Get the ASGI app (for custom deployment) |
|
|
429
376
|
|
|
@@ -431,7 +378,7 @@ Agent(
|
|
|
431
378
|
|
|
432
379
|
```python
|
|
433
380
|
@agent.skill(
|
|
434
|
-
name: str,
|
|
381
|
+
name: str = None, # Skill name (defaults to function name)
|
|
435
382
|
description: str = None, # Human-readable description
|
|
436
383
|
tags: List[str] = None, # Categorization tags
|
|
437
384
|
streaming: bool = False, # Enable streaming
|
|
@@ -444,18 +391,19 @@ Agent(
|
|
|
444
391
|
|----------|-------|
|
|
445
392
|
| `APIKeyAuth(keys=[...])` | API key auth (keys hashed with SHA-256) |
|
|
446
393
|
| `BearerAuth(secret=...)` | JWT/Bearer token auth |
|
|
447
|
-
| `OAuth2Auth(issuer=..., audience=...)` | OAuth2 auth |
|
|
394
|
+
| `OAuth2Auth(issuer=..., audience=...)` | OAuth2 auth (requires `a2a-lite[oauth]`) |
|
|
448
395
|
| `NoAuth()` | No auth (default) |
|
|
449
396
|
|
|
450
397
|
### Special Parameter Types
|
|
451
398
|
|
|
452
|
-
These are auto-injected when detected in skill signatures:
|
|
399
|
+
These are auto-injected when detected in skill function signatures:
|
|
453
400
|
|
|
454
401
|
| Type | Description |
|
|
455
402
|
|------|-------------|
|
|
456
403
|
| `TaskContext` | Task lifecycle management (requires `task_store`) |
|
|
457
|
-
| `
|
|
404
|
+
| `AuthResult` | Authentication result injection |
|
|
458
405
|
| `FilePart` | File upload handling |
|
|
406
|
+
| `DataPart` | Structured data handling |
|
|
459
407
|
|
|
460
408
|
---
|
|
461
409
|
|
|
@@ -468,10 +416,9 @@ These are auto-injected when detected in skill signatures:
|
|
|
468
416
|
| [06_pydantic_models.py](examples/06_pydantic_models.py) | Auto Pydantic conversion |
|
|
469
417
|
| [08_streaming.py](examples/08_streaming.py) | Streaming responses |
|
|
470
418
|
| [09_testing.py](examples/09_testing.py) | Testing your agents |
|
|
471
|
-
| [
|
|
472
|
-
| [
|
|
473
|
-
| [
|
|
474
|
-
| [14_with_auth.py](examples/14_with_auth.py) | Authentication |
|
|
419
|
+
| [10_file_handling.py](examples/10_file_handling.py) | Handle files |
|
|
420
|
+
| [11_task_tracking.py](examples/11_task_tracking.py) | Progress updates |
|
|
421
|
+
| [12_with_auth.py](examples/12_with_auth.py) | Authentication |
|
|
475
422
|
|
|
476
423
|
---
|
|
477
424
|
|
|
@@ -483,9 +430,9 @@ A2A Lite wraps the official A2A Python SDK. Every feature maps to real A2A proto
|
|
|
483
430
|
|----------|--------------|
|
|
484
431
|
| `@agent.skill()` | Agent Skills |
|
|
485
432
|
| `streaming=True` | SSE Streaming |
|
|
486
|
-
| `InteractionContext.ask()` | `input-required` state |
|
|
487
433
|
| `TaskContext.update()` | Task lifecycle states |
|
|
488
434
|
| `FilePart` | A2A File parts |
|
|
435
|
+
| `DataPart` | A2A Data parts |
|
|
489
436
|
| `APIKeyAuth` / `BearerAuth` | Security schemes |
|
|
490
437
|
|
|
491
438
|
---
|
|
@@ -6,7 +6,7 @@ This is the SIMPLEST way to handle complex data - just use Pydantic!
|
|
|
6
6
|
Run: python examples/06_pydantic_models.py
|
|
7
7
|
Test: a2a-lite test http://localhost:8787 create_user -p '{"user": {"name": "Alice", "email": "alice@example.com", "age": 30}}'
|
|
8
8
|
"""
|
|
9
|
-
from pydantic import BaseModel
|
|
9
|
+
from pydantic import BaseModel
|
|
10
10
|
from typing import List, Optional
|
|
11
11
|
from a2a_lite import Agent
|
|
12
12
|
|
|
@@ -3,7 +3,7 @@ Example: Authentication (optional).
|
|
|
3
3
|
|
|
4
4
|
Add auth when you need it - skip it when you don't.
|
|
5
5
|
|
|
6
|
-
Run: python examples/
|
|
6
|
+
Run: python examples/12_with_auth.py
|
|
7
7
|
Test: curl -H "X-API-Key: secret-key" http://localhost:8787/...
|
|
8
8
|
"""
|
|
9
9
|
from a2a_lite import Agent, APIKeyAuth
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "a2a-lite"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.3"
|
|
4
4
|
description = "Simplified wrapper for Google's A2A Protocol SDK"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -24,14 +24,15 @@ dependencies = [
|
|
|
24
24
|
"pydantic>=2.0",
|
|
25
25
|
"typer>=0.9.0",
|
|
26
26
|
"rich>=13.0",
|
|
27
|
-
"watchfiles>=0.20.0",
|
|
28
|
-
"zeroconf>=0.80.0",
|
|
29
27
|
"uvicorn>=0.30.0",
|
|
30
28
|
"httpx>=0.25.0",
|
|
31
29
|
"starlette>=0.40.0",
|
|
32
30
|
]
|
|
33
31
|
|
|
34
32
|
[project.optional-dependencies]
|
|
33
|
+
oauth = [
|
|
34
|
+
"pyjwt[crypto]>=2.0",
|
|
35
|
+
]
|
|
35
36
|
dev = [
|
|
36
37
|
"pytest>=7.0",
|
|
37
38
|
"pytest-asyncio>=0.21",
|