a2a-lite 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """
2
- A2A Lite - Simple by default, powerful when needed.
2
+ A2A Lite - Build A2A agents in 8 lines. Add enterprise features when you need them.
3
3
 
4
4
  SIMPLE (8 lines):
5
5
  from a2a_lite import Agent
@@ -43,14 +43,6 @@ WITH TASK TRACKING (opt-in):
43
43
  await task.update("working", progress=0.5)
44
44
  return "done"
45
45
 
46
- WITH HUMAN-IN-THE-LOOP (opt-in):
47
- from a2a_lite.human_loop import InteractionContext
48
-
49
- @agent.skill("wizard")
50
- async def wizard(ctx: InteractionContext) -> str:
51
- name = await ctx.ask("What's your name?")
52
- return f"Hello, {name}!"
53
-
54
46
  WITH FILES (opt-in):
55
47
  from a2a_lite.parts import FilePart
56
48
 
@@ -60,13 +52,12 @@ WITH FILES (opt-in):
60
52
  return summarize(text)
61
53
  """
62
54
 
63
- # Core - always available
55
+ # Core
64
56
  from .agent import Agent
65
57
  from .decorators import SkillDefinition
66
- from .discovery import AgentDiscovery, DiscoveredAgent
67
58
  from .testing import AgentTestClient, AsyncAgentTestClient, TestResult
68
59
 
69
- # Middleware - always available
60
+ # Middleware
70
61
  from .middleware import (
71
62
  MiddlewareContext,
72
63
  MiddlewareChain,
@@ -76,22 +67,13 @@ from .middleware import (
76
67
  rate_limit_middleware,
77
68
  )
78
69
 
79
- # Webhooks - always available
80
- from .webhooks import WebhookClient, WebhookConfig, NotificationManager
81
-
82
- # Streaming - always available
83
- from .streaming import StreamingResponse
84
-
85
- # Parts - opt-in for multi-modal
70
+ # Parts (multi-modal)
86
71
  from .parts import TextPart, FilePart, DataPart, Artifact
87
72
 
88
- # Tasks - opt-in for task lifecycle
73
+ # Tasks
89
74
  from .tasks import TaskContext, TaskState, TaskStatus, Task, TaskStore
90
75
 
91
- # Human-in-the-loop - opt-in
92
- from .human_loop import InteractionContext, ConversationMemory
93
-
94
- # Auth - opt-in
76
+ # Auth
95
77
  from .auth import (
96
78
  AuthProvider,
97
79
  AuthResult,
@@ -102,14 +84,12 @@ from .auth import (
102
84
  require_auth,
103
85
  )
104
86
 
105
- __version__ = "0.2.2"
87
+ __version__ = "0.2.3"
106
88
 
107
89
  __all__ = [
108
90
  # Core
109
91
  "Agent",
110
92
  "SkillDefinition",
111
- "AgentDiscovery",
112
- "DiscoveredAgent",
113
93
  # Testing
114
94
  "AgentTestClient",
115
95
  "AsyncAgentTestClient",
@@ -121,12 +101,6 @@ __all__ = [
121
101
  "timing_middleware",
122
102
  "retry_middleware",
123
103
  "rate_limit_middleware",
124
- # Webhooks
125
- "WebhookClient",
126
- "WebhookConfig",
127
- "NotificationManager",
128
- # Streaming
129
- "StreamingResponse",
130
104
  # Parts (multi-modal)
131
105
  "TextPart",
132
106
  "FilePart",
@@ -138,9 +112,6 @@ __all__ = [
138
112
  "TaskStatus",
139
113
  "Task",
140
114
  "TaskStore",
141
- # Human-in-the-loop
142
- "InteractionContext",
143
- "ConversationMemory",
144
115
  # Auth
145
116
  "AuthProvider",
146
117
  "AuthResult",
a2a_lite/agent.py CHANGED
@@ -29,7 +29,6 @@ from .decorators import SkillDefinition
29
29
  from .utils import type_to_json_schema, extract_function_schemas, _is_or_subclass
30
30
  from .middleware import MiddlewareChain, MiddlewareContext
31
31
  from .streaming import is_generator_function
32
- from .webhooks import NotificationManager, WebhookClient
33
32
 
34
33
 
35
34
  @dataclass
@@ -73,12 +72,6 @@ class Agent:
73
72
  async def process(data: str, task: TaskContext) -> str:
74
73
  await task.update("working", progress=0.5)
75
74
  return "done"
76
-
77
- WITH HUMAN-IN-THE-LOOP (optional):
78
- @agent.skill("wizard")
79
- async def wizard(ctx: InteractionContext) -> str:
80
- name = await ctx.ask("What's your name?")
81
- return f"Hello, {name}!"
82
75
  """
83
76
  name: str
84
77
  description: str
@@ -98,10 +91,7 @@ class Agent:
98
91
  self._on_startup: List[Callable] = []
99
92
  self._on_shutdown: List[Callable] = []
100
93
  self._on_complete: List[Callable] = []
101
- self._discovery = None
102
94
  self._middleware = MiddlewareChain()
103
- self._notifications = NotificationManager()
104
- self._webhook = WebhookClient()
105
95
  self._has_streaming = False
106
96
 
107
97
  # Setup optional task store
@@ -146,12 +136,6 @@ class Agent:
146
136
  async def process(data: str, task: TaskContext) -> str:
147
137
  await task.update("working", progress=0.5)
148
138
  return "done"
149
-
150
- With human-in-the-loop (opt-in):
151
- @agent.skill("wizard")
152
- async def wizard(ctx: InteractionContext) -> str:
153
- name = await ctx.ask("What's your name?")
154
- return f"Hello, {name}!"
155
139
  """
156
140
  def decorator(func: Callable) -> Callable:
157
141
  skill_name = name or func.__name__
@@ -169,14 +153,11 @@ class Agent:
169
153
  # Detect special parameter types using proper type introspection
170
154
  import typing
171
155
  from .tasks import TaskContext as _TaskContext
172
- from .human_loop import InteractionContext as _InteractionContext
173
156
  from .auth import AuthResult as _AuthResult
174
157
 
175
158
  needs_task_context = False
176
- needs_interaction = False
177
159
  needs_auth = False
178
160
  task_context_param: str | None = None
179
- interaction_param: str | None = None
180
161
  auth_param: str | None = None
181
162
 
182
163
  try:
@@ -190,9 +171,6 @@ class Agent:
190
171
  if _is_or_subclass(hint, _TaskContext):
191
172
  needs_task_context = True
192
173
  task_context_param = param_name
193
- elif _is_or_subclass(hint, _InteractionContext):
194
- needs_interaction = True
195
- interaction_param = param_name
196
174
  elif _is_or_subclass(hint, _AuthResult):
197
175
  needs_auth = True
198
176
  auth_param = param_name
@@ -215,10 +193,8 @@ class Agent:
215
193
  is_async=asyncio.iscoroutinefunction(func) or is_streaming,
216
194
  is_streaming=is_streaming,
217
195
  needs_task_context=needs_task_context,
218
- needs_interaction=needs_interaction,
219
196
  needs_auth=needs_auth,
220
197
  task_context_param=task_context_param,
221
- interaction_param=interaction_param,
222
198
  auth_param=auth_param,
223
199
  )
224
200
 
@@ -264,16 +240,6 @@ class Agent:
264
240
  self._on_complete.append(func)
265
241
  return func
266
242
 
267
- @property
268
- def webhook(self) -> WebhookClient:
269
- """Get the webhook client for sending notifications."""
270
- return self._webhook
271
-
272
- @property
273
- def notifications(self) -> NotificationManager:
274
- """Get the notification manager for push notifications."""
275
- return self._notifications
276
-
277
243
  def build_agent_card(self, host: str = "localhost", port: int = 8787) -> AgentCard:
278
244
  """Generate A2A-compliant Agent Card from registered skills."""
279
245
  skills = []
@@ -291,12 +257,6 @@ class Agent:
291
257
 
292
258
  url = self.url or f"http://{host}:{port}"
293
259
 
294
- # Check if any skills need human-in-the-loop
295
- has_input_required = any(
296
- getattr(s, 'needs_interaction', False)
297
- for s in self._skills.values()
298
- )
299
-
300
260
  return AgentCard(
301
261
  name=self.name,
302
262
  description=self.description,
@@ -315,9 +275,7 @@ class Agent:
315
275
  self,
316
276
  host: str = "0.0.0.0",
317
277
  port: int = 8787,
318
- reload: bool = False,
319
278
  log_level: str = "info",
320
- enable_discovery: bool = False,
321
279
  ) -> None:
322
280
  """
323
281
  Start the A2A server.
@@ -326,7 +284,7 @@ class Agent:
326
284
  agent.run()
327
285
 
328
286
  With options:
329
- agent.run(port=9000, enable_discovery=True)
287
+ agent.run(port=9000)
330
288
  """
331
289
  from rich.console import Console
332
290
  from rich.panel import Panel
@@ -362,8 +320,7 @@ class Agent:
362
320
  # Build display info
363
321
  skills_list = "\n".join([
364
322
  f" • {s.name}: {s.description}" +
365
- (" [streaming]" if getattr(s, 'is_streaming', False) else "") +
366
- (" [interactive]" if getattr(s, 'needs_interaction', False) else "")
323
+ (" [streaming]" if getattr(s, 'is_streaming', False) else "")
367
324
  for s in self._skills.values()
368
325
  ])
369
326
  if not skills_list:
@@ -375,8 +332,6 @@ class Agent:
375
332
  features.append(f"{len(self._middleware._middlewares)} middleware")
376
333
  if self._has_streaming:
377
334
  features.append("streaming")
378
- if self._on_complete:
379
- features.append("webhooks")
380
335
  if self.auth:
381
336
  features.append("auth")
382
337
  if self._task_store:
@@ -405,17 +360,6 @@ class Agent:
405
360
  if self._on_startup:
406
361
  asyncio.run(_run_startup())
407
362
 
408
- # Enable discovery if requested
409
- if enable_discovery:
410
- from .discovery import AgentDiscovery
411
- self._discovery = AgentDiscovery()
412
- self._discovery.register(
413
- name=self.name,
414
- port=port,
415
- properties={"version": self.version},
416
- )
417
- console.print(f"[dim]mDNS discovery enabled for {self.name}[/]")
418
-
419
363
  # Production mode warning
420
364
  if self.production:
421
365
  url_str = self.url or f"http://{display_host}:{port}"
@@ -431,8 +375,6 @@ class Agent:
431
375
  # Add CORS middleware if configured
432
376
  if self.cors_origins is not None:
433
377
  from starlette.middleware.cors import CORSMiddleware
434
- from starlette.middleware import Middleware as StarletteMiddleware
435
- # Wrap the existing app with CORS
436
378
  app.add_middleware(
437
379
  CORSMiddleware,
438
380
  allow_origins=self.cors_origins,
@@ -459,10 +401,6 @@ class Agent:
459
401
  if self._on_shutdown:
460
402
  asyncio.run(_run_shutdown())
461
403
 
462
- # Unregister discovery
463
- if self._discovery:
464
- self._discovery.unregister()
465
-
466
404
  async def call_remote(
467
405
  self,
468
406
  agent_url: str,
a2a_lite/cli.py CHANGED
@@ -75,7 +75,7 @@ version = "0.1.0"
75
75
  description = "A2A Agent: {name}"
76
76
  requires-python = ">=3.10"
77
77
  dependencies = [
78
- "a2a-lite>=0.2.2",
78
+ "a2a-lite>=0.2.3",
79
79
  ]
80
80
  '''
81
81
  (project_path / "pyproject.toml").write_text(pyproject)
@@ -232,51 +232,10 @@ def test(
232
232
  raise typer.Exit(1)
233
233
 
234
234
 
235
- @app.command()
236
- def discover(
237
- timeout: float = typer.Option(5.0, help="Discovery timeout in seconds"),
238
- ):
239
- """
240
- Discover A2A agents on the local network.
241
-
242
- Uses mDNS to find agents advertising themselves.
243
- """
244
- from .discovery import AgentDiscovery
245
-
246
- async def _discover():
247
- console.print("[dim]Scanning local network...[/]\n")
248
-
249
- discovery = AgentDiscovery()
250
- agents = await discovery.discover(timeout=timeout)
251
-
252
- if not agents:
253
- console.print("[yellow]No agents found.[/]")
254
- console.print("[dim]Make sure agents are running with discovery enabled (enable_discovery=True).[/]")
255
- return
256
-
257
- table = Table(title=f"🔍 Found {len(agents)} Agent(s)")
258
- table.add_column("Name", style="cyan")
259
- table.add_column("URL", style="blue")
260
- table.add_column("Properties", style="dim")
261
-
262
- for agent in agents:
263
- table.add_row(
264
- agent.name,
265
- agent.url,
266
- json.dumps(agent.properties) if agent.properties else "-",
267
- )
268
-
269
- console.print(table)
270
-
271
- asyncio.run(_discover())
272
-
273
-
274
235
  @app.command()
275
236
  def serve(
276
237
  file: Path = typer.Argument(..., help="Python file containing the agent"),
277
238
  port: int = typer.Option(8787, help="Port to run on"),
278
- reload: bool = typer.Option(False, "--reload", "-r", help="Enable hot reload"),
279
- discovery: bool = typer.Option(False, "--discovery", "-d", help="Enable mDNS discovery"),
280
239
  ):
281
240
  """
282
241
  Run an agent from a Python file.
@@ -312,7 +271,7 @@ def serve(
312
271
  raise typer.Exit(1)
313
272
 
314
273
  agent = module.agent
315
- agent.run(port=port, reload=reload, enable_discovery=discovery)
274
+ agent.run(port=port)
316
275
 
317
276
 
318
277
  @app.command()
a2a_lite/decorators.py CHANGED
@@ -17,10 +17,8 @@ class SkillDefinition:
17
17
  is_async: bool = False
18
18
  is_streaming: bool = False
19
19
  needs_task_context: bool = False
20
- needs_interaction: bool = False
21
20
  needs_auth: bool = False
22
21
  task_context_param: Optional[str] = None
23
- interaction_param: Optional[str] = None
24
22
  auth_param: Optional[str] = None
25
23
 
26
24
  def to_dict(self) -> Dict[str, Any]:
@@ -32,5 +30,4 @@ class SkillDefinition:
32
30
  "input_schema": self.input_schema,
33
31
  "output_schema": self.output_schema,
34
32
  "is_streaming": self.is_streaming,
35
- "needs_interaction": self.needs_interaction,
36
33
  }
a2a_lite/executor.py CHANGED
@@ -168,13 +168,6 @@ class LiteAgentExecutor(AgentExecutor):
168
168
  param_name = skill_def.task_context_param or "task"
169
169
  params[param_name] = task_ctx
170
170
 
171
- if skill_def.needs_interaction:
172
- from .human_loop import InteractionContext
173
- task_id = metadata.get("task_id", "unknown")
174
- interaction_ctx = InteractionContext(task_id, event_queue)
175
- param_name = skill_def.interaction_param or "ctx"
176
- params[param_name] = interaction_ctx
177
-
178
171
  if skill_def.needs_auth:
179
172
  param_name = skill_def.auth_param or "auth"
180
173
  params[param_name] = metadata.get("auth_result")
@@ -217,9 +210,8 @@ class LiteAgentExecutor(AgentExecutor):
217
210
 
218
211
  # Skip special context types
219
212
  from .tasks import TaskContext as _TaskContext
220
- from .human_loop import InteractionContext as _InteractionContext
221
213
  from .auth import AuthResult as _AuthResult
222
- if _is_or_subclass(param_type, _TaskContext) or _is_or_subclass(param_type, _InteractionContext) or _is_or_subclass(param_type, _AuthResult):
214
+ if _is_or_subclass(param_type, _TaskContext) or _is_or_subclass(param_type, _AuthResult):
223
215
  continue
224
216
 
225
217
  # Convert FilePart
a2a_lite/streaming.py CHANGED
@@ -58,32 +58,3 @@ async def stream_generator(
58
58
  await event_queue.enqueue_event(new_agent_text_message(text))
59
59
 
60
60
 
61
- class StreamingResponse:
62
- """
63
- Helper class for building streaming responses.
64
-
65
- Example:
66
- @agent.skill("count")
67
- async def count(n: int):
68
- stream = StreamingResponse()
69
- for i in range(n):
70
- stream.write(f"Count: {i}")
71
- return stream
72
- """
73
-
74
- def __init__(self):
75
- self._chunks: list[str] = []
76
-
77
- def write(self, chunk: str) -> None:
78
- """Add a chunk to the stream."""
79
- self._chunks.append(chunk)
80
-
81
- def __iter__(self):
82
- return iter(self._chunks)
83
-
84
- def __aiter__(self):
85
- return self._async_iter()
86
-
87
- async def _async_iter(self):
88
- for chunk in self._chunks:
89
- yield chunk