jaf-py 2.5.10__py3-none-any.whl → 2.5.12__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 +310 -210
  54. jaf/core/types.py +403 -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 +475 -283
  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.12.dist-info}/METADATA +2 -2
  87. jaf_py-2.5.12.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.12.dist-info}/WHEEL +0 -0
  90. {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/entry_points.txt +0 -0
  91. {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/licenses/LICENSE +0 -0
  92. {jaf_py-2.5.10.dist-info → jaf_py-2.5.12.dist-info}/top_level.txt +0 -0
jaf/a2a/memory/types.py CHANGED
@@ -16,14 +16,16 @@ from ...memory.types import Failure, Success
16
16
  from ..types import A2ATask, TaskState
17
17
 
18
18
  # Generic types for A2A results
19
- T = TypeVar('T')
20
- E = TypeVar('E', bound='A2ATaskError')
19
+ T = TypeVar("T")
20
+ E = TypeVar("E", bound="A2ATaskError")
21
21
 
22
22
  # A2A Task storage and retrieval types
23
23
 
24
+
24
25
  @dataclass(frozen=True)
25
26
  class A2ATaskQuery:
26
27
  """Query parameters for searching A2A tasks in memory providers"""
28
+
27
29
  task_id: Optional[str] = None
28
30
  context_id: Optional[str] = None
29
31
  state: Optional[TaskState] = None
@@ -34,9 +36,11 @@ class A2ATaskQuery:
34
36
  include_history: bool = True
35
37
  include_artifacts: bool = True
36
38
 
39
+
37
40
  @dataclass(frozen=True)
38
41
  class A2ATaskStorage:
39
42
  """Internal storage representation of an A2A task"""
43
+
40
44
  task_id: str
41
45
  context_id: str
42
46
  state: TaskState
@@ -49,35 +53,32 @@ class A2ATaskStorage:
49
53
 
50
54
  def __post_init__(self):
51
55
  if self.created_at is None:
52
- object.__setattr__(self, 'created_at', datetime.now())
56
+ object.__setattr__(self, "created_at", datetime.now())
53
57
  if self.updated_at is None:
54
- object.__setattr__(self, 'updated_at', datetime.now())
58
+ object.__setattr__(self, "updated_at", datetime.now())
59
+
55
60
 
56
61
  class A2ATaskProvider(Protocol):
57
62
  """
58
63
  Protocol defining the interface for A2A task storage providers.
59
-
64
+
60
65
  This extends the memory provider pattern for A2A-specific task persistence,
61
66
  providing optimized operations for task lifecycle management.
62
67
  """
63
68
 
64
69
  async def store_task(
65
- self,
66
- task: A2ATask,
67
- metadata: Optional[Dict[str, Any]] = None
68
- ) -> 'A2AResult[None]':
70
+ self, task: A2ATask, metadata: Optional[Dict[str, Any]] = None
71
+ ) -> "A2AResult[None]":
69
72
  """Store a new A2A task"""
70
73
  ...
71
74
 
72
- async def get_task(self, task_id: str) -> 'A2AResult[Optional[A2ATask]]':
75
+ async def get_task(self, task_id: str) -> "A2AResult[Optional[A2ATask]]":
73
76
  """Retrieve a task by ID"""
74
77
  ...
75
78
 
76
79
  async def update_task(
77
- self,
78
- task: A2ATask,
79
- metadata: Optional[Dict[str, Any]] = None
80
- ) -> 'A2AResult[None]':
80
+ self, task: A2ATask, metadata: Optional[Dict[str, Any]] = None
81
+ ) -> "A2AResult[None]":
81
82
  """Update an existing task"""
82
83
  ...
83
84
 
@@ -86,51 +87,52 @@ class A2ATaskProvider(Protocol):
86
87
  task_id: str,
87
88
  state: TaskState,
88
89
  status_message: Optional[Any] = None,
89
- timestamp: Optional[str] = None
90
- ) -> 'A2AResult[None]':
90
+ timestamp: Optional[str] = None,
91
+ ) -> "A2AResult[None]":
91
92
  """Update task status only (optimized for frequent status changes)"""
92
93
  ...
93
94
 
94
- async def find_tasks(self, query: A2ATaskQuery) -> 'A2AResult[List[A2ATask]]':
95
+ async def find_tasks(self, query: A2ATaskQuery) -> "A2AResult[List[A2ATask]]":
95
96
  """Search tasks by query parameters"""
96
97
  ...
97
98
 
98
99
  async def get_tasks_by_context(
99
- self,
100
- context_id: str,
101
- limit: Optional[int] = None
102
- ) -> 'A2AResult[List[A2ATask]]':
100
+ self, context_id: str, limit: Optional[int] = None
101
+ ) -> "A2AResult[List[A2ATask]]":
103
102
  """Get tasks by context ID"""
104
103
  ...
105
104
 
106
- async def delete_task(self, task_id: str) -> 'A2AResult[bool]':
105
+ async def delete_task(self, task_id: str) -> "A2AResult[bool]":
107
106
  """Delete a task and return True if it existed"""
108
107
  ...
109
108
 
110
- async def delete_tasks_by_context(self, context_id: str) -> 'A2AResult[int]':
109
+ async def delete_tasks_by_context(self, context_id: str) -> "A2AResult[int]":
111
110
  """Delete tasks by context ID and return count deleted"""
112
111
  ...
113
112
 
114
- async def cleanup_expired_tasks(self) -> 'A2AResult[int]':
113
+ async def cleanup_expired_tasks(self) -> "A2AResult[int]":
115
114
  """Clean up expired tasks and return count deleted"""
116
115
  ...
117
116
 
118
- async def get_task_stats(self, context_id: Optional[str] = None) -> 'A2AResult[Dict[str, Any]]':
117
+ async def get_task_stats(self, context_id: Optional[str] = None) -> "A2AResult[Dict[str, Any]]":
119
118
  """Get task statistics"""
120
119
  ...
121
120
 
122
- async def health_check(self) -> 'A2AResult[Dict[str, Any]]':
121
+ async def health_check(self) -> "A2AResult[Dict[str, Any]]":
123
122
  """Check provider health and return status information"""
124
123
  ...
125
124
 
126
- async def close(self) -> 'A2AResult[None]':
125
+ async def close(self) -> "A2AResult[None]":
127
126
  """Close/cleanup the provider"""
128
127
  ...
129
128
 
129
+
130
130
  # Configuration models for A2A task storage
131
131
 
132
+
132
133
  class A2ATaskMemoryConfig(BaseModel):
133
134
  """Base configuration for A2A task memory"""
135
+
134
136
  model_config = {"frozen": True}
135
137
 
136
138
  type: str
@@ -141,15 +143,19 @@ class A2ATaskMemoryConfig(BaseModel):
141
143
  enable_history: bool = Field(default=True) # Store task history
142
144
  enable_artifacts: bool = Field(default=True) # Store task artifacts
143
145
 
146
+
144
147
  class A2AInMemoryTaskConfig(A2ATaskMemoryConfig):
145
148
  """Configuration for A2A in-memory task provider"""
149
+
146
150
  model_config = {"frozen": True}
147
151
 
148
152
  type: str = Field(default="memory", frozen=True)
149
153
  max_tasks_per_context: int = Field(default=1000)
150
154
 
155
+
151
156
  class A2ARedisTaskConfig(A2ATaskMemoryConfig):
152
157
  """Configuration for A2A Redis task provider"""
158
+
153
159
  model_config = {"frozen": True}
154
160
 
155
161
  type: str = Field(default="redis", frozen=True)
@@ -158,8 +164,10 @@ class A2ARedisTaskConfig(A2ATaskMemoryConfig):
158
164
  password: Optional[str] = None
159
165
  db: int = Field(default=0, ge=0)
160
166
 
167
+
161
168
  class A2APostgresTaskConfig(A2ATaskMemoryConfig):
162
169
  """Configuration for A2A PostgreSQL task provider"""
170
+
163
171
  model_config = {"frozen": True}
164
172
 
165
173
  type: str = Field(default="postgres", frozen=True)
@@ -172,14 +180,24 @@ class A2APostgresTaskConfig(A2ATaskMemoryConfig):
172
180
  table_name: str = Field(default="a2a_tasks")
173
181
  max_connections: int = Field(default=10, ge=1)
174
182
 
183
+
175
184
  # Union type for all A2A task provider configurations
176
185
  A2ATaskProviderConfig = Union[A2AInMemoryTaskConfig, A2ARedisTaskConfig, A2APostgresTaskConfig]
177
186
 
178
187
  # Error types specific to A2A task storage
179
188
 
189
+
180
190
  class A2ATaskError:
181
191
  """Base class for A2A task-related errors"""
182
- def __init__(self, message: str, code: str, provider: str, task_id: Optional[str] = None, cause: Optional[Exception] = None):
192
+
193
+ def __init__(
194
+ self,
195
+ message: str,
196
+ code: str,
197
+ provider: str,
198
+ task_id: Optional[str] = None,
199
+ cause: Optional[Exception] = None,
200
+ ):
183
201
  self.message = message
184
202
  self.code = code
185
203
  self.provider = provider
@@ -189,23 +207,44 @@ class A2ATaskError:
189
207
  def __eq__(self, other):
190
208
  if not isinstance(other, A2ATaskError):
191
209
  return False
192
- return (self.message == other.message and
193
- self.code == other.code and
194
- self.provider == other.provider and
195
- self.task_id == other.task_id and
196
- self.cause == other.cause)
210
+ return (
211
+ self.message == other.message
212
+ and self.code == other.code
213
+ and self.provider == other.provider
214
+ and self.task_id == other.task_id
215
+ and self.cause == other.cause
216
+ )
197
217
 
198
218
  def __repr__(self):
199
219
  return f"{self.__class__.__name__}(message={self.message!r}, code={self.code!r}, provider={self.provider!r}, task_id={self.task_id!r}, cause={self.cause!r})"
200
220
 
221
+
201
222
  class A2ATaskNotFoundError(A2ATaskError):
202
223
  """Error when an A2A task is not found"""
203
- def __init__(self, message: str, code: str, provider: str, task_id: str, cause: Optional[Exception] = None):
224
+
225
+ def __init__(
226
+ self,
227
+ message: str,
228
+ code: str,
229
+ provider: str,
230
+ task_id: str,
231
+ cause: Optional[Exception] = None,
232
+ ):
204
233
  super().__init__(message, code, provider, task_id, cause)
205
234
 
235
+
206
236
  class A2ATaskStorageError(A2ATaskError):
207
237
  """Error for A2A task storage operation failures"""
208
- def __init__(self, message: str, code: str, provider: str, operation: str, task_id: Optional[str] = None, cause: Optional[Exception] = None):
238
+
239
+ def __init__(
240
+ self,
241
+ message: str,
242
+ code: str,
243
+ provider: str,
244
+ operation: str,
245
+ task_id: Optional[str] = None,
246
+ cause: Optional[Exception] = None,
247
+ ):
209
248
  super().__init__(message, code, provider, task_id, cause)
210
249
  self.operation = operation
211
250
 
@@ -217,6 +256,7 @@ class A2ATaskStorageError(A2ATaskError):
217
256
  def __repr__(self):
218
257
  return f"{self.__class__.__name__}(message={self.message!r}, code={self.code!r}, provider={self.provider!r}, operation={self.operation!r}, task_id={self.task_id!r}, cause={self.cause!r})"
219
258
 
259
+
220
260
  # Union of all possible A2A task errors
221
261
  A2ATaskErrorUnion = Union[A2ATaskError, A2ATaskNotFoundError, A2ATaskStorageError]
222
262
 
@@ -225,39 +265,30 @@ A2AResult = Union[Success[T], Failure[A2ATaskErrorUnion]]
225
265
 
226
266
  # Error factory functions
227
267
 
268
+
228
269
  def create_a2a_task_error(
229
270
  message: str,
230
271
  code: str,
231
272
  provider: str,
232
273
  task_id: Optional[str] = None,
233
- cause: Optional[Exception] = None
274
+ cause: Optional[Exception] = None,
234
275
  ) -> A2ATaskError:
235
276
  """Create an A2A task error"""
236
- return A2ATaskError(
237
- message=message,
238
- code=code,
239
- provider=provider,
240
- task_id=task_id,
241
- cause=cause
242
- )
277
+ return A2ATaskError(message=message, code=code, provider=provider, task_id=task_id, cause=cause)
278
+
243
279
 
244
- def create_a2a_task_not_found_error(
245
- task_id: str,
246
- provider: str
247
- ) -> A2ATaskNotFoundError:
280
+ def create_a2a_task_not_found_error(task_id: str, provider: str) -> A2ATaskNotFoundError:
248
281
  """Create an A2A task not found error"""
249
282
  return A2ATaskNotFoundError(
250
283
  message=f"A2A task {task_id} not found",
251
284
  code="TASK_NOT_FOUND",
252
285
  provider=provider,
253
- task_id=task_id
286
+ task_id=task_id,
254
287
  )
255
288
 
289
+
256
290
  def create_a2a_task_storage_error(
257
- operation: str,
258
- provider: str,
259
- task_id: Optional[str] = None,
260
- cause: Optional[Exception] = None
291
+ operation: str, provider: str, task_id: Optional[str] = None, cause: Optional[Exception] = None
261
292
  ) -> A2ATaskStorageError:
262
293
  """Create an A2A task storage error"""
263
294
  message = f"Failed to {operation} A2A task"
@@ -271,29 +302,36 @@ def create_a2a_task_storage_error(
271
302
  provider=provider,
272
303
  operation=operation,
273
304
  task_id=task_id,
274
- cause=cause
305
+ cause=cause,
275
306
  )
276
307
 
308
+
277
309
  # Error checking functions
278
310
 
311
+
279
312
  def is_a2a_task_error(error: Any) -> bool:
280
313
  """Check if error is an A2A task error"""
281
314
  return isinstance(error, (A2ATaskError, A2ATaskNotFoundError, A2ATaskStorageError))
282
315
 
316
+
283
317
  def is_a2a_task_not_found_error(error: Any) -> bool:
284
318
  """Check if error is an A2A task not found error"""
285
319
  return isinstance(error, A2ATaskNotFoundError)
286
320
 
321
+
287
322
  def is_a2a_task_storage_error(error: Any) -> bool:
288
323
  """Check if error is an A2A task storage error"""
289
324
  return isinstance(error, A2ATaskStorageError)
290
325
 
326
+
291
327
  # A2A-specific Result factory functions
292
328
 
329
+
293
330
  def create_a2a_success(data: T) -> Success[T]:
294
331
  """Create an A2A success result"""
295
332
  return Success(data=data)
296
333
 
334
+
297
335
  def create_a2a_failure(error: A2ATaskErrorUnion) -> Failure[A2ATaskErrorUnion]:
298
336
  """Create an A2A failure result"""
299
337
  return Failure(error=error)