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
@@ -57,12 +57,7 @@ class MockModelProvider:
57
57
  """Mock model provider for testing"""
58
58
 
59
59
  async def get_completion(self, state, agent, config):
60
- return {
61
- "message": {
62
- "content": "Mock response from agent",
63
- "tool_calls": None
64
- }
65
- }
60
+ return {"message": {"content": "Mock response from agent", "tool_calls": None}}
66
61
 
67
62
 
68
63
  class TestAgentCreation:
@@ -70,12 +65,7 @@ class TestAgentCreation:
70
65
 
71
66
  def test_create_a2a_agent_basic(self):
72
67
  """Test basic A2A agent creation"""
73
- agent = create_a2a_agent(
74
- "TestAgent",
75
- "A test agent",
76
- "You are helpful",
77
- []
78
- )
68
+ agent = create_a2a_agent("TestAgent", "A test agent", "You are helpful", [])
79
69
 
80
70
  assert isinstance(agent, A2AAgent)
81
71
  assert agent.name == "TestAgent"
@@ -87,19 +77,10 @@ class TestAgentCreation:
87
77
 
88
78
  def test_create_a2a_agent_with_tools(self):
89
79
  """Test A2A agent creation with tools"""
90
- tool = create_a2a_tool(
91
- "test_tool",
92
- "A test tool",
93
- {"type": "object"},
94
- mock_tool_function
95
- )
80
+ tool = create_a2a_tool("test_tool", "A test tool", {"type": "object"}, mock_tool_function)
96
81
 
97
82
  agent = create_a2a_agent(
98
- "ToolAgent",
99
- "Agent with tools",
100
- "Use tools wisely",
101
- [tool],
102
- ["text/plain", "text/html"]
83
+ "ToolAgent", "Agent with tools", "Use tools wisely", [tool], ["text/plain", "text/html"]
103
84
  )
104
85
 
105
86
  assert len(agent.tools) == 1
@@ -108,19 +89,9 @@ class TestAgentCreation:
108
89
 
109
90
  def test_create_a2a_tool(self):
110
91
  """Test A2A tool creation"""
111
- parameters = {
112
- "type": "object",
113
- "properties": {
114
- "message": {"type": "string"}
115
- }
116
- }
92
+ parameters = {"type": "object", "properties": {"message": {"type": "string"}}}
117
93
 
118
- tool = create_a2a_tool(
119
- "echo_tool",
120
- "Echoes back the input",
121
- parameters,
122
- mock_tool_function
123
- )
94
+ tool = create_a2a_tool("echo_tool", "Echoes back the input", parameters, mock_tool_function)
124
95
 
125
96
  assert isinstance(tool, A2AAgentTool)
126
97
  assert tool.name == "echo_tool"
@@ -134,19 +105,9 @@ class TestAgentTransformation:
134
105
 
135
106
  def test_transform_a2a_agent_to_jaf(self):
136
107
  """Test transforming A2A agent to JAF agent"""
137
- a2a_tool = create_a2a_tool(
138
- "test_tool",
139
- "Test tool",
140
- {"type": "object"},
141
- mock_tool_function
142
- )
108
+ a2a_tool = create_a2a_tool("test_tool", "Test tool", {"type": "object"}, mock_tool_function)
143
109
 
144
- a2a_agent = create_a2a_agent(
145
- "TestAgent",
146
- "Test agent",
147
- "You are a test agent",
148
- [a2a_tool]
149
- )
110
+ a2a_agent = create_a2a_agent("TestAgent", "Test agent", "You are a test agent", [a2a_tool])
150
111
 
151
112
  jaf_agent = transform_a2a_agent_to_jaf(a2a_agent)
152
113
 
@@ -162,17 +123,14 @@ class TestAgentTransformation:
162
123
  async def test_transform_a2a_tool_to_jaf(self):
163
124
  """Test transforming A2A tool to JAF tool"""
164
125
  a2a_tool = create_a2a_tool(
165
- "math_tool",
166
- "Performs math calculations",
167
- {"type": "object"},
168
- mock_tool_function
126
+ "math_tool", "Performs math calculations", {"type": "object"}, mock_tool_function
169
127
  )
170
128
 
171
129
  jaf_tool = transform_a2a_tool_to_jaf(a2a_tool)
172
130
 
173
131
  # Check that it has the required Tool protocol attributes
174
- assert hasattr(jaf_tool, 'schema')
175
- assert hasattr(jaf_tool, 'execute')
132
+ assert hasattr(jaf_tool, "schema")
133
+ assert hasattr(jaf_tool, "execute")
176
134
  assert jaf_tool.schema.name == "math_tool"
177
135
  assert jaf_tool.schema.description == "Performs math calculations"
178
136
 
@@ -183,15 +141,13 @@ class TestAgentTransformation:
183
141
  @pytest.mark.asyncio
184
142
  async def test_tool_result_handling(self):
185
143
  """Test tool result format handling"""
144
+
186
145
  # Tool that returns ToolResult format
187
146
  async def tool_with_result_format(args, context):
188
147
  return {"result": "formatted result"}
189
148
 
190
149
  a2a_tool = create_a2a_tool(
191
- "format_tool",
192
- "Tool with result format",
193
- {},
194
- tool_with_result_format
150
+ "format_tool", "Tool with result format", {}, tool_with_result_format
195
151
  )
196
152
 
197
153
  jaf_tool = transform_a2a_tool_to_jaf(a2a_tool)
@@ -218,9 +174,7 @@ class TestStateManagement:
218
174
  """Test adding message to agent state"""
219
175
  initial_state = create_initial_agent_state("session_123")
220
176
  message = create_a2a_message(
221
- role="user",
222
- parts=[create_a2a_text_part("Hello")],
223
- context_id="session_123"
177
+ role="user", parts=[create_a2a_text_part("Hello")], context_id="session_123"
224
178
  )
225
179
 
226
180
  new_state = add_message_to_state(initial_state, message)
@@ -269,7 +223,7 @@ class TestMessageProcessing:
269
223
  "parts": [
270
224
  {"kind": "text", "text": "First part"},
271
225
  {"kind": "data", "data": {"key": "value"}},
272
- {"kind": "text", "text": "Second part"}
226
+ {"kind": "text", "text": "Second part"},
273
227
  ]
274
228
  }
275
229
 
@@ -286,11 +240,7 @@ class TestMessageProcessing:
286
240
 
287
241
  def test_create_a2a_text_message(self):
288
242
  """Test creating A2A text message"""
289
- message = create_a2a_text_message(
290
- "Hello world",
291
- "ctx_123",
292
- "task_456"
293
- )
243
+ message = create_a2a_text_message("Hello world", "ctx_123", "task_456")
294
244
 
295
245
  assert message["role"] == "agent"
296
246
  assert message["contextId"] == "ctx_123"
@@ -304,10 +254,7 @@ class TestMessageProcessing:
304
254
  def test_create_a2a_data_message(self):
305
255
  """Test creating A2A data message"""
306
256
  data = {"result": "success", "count": 42}
307
- message = create_a2a_data_message(
308
- data,
309
- "ctx_789"
310
- )
257
+ message = create_a2a_data_message(data, "ctx_789")
311
258
 
312
259
  assert message["role"] == "agent"
313
260
  assert message["contextId"] == "ctx_789"
@@ -323,9 +270,7 @@ class TestTaskManagement:
323
270
  def test_create_a2a_task(self):
324
271
  """Test creating A2A task"""
325
272
  message = create_a2a_message(
326
- role="user",
327
- parts=[create_a2a_text_part("Task request")],
328
- context_id="ctx_123"
273
+ role="user", parts=[create_a2a_text_part("Task request")], context_id="ctx_123"
329
274
  )
330
275
 
331
276
  task = create_a2a_task(message.model_dump(), "ctx_123")
@@ -340,9 +285,7 @@ class TestTaskManagement:
340
285
  def test_update_a2a_task_status(self):
341
286
  """Test updating A2A task status"""
342
287
  message = create_a2a_message(
343
- role="user",
344
- parts=[create_a2a_text_part("Test")],
345
- context_id="ctx_123"
288
+ role="user", parts=[create_a2a_text_part("Test")], context_id="ctx_123"
346
289
  )
347
290
 
348
291
  task = create_a2a_task(message.model_dump(), "ctx_123")
@@ -360,9 +303,7 @@ class TestTaskManagement:
360
303
  def test_add_artifact_to_a2a_task(self):
361
304
  """Test adding artifact to A2A task"""
362
305
  message = create_a2a_message(
363
- role="user",
364
- parts=[create_a2a_text_part("Test")],
365
- context_id="ctx_123"
306
+ role="user", parts=[create_a2a_text_part("Test")], context_id="ctx_123"
366
307
  )
367
308
 
368
309
  task = create_a2a_task(message.model_dump(), "ctx_123")
@@ -383,9 +324,7 @@ class TestTaskManagement:
383
324
  def test_complete_a2a_task(self):
384
325
  """Test completing A2A task"""
385
326
  message = create_a2a_message(
386
- role="user",
387
- parts=[create_a2a_text_part("Test")],
388
- context_id="ctx_123"
327
+ role="user", parts=[create_a2a_text_part("Test")], context_id="ctx_123"
389
328
  )
390
329
 
391
330
  task = create_a2a_task(message.model_dump(), "ctx_123")
@@ -405,12 +344,7 @@ class TestAgentExecution:
405
344
 
406
345
  def test_create_run_config_for_a2a_agent(self):
407
346
  """Test creating run configuration for A2A agent"""
408
- agent = create_a2a_agent(
409
- "TestAgent",
410
- "Test agent",
411
- "You are helpful",
412
- []
413
- )
347
+ agent = create_a2a_agent("TestAgent", "Test agent", "You are helpful", [])
414
348
 
415
349
  model_provider = MockModelProvider()
416
350
 
@@ -430,14 +364,10 @@ class TestAgentExecution:
430
364
  messages=[message],
431
365
  context={},
432
366
  artifacts=[],
433
- timestamp=datetime.now().isoformat()
367
+ timestamp=datetime.now().isoformat(),
434
368
  )
435
369
 
436
- run_state = transform_to_run_state(
437
- agent_state,
438
- "TestAgent",
439
- {"user_id": "user_123"}
440
- )
370
+ run_state = transform_to_run_state(agent_state, "TestAgent", {"user_id": "user_123"})
441
371
 
442
372
  assert run_state.current_agent_name == "TestAgent"
443
373
  assert len(run_state.messages) == 1
@@ -448,12 +378,7 @@ class TestAgentExecution:
448
378
  @pytest.mark.asyncio
449
379
  async def test_process_agent_query(self):
450
380
  """Test processing agent query"""
451
- agent = create_a2a_agent(
452
- "TestAgent",
453
- "Test agent",
454
- "You are helpful",
455
- []
456
- )
381
+ agent = create_a2a_agent("TestAgent", "Test agent", "You are helpful", [])
457
382
 
458
383
  initial_state = create_initial_agent_state("session_123")
459
384
  model_provider = MockModelProvider()
@@ -476,9 +401,7 @@ class TestAgentExecution:
476
401
  m.setattr("jaf.a2a.agent.run", mock_run)
477
402
 
478
403
  events = []
479
- async for event in process_agent_query(
480
- agent, "Hello", initial_state, model_provider
481
- ):
404
+ async for event in process_agent_query(agent, "Hello", initial_state, model_provider):
482
405
  events.append(event)
483
406
 
484
407
  assert len(events) >= 1
@@ -490,18 +413,11 @@ class TestAgentExecution:
490
413
  @pytest.mark.asyncio
491
414
  async def test_execute_a2a_agent_success(self):
492
415
  """Test successful A2A agent execution"""
493
- agent = create_a2a_agent(
494
- "TestAgent",
495
- "Test agent",
496
- "You are helpful",
497
- []
498
- )
416
+ agent = create_a2a_agent("TestAgent", "Test agent", "You are helpful", [])
499
417
 
500
418
  context = {
501
- "message": {
502
- "parts": [{"kind": "text", "text": "Hello"}]
503
- },
504
- "session_id": "session_123"
419
+ "message": {"parts": [{"kind": "text", "text": "Hello"}]},
420
+ "session_id": "session_123",
505
421
  }
506
422
 
507
423
  model_provider = MockModelProvider()
@@ -512,7 +428,7 @@ class TestAgentExecution:
512
428
  isTaskComplete=True,
513
429
  content="Hello back!",
514
430
  new_state={},
515
- timestamp=datetime.now().isoformat()
431
+ timestamp=datetime.now().isoformat(),
516
432
  )
517
433
 
518
434
  with pytest.MonkeyPatch().context() as m:
@@ -528,18 +444,11 @@ class TestAgentExecution:
528
444
  @pytest.mark.asyncio
529
445
  async def test_execute_a2a_agent_error(self):
530
446
  """Test A2A agent execution with error"""
531
- agent = create_a2a_agent(
532
- "TestAgent",
533
- "Test agent",
534
- "You are helpful",
535
- []
536
- )
447
+ agent = create_a2a_agent("TestAgent", "Test agent", "You are helpful", [])
537
448
 
538
449
  context = {
539
- "message": {
540
- "parts": [{"kind": "text", "text": "Hello"}]
541
- },
542
- "session_id": "session_123"
450
+ "message": {"parts": [{"kind": "text", "text": "Hello"}]},
451
+ "session_id": "session_123",
543
452
  }
544
453
 
545
454
  model_provider = MockModelProvider()
@@ -550,7 +459,7 @@ class TestAgentExecution:
550
459
  isTaskComplete=True,
551
460
  content="Error: Something went wrong",
552
461
  new_state={},
553
- timestamp=datetime.now().isoformat()
462
+ timestamp=datetime.now().isoformat(),
554
463
  )
555
464
 
556
465
  with pytest.MonkeyPatch().context() as m:
@@ -565,18 +474,11 @@ class TestAgentExecution:
565
474
  @pytest.mark.asyncio
566
475
  async def test_execute_a2a_agent_with_streaming(self):
567
476
  """Test A2A agent execution with streaming"""
568
- agent = create_a2a_agent(
569
- "TestAgent",
570
- "Test agent",
571
- "You are helpful",
572
- []
573
- )
477
+ agent = create_a2a_agent("TestAgent", "Test agent", "You are helpful", [])
574
478
 
575
479
  context = {
576
- "message": {
577
- "parts": [{"kind": "text", "text": "Hello"}]
578
- },
579
- "session_id": "session_123"
480
+ "message": {"parts": [{"kind": "text", "text": "Hello"}]},
481
+ "session_id": "session_123",
580
482
  }
581
483
 
582
484
  model_provider = MockModelProvider()
@@ -589,7 +491,7 @@ class TestAgentExecution:
589
491
  content="Processing...",
590
492
  updates="Working on task",
591
493
  new_state={},
592
- timestamp=datetime.now().isoformat()
494
+ timestamp=datetime.now().isoformat(),
593
495
  )
594
496
 
595
497
  # Completion event
@@ -597,16 +499,14 @@ class TestAgentExecution:
597
499
  isTaskComplete=True,
598
500
  content="Task completed",
599
501
  new_state={},
600
- timestamp=datetime.now().isoformat()
502
+ timestamp=datetime.now().isoformat(),
601
503
  )
602
504
 
603
505
  with pytest.MonkeyPatch().context() as m:
604
506
  m.setattr("jaf.a2a.agent.process_agent_query", mock_process_streaming)
605
507
 
606
508
  events = []
607
- async for event in execute_a2a_agent_with_streaming(
608
- context, agent, model_provider
609
- ):
509
+ async for event in execute_a2a_agent_with_streaming(context, agent, model_provider):
610
510
  events.append(event)
611
511
 
612
512
  # Should get: task submitted, working status, artifact, completion