jaf-py 2.5.10__py3-none-any.whl → 2.5.11__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.
Files changed (92) hide show
  1. jaf/__init__.py +154 -57
  2. jaf/a2a/__init__.py +42 -21
  3. jaf/a2a/agent.py +79 -126
  4. jaf/a2a/agent_card.py +87 -78
  5. jaf/a2a/client.py +30 -66
  6. jaf/a2a/examples/client_example.py +12 -12
  7. jaf/a2a/examples/integration_example.py +38 -47
  8. jaf/a2a/examples/server_example.py +56 -53
  9. jaf/a2a/memory/__init__.py +0 -4
  10. jaf/a2a/memory/cleanup.py +28 -21
  11. jaf/a2a/memory/factory.py +155 -133
  12. jaf/a2a/memory/providers/composite.py +21 -26
  13. jaf/a2a/memory/providers/in_memory.py +89 -83
  14. jaf/a2a/memory/providers/postgres.py +117 -115
  15. jaf/a2a/memory/providers/redis.py +128 -121
  16. jaf/a2a/memory/serialization.py +77 -87
  17. jaf/a2a/memory/tests/run_comprehensive_tests.py +112 -83
  18. jaf/a2a/memory/tests/test_cleanup.py +211 -94
  19. jaf/a2a/memory/tests/test_serialization.py +73 -68
  20. jaf/a2a/memory/tests/test_stress_concurrency.py +186 -133
  21. jaf/a2a/memory/tests/test_task_lifecycle.py +138 -120
  22. jaf/a2a/memory/types.py +91 -53
  23. jaf/a2a/protocol.py +95 -125
  24. jaf/a2a/server.py +90 -118
  25. jaf/a2a/standalone_client.py +30 -43
  26. jaf/a2a/tests/__init__.py +16 -33
  27. jaf/a2a/tests/run_tests.py +17 -53
  28. jaf/a2a/tests/test_agent.py +40 -140
  29. jaf/a2a/tests/test_client.py +54 -117
  30. jaf/a2a/tests/test_integration.py +28 -82
  31. jaf/a2a/tests/test_protocol.py +54 -139
  32. jaf/a2a/tests/test_types.py +50 -136
  33. jaf/a2a/types.py +58 -34
  34. jaf/cli.py +21 -41
  35. jaf/core/__init__.py +7 -1
  36. jaf/core/agent_tool.py +93 -72
  37. jaf/core/analytics.py +257 -207
  38. jaf/core/checkpoint.py +223 -0
  39. jaf/core/composition.py +249 -235
  40. jaf/core/engine.py +817 -519
  41. jaf/core/errors.py +55 -42
  42. jaf/core/guardrails.py +276 -202
  43. jaf/core/handoff.py +47 -31
  44. jaf/core/parallel_agents.py +69 -75
  45. jaf/core/performance.py +75 -73
  46. jaf/core/proxy.py +43 -44
  47. jaf/core/proxy_helpers.py +24 -27
  48. jaf/core/regeneration.py +220 -129
  49. jaf/core/state.py +68 -66
  50. jaf/core/streaming.py +115 -108
  51. jaf/core/tool_results.py +111 -101
  52. jaf/core/tools.py +114 -116
  53. jaf/core/tracing.py +269 -210
  54. jaf/core/types.py +371 -151
  55. jaf/core/workflows.py +209 -168
  56. jaf/exceptions.py +46 -38
  57. jaf/memory/__init__.py +1 -6
  58. jaf/memory/approval_storage.py +54 -77
  59. jaf/memory/factory.py +4 -4
  60. jaf/memory/providers/in_memory.py +216 -180
  61. jaf/memory/providers/postgres.py +216 -146
  62. jaf/memory/providers/redis.py +173 -116
  63. jaf/memory/types.py +70 -51
  64. jaf/memory/utils.py +36 -34
  65. jaf/plugins/__init__.py +12 -12
  66. jaf/plugins/base.py +105 -96
  67. jaf/policies/__init__.py +0 -1
  68. jaf/policies/handoff.py +37 -46
  69. jaf/policies/validation.py +76 -52
  70. jaf/providers/__init__.py +6 -3
  71. jaf/providers/mcp.py +97 -51
  72. jaf/providers/model.py +360 -279
  73. jaf/server/__init__.py +1 -1
  74. jaf/server/main.py +7 -11
  75. jaf/server/server.py +514 -359
  76. jaf/server/types.py +208 -52
  77. jaf/utils/__init__.py +17 -18
  78. jaf/utils/attachments.py +111 -116
  79. jaf/utils/document_processor.py +175 -174
  80. jaf/visualization/__init__.py +1 -1
  81. jaf/visualization/example.py +111 -110
  82. jaf/visualization/functional_core.py +46 -71
  83. jaf/visualization/graphviz.py +154 -189
  84. jaf/visualization/imperative_shell.py +7 -16
  85. jaf/visualization/types.py +8 -4
  86. {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/METADATA +2 -2
  87. jaf_py-2.5.11.dist-info/RECORD +97 -0
  88. jaf_py-2.5.10.dist-info/RECORD +0 -96
  89. {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/WHEEL +0 -0
  90. {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/entry_points.txt +0 -0
  91. {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/licenses/LICENSE +0 -0
  92. {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/top_level.txt +0 -0
jaf/a2a/agent.py CHANGED
@@ -27,7 +27,7 @@ def create_a2a_agent(
27
27
  description: str,
28
28
  instruction: str,
29
29
  tools: List[A2AAgentTool],
30
- supported_content_types: Optional[List[str]] = None
30
+ supported_content_types: Optional[List[str]] = None,
31
31
  ) -> A2AAgent:
32
32
  """Pure function to create A2A compatible agent"""
33
33
  return A2AAgent(
@@ -35,7 +35,7 @@ def create_a2a_agent(
35
35
  description=description,
36
36
  supportedContentTypes=supported_content_types or ["text/plain", "application/json"],
37
37
  instruction=instruction,
38
- tools=tools
38
+ tools=tools,
39
39
  )
40
40
 
41
41
 
@@ -43,14 +43,11 @@ def create_a2a_tool(
43
43
  name: str,
44
44
  description: str,
45
45
  parameters: Dict[str, Any],
46
- execute_func: Callable[[Any, Optional[ToolContext]], Any]
46
+ execute_func: Callable[[Any, Optional[ToolContext]], Any],
47
47
  ) -> A2AAgentTool:
48
48
  """Pure function to create A2A tool"""
49
49
  return A2AAgentTool(
50
- name=name,
51
- description=description,
52
- parameters=parameters,
53
- execute=execute_func
50
+ name=name, description=description, parameters=parameters, execute=execute_func
54
51
  )
55
52
 
56
53
 
@@ -66,7 +63,7 @@ def create_initial_agent_state(session_id: str) -> AgentState:
66
63
  messages=[],
67
64
  context={},
68
65
  artifacts=[],
69
- timestamp=datetime.now().isoformat()
66
+ timestamp=datetime.now().isoformat(),
70
67
  )
71
68
 
72
69
 
@@ -77,14 +74,14 @@ def add_message_to_state(state: AgentState, message: Any) -> AgentState:
77
74
  messages=[*state.messages, message],
78
75
  context=state.context,
79
76
  artifacts=state.artifacts,
80
- timestamp=datetime.now().isoformat()
77
+ timestamp=datetime.now().isoformat(),
81
78
  )
82
79
 
83
80
 
84
81
  def update_state_from_run_result(state: AgentState, outcome: Any) -> AgentState:
85
82
  """Pure function to update state from run result"""
86
83
  new_artifacts = state.artifacts
87
- if hasattr(outcome, 'artifacts') and outcome.artifacts:
84
+ if hasattr(outcome, "artifacts") and outcome.artifacts:
88
85
  new_artifacts = [*state.artifacts, *outcome.artifacts]
89
86
 
90
87
  return AgentState(
@@ -92,7 +89,7 @@ def update_state_from_run_result(state: AgentState, outcome: Any) -> AgentState:
92
89
  messages=state.messages,
93
90
  context=state.context,
94
91
  artifacts=new_artifacts,
95
- timestamp=datetime.now().isoformat()
92
+ timestamp=datetime.now().isoformat(),
96
93
  )
97
94
 
98
95
 
@@ -110,24 +107,16 @@ def transform_a2a_agent_to_jaf(a2a_agent: A2AAgent) -> Agent:
110
107
  # Transform tools using functional approach
111
108
  jaf_tools = [transform_a2a_tool_to_jaf(a2a_tool) for a2a_tool in a2a_agent.tools]
112
109
 
113
- return Agent(
114
- name=a2a_agent.name,
115
- instructions=instructions_func,
116
- tools=jaf_tools
117
- )
110
+ return Agent(name=a2a_agent.name, instructions=instructions_func, tools=jaf_tools)
118
111
 
119
112
 
120
- def transform_a2a_tool_to_jaf(a2a_tool: A2AAgentTool) -> 'JAFToolImplementation':
113
+ def transform_a2a_tool_to_jaf(a2a_tool: A2AAgentTool) -> "JAFToolImplementation":
121
114
  """Pure function to transform A2A tool to JAF tool"""
122
115
 
123
116
  async def execute_wrapper(args: Any, context: Any) -> Any:
124
117
  tool_context = ToolContext(
125
- actions={
126
- "requiresInput": False,
127
- "skipSummarization": False,
128
- "escalate": False
129
- },
130
- metadata=context or {}
118
+ actions={"requiresInput": False, "skipSummarization": False, "escalate": False},
119
+ metadata=context or {},
131
120
  )
132
121
 
133
122
  result = await a2a_tool.execute(args, tool_context)
@@ -140,16 +129,12 @@ def transform_a2a_tool_to_jaf(a2a_tool: A2AAgentTool) -> 'JAFToolImplementation'
140
129
 
141
130
  # Create ToolSchema object
142
131
  from ..core.types import ToolSchema
132
+
143
133
  tool_schema = ToolSchema(
144
- name=a2a_tool.name,
145
- description=a2a_tool.description,
146
- parameters=a2a_tool.parameters
134
+ name=a2a_tool.name, description=a2a_tool.description, parameters=a2a_tool.parameters
147
135
  )
148
136
 
149
- return JAFToolImplementation(
150
- schema=tool_schema,
151
- execute=execute_wrapper
152
- )
137
+ return JAFToolImplementation(schema=tool_schema, execute=execute_wrapper)
153
138
 
154
139
 
155
140
  class JAFToolImplementation:
@@ -170,21 +155,18 @@ class JAFToolImplementation:
170
155
  return await self._execute(args, context)
171
156
 
172
157
 
173
- def create_run_config_for_a2a_agent(
174
- a2a_agent: A2AAgent,
175
- model_provider: Any
176
- ) -> RunConfig:
158
+ def create_run_config_for_a2a_agent(a2a_agent: A2AAgent, model_provider: Any) -> RunConfig:
177
159
  """Pure function to create run configuration for A2A agent"""
178
160
  jaf_agent = transform_a2a_agent_to_jaf(a2a_agent)
179
161
 
180
162
  def event_handler(event: Any) -> None:
181
163
  # Handle different event types properly
182
- if hasattr(event, 'data'):
164
+ if hasattr(event, "data"):
183
165
  event_data = event.data
184
166
  event_type = type(event).__name__
185
167
  elif isinstance(event, dict):
186
- event_data = event.get('data', '')
187
- event_type = event.get('type', 'unknown')
168
+ event_data = event.get("data", "")
169
+ event_type = event.get("type", "unknown")
188
170
  else:
189
171
  event_data = str(event)
190
172
  event_type = type(event).__name__
@@ -195,21 +177,19 @@ def create_run_config_for_a2a_agent(
195
177
  agent_registry={a2a_agent.name: jaf_agent},
196
178
  model_provider=model_provider,
197
179
  max_turns=10,
198
- on_event=event_handler
180
+ on_event=event_handler,
199
181
  )
200
182
 
201
183
 
202
184
  def transform_to_run_state(
203
- state: AgentState,
204
- agent_name: str,
205
- context: Optional[Dict[str, Any]] = None
185
+ state: AgentState, agent_name: str, context: Optional[Dict[str, Any]] = None
206
186
  ) -> RunState:
207
187
  """Pure function to transform agent state to JAF run state"""
208
188
  from ..core.types import generate_run_id, generate_trace_id
209
189
 
210
190
  # Convert A2A messages to JAF Message format
211
191
  jaf_messages = [
212
- create_user_message(msg.content if hasattr(msg, 'content') else str(msg))
192
+ create_user_message(msg.content if hasattr(msg, "content") else str(msg))
213
193
  for msg in state.messages
214
194
  if msg is not None
215
195
  ]
@@ -220,15 +200,12 @@ def transform_to_run_state(
220
200
  messages=jaf_messages,
221
201
  current_agent_name=agent_name,
222
202
  context=context or {},
223
- turn_count=0
203
+ turn_count=0,
224
204
  )
225
205
 
226
206
 
227
207
  async def process_agent_query(
228
- agent: A2AAgent,
229
- query: str,
230
- state: AgentState,
231
- model_provider: Any
208
+ agent: A2AAgent, query: str, state: AgentState, model_provider: Any
232
209
  ) -> AsyncGenerator[StreamEvent, None]:
233
210
  """Pure async generator function to process agent query with improved streaming"""
234
211
  start_time = time.time()
@@ -248,7 +225,7 @@ async def process_agent_query(
248
225
  new_state=new_state.model_dump(),
249
226
  timestamp=datetime.now().isoformat(),
250
227
  updates="Initializing agent state",
251
- metrics={"start_time": start_time, "status": "starting"}
228
+ metrics={"start_time": start_time, "status": "starting"},
252
229
  )
253
230
 
254
231
  try:
@@ -266,19 +243,19 @@ async def process_agent_query(
266
243
  metrics={
267
244
  "start_time": start_time,
268
245
  "processing_time": processing_time,
269
- "status": "processing"
270
- }
246
+ "status": "processing",
247
+ },
271
248
  )
272
249
 
273
250
  # Execute JAF engine (pure function)
274
251
  result = await run(run_state, run_config)
275
252
  completion_time = time.time()
276
253
 
277
- if hasattr(result.outcome, 'status') and result.outcome.status == 'completed':
254
+ if hasattr(result.outcome, "status") and result.outcome.status == "completed":
278
255
  final_state = update_state_from_run_result(new_state, result.outcome)
279
256
  yield StreamEvent(
280
257
  isTaskComplete=True,
281
- content=getattr(result.outcome, 'output', 'Task completed'),
258
+ content=getattr(result.outcome, "output", "Task completed"),
282
259
  new_state=final_state.model_dump(),
283
260
  timestamp=datetime.now().isoformat(),
284
261
  updates="Task completed successfully",
@@ -286,12 +263,12 @@ async def process_agent_query(
286
263
  "start_time": start_time,
287
264
  "completion_time": completion_time,
288
265
  "total_duration": completion_time - start_time,
289
- "status": "completed"
290
- }
266
+ "status": "completed",
267
+ },
291
268
  )
292
269
  else:
293
270
  final_state = update_state_from_run_result(new_state, result.outcome)
294
- error_content = getattr(result.outcome, 'error', 'Unknown error')
271
+ error_content = getattr(result.outcome, "error", "Unknown error")
295
272
  yield StreamEvent(
296
273
  isTaskComplete=True,
297
274
  content=f"Error: {json.dumps(error_content) if isinstance(error_content, dict) else str(error_content)}",
@@ -302,14 +279,15 @@ async def process_agent_query(
302
279
  "start_time": start_time,
303
280
  "completion_time": completion_time,
304
281
  "total_duration": completion_time - start_time,
305
- "status": "error"
306
- }
282
+ "status": "error",
283
+ },
307
284
  )
308
285
  except Exception as error:
309
286
  error_time = time.time()
310
287
 
311
288
  # Log the actual error for debugging but don't expose internal details
312
289
  import logging
290
+
313
291
  logging.error(f"Agent execution error for {agent.name}: {error!s}", exc_info=True)
314
292
 
315
293
  # Determine error type and provide safe error message
@@ -335,8 +313,8 @@ async def process_agent_query(
335
313
  "error_time": error_time,
336
314
  "total_duration": error_time - start_time,
337
315
  "status": "failed",
338
- "error_type": type(error).__name__
339
- }
316
+ "error_type": type(error).__name__,
317
+ },
340
318
  )
341
319
 
342
320
 
@@ -346,19 +324,13 @@ def extract_text_from_a2a_message(message: Dict[str, Any]) -> str:
346
324
  return ""
347
325
 
348
326
  # Use functional approach instead of mutation
349
- text_parts = [
350
- part.get("text", "")
351
- for part in message["parts"]
352
- if part.get("kind") == "text"
353
- ]
327
+ text_parts = [part.get("text", "") for part in message["parts"] if part.get("kind") == "text"]
354
328
 
355
329
  return "\n".join(text_parts)
356
330
 
357
331
 
358
332
  def create_a2a_text_message(
359
- text: str,
360
- context_id: str,
361
- task_id: Optional[str] = None
333
+ text: str, context_id: str, task_id: Optional[str] = None
362
334
  ) -> Dict[str, Any]:
363
335
  """Pure function to create A2A text message"""
364
336
  return {
@@ -368,14 +340,12 @@ def create_a2a_text_message(
368
340
  "contextId": context_id,
369
341
  "taskId": task_id,
370
342
  "kind": "message",
371
- "timestamp": datetime.now().isoformat()
343
+ "timestamp": datetime.now().isoformat(),
372
344
  }
373
345
 
374
346
 
375
347
  def create_a2a_data_message(
376
- data: Dict[str, Any],
377
- context_id: str,
378
- task_id: Optional[str] = None
348
+ data: Dict[str, Any], context_id: str, task_id: Optional[str] = None
379
349
  ) -> Dict[str, Any]:
380
350
  """Pure function to create A2A data message"""
381
351
  return {
@@ -385,14 +355,11 @@ def create_a2a_data_message(
385
355
  "contextId": context_id,
386
356
  "taskId": task_id,
387
357
  "kind": "message",
388
- "timestamp": datetime.now().isoformat()
358
+ "timestamp": datetime.now().isoformat(),
389
359
  }
390
360
 
391
361
 
392
- def create_a2a_task(
393
- message: Dict[str, Any],
394
- context_id: Optional[str] = None
395
- ) -> Dict[str, Any]:
362
+ def create_a2a_task(message: Dict[str, Any], context_id: Optional[str] = None) -> Dict[str, Any]:
396
363
  """Pure function to create A2A task"""
397
364
  task_id = f"task_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
398
365
  current_time = datetime.now().isoformat()
@@ -400,42 +367,35 @@ def create_a2a_task(
400
367
  return {
401
368
  "id": task_id,
402
369
  "contextId": context_id or f"ctx_{int(time.time() * 1000)}",
403
- "status": {
404
- "state": "submitted",
405
- "timestamp": current_time
406
- },
370
+ "status": {"state": "submitted", "timestamp": current_time},
407
371
  "history": [message],
408
372
  "artifacts": [],
409
- "kind": "task"
373
+ "kind": "task",
410
374
  }
411
375
 
412
376
 
413
377
  def update_a2a_task_status(
414
- task: Dict[str, Any],
415
- state: str,
416
- message: Optional[Dict[str, Any]] = None
378
+ task: Dict[str, Any], state: str, message: Optional[Dict[str, Any]] = None
417
379
  ) -> Dict[str, Any]:
418
380
  """Pure function to update A2A task status"""
419
381
  updated_task = task.copy()
420
382
  updated_task["status"] = {
421
383
  "state": state,
422
384
  "message": message,
423
- "timestamp": datetime.now().isoformat()
385
+ "timestamp": datetime.now().isoformat(),
424
386
  }
425
387
  return updated_task
426
388
 
427
389
 
428
390
  def add_artifact_to_a2a_task(
429
- task: Dict[str, Any],
430
- parts: List[Dict[str, Any]],
431
- name: str
391
+ task: Dict[str, Any], parts: List[Dict[str, Any]], name: str
432
392
  ) -> Dict[str, Any]:
433
393
  """Pure function to add artifact to A2A task"""
434
394
  artifact = {
435
395
  "artifactId": f"artifact_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}",
436
396
  "name": name,
437
397
  "parts": parts,
438
- "timestamp": datetime.now().isoformat()
398
+ "timestamp": datetime.now().isoformat(),
439
399
  }
440
400
 
441
401
  updated_task = task.copy()
@@ -452,7 +412,7 @@ def complete_a2a_task(task: Dict[str, Any], result: Optional[Any] = None) -> Dic
452
412
  "artifactId": f"result_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}",
453
413
  "name": "final_result",
454
414
  "parts": [{"kind": "text", "text": str(result)}],
455
- "timestamp": datetime.now().isoformat()
415
+ "timestamp": datetime.now().isoformat(),
456
416
  }
457
417
 
458
418
  updated_task["artifacts"] = [*task.get("artifacts", []), result_artifact]
@@ -462,10 +422,9 @@ def complete_a2a_task(task: Dict[str, Any], result: Optional[Any] = None) -> Dic
462
422
 
463
423
  # Execution functions
464
424
 
425
+
465
426
  async def execute_a2a_agent(
466
- context: Dict[str, Any],
467
- agent: A2AAgent,
468
- model_provider: Any
427
+ context: Dict[str, Any], agent: A2AAgent, model_provider: Any
469
428
  ) -> Dict[str, Any]:
470
429
  """Execute A2A agent and return result"""
471
430
  message = context.get("message", {})
@@ -495,40 +454,36 @@ async def execute_a2a_agent(
495
454
  updated_task = update_a2a_task_status(
496
455
  current_task,
497
456
  "failed",
498
- create_a2a_text_message(error_message, session_id, current_task["id"])
457
+ create_a2a_text_message(error_message, session_id, current_task["id"]),
499
458
  )
500
459
 
501
460
  return {
502
461
  "events": [*events, *processing_events],
503
462
  "final_task": updated_task,
504
- "error": error_message
463
+ "error": error_message,
505
464
  }
506
465
  else:
507
466
  # Success case
508
467
  content = event.content
509
468
  updated_task = add_artifact_to_a2a_task(
510
- current_task,
511
- [{"kind": "text", "text": str(content)}],
512
- "response"
469
+ current_task, [{"kind": "text", "text": str(content)}], "response"
513
470
  )
514
471
  updated_task = complete_a2a_task(updated_task, content)
515
472
 
516
- return {
517
- "events": [*events, *processing_events],
518
- "final_task": updated_task
519
- }
473
+ return {"events": [*events, *processing_events], "final_task": updated_task}
520
474
 
521
475
  # If we reach here without completion, mark as failed
522
476
  updated_task = update_a2a_task_status(current_task, "failed")
523
477
  return {
524
478
  "events": [*events, *processing_events],
525
479
  "final_task": updated_task,
526
- "error": "Agent processing did not complete"
480
+ "error": "Agent processing did not complete",
527
481
  }
528
482
 
529
483
  except Exception as error:
530
484
  # Log the actual error for debugging but don't expose internal details
531
485
  import logging
486
+
532
487
  logging.error(f"Agent execution error for {agent.name}: {error!s}", exc_info=True)
533
488
 
534
489
  # Determine error type and provide safe error message
@@ -546,20 +501,14 @@ async def execute_a2a_agent(
546
501
  failed_task = update_a2a_task_status(
547
502
  current_task,
548
503
  "failed",
549
- create_a2a_text_message(safe_error, session_id, current_task["id"])
504
+ create_a2a_text_message(safe_error, session_id, current_task["id"]),
550
505
  )
551
506
 
552
- return {
553
- "events": events,
554
- "final_task": failed_task,
555
- "error": safe_error
556
- }
507
+ return {"events": events, "final_task": failed_task, "error": safe_error}
557
508
 
558
509
 
559
510
  async def execute_a2a_agent_with_streaming(
560
- context: Dict[str, Any],
561
- agent: A2AAgent,
562
- model_provider: Any
511
+ context: Dict[str, Any], agent: A2AAgent, model_provider: Any
563
512
  ) -> AsyncGenerator[Dict[str, Any], None]:
564
513
  """Execute A2A agent with streaming"""
565
514
  message = context.get("message", {})
@@ -576,7 +525,7 @@ async def execute_a2a_agent_with_streaming(
576
525
  "taskId": current_task["id"],
577
526
  "contextId": current_task["contextId"],
578
527
  "status": {"state": "submitted", "timestamp": datetime.now().isoformat()},
579
- "final": False
528
+ "final": False,
580
529
  }
581
530
 
582
531
  yield {
@@ -584,7 +533,7 @@ async def execute_a2a_agent_with_streaming(
584
533
  "taskId": current_task["id"],
585
534
  "contextId": current_task["contextId"],
586
535
  "status": {"state": "working", "timestamp": datetime.now().isoformat()},
587
- "final": False
536
+ "final": False,
588
537
  }
589
538
 
590
539
  try:
@@ -601,11 +550,11 @@ async def execute_a2a_agent_with_streaming(
601
550
  "message": create_a2a_text_message(
602
551
  event.updates or "Processing...",
603
552
  current_task["contextId"],
604
- current_task["id"]
553
+ current_task["id"],
605
554
  ),
606
- "timestamp": event.timestamp
555
+ "timestamp": event.timestamp,
607
556
  },
608
- "final": False
557
+ "final": False,
609
558
  }
610
559
  else:
611
560
  # Handle final result
@@ -617,10 +566,12 @@ async def execute_a2a_agent_with_streaming(
617
566
  "contextId": current_task["contextId"],
618
567
  "status": {
619
568
  "state": "failed",
620
- "message": create_a2a_text_message(error_message, current_task["contextId"], current_task["id"]),
621
- "timestamp": event.timestamp
569
+ "message": create_a2a_text_message(
570
+ error_message, current_task["contextId"], current_task["id"]
571
+ ),
572
+ "timestamp": event.timestamp,
622
573
  },
623
- "final": True
574
+ "final": True,
624
575
  }
625
576
  else:
626
577
  yield {
@@ -630,8 +581,8 @@ async def execute_a2a_agent_with_streaming(
630
581
  "artifact": {
631
582
  "artifactId": f"result_{int(time.time() * 1000)}",
632
583
  "name": "response",
633
- "parts": [{"kind": "text", "text": str(event.content)}]
634
- }
584
+ "parts": [{"kind": "text", "text": str(event.content)}],
585
+ },
635
586
  }
636
587
 
637
588
  yield {
@@ -639,7 +590,7 @@ async def execute_a2a_agent_with_streaming(
639
590
  "taskId": current_task["id"],
640
591
  "contextId": current_task["contextId"],
641
592
  "status": {"state": "completed", "timestamp": event.timestamp},
642
- "final": True
593
+ "final": True,
643
594
  }
644
595
  break
645
596
 
@@ -651,8 +602,10 @@ async def execute_a2a_agent_with_streaming(
651
602
  "contextId": current_task["contextId"],
652
603
  "status": {
653
604
  "state": "failed",
654
- "message": create_a2a_text_message(error_message, current_task["contextId"], current_task["id"]),
655
- "timestamp": datetime.now().isoformat()
605
+ "message": create_a2a_text_message(
606
+ error_message, current_task["contextId"], current_task["id"]
607
+ ),
608
+ "timestamp": datetime.now().isoformat(),
656
609
  },
657
- "final": True
610
+ "final": True,
658
611
  }