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.
- 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 +361 -280
- 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.9.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.9.dist-info/RECORD +0 -96
- {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/WHEEL +0 -0
- {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/entry_points.txt +0 -0
- {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/licenses/LICENSE +0 -0
- {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/top_level.txt +0 -0
jaf/a2a/memory/factory.py
CHANGED
|
@@ -27,16 +27,15 @@ from .types import (
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
async def create_a2a_task_provider(
|
|
30
|
-
config: A2ATaskProviderConfig,
|
|
31
|
-
external_clients: Optional[Dict[str, Any]] = None
|
|
30
|
+
config: A2ATaskProviderConfig, external_clients: Optional[Dict[str, Any]] = None
|
|
32
31
|
) -> A2AResult[A2ATaskProvider]:
|
|
33
32
|
"""
|
|
34
33
|
Create an A2A task provider from configuration
|
|
35
|
-
|
|
34
|
+
|
|
36
35
|
Args:
|
|
37
36
|
config: Provider configuration
|
|
38
37
|
external_clients: Optional external clients (redis, postgres)
|
|
39
|
-
|
|
38
|
+
|
|
40
39
|
Returns:
|
|
41
40
|
A2AResult containing the task provider or an error
|
|
42
41
|
"""
|
|
@@ -44,32 +43,34 @@ async def create_a2a_task_provider(
|
|
|
44
43
|
external_clients = external_clients or {}
|
|
45
44
|
|
|
46
45
|
if config.type == "memory":
|
|
47
|
-
return create_a2a_success(
|
|
48
|
-
create_a2a_in_memory_task_provider(config)
|
|
49
|
-
)
|
|
46
|
+
return create_a2a_success(create_a2a_in_memory_task_provider(config))
|
|
50
47
|
|
|
51
48
|
elif config.type == "redis":
|
|
52
|
-
redis_client = external_clients.get(
|
|
49
|
+
redis_client = external_clients.get("redis")
|
|
53
50
|
if not redis_client:
|
|
54
51
|
return create_a2a_failure(
|
|
55
52
|
create_a2a_task_storage_error(
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
"create-provider",
|
|
54
|
+
"redis",
|
|
58
55
|
None,
|
|
59
|
-
Exception(
|
|
56
|
+
Exception(
|
|
57
|
+
"Redis client instance required. Please provide a Redis client in external_clients.redis"
|
|
58
|
+
),
|
|
60
59
|
)
|
|
61
60
|
)
|
|
62
61
|
return await create_a2a_redis_task_provider(config, redis_client)
|
|
63
62
|
|
|
64
63
|
elif config.type == "postgres":
|
|
65
|
-
postgres_client = external_clients.get(
|
|
64
|
+
postgres_client = external_clients.get("postgres")
|
|
66
65
|
if not postgres_client:
|
|
67
66
|
return create_a2a_failure(
|
|
68
67
|
create_a2a_task_storage_error(
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
"create-provider",
|
|
69
|
+
"postgres",
|
|
71
70
|
None,
|
|
72
|
-
Exception(
|
|
71
|
+
Exception(
|
|
72
|
+
"PostgreSQL client instance required. Please provide a PostgreSQL client in external_clients.postgres"
|
|
73
|
+
),
|
|
73
74
|
)
|
|
74
75
|
)
|
|
75
76
|
return await create_a2a_postgres_task_provider(config, postgres_client)
|
|
@@ -77,287 +78,308 @@ async def create_a2a_task_provider(
|
|
|
77
78
|
else:
|
|
78
79
|
return create_a2a_failure(
|
|
79
80
|
create_a2a_task_storage_error(
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
"create-provider",
|
|
82
|
+
"unknown",
|
|
82
83
|
None,
|
|
83
|
-
Exception(f
|
|
84
|
+
Exception(f"Unknown A2A task provider type: {config.type}"),
|
|
84
85
|
)
|
|
85
86
|
)
|
|
86
87
|
|
|
87
88
|
except Exception as error:
|
|
88
89
|
return create_a2a_failure(
|
|
89
|
-
create_a2a_task_storage_error(
|
|
90
|
+
create_a2a_task_storage_error("create-provider", "factory", None, error)
|
|
90
91
|
)
|
|
91
92
|
|
|
93
|
+
|
|
92
94
|
async def create_a2a_task_provider_from_env(
|
|
93
|
-
external_clients: Optional[Dict[str, Any]] = None
|
|
95
|
+
external_clients: Optional[Dict[str, Any]] = None,
|
|
94
96
|
) -> A2AResult[A2ATaskProvider]:
|
|
95
97
|
"""
|
|
96
98
|
Create A2A task provider from environment variables
|
|
97
|
-
|
|
99
|
+
|
|
98
100
|
Args:
|
|
99
101
|
external_clients: Optional external clients (redis, postgres)
|
|
100
|
-
|
|
102
|
+
|
|
101
103
|
Returns:
|
|
102
104
|
A2AResult containing the task provider or an error
|
|
103
105
|
"""
|
|
104
106
|
try:
|
|
105
107
|
# Fall back to regular JAF memory type if A2A-specific one isn't set
|
|
106
|
-
task_memory_type = os.getenv(
|
|
108
|
+
task_memory_type = os.getenv(
|
|
109
|
+
"JAF_A2A_MEMORY_TYPE", os.getenv("JAF_MEMORY_TYPE", "memory")
|
|
110
|
+
).lower()
|
|
107
111
|
external_clients = external_clients or {}
|
|
108
112
|
|
|
109
|
-
if task_memory_type ==
|
|
113
|
+
if task_memory_type == "memory":
|
|
110
114
|
config = A2AInMemoryTaskConfig(
|
|
111
|
-
type=
|
|
112
|
-
key_prefix=os.getenv(
|
|
113
|
-
default_ttl=int(os.getenv(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
type="memory",
|
|
116
|
+
key_prefix=os.getenv("JAF_A2A_KEY_PREFIX", "jaf:a2a:tasks:"),
|
|
117
|
+
default_ttl=int(os.getenv("JAF_A2A_DEFAULT_TTL"))
|
|
118
|
+
if os.getenv("JAF_A2A_DEFAULT_TTL")
|
|
119
|
+
else None,
|
|
120
|
+
cleanup_interval=int(os.getenv("JAF_A2A_CLEANUP_INTERVAL", "3600")),
|
|
121
|
+
max_tasks=int(os.getenv("JAF_A2A_MAX_TASKS", "10000")),
|
|
122
|
+
max_tasks_per_context=int(os.getenv("JAF_A2A_MAX_TASKS_PER_CONTEXT", "1000")),
|
|
123
|
+
enable_history=os.getenv("JAF_A2A_ENABLE_HISTORY", "true").lower() != "false",
|
|
124
|
+
enable_artifacts=os.getenv("JAF_A2A_ENABLE_ARTIFACTS", "true").lower() != "false",
|
|
119
125
|
)
|
|
120
126
|
return create_a2a_success(create_a2a_in_memory_task_provider(config))
|
|
121
127
|
|
|
122
|
-
elif task_memory_type ==
|
|
123
|
-
if not external_clients.get(
|
|
128
|
+
elif task_memory_type == "redis":
|
|
129
|
+
if not external_clients.get("redis"):
|
|
124
130
|
return create_a2a_failure(
|
|
125
131
|
create_a2a_task_storage_error(
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
"create-provider-from-env",
|
|
133
|
+
"redis",
|
|
128
134
|
None,
|
|
129
|
-
Exception(
|
|
135
|
+
Exception("Redis client required for Redis A2A task provider"),
|
|
130
136
|
)
|
|
131
137
|
)
|
|
132
138
|
|
|
133
139
|
config = A2ARedisTaskConfig(
|
|
134
|
-
type=
|
|
135
|
-
key_prefix=os.getenv(
|
|
136
|
-
default_ttl=int(os.getenv(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
type="redis",
|
|
141
|
+
key_prefix=os.getenv("JAF_A2A_KEY_PREFIX", "jaf:a2a:tasks:"),
|
|
142
|
+
default_ttl=int(os.getenv("JAF_A2A_DEFAULT_TTL", os.getenv("JAF_REDIS_TTL")))
|
|
143
|
+
if os.getenv("JAF_A2A_DEFAULT_TTL", os.getenv("JAF_REDIS_TTL"))
|
|
144
|
+
else None,
|
|
145
|
+
cleanup_interval=int(os.getenv("JAF_A2A_CLEANUP_INTERVAL", "3600")),
|
|
146
|
+
max_tasks=int(os.getenv("JAF_A2A_MAX_TASKS", "10000")),
|
|
147
|
+
enable_history=os.getenv("JAF_A2A_ENABLE_HISTORY", "true").lower() != "false",
|
|
148
|
+
enable_artifacts=os.getenv("JAF_A2A_ENABLE_ARTIFACTS", "true").lower() != "false",
|
|
149
|
+
host=os.getenv("JAF_A2A_REDIS_HOST", os.getenv("JAF_REDIS_HOST", "localhost")),
|
|
150
|
+
port=int(os.getenv("JAF_A2A_REDIS_PORT", os.getenv("JAF_REDIS_PORT", "6379"))),
|
|
151
|
+
password=os.getenv("JAF_A2A_REDIS_PASSWORD", os.getenv("JAF_REDIS_PASSWORD")),
|
|
152
|
+
db=int(os.getenv("JAF_A2A_REDIS_DB", os.getenv("JAF_REDIS_DB", "0"))),
|
|
145
153
|
)
|
|
146
|
-
return await create_a2a_redis_task_provider(config, external_clients[
|
|
154
|
+
return await create_a2a_redis_task_provider(config, external_clients["redis"])
|
|
147
155
|
|
|
148
|
-
elif task_memory_type ==
|
|
149
|
-
if not external_clients.get(
|
|
156
|
+
elif task_memory_type == "postgres":
|
|
157
|
+
if not external_clients.get("postgres"):
|
|
150
158
|
return create_a2a_failure(
|
|
151
159
|
create_a2a_task_storage_error(
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
"create-provider-from-env",
|
|
161
|
+
"postgres",
|
|
154
162
|
None,
|
|
155
|
-
Exception(
|
|
163
|
+
Exception("PostgreSQL client required for PostgreSQL A2A task provider"),
|
|
156
164
|
)
|
|
157
165
|
)
|
|
158
166
|
|
|
159
167
|
config = A2APostgresTaskConfig(
|
|
160
|
-
type=
|
|
161
|
-
key_prefix=os.getenv(
|
|
162
|
-
default_ttl=int(os.getenv(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
168
|
+
type="postgres",
|
|
169
|
+
key_prefix=os.getenv("JAF_A2A_KEY_PREFIX", "jaf:a2a:tasks:"),
|
|
170
|
+
default_ttl=int(os.getenv("JAF_A2A_DEFAULT_TTL"))
|
|
171
|
+
if os.getenv("JAF_A2A_DEFAULT_TTL")
|
|
172
|
+
else None,
|
|
173
|
+
cleanup_interval=int(os.getenv("JAF_A2A_CLEANUP_INTERVAL", "3600")),
|
|
174
|
+
max_tasks=int(os.getenv("JAF_A2A_MAX_TASKS", "10000")),
|
|
175
|
+
enable_history=os.getenv("JAF_A2A_ENABLE_HISTORY", "true").lower() != "false",
|
|
176
|
+
enable_artifacts=os.getenv("JAF_A2A_ENABLE_ARTIFACTS", "true").lower() != "false",
|
|
177
|
+
host=os.getenv(
|
|
178
|
+
"JAF_A2A_POSTGRES_HOST", os.getenv("JAF_POSTGRES_HOST", "localhost")
|
|
179
|
+
),
|
|
180
|
+
port=int(
|
|
181
|
+
os.getenv("JAF_A2A_POSTGRES_PORT", os.getenv("JAF_POSTGRES_PORT", "5432"))
|
|
182
|
+
),
|
|
183
|
+
database=os.getenv("JAF_A2A_POSTGRES_DB", os.getenv("JAF_POSTGRES_DB", "jaf_a2a")),
|
|
184
|
+
username=os.getenv(
|
|
185
|
+
"JAF_A2A_POSTGRES_USER", os.getenv("JAF_POSTGRES_USER", "postgres")
|
|
186
|
+
),
|
|
187
|
+
password=os.getenv("JAF_A2A_POSTGRES_PASSWORD", os.getenv("JAF_POSTGRES_PASSWORD")),
|
|
188
|
+
ssl=os.getenv(
|
|
189
|
+
"JAF_A2A_POSTGRES_SSL", os.getenv("JAF_POSTGRES_SSL", "false")
|
|
190
|
+
).lower()
|
|
191
|
+
== "true",
|
|
192
|
+
table_name=os.getenv("JAF_A2A_POSTGRES_TABLE", "a2a_tasks"),
|
|
193
|
+
max_connections=int(
|
|
194
|
+
os.getenv(
|
|
195
|
+
"JAF_A2A_POSTGRES_MAX_CONNECTIONS",
|
|
196
|
+
os.getenv("JAF_POSTGRES_MAX_CONNECTIONS", "10"),
|
|
197
|
+
)
|
|
198
|
+
),
|
|
175
199
|
)
|
|
176
|
-
return await create_a2a_postgres_task_provider(config, external_clients[
|
|
200
|
+
return await create_a2a_postgres_task_provider(config, external_clients["postgres"])
|
|
177
201
|
|
|
178
202
|
else:
|
|
179
203
|
return create_a2a_failure(
|
|
180
204
|
create_a2a_task_storage_error(
|
|
181
|
-
|
|
182
|
-
|
|
205
|
+
"create-provider-from-env",
|
|
206
|
+
"unknown",
|
|
183
207
|
None,
|
|
184
|
-
Exception(f
|
|
208
|
+
Exception(f"Unknown A2A task provider type: {task_memory_type}"),
|
|
185
209
|
)
|
|
186
210
|
)
|
|
187
211
|
|
|
188
212
|
except Exception as error:
|
|
189
213
|
return create_a2a_failure(
|
|
190
|
-
create_a2a_task_storage_error(
|
|
214
|
+
create_a2a_task_storage_error("create-provider-from-env", "factory", None, error)
|
|
191
215
|
)
|
|
192
216
|
|
|
217
|
+
|
|
193
218
|
async def create_simple_a2a_task_provider(
|
|
194
|
-
provider_type: str,
|
|
195
|
-
client: Optional[Any] = None,
|
|
196
|
-
config: Optional[Dict[str, Any]] = None
|
|
219
|
+
provider_type: str, client: Optional[Any] = None, config: Optional[Dict[str, Any]] = None
|
|
197
220
|
) -> A2AResult[A2ATaskProvider]:
|
|
198
221
|
"""
|
|
199
222
|
Helper function to create A2A task provider with sensible defaults
|
|
200
|
-
|
|
223
|
+
|
|
201
224
|
Args:
|
|
202
225
|
provider_type: Type of provider ('memory', 'redis', 'postgres')
|
|
203
226
|
client: External client for redis/postgres providers
|
|
204
227
|
config: Optional configuration overrides
|
|
205
|
-
|
|
228
|
+
|
|
206
229
|
Returns:
|
|
207
230
|
A2AResult containing the task provider or an error
|
|
208
231
|
"""
|
|
209
232
|
try:
|
|
210
233
|
config = config or {}
|
|
211
234
|
|
|
212
|
-
if provider_type ==
|
|
235
|
+
if provider_type == "memory":
|
|
213
236
|
provider_config = A2AInMemoryTaskConfig(
|
|
214
|
-
type=
|
|
215
|
-
key_prefix=
|
|
237
|
+
type="memory",
|
|
238
|
+
key_prefix="jaf:a2a:tasks:",
|
|
216
239
|
default_ttl=None,
|
|
217
240
|
cleanup_interval=3600,
|
|
218
241
|
max_tasks=10000,
|
|
219
242
|
max_tasks_per_context=1000,
|
|
220
243
|
enable_history=True,
|
|
221
244
|
enable_artifacts=True,
|
|
222
|
-
**config
|
|
245
|
+
**config,
|
|
223
246
|
)
|
|
224
247
|
return create_a2a_success(create_a2a_in_memory_task_provider(provider_config))
|
|
225
248
|
|
|
226
|
-
elif provider_type ==
|
|
249
|
+
elif provider_type == "redis":
|
|
227
250
|
if not client:
|
|
228
251
|
return create_a2a_failure(
|
|
229
252
|
create_a2a_task_storage_error(
|
|
230
|
-
|
|
231
|
-
|
|
253
|
+
"create-simple-provider",
|
|
254
|
+
"redis",
|
|
232
255
|
None,
|
|
233
|
-
Exception(
|
|
256
|
+
Exception("Redis client required for Redis A2A task provider"),
|
|
234
257
|
)
|
|
235
258
|
)
|
|
236
259
|
|
|
237
260
|
provider_config = A2ARedisTaskConfig(
|
|
238
|
-
type=
|
|
239
|
-
key_prefix=
|
|
261
|
+
type="redis",
|
|
262
|
+
key_prefix="jaf:a2a:tasks:",
|
|
240
263
|
default_ttl=None,
|
|
241
264
|
cleanup_interval=3600,
|
|
242
265
|
max_tasks=10000,
|
|
243
266
|
enable_history=True,
|
|
244
267
|
enable_artifacts=True,
|
|
245
|
-
host=
|
|
268
|
+
host="localhost",
|
|
246
269
|
port=6379,
|
|
247
270
|
password=None,
|
|
248
271
|
db=0,
|
|
249
|
-
**config
|
|
272
|
+
**config,
|
|
250
273
|
)
|
|
251
274
|
return await create_a2a_redis_task_provider(provider_config, client)
|
|
252
275
|
|
|
253
|
-
elif provider_type ==
|
|
276
|
+
elif provider_type == "postgres":
|
|
254
277
|
if not client:
|
|
255
278
|
return create_a2a_failure(
|
|
256
279
|
create_a2a_task_storage_error(
|
|
257
|
-
|
|
258
|
-
|
|
280
|
+
"create-simple-provider",
|
|
281
|
+
"postgres",
|
|
259
282
|
None,
|
|
260
|
-
Exception(
|
|
283
|
+
Exception("PostgreSQL client required for PostgreSQL A2A task provider"),
|
|
261
284
|
)
|
|
262
285
|
)
|
|
263
286
|
|
|
264
287
|
provider_config = A2APostgresTaskConfig(
|
|
265
|
-
type=
|
|
266
|
-
key_prefix=
|
|
288
|
+
type="postgres",
|
|
289
|
+
key_prefix="jaf:a2a:tasks:",
|
|
267
290
|
default_ttl=None,
|
|
268
291
|
cleanup_interval=3600,
|
|
269
292
|
max_tasks=10000,
|
|
270
293
|
enable_history=True,
|
|
271
294
|
enable_artifacts=True,
|
|
272
|
-
host=
|
|
295
|
+
host="localhost",
|
|
273
296
|
port=5432,
|
|
274
|
-
database=
|
|
275
|
-
username=
|
|
297
|
+
database="jaf_a2a",
|
|
298
|
+
username="postgres",
|
|
276
299
|
password=None,
|
|
277
300
|
ssl=False,
|
|
278
|
-
table_name=
|
|
301
|
+
table_name="a2a_tasks",
|
|
279
302
|
max_connections=10,
|
|
280
|
-
**config
|
|
303
|
+
**config,
|
|
281
304
|
)
|
|
282
305
|
return await create_a2a_postgres_task_provider(provider_config, client)
|
|
283
306
|
|
|
284
307
|
else:
|
|
285
308
|
return create_a2a_failure(
|
|
286
309
|
create_a2a_task_storage_error(
|
|
287
|
-
|
|
288
|
-
|
|
310
|
+
"create-simple-provider",
|
|
311
|
+
"unknown",
|
|
289
312
|
None,
|
|
290
|
-
Exception(f
|
|
313
|
+
Exception(f"Unknown A2A task provider type: {provider_type}"),
|
|
291
314
|
)
|
|
292
315
|
)
|
|
293
316
|
|
|
294
317
|
except Exception as error:
|
|
295
318
|
return create_a2a_failure(
|
|
296
|
-
create_a2a_task_storage_error(
|
|
319
|
+
create_a2a_task_storage_error("create-simple-provider", "factory", None, error)
|
|
297
320
|
)
|
|
298
321
|
|
|
322
|
+
|
|
299
323
|
def create_composite_a2a_task_provider(
|
|
300
|
-
primary: A2ATaskProvider,
|
|
301
|
-
fallback: Optional[A2ATaskProvider] = None
|
|
324
|
+
primary: A2ATaskProvider, fallback: Optional[A2ATaskProvider] = None
|
|
302
325
|
) -> A2ATaskProvider:
|
|
303
326
|
"""
|
|
304
327
|
Create a composite A2A task provider that can use multiple backends
|
|
305
328
|
Useful for implementing failover or read/write splitting
|
|
306
|
-
|
|
329
|
+
|
|
307
330
|
Args:
|
|
308
331
|
primary: Primary task provider
|
|
309
332
|
fallback: Optional fallback provider
|
|
310
|
-
|
|
333
|
+
|
|
311
334
|
Returns:
|
|
312
335
|
Composite A2ATaskProvider
|
|
313
336
|
"""
|
|
314
337
|
from .providers.composite import create_composite_a2a_task_provider as create_composite
|
|
338
|
+
|
|
315
339
|
return create_composite(primary, fallback)
|
|
316
340
|
|
|
341
|
+
|
|
317
342
|
def validate_a2a_task_provider_config(config: A2ATaskProviderConfig) -> Dict[str, Any]:
|
|
318
343
|
"""
|
|
319
344
|
Pure function to validate A2A task provider configuration
|
|
320
|
-
|
|
345
|
+
|
|
321
346
|
Args:
|
|
322
347
|
config: Configuration to validate
|
|
323
|
-
|
|
348
|
+
|
|
324
349
|
Returns:
|
|
325
350
|
Dictionary with 'valid' boolean and 'errors' list
|
|
326
351
|
"""
|
|
327
352
|
errors = []
|
|
328
353
|
|
|
329
354
|
if not config.type:
|
|
330
|
-
errors.append(
|
|
331
|
-
elif config.type not in [
|
|
332
|
-
errors.append(f
|
|
355
|
+
errors.append("Provider type is required")
|
|
356
|
+
elif config.type not in ["memory", "redis", "postgres"]:
|
|
357
|
+
errors.append(f"Invalid provider type: {config.type}")
|
|
333
358
|
|
|
334
359
|
if config.max_tasks and config.max_tasks <= 0:
|
|
335
|
-
errors.append(
|
|
360
|
+
errors.append("max_tasks must be greater than 0")
|
|
336
361
|
|
|
337
362
|
if config.cleanup_interval and config.cleanup_interval <= 0:
|
|
338
|
-
errors.append(
|
|
363
|
+
errors.append("cleanup_interval must be greater than 0")
|
|
339
364
|
|
|
340
365
|
if config.default_ttl and config.default_ttl <= 0:
|
|
341
|
-
errors.append(
|
|
366
|
+
errors.append("default_ttl must be greater than 0")
|
|
342
367
|
|
|
343
368
|
# Type-specific validation
|
|
344
|
-
if config.type ==
|
|
345
|
-
if hasattr(config,
|
|
346
|
-
errors.append(
|
|
369
|
+
if config.type == "memory":
|
|
370
|
+
if hasattr(config, "max_tasks_per_context") and config.max_tasks_per_context <= 0:
|
|
371
|
+
errors.append("max_tasks_per_context must be greater than 0")
|
|
347
372
|
|
|
348
|
-
elif config.type ==
|
|
373
|
+
elif config.type == "redis":
|
|
349
374
|
if config.port and (config.port < 1 or config.port > 65535):
|
|
350
|
-
errors.append(
|
|
375
|
+
errors.append("Redis port must be between 1 and 65535")
|
|
351
376
|
if config.db and config.db < 0:
|
|
352
|
-
errors.append(
|
|
377
|
+
errors.append("Redis database index must be non-negative")
|
|
353
378
|
|
|
354
|
-
elif config.type ==
|
|
379
|
+
elif config.type == "postgres":
|
|
355
380
|
if config.port and (config.port < 1 or config.port > 65535):
|
|
356
|
-
errors.append(
|
|
357
|
-
if hasattr(config,
|
|
358
|
-
errors.append(
|
|
359
|
-
|
|
360
|
-
return {
|
|
361
|
-
'valid': len(errors) == 0,
|
|
362
|
-
'errors': errors
|
|
363
|
-
}
|
|
381
|
+
errors.append("PostgreSQL port must be between 1 and 65535")
|
|
382
|
+
if hasattr(config, "max_connections") and config.max_connections <= 0:
|
|
383
|
+
errors.append("max_connections must be greater than 0")
|
|
384
|
+
|
|
385
|
+
return {"valid": len(errors) == 0, "errors": errors}
|
|
@@ -21,16 +21,15 @@ from ..types import (
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def create_composite_a2a_task_provider(
|
|
24
|
-
primary: A2ATaskProvider,
|
|
25
|
-
fallback: Optional[A2ATaskProvider] = None
|
|
24
|
+
primary: A2ATaskProvider, fallback: Optional[A2ATaskProvider] = None
|
|
26
25
|
) -> A2ATaskProvider:
|
|
27
26
|
"""
|
|
28
27
|
Create a composite A2A task provider that can use multiple backends
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
Args:
|
|
31
30
|
primary: Primary task provider
|
|
32
31
|
fallback: Optional fallback provider for failover
|
|
33
|
-
|
|
32
|
+
|
|
34
33
|
Returns:
|
|
35
34
|
Composite A2ATaskProvider
|
|
36
35
|
"""
|
|
@@ -39,9 +38,7 @@ def create_composite_a2a_task_provider(
|
|
|
39
38
|
"""Composite implementation of A2ATaskProvider"""
|
|
40
39
|
|
|
41
40
|
async def store_task(
|
|
42
|
-
self,
|
|
43
|
-
task: A2ATask,
|
|
44
|
-
metadata: Optional[Dict[str, Any]] = None
|
|
41
|
+
self, task: A2ATask, metadata: Optional[Dict[str, Any]] = None
|
|
45
42
|
) -> A2AResult[None]:
|
|
46
43
|
"""Store task in primary, fallback to secondary if primary fails"""
|
|
47
44
|
result = await primary.store_task(task, metadata)
|
|
@@ -57,9 +54,7 @@ def create_composite_a2a_task_provider(
|
|
|
57
54
|
return result
|
|
58
55
|
|
|
59
56
|
async def update_task(
|
|
60
|
-
self,
|
|
61
|
-
task: A2ATask,
|
|
62
|
-
metadata: Optional[Dict[str, Any]] = None
|
|
57
|
+
self, task: A2ATask, metadata: Optional[Dict[str, Any]] = None
|
|
63
58
|
) -> A2AResult[None]:
|
|
64
59
|
"""Update task in primary, fallback to secondary if primary fails"""
|
|
65
60
|
result = await primary.update_task(task, metadata)
|
|
@@ -72,7 +67,7 @@ def create_composite_a2a_task_provider(
|
|
|
72
67
|
task_id: str,
|
|
73
68
|
state: TaskState,
|
|
74
69
|
status_message: Optional[Any] = None,
|
|
75
|
-
timestamp: Optional[str] = None
|
|
70
|
+
timestamp: Optional[str] = None,
|
|
76
71
|
) -> A2AResult[None]:
|
|
77
72
|
"""Update task status in primary, fallback to secondary if primary fails"""
|
|
78
73
|
result = await primary.update_task_status(task_id, state, status_message, timestamp)
|
|
@@ -88,9 +83,7 @@ def create_composite_a2a_task_provider(
|
|
|
88
83
|
return result
|
|
89
84
|
|
|
90
85
|
async def get_tasks_by_context(
|
|
91
|
-
self,
|
|
92
|
-
context_id: str,
|
|
93
|
-
limit: Optional[int] = None
|
|
86
|
+
self, context_id: str, limit: Optional[int] = None
|
|
94
87
|
) -> A2AResult[List[A2ATask]]:
|
|
95
88
|
"""Get tasks by context from primary, fallback to secondary if primary fails"""
|
|
96
89
|
result = await primary.get_tasks_by_context(context_id, limit)
|
|
@@ -144,8 +137,7 @@ def create_composite_a2a_task_provider(
|
|
|
144
137
|
return primary_result
|
|
145
138
|
|
|
146
139
|
async def get_task_stats(
|
|
147
|
-
self,
|
|
148
|
-
context_id: Optional[str] = None
|
|
140
|
+
self, context_id: Optional[str] = None
|
|
149
141
|
) -> A2AResult[Dict[str, Any]]:
|
|
150
142
|
"""Get task stats from primary, fallback to secondary if primary fails"""
|
|
151
143
|
result = await primary.get_task_stats(context_id)
|
|
@@ -162,21 +154,24 @@ def create_composite_a2a_task_provider(
|
|
|
162
154
|
fallback_health = await fallback.health_check()
|
|
163
155
|
|
|
164
156
|
# Determine overall health
|
|
165
|
-
primary_healthy = (
|
|
166
|
-
|
|
167
|
-
primary_health.data.get('healthy', False)
|
|
157
|
+
primary_healthy = isinstance(primary_health.data, dict) and primary_health.data.get(
|
|
158
|
+
"healthy", False
|
|
168
159
|
)
|
|
169
|
-
fallback_healthy = (
|
|
170
|
-
fallback_health
|
|
171
|
-
|
|
160
|
+
fallback_healthy = fallback_health is None or (
|
|
161
|
+
isinstance(fallback_health.data, dict)
|
|
162
|
+
and fallback_health.data.get("healthy", False)
|
|
172
163
|
)
|
|
173
164
|
|
|
174
165
|
overall_healthy = primary_healthy or fallback_healthy
|
|
175
166
|
|
|
176
167
|
health_data = {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
168
|
+
"healthy": overall_healthy,
|
|
169
|
+
"primary": primary_health.data
|
|
170
|
+
if isinstance(primary_health.data, dict)
|
|
171
|
+
else {"healthy": False},
|
|
172
|
+
"fallback": fallback_health.data
|
|
173
|
+
if fallback_health and isinstance(fallback_health.data, dict)
|
|
174
|
+
else None,
|
|
180
175
|
}
|
|
181
176
|
|
|
182
177
|
return create_a2a_success(health_data)
|
|
@@ -195,7 +190,7 @@ def create_composite_a2a_task_provider(
|
|
|
195
190
|
|
|
196
191
|
except Exception as error:
|
|
197
192
|
return create_a2a_failure(
|
|
198
|
-
create_a2a_task_storage_error(
|
|
193
|
+
create_a2a_task_storage_error("close", "composite", None, error)
|
|
199
194
|
)
|
|
200
195
|
|
|
201
196
|
return CompositeA2ATaskProvider()
|