jaf-py 2.5.9__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 +361 -280
  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.9.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.9.dist-info/RECORD +0 -96
  89. {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/WHEEL +0 -0
  90. {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/entry_points.txt +0 -0
  91. {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/licenses/LICENSE +0 -0
  92. {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/top_level.txt +0 -0
@@ -53,10 +53,7 @@ class TestA2AParts:
53
53
 
54
54
  assert part.kind == "text"
55
55
  assert part.text == "Hello world"
56
- assert part.model_dump() == {
57
- "kind": "text",
58
- "text": "Hello world"
59
- }
56
+ assert part.model_dump() == {"kind": "text", "text": "Hello world"}
60
57
 
61
58
  def test_create_data_part(self):
62
59
  """Test data part creation"""
@@ -65,10 +62,7 @@ class TestA2AParts:
65
62
 
66
63
  assert part.kind == "data"
67
64
  assert part.data == data
68
- assert part.model_dump() == {
69
- "kind": "data",
70
- "data": data
71
- }
65
+ assert part.model_dump() == {"kind": "data", "data": data}
72
66
 
73
67
  def test_part_immutability(self):
74
68
  """Test that parts are immutable"""
@@ -84,9 +78,7 @@ class TestA2AMessage:
84
78
  def test_create_a2a_message(self):
85
79
  """Test A2A message creation"""
86
80
  message = create_a2a_message(
87
- role="user",
88
- parts=[create_a2a_text_part("Hello")],
89
- context_id="ctx_123"
81
+ role="user", parts=[create_a2a_text_part("Hello")], context_id="ctx_123"
90
82
  )
91
83
 
92
84
  assert message.role == "user"
@@ -104,7 +96,7 @@ class TestA2AMessage:
104
96
  parts=[create_a2a_text_part("test")],
105
97
  messageId="msg_123",
106
98
  contextId="ctx_123",
107
- kind="message"
99
+ kind="message",
108
100
  )
109
101
  assert message.role == "user"
110
102
 
@@ -115,18 +107,15 @@ class TestA2AMessage:
115
107
  parts=[create_a2a_text_part("test")],
116
108
  messageId="msg_123",
117
109
  contextId="ctx_123",
118
- kind="message"
110
+ kind="message",
119
111
  )
120
112
 
121
113
  def test_message_serialization(self):
122
114
  """Test message JSON serialization"""
123
115
  message = create_a2a_message(
124
116
  role="agent",
125
- parts=[
126
- create_a2a_text_part("Response"),
127
- create_a2a_data_part({"result": "success"})
128
- ],
129
- context_id="ctx_456"
117
+ parts=[create_a2a_text_part("Response"), create_a2a_data_part({"result": "success"})],
118
+ context_id="ctx_456",
130
119
  )
131
120
 
132
121
  # Should serialize to dict
@@ -162,15 +151,10 @@ class TestA2ATask:
162
151
  def test_create_a2a_task(self):
163
152
  """Test A2A task creation"""
164
153
  message = create_a2a_message(
165
- role="user",
166
- parts=[create_a2a_text_part("Task request")],
167
- context_id="ctx_123"
154
+ role="user", parts=[create_a2a_text_part("Task request")], context_id="ctx_123"
168
155
  )
169
156
 
170
- task = create_a2a_task(
171
- initial_message=message,
172
- context_id="ctx_123"
173
- )
157
+ task = create_a2a_task(initial_message=message, context_id="ctx_123")
174
158
 
175
159
  assert task.context_id == "ctx_123"
176
160
  assert task.status.state == "submitted"
@@ -182,9 +166,7 @@ class TestA2ATask:
182
166
  def test_task_status_updates(self):
183
167
  """Test task status progression"""
184
168
  message = create_a2a_message(
185
- role="user",
186
- parts=[create_a2a_text_part("Test")],
187
- context_id="ctx_123"
169
+ role="user", parts=[create_a2a_text_part("Test")], context_id="ctx_123"
188
170
  )
189
171
 
190
172
  task = create_a2a_task(message, "ctx_123")
@@ -194,10 +176,7 @@ class TestA2ATask:
194
176
  assert task.status.timestamp is not None
195
177
 
196
178
  # Update to working
197
- working_status = A2ATaskStatus(
198
- state="working",
199
- timestamp=datetime.now().isoformat()
200
- )
179
+ working_status = A2ATaskStatus(state="working", timestamp=datetime.now().isoformat())
201
180
 
202
181
  # Task should be immutable, so we create new one
203
182
  working_task = task.model_copy(update={"status": working_status})
@@ -207,20 +186,15 @@ class TestA2ATask:
207
186
  def test_task_with_artifacts(self):
208
187
  """Test task with artifacts"""
209
188
  message = create_a2a_message(
210
- role="user",
211
- parts=[create_a2a_text_part("Create artifact")],
212
- context_id="ctx_123"
189
+ role="user", parts=[create_a2a_text_part("Create artifact")], context_id="ctx_123"
213
190
  )
214
191
 
215
192
  artifact = create_a2a_artifact(
216
- name="test_artifact",
217
- parts=[create_a2a_text_part("Artifact content")]
193
+ name="test_artifact", parts=[create_a2a_text_part("Artifact content")]
218
194
  )
219
195
 
220
196
  task = create_a2a_task(message, "ctx_123")
221
- task_with_artifact = task.model_copy(
222
- update={"artifacts": [artifact]}
223
- )
197
+ task_with_artifact = task.model_copy(update={"artifacts": [artifact]})
224
198
 
225
199
  assert len(task_with_artifact.artifacts) == 1
226
200
  assert task_with_artifact.artifacts[0].name == "test_artifact"
@@ -232,9 +206,7 @@ class TestJSONRPCTypes:
232
206
  def test_create_jsonrpc_request(self):
233
207
  """Test JSON-RPC request creation"""
234
208
  request = create_jsonrpc_request(
235
- method="message/send",
236
- params={"test": "data"},
237
- request_id="req_123"
209
+ method="message/send", params={"test": "data"}, request_id="req_123"
238
210
  )
239
211
 
240
212
  assert request.jsonrpc == "2.0"
@@ -244,10 +216,7 @@ class TestJSONRPCTypes:
244
216
 
245
217
  def test_create_jsonrpc_success_response(self):
246
218
  """Test JSON-RPC success response creation"""
247
- response = create_jsonrpc_success_response(
248
- id="req_123",
249
- result={"success": True}
250
- )
219
+ response = create_jsonrpc_success_response(id="req_123", result={"success": True})
251
220
 
252
221
  assert response.jsonrpc == "2.0"
253
222
  assert response.id == "req_123"
@@ -256,14 +225,10 @@ class TestJSONRPCTypes:
256
225
  def test_create_jsonrpc_error_response(self):
257
226
  """Test JSON-RPC error response creation"""
258
227
  error = create_a2a_error(
259
- code=A2AErrorCodes.INVALID_REQUEST,
260
- message="Invalid request format"
228
+ code=A2AErrorCodes.INVALID_REQUEST, message="Invalid request format"
261
229
  )
262
230
 
263
- response = create_jsonrpc_error_response(
264
- id="req_123",
265
- error=error
266
- )
231
+ response = create_jsonrpc_error_response(id="req_123", error=error)
267
232
 
268
233
  assert response.jsonrpc == "2.0"
269
234
  assert response.id == "req_123"
@@ -273,9 +238,7 @@ class TestJSONRPCTypes:
273
238
  def test_message_send_request(self):
274
239
  """Test SendMessageRequest creation"""
275
240
  message = create_a2a_message(
276
- role="user",
277
- parts=[create_a2a_text_part("Hello")],
278
- context_id="ctx_123"
241
+ role="user", parts=[create_a2a_text_part("Hello")], context_id="ctx_123"
279
242
  )
280
243
 
281
244
  request = SendMessageRequest(
@@ -284,11 +247,8 @@ class TestJSONRPCTypes:
284
247
  method="message/send",
285
248
  params={
286
249
  "message": message,
287
- "configuration": MessageSendConfiguration(
288
- model="gpt-4",
289
- temperature=0.7
290
- )
291
- }
250
+ "configuration": MessageSendConfiguration(model="gpt-4", temperature=0.7),
251
+ },
292
252
  )
293
253
 
294
254
  assert request.params.message.role == "user"
@@ -301,6 +261,7 @@ class TestAgentTypes:
301
261
 
302
262
  def test_create_a2a_agent_tool(self):
303
263
  """Test A2A agent tool creation"""
264
+
304
265
  async def mock_execute(args, context):
305
266
  return {"result": "success"}
306
267
 
@@ -308,7 +269,7 @@ class TestAgentTypes:
308
269
  name="test_tool",
309
270
  description="A test tool",
310
271
  parameters={"type": "object", "properties": {}},
311
- execute_func=mock_execute
272
+ execute_func=mock_execute,
312
273
  )
313
274
 
314
275
  assert tool.name == "test_tool"
@@ -318,14 +279,12 @@ class TestAgentTypes:
318
279
 
319
280
  def test_a2a_agent_creation(self):
320
281
  """Test A2A agent creation"""
282
+
321
283
  async def mock_execute(args, context):
322
284
  return {"result": "tool executed"}
323
285
 
324
286
  tool = create_a2a_agent_tool(
325
- name="helper_tool",
326
- description="Helper tool",
327
- parameters={},
328
- execute_func=mock_execute
287
+ name="helper_tool", description="Helper tool", parameters={}, execute_func=mock_execute
329
288
  )
330
289
 
331
290
  agent = A2AAgent(
@@ -333,7 +292,7 @@ class TestAgentTypes:
333
292
  description="A test agent",
334
293
  supportedContentTypes=["text/plain"],
335
294
  instruction="You are a test agent",
336
- tools=[tool]
295
+ tools=[tool],
337
296
  )
338
297
 
339
298
  assert agent.name == "TestAgent"
@@ -353,20 +312,15 @@ class TestAgentTypes:
353
312
  tags=["test"],
354
313
  examples=["Example usage"],
355
314
  inputModes=["text/plain"],
356
- outputModes=["text/plain"]
315
+ outputModes=["text/plain"],
357
316
  )
358
317
  ]
359
318
 
360
319
  capabilities = AgentCapabilities(
361
- streaming=True,
362
- pushNotifications=False,
363
- stateTransitionHistory=True
320
+ streaming=True, pushNotifications=False, stateTransitionHistory=True
364
321
  )
365
322
 
366
- provider = AgentProvider(
367
- organization="Test Org",
368
- url="https://test.com"
369
- )
323
+ provider = AgentProvider(organization="Test Org", url="https://test.com")
370
324
 
371
325
  card = AgentCard(
372
326
  protocolVersion="0.3.0",
@@ -379,7 +333,7 @@ class TestAgentTypes:
379
333
  capabilities=capabilities,
380
334
  defaultInputModes=["text/plain"],
381
335
  defaultOutputModes=["text/plain"],
382
- skills=skills
336
+ skills=skills,
383
337
  )
384
338
 
385
339
  assert card.protocol_version == "0.3.0"
@@ -399,7 +353,7 @@ class TestStreamTypes:
399
353
  content="Processing...",
400
354
  updates="Working on task",
401
355
  newState={"status": "working"},
402
- timestamp=datetime.now().isoformat()
356
+ timestamp=datetime.now().isoformat(),
403
357
  )
404
358
 
405
359
  assert event.isTaskComplete is False
@@ -411,17 +365,10 @@ class TestStreamTypes:
411
365
  """Test A2A stream event"""
412
366
  from jaf.a2a.types import A2AStatusUpdateEvent, A2ATaskStatus
413
367
 
414
- status = A2ATaskStatus(
415
- state="working",
416
- timestamp="2025-08-12T22:52:29.159776"
417
- )
368
+ status = A2ATaskStatus(state="working", timestamp="2025-08-12T22:52:29.159776")
418
369
 
419
370
  event = A2AStatusUpdateEvent(
420
- kind="status-update",
421
- taskId="task_123",
422
- contextId="ctx_456",
423
- status=status,
424
- final=False
371
+ kind="status-update", taskId="task_123", contextId="ctx_456", status=status, final=False
425
372
  )
426
373
 
427
374
  assert event.kind == "status-update"
@@ -432,9 +379,7 @@ class TestStreamTypes:
432
379
  def test_agent_state(self):
433
380
  """Test agent state management"""
434
381
  message = create_a2a_message(
435
- role="user",
436
- parts=[create_a2a_text_part("Test")],
437
- context_id="ctx_123"
382
+ role="user", parts=[create_a2a_text_part("Test")], context_id="ctx_123"
438
383
  )
439
384
 
440
385
  state = AgentState(
@@ -442,7 +387,7 @@ class TestStreamTypes:
442
387
  messages=[message],
443
388
  context={"key": "value"},
444
389
  artifacts=[],
445
- timestamp=datetime.now().isoformat()
390
+ timestamp=datetime.now().isoformat(),
446
391
  )
447
392
 
448
393
  assert state.sessionId == "session_123"
@@ -456,25 +401,16 @@ class TestClientTypes:
456
401
 
457
402
  def test_client_config(self):
458
403
  """Test A2A client configuration"""
459
- config = A2AClientConfig(
460
- baseUrl="http://localhost:3000",
461
- timeout=30000
462
- )
404
+ config = A2AClientConfig(baseUrl="http://localhost:3000", timeout=30000)
463
405
 
464
406
  assert config.base_url == "http://localhost:3000"
465
407
  assert config.timeout == 30000
466
408
 
467
409
  def test_client_state(self):
468
410
  """Test A2A client state"""
469
- config = A2AClientConfig(
470
- baseUrl="http://localhost:3000",
471
- timeout=30000
472
- )
411
+ config = A2AClientConfig(baseUrl="http://localhost:3000", timeout=30000)
473
412
 
474
- state = A2AClientState(
475
- config=config,
476
- sessionId="session_123"
477
- )
413
+ state = A2AClientState(config=config, sessionId="session_123")
478
414
 
479
415
  assert state.config.base_url == "http://localhost:3000"
480
416
  assert state.session_id == "session_123"
@@ -482,12 +418,8 @@ class TestClientTypes:
482
418
  def test_tool_context(self):
483
419
  """Test tool context"""
484
420
  context = ToolContext(
485
- actions={
486
- "requiresInput": False,
487
- "skipSummarization": True,
488
- "escalate": False
489
- },
490
- metadata={"user_id": "user_123"}
421
+ actions={"requiresInput": False, "skipSummarization": True, "escalate": False},
422
+ metadata={"user_id": "user_123"},
491
423
  )
492
424
 
493
425
  assert context.actions["requiresInput"] is False
@@ -510,7 +442,7 @@ class TestErrorTypes:
510
442
  error = create_a2a_error(
511
443
  code=A2AErrorCodes.INVALID_PARAMS,
512
444
  message="Invalid parameters provided",
513
- data={"field": "missing_field"}
445
+ data={"field": "missing_field"},
514
446
  )
515
447
 
516
448
  assert error.code == A2AErrorCodes.INVALID_PARAMS.value
@@ -524,7 +456,7 @@ class TestErrorTypes:
524
456
  status="success",
525
457
  result={"result": "completed"},
526
458
  data={"result": "completed"},
527
- error=None
459
+ error=None,
528
460
  )
529
461
 
530
462
  assert success_result.status == "success"
@@ -537,9 +469,8 @@ class TestErrorTypes:
537
469
  result=None,
538
470
  data=None,
539
471
  error=create_a2a_error(
540
- code=A2AErrorCodes.INTERNAL_ERROR,
541
- message="Tool execution failed"
542
- )
472
+ code=A2AErrorCodes.INTERNAL_ERROR, message="Tool execution failed"
473
+ ),
543
474
  )
544
475
 
545
476
  assert error_result.status == "error"
@@ -559,41 +490,24 @@ class TestFactoryFunctions:
559
490
 
560
491
  # Message
561
492
  message = create_a2a_message(
562
- role="user",
563
- parts=[text_part, data_part],
564
- context_id="ctx_123"
493
+ role="user", parts=[text_part, data_part], context_id="ctx_123"
565
494
  )
566
495
 
567
496
  # Task
568
497
  task = create_a2a_task(message, "ctx_123")
569
498
 
570
499
  # Artifact
571
- artifact = create_a2a_artifact(
572
- name="test_artifact",
573
- parts=[text_part]
574
- )
500
+ artifact = create_a2a_artifact(name="test_artifact", parts=[text_part])
575
501
 
576
502
  # JSON-RPC request
577
- request = create_jsonrpc_request(
578
- method="test/method",
579
- params={"test": True}
580
- )
503
+ request = create_jsonrpc_request(method="test/method", params={"test": True})
581
504
 
582
505
  # JSON-RPC responses
583
- success_response = create_jsonrpc_success_response(
584
- id="req_123",
585
- result={"success": True}
586
- )
506
+ success_response = create_jsonrpc_success_response(id="req_123", result={"success": True})
587
507
 
588
- error = create_a2a_error(
589
- code=A2AErrorCodes.INTERNAL_ERROR,
590
- message="Test error"
591
- )
508
+ error = create_a2a_error(code=A2AErrorCodes.INTERNAL_ERROR, message="Test error")
592
509
 
593
- error_response = create_jsonrpc_error_response(
594
- id="req_123",
595
- error=error
596
- )
510
+ error_response = create_jsonrpc_error_response(id="req_123", error=error)
597
511
 
598
512
  # Verify all created correctly
599
513
  assert text_part.kind == "text"