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
@@ -5,7 +5,6 @@ Tests JSON-RPC protocol validation, request routing, and response handling
5
5
  for the A2A implementation.
6
6
  """
7
7
 
8
-
9
8
  import pytest
10
9
 
11
10
  from jaf.a2a.protocol import (
@@ -40,7 +39,7 @@ def create_mock_agent():
40
39
  name="test_tool",
41
40
  description="A test tool",
42
41
  parameters={"type": "object"},
43
- execute=mock_tool_execute
42
+ execute=mock_tool_execute,
44
43
  )
45
44
 
46
45
  return A2AAgent(
@@ -48,7 +47,7 @@ def create_mock_agent():
48
47
  description="A test agent",
49
48
  supportedContentTypes=["text/plain"],
50
49
  instruction="You are a test agent",
51
- tools=[tool]
50
+ tools=[tool],
52
51
  )
53
52
 
54
53
 
@@ -61,7 +60,7 @@ class TestJSONRPCValidation:
61
60
  "jsonrpc": "2.0",
62
61
  "id": "req_123",
63
62
  "method": "message/send",
64
- "params": {"test": "data"}
63
+ "params": {"test": "data"},
65
64
  }
66
65
 
67
66
  assert validate_jsonrpc_request(valid_request) is True
@@ -69,36 +68,19 @@ class TestJSONRPCValidation:
69
68
  def test_validate_jsonrpc_request_invalid(self):
70
69
  """Test validation of invalid JSON-RPC requests"""
71
70
  # Missing jsonrpc field
72
- invalid1 = {
73
- "id": "req_123",
74
- "method": "test",
75
- "params": {}
76
- }
71
+ invalid1 = {"id": "req_123", "method": "test", "params": {}}
77
72
  assert validate_jsonrpc_request(invalid1) is False
78
73
 
79
74
  # Wrong jsonrpc version
80
- invalid2 = {
81
- "jsonrpc": "1.0",
82
- "id": "req_123",
83
- "method": "test",
84
- "params": {}
85
- }
75
+ invalid2 = {"jsonrpc": "1.0", "id": "req_123", "method": "test", "params": {}}
86
76
  assert validate_jsonrpc_request(invalid2) is False
87
77
 
88
78
  # Missing id
89
- invalid3 = {
90
- "jsonrpc": "2.0",
91
- "method": "test",
92
- "params": {}
93
- }
79
+ invalid3 = {"jsonrpc": "2.0", "method": "test", "params": {}}
94
80
  assert validate_jsonrpc_request(invalid3) is False
95
81
 
96
82
  # Missing method
97
- invalid4 = {
98
- "jsonrpc": "2.0",
99
- "id": "req_123",
100
- "params": {}
101
- }
83
+ invalid4 = {"jsonrpc": "2.0", "id": "req_123", "params": {}}
102
84
  assert validate_jsonrpc_request(invalid4) is False
103
85
 
104
86
  # Non-dict input
@@ -117,9 +99,9 @@ class TestJSONRPCValidation:
117
99
  "parts": [{"kind": "text", "text": "Hello"}],
118
100
  "messageId": "msg_123",
119
101
  "contextId": "ctx_123",
120
- "kind": "message"
102
+ "kind": "message",
121
103
  }
122
- }
104
+ },
123
105
  }
124
106
 
125
107
  result = validate_send_message_request(valid_request)
@@ -129,11 +111,7 @@ class TestJSONRPCValidation:
129
111
  def test_validate_send_message_request_invalid(self):
130
112
  """Test validation of invalid send message requests"""
131
113
  # Invalid JSON-RPC base
132
- invalid1 = {
133
- "id": "req_123",
134
- "method": "message/send",
135
- "params": {"message": {}}
136
- }
114
+ invalid1 = {"id": "req_123", "method": "message/send", "params": {"message": {}}}
137
115
 
138
116
  result1 = validate_send_message_request(invalid1)
139
117
  assert result1["is_valid"] is False
@@ -144,7 +122,7 @@ class TestJSONRPCValidation:
144
122
  "jsonrpc": "2.0",
145
123
  "id": "req_123",
146
124
  "method": "wrong/method",
147
- "params": {"message": {}}
125
+ "params": {"message": {}},
148
126
  }
149
127
 
150
128
  result2 = validate_send_message_request(invalid2)
@@ -156,7 +134,7 @@ class TestJSONRPCValidation:
156
134
  "jsonrpc": "2.0",
157
135
  "id": "req_123",
158
136
  "method": "message/send",
159
- "params": "not a dict"
137
+ "params": "not a dict",
160
138
  }
161
139
 
162
140
  result3 = validate_send_message_request(invalid3)
@@ -168,9 +146,7 @@ class TestJSONRPCValidation:
168
146
  "jsonrpc": "2.0",
169
147
  "id": "req_123",
170
148
  "method": "message/send",
171
- "params": {
172
- "message": "not a dict"
173
- }
149
+ "params": {"message": "not a dict"},
174
150
  }
175
151
 
176
152
  result4 = validate_send_message_request(invalid4)
@@ -182,12 +158,7 @@ class TestJSONRPCValidation:
182
158
  "jsonrpc": "2.0",
183
159
  "id": "req_123",
184
160
  "method": "message/send",
185
- "params": {
186
- "message": {
187
- "kind": "wrong_kind",
188
- "parts": []
189
- }
190
- }
161
+ "params": {"message": {"kind": "wrong_kind", "parts": []}},
191
162
  }
192
163
 
193
164
  result5 = validate_send_message_request(invalid5)
@@ -199,12 +170,7 @@ class TestJSONRPCValidation:
199
170
  "jsonrpc": "2.0",
200
171
  "id": "req_123",
201
172
  "method": "message/send",
202
- "params": {
203
- "message": {
204
- "kind": "message",
205
- "parts": "not a list"
206
- }
207
- }
173
+ "params": {"message": {"kind": "message", "parts": "not a list"}},
208
174
  }
209
175
 
210
176
  result6 = validate_send_message_request(invalid6)
@@ -226,7 +192,7 @@ class TestMessageHandlers:
226
192
  "final_task": {
227
193
  "id": "task_123",
228
194
  "status": {"state": "completed"},
229
- "result": "Success"
195
+ "result": "Success",
230
196
  }
231
197
  }
232
198
 
@@ -240,14 +206,12 @@ class TestMessageHandlers:
240
206
  "parts": [{"kind": "text", "text": "Hello"}],
241
207
  "messageId": "msg_123",
242
208
  "contextId": "ctx_123",
243
- "kind": "message"
209
+ "kind": "message",
244
210
  }
245
- }
211
+ },
246
212
  }
247
213
 
248
- result = await handle_message_send(
249
- request, agent, None, mock_executor
250
- )
214
+ result = await handle_message_send(request, agent, None, mock_executor)
251
215
 
252
216
  assert result["jsonrpc"] == "2.0"
253
217
  assert result["id"] == "req_123"
@@ -261,9 +225,7 @@ class TestMessageHandlers:
261
225
 
262
226
  # Mock executor function that returns error
263
227
  async def mock_executor_error(context, agent, model_provider):
264
- return {
265
- "error": "Something went wrong"
266
- }
228
+ return {"error": "Something went wrong"}
267
229
 
268
230
  request = {
269
231
  "jsonrpc": "2.0",
@@ -275,14 +237,12 @@ class TestMessageHandlers:
275
237
  "parts": [{"kind": "text", "text": "Hello"}],
276
238
  "messageId": "msg_123",
277
239
  "contextId": "ctx_123",
278
- "kind": "message"
240
+ "kind": "message",
279
241
  }
280
- }
242
+ },
281
243
  }
282
244
 
283
- result = await handle_message_send(
284
- request, agent, None, mock_executor_error
285
- )
245
+ result = await handle_message_send(request, agent, None, mock_executor_error)
286
246
 
287
247
  assert result["jsonrpc"] == "2.0"
288
248
  assert result["id"] == "req_123"
@@ -308,14 +268,12 @@ class TestMessageHandlers:
308
268
  "parts": [{"kind": "text", "text": "Hello"}],
309
269
  "messageId": "msg_123",
310
270
  "contextId": "ctx_123",
311
- "kind": "message"
271
+ "kind": "message",
312
272
  }
313
- }
273
+ },
314
274
  }
315
275
 
316
- result = await handle_message_send(
317
- request, agent, None, mock_executor_exception
318
- )
276
+ result = await handle_message_send(request, agent, None, mock_executor_exception)
319
277
 
320
278
  assert result["jsonrpc"] == "2.0"
321
279
  assert result["id"] == "req_123"
@@ -342,15 +300,13 @@ class TestMessageHandlers:
342
300
  "parts": [{"kind": "text", "text": "Hello"}],
343
301
  "messageId": "msg_123",
344
302
  "contextId": "ctx_123",
345
- "kind": "message"
303
+ "kind": "message",
346
304
  }
347
- }
305
+ },
348
306
  }
349
307
 
350
308
  events = []
351
- async for event in handle_message_stream(
352
- request, agent, None, mock_streaming_executor
353
- ):
309
+ async for event in handle_message_stream(request, agent, None, mock_streaming_executor):
354
310
  events.append(event)
355
311
 
356
312
  assert len(events) == 2
@@ -379,9 +335,9 @@ class TestMessageHandlers:
379
335
  "parts": [{"kind": "text", "text": "Hello"}],
380
336
  "messageId": "msg_123",
381
337
  "contextId": "ctx_123",
382
- "kind": "message"
338
+ "kind": "message",
383
339
  }
384
- }
340
+ },
385
341
  }
386
342
 
387
343
  events = []
@@ -409,9 +365,9 @@ class TestTaskHandlers:
409
365
  "status": {"state": "completed"},
410
366
  "history": [
411
367
  {"role": "user", "content": "Hello"},
412
- {"role": "agent", "content": "Hi there"}
368
+ {"role": "agent", "content": "Hi there"},
413
369
  ],
414
- "artifacts": []
370
+ "artifacts": [],
415
371
  }
416
372
  }
417
373
 
@@ -419,9 +375,7 @@ class TestTaskHandlers:
419
375
  "jsonrpc": "2.0",
420
376
  "id": "req_123",
421
377
  "method": "tasks/get",
422
- "params": {
423
- "id": "task_123"
424
- }
378
+ "params": {"id": "task_123"},
425
379
  }
426
380
 
427
381
  result = await handle_tasks_get(request, task_storage)
@@ -441,9 +395,7 @@ class TestTaskHandlers:
441
395
  "jsonrpc": "2.0",
442
396
  "id": "req_123",
443
397
  "method": "tasks/get",
444
- "params": {
445
- "id": "nonexistent_task"
446
- }
398
+ "params": {"id": "nonexistent_task"},
447
399
  }
448
400
 
449
401
  result = await handle_tasks_get(request, task_storage)
@@ -458,12 +410,7 @@ class TestTaskHandlers:
458
410
  """Test task retrieval with missing task ID"""
459
411
  task_storage = {}
460
412
 
461
- request = {
462
- "jsonrpc": "2.0",
463
- "id": "req_123",
464
- "method": "tasks/get",
465
- "params": {}
466
- }
413
+ request = {"jsonrpc": "2.0", "id": "req_123", "method": "tasks/get", "params": {}}
467
414
 
468
415
  result = await handle_tasks_get(request, task_storage)
469
416
 
@@ -486,8 +433,8 @@ class TestTaskHandlers:
486
433
  {"role": "user", "content": "Message 2"},
487
434
  {"role": "agent", "content": "Response 2"},
488
435
  {"role": "user", "content": "Message 3"},
489
- {"role": "agent", "content": "Response 3"}
490
- ]
436
+ {"role": "agent", "content": "Response 3"},
437
+ ],
491
438
  }
492
439
  }
493
440
 
@@ -495,10 +442,7 @@ class TestTaskHandlers:
495
442
  "jsonrpc": "2.0",
496
443
  "id": "req_123",
497
444
  "method": "tasks/get",
498
- "params": {
499
- "id": "task_123",
500
- "historyLength": 2
501
- }
445
+ "params": {"id": "task_123", "historyLength": 2},
502
446
  }
503
447
 
504
448
  result = await handle_tasks_get(request, task_storage)
@@ -514,20 +458,14 @@ class TestTaskHandlers:
514
458
  async def test_handle_tasks_cancel_success(self):
515
459
  """Test successful task cancellation"""
516
460
  task_storage = {
517
- "task_123": {
518
- "id": "task_123",
519
- "status": {"state": "working"},
520
- "history": []
521
- }
461
+ "task_123": {"id": "task_123", "status": {"state": "working"}, "history": []}
522
462
  }
523
463
 
524
464
  request = {
525
465
  "jsonrpc": "2.0",
526
466
  "id": "req_123",
527
467
  "method": "tasks/cancel",
528
- "params": {
529
- "id": "task_123"
530
- }
468
+ "params": {"id": "task_123"},
531
469
  }
532
470
 
533
471
  result = await handle_tasks_cancel(request, task_storage)
@@ -541,20 +479,14 @@ class TestTaskHandlers:
541
479
  async def test_handle_tasks_cancel_not_cancelable(self):
542
480
  """Test task cancellation for non-cancelable task"""
543
481
  task_storage = {
544
- "task_123": {
545
- "id": "task_123",
546
- "status": {"state": "completed"},
547
- "history": []
548
- }
482
+ "task_123": {"id": "task_123", "status": {"state": "completed"}, "history": []}
549
483
  }
550
484
 
551
485
  request = {
552
486
  "jsonrpc": "2.0",
553
487
  "id": "req_123",
554
488
  "method": "tasks/cancel",
555
- "params": {
556
- "id": "task_123"
557
- }
489
+ "params": {"id": "task_123"},
558
490
  }
559
491
 
560
492
  result = await handle_tasks_cancel(request, task_storage)
@@ -576,13 +508,13 @@ class TestAgentCardHandler:
576
508
  "name": "Test Agent",
577
509
  "description": "A test agent",
578
510
  "url": "http://localhost:3000/a2a",
579
- "skills": []
511
+ "skills": [],
580
512
  }
581
513
 
582
514
  request = {
583
515
  "jsonrpc": "2.0",
584
516
  "id": "req_123",
585
- "method": "agent/getAuthenticatedExtendedCard"
517
+ "method": "agent/getAuthenticatedExtendedCard",
586
518
  }
587
519
 
588
520
  result = await handle_get_authenticated_extended_card(request, agent_card)
@@ -616,14 +548,12 @@ class TestRequestRouting:
616
548
  "parts": [{"kind": "text", "text": "Hello"}],
617
549
  "messageId": "msg_123",
618
550
  "contextId": "ctx_123",
619
- "kind": "message"
551
+ "kind": "message",
620
552
  }
621
- }
553
+ },
622
554
  }
623
555
 
624
- result = await route_a2a_request(
625
- request, agent, None, {}, {}, mock_executor, None
626
- )
556
+ result = await route_a2a_request(request, agent, None, {}, {}, mock_executor, None)
627
557
 
628
558
  assert result["jsonrpc"] == "2.0"
629
559
  assert result["id"] == "req_123"
@@ -634,16 +564,9 @@ class TestRequestRouting:
634
564
  """Test routing invalid method requests"""
635
565
  agent = create_mock_agent()
636
566
 
637
- request = {
638
- "jsonrpc": "2.0",
639
- "id": "req_123",
640
- "method": "invalid/method",
641
- "params": {}
642
- }
567
+ request = {"jsonrpc": "2.0", "id": "req_123", "method": "invalid/method", "params": {}}
643
568
 
644
- result = await route_a2a_request(
645
- request, agent, None, {}, {}, None, None
646
- )
569
+ result = await route_a2a_request(request, agent, None, {}, {}, None, None)
647
570
 
648
571
  assert result["jsonrpc"] == "2.0"
649
572
  assert result["id"] == "req_123"
@@ -657,13 +580,11 @@ class TestRequestRouting:
657
580
 
658
581
  invalid_request = {
659
582
  "id": "req_123",
660
- "method": "message/send"
583
+ "method": "message/send",
661
584
  # Missing jsonrpc field
662
585
  }
663
586
 
664
- result = await route_a2a_request(
665
- invalid_request, agent, None, {}, {}, None, None
666
- )
587
+ result = await route_a2a_request(invalid_request, agent, None, {}, {}, None, None)
667
588
 
668
589
  assert result["jsonrpc"] == "2.0"
669
590
  assert result["id"] == "req_123"
@@ -676,10 +597,7 @@ class TestUtilityFunctions:
676
597
 
677
598
  def test_create_jsonrpc_success_response_dict(self):
678
599
  """Test creating JSON-RPC success response dict"""
679
- response = create_jsonrpc_success_response_dict(
680
- "req_123",
681
- {"status": "success"}
682
- )
600
+ response = create_jsonrpc_success_response_dict("req_123", {"status": "success"})
683
601
 
684
602
  assert response["jsonrpc"] == "2.0"
685
603
  assert response["id"] == "req_123"
@@ -687,10 +605,7 @@ class TestUtilityFunctions:
687
605
 
688
606
  def test_create_jsonrpc_error_response_dict(self):
689
607
  """Test creating JSON-RPC error response dict"""
690
- error = {
691
- "code": -32603,
692
- "message": "Internal error"
693
- }
608
+ error = {"code": -32603, "message": "Internal error"}
694
609
 
695
610
  response = create_jsonrpc_error_response_dict("req_123", error)
696
611