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.
- jaf/__init__.py +154 -57
- jaf/a2a/__init__.py +42 -21
- jaf/a2a/agent.py +79 -126
- jaf/a2a/agent_card.py +87 -78
- jaf/a2a/client.py +30 -66
- jaf/a2a/examples/client_example.py +12 -12
- jaf/a2a/examples/integration_example.py +38 -47
- jaf/a2a/examples/server_example.py +56 -53
- jaf/a2a/memory/__init__.py +0 -4
- jaf/a2a/memory/cleanup.py +28 -21
- jaf/a2a/memory/factory.py +155 -133
- jaf/a2a/memory/providers/composite.py +21 -26
- jaf/a2a/memory/providers/in_memory.py +89 -83
- jaf/a2a/memory/providers/postgres.py +117 -115
- jaf/a2a/memory/providers/redis.py +128 -121
- jaf/a2a/memory/serialization.py +77 -87
- jaf/a2a/memory/tests/run_comprehensive_tests.py +112 -83
- jaf/a2a/memory/tests/test_cleanup.py +211 -94
- jaf/a2a/memory/tests/test_serialization.py +73 -68
- jaf/a2a/memory/tests/test_stress_concurrency.py +186 -133
- jaf/a2a/memory/tests/test_task_lifecycle.py +138 -120
- jaf/a2a/memory/types.py +91 -53
- jaf/a2a/protocol.py +95 -125
- jaf/a2a/server.py +90 -118
- jaf/a2a/standalone_client.py +30 -43
- jaf/a2a/tests/__init__.py +16 -33
- jaf/a2a/tests/run_tests.py +17 -53
- jaf/a2a/tests/test_agent.py +40 -140
- jaf/a2a/tests/test_client.py +54 -117
- jaf/a2a/tests/test_integration.py +28 -82
- jaf/a2a/tests/test_protocol.py +54 -139
- jaf/a2a/tests/test_types.py +50 -136
- jaf/a2a/types.py +58 -34
- jaf/cli.py +21 -41
- jaf/core/__init__.py +7 -1
- jaf/core/agent_tool.py +93 -72
- jaf/core/analytics.py +257 -207
- jaf/core/checkpoint.py +223 -0
- jaf/core/composition.py +249 -235
- jaf/core/engine.py +817 -519
- jaf/core/errors.py +55 -42
- jaf/core/guardrails.py +276 -202
- jaf/core/handoff.py +47 -31
- jaf/core/parallel_agents.py +69 -75
- jaf/core/performance.py +75 -73
- jaf/core/proxy.py +43 -44
- jaf/core/proxy_helpers.py +24 -27
- jaf/core/regeneration.py +220 -129
- jaf/core/state.py +68 -66
- jaf/core/streaming.py +115 -108
- jaf/core/tool_results.py +111 -101
- jaf/core/tools.py +114 -116
- jaf/core/tracing.py +269 -210
- jaf/core/types.py +371 -151
- jaf/core/workflows.py +209 -168
- jaf/exceptions.py +46 -38
- jaf/memory/__init__.py +1 -6
- jaf/memory/approval_storage.py +54 -77
- jaf/memory/factory.py +4 -4
- jaf/memory/providers/in_memory.py +216 -180
- jaf/memory/providers/postgres.py +216 -146
- jaf/memory/providers/redis.py +173 -116
- jaf/memory/types.py +70 -51
- jaf/memory/utils.py +36 -34
- jaf/plugins/__init__.py +12 -12
- jaf/plugins/base.py +105 -96
- jaf/policies/__init__.py +0 -1
- jaf/policies/handoff.py +37 -46
- jaf/policies/validation.py +76 -52
- jaf/providers/__init__.py +6 -3
- jaf/providers/mcp.py +97 -51
- jaf/providers/model.py +360 -279
- jaf/server/__init__.py +1 -1
- jaf/server/main.py +7 -11
- jaf/server/server.py +514 -359
- jaf/server/types.py +208 -52
- jaf/utils/__init__.py +17 -18
- jaf/utils/attachments.py +111 -116
- jaf/utils/document_processor.py +175 -174
- jaf/visualization/__init__.py +1 -1
- jaf/visualization/example.py +111 -110
- jaf/visualization/functional_core.py +46 -71
- jaf/visualization/graphviz.py +154 -189
- jaf/visualization/imperative_shell.py +7 -16
- jaf/visualization/types.py +8 -4
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/METADATA +2 -2
- jaf_py-2.5.11.dist-info/RECORD +97 -0
- jaf_py-2.5.10.dist-info/RECORD +0 -96
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/WHEEL +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/entry_points.txt +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.dist-info}/licenses/LICENSE +0 -0
- {jaf_py-2.5.10.dist-info → jaf_py-2.5.11.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(
|
|
20
|
-
E = TypeVar(
|
|
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,
|
|
56
|
+
object.__setattr__(self, "created_at", datetime.now())
|
|
53
57
|
if self.updated_at is None:
|
|
54
|
-
object.__setattr__(self,
|
|
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
|
-
|
|
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) ->
|
|
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
|
-
|
|
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
|
-
) ->
|
|
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) ->
|
|
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
|
-
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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
|
-
|
|
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 (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|