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
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('redis')
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
- 'create-provider',
57
- 'redis',
53
+ "create-provider",
54
+ "redis",
58
55
  None,
59
- Exception('Redis client instance required. Please provide a Redis client in external_clients.redis')
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('postgres')
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
- 'create-provider',
70
- 'postgres',
68
+ "create-provider",
69
+ "postgres",
71
70
  None,
72
- Exception('PostgreSQL client instance required. Please provide a PostgreSQL client in external_clients.postgres')
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
- 'create-provider',
81
- 'unknown',
81
+ "create-provider",
82
+ "unknown",
82
83
  None,
83
- Exception(f'Unknown A2A task provider type: {config.type}')
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('create-provider', 'factory', None, 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('JAF_A2A_MEMORY_TYPE', os.getenv('JAF_MEMORY_TYPE', 'memory')).lower()
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 == 'memory':
113
+ if task_memory_type == "memory":
110
114
  config = A2AInMemoryTaskConfig(
111
- type='memory',
112
- key_prefix=os.getenv('JAF_A2A_KEY_PREFIX', 'jaf:a2a:tasks:'),
113
- default_ttl=int(os.getenv('JAF_A2A_DEFAULT_TTL')) if os.getenv('JAF_A2A_DEFAULT_TTL') else None,
114
- cleanup_interval=int(os.getenv('JAF_A2A_CLEANUP_INTERVAL', '3600')),
115
- max_tasks=int(os.getenv('JAF_A2A_MAX_TASKS', '10000')),
116
- max_tasks_per_context=int(os.getenv('JAF_A2A_MAX_TASKS_PER_CONTEXT', '1000')),
117
- enable_history=os.getenv('JAF_A2A_ENABLE_HISTORY', 'true').lower() != 'false',
118
- enable_artifacts=os.getenv('JAF_A2A_ENABLE_ARTIFACTS', 'true').lower() != 'false'
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 == 'redis':
123
- if not external_clients.get('redis'):
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
- 'create-provider-from-env',
127
- 'redis',
132
+ "create-provider-from-env",
133
+ "redis",
128
134
  None,
129
- Exception('Redis client required for Redis A2A task provider')
135
+ Exception("Redis client required for Redis A2A task provider"),
130
136
  )
131
137
  )
132
138
 
133
139
  config = A2ARedisTaskConfig(
134
- type='redis',
135
- key_prefix=os.getenv('JAF_A2A_KEY_PREFIX', 'jaf:a2a:tasks:'),
136
- default_ttl=int(os.getenv('JAF_A2A_DEFAULT_TTL', os.getenv('JAF_REDIS_TTL'))) if os.getenv('JAF_A2A_DEFAULT_TTL', os.getenv('JAF_REDIS_TTL')) else None,
137
- cleanup_interval=int(os.getenv('JAF_A2A_CLEANUP_INTERVAL', '3600')),
138
- max_tasks=int(os.getenv('JAF_A2A_MAX_TASKS', '10000')),
139
- enable_history=os.getenv('JAF_A2A_ENABLE_HISTORY', 'true').lower() != 'false',
140
- enable_artifacts=os.getenv('JAF_A2A_ENABLE_ARTIFACTS', 'true').lower() != 'false',
141
- host=os.getenv('JAF_A2A_REDIS_HOST', os.getenv('JAF_REDIS_HOST', 'localhost')),
142
- port=int(os.getenv('JAF_A2A_REDIS_PORT', os.getenv('JAF_REDIS_PORT', '6379'))),
143
- password=os.getenv('JAF_A2A_REDIS_PASSWORD', os.getenv('JAF_REDIS_PASSWORD')),
144
- db=int(os.getenv('JAF_A2A_REDIS_DB', os.getenv('JAF_REDIS_DB', '0')))
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['redis'])
154
+ return await create_a2a_redis_task_provider(config, external_clients["redis"])
147
155
 
148
- elif task_memory_type == 'postgres':
149
- if not external_clients.get('postgres'):
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
- 'create-provider-from-env',
153
- 'postgres',
160
+ "create-provider-from-env",
161
+ "postgres",
154
162
  None,
155
- Exception('PostgreSQL client required for PostgreSQL A2A task provider')
163
+ Exception("PostgreSQL client required for PostgreSQL A2A task provider"),
156
164
  )
157
165
  )
158
166
 
159
167
  config = A2APostgresTaskConfig(
160
- type='postgres',
161
- key_prefix=os.getenv('JAF_A2A_KEY_PREFIX', 'jaf:a2a:tasks:'),
162
- default_ttl=int(os.getenv('JAF_A2A_DEFAULT_TTL')) if os.getenv('JAF_A2A_DEFAULT_TTL') else None,
163
- cleanup_interval=int(os.getenv('JAF_A2A_CLEANUP_INTERVAL', '3600')),
164
- max_tasks=int(os.getenv('JAF_A2A_MAX_TASKS', '10000')),
165
- enable_history=os.getenv('JAF_A2A_ENABLE_HISTORY', 'true').lower() != 'false',
166
- enable_artifacts=os.getenv('JAF_A2A_ENABLE_ARTIFACTS', 'true').lower() != 'false',
167
- host=os.getenv('JAF_A2A_POSTGRES_HOST', os.getenv('JAF_POSTGRES_HOST', 'localhost')),
168
- port=int(os.getenv('JAF_A2A_POSTGRES_PORT', os.getenv('JAF_POSTGRES_PORT', '5432'))),
169
- database=os.getenv('JAF_A2A_POSTGRES_DB', os.getenv('JAF_POSTGRES_DB', 'jaf_a2a')),
170
- username=os.getenv('JAF_A2A_POSTGRES_USER', os.getenv('JAF_POSTGRES_USER', 'postgres')),
171
- password=os.getenv('JAF_A2A_POSTGRES_PASSWORD', os.getenv('JAF_POSTGRES_PASSWORD')),
172
- ssl=os.getenv('JAF_A2A_POSTGRES_SSL', os.getenv('JAF_POSTGRES_SSL', 'false')).lower() == 'true',
173
- table_name=os.getenv('JAF_A2A_POSTGRES_TABLE', 'a2a_tasks'),
174
- max_connections=int(os.getenv('JAF_A2A_POSTGRES_MAX_CONNECTIONS', os.getenv('JAF_POSTGRES_MAX_CONNECTIONS', '10')))
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['postgres'])
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
- 'create-provider-from-env',
182
- 'unknown',
205
+ "create-provider-from-env",
206
+ "unknown",
183
207
  None,
184
- Exception(f'Unknown A2A task provider type: {task_memory_type}')
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('create-provider-from-env', 'factory', None, 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 == 'memory':
235
+ if provider_type == "memory":
213
236
  provider_config = A2AInMemoryTaskConfig(
214
- type='memory',
215
- key_prefix='jaf:a2a:tasks:',
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 == 'redis':
249
+ elif provider_type == "redis":
227
250
  if not client:
228
251
  return create_a2a_failure(
229
252
  create_a2a_task_storage_error(
230
- 'create-simple-provider',
231
- 'redis',
253
+ "create-simple-provider",
254
+ "redis",
232
255
  None,
233
- Exception('Redis client required for Redis A2A task provider')
256
+ Exception("Redis client required for Redis A2A task provider"),
234
257
  )
235
258
  )
236
259
 
237
260
  provider_config = A2ARedisTaskConfig(
238
- type='redis',
239
- key_prefix='jaf:a2a:tasks:',
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='localhost',
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 == 'postgres':
276
+ elif provider_type == "postgres":
254
277
  if not client:
255
278
  return create_a2a_failure(
256
279
  create_a2a_task_storage_error(
257
- 'create-simple-provider',
258
- 'postgres',
280
+ "create-simple-provider",
281
+ "postgres",
259
282
  None,
260
- Exception('PostgreSQL client required for PostgreSQL A2A task provider')
283
+ Exception("PostgreSQL client required for PostgreSQL A2A task provider"),
261
284
  )
262
285
  )
263
286
 
264
287
  provider_config = A2APostgresTaskConfig(
265
- type='postgres',
266
- key_prefix='jaf:a2a:tasks:',
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='localhost',
295
+ host="localhost",
273
296
  port=5432,
274
- database='jaf_a2a',
275
- username='postgres',
297
+ database="jaf_a2a",
298
+ username="postgres",
276
299
  password=None,
277
300
  ssl=False,
278
- table_name='a2a_tasks',
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
- 'create-simple-provider',
288
- 'unknown',
310
+ "create-simple-provider",
311
+ "unknown",
289
312
  None,
290
- Exception(f'Unknown A2A task provider type: {provider_type}')
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('create-simple-provider', 'factory', None, 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('Provider type is required')
331
- elif config.type not in ['memory', 'redis', 'postgres']:
332
- errors.append(f'Invalid provider type: {config.type}')
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('max_tasks must be greater than 0')
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('cleanup_interval must be greater than 0')
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('default_ttl must be greater than 0')
366
+ errors.append("default_ttl must be greater than 0")
342
367
 
343
368
  # Type-specific validation
344
- if config.type == 'memory':
345
- if hasattr(config, 'max_tasks_per_context') and config.max_tasks_per_context <= 0:
346
- errors.append('max_tasks_per_context must be greater than 0')
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 == 'redis':
373
+ elif config.type == "redis":
349
374
  if config.port and (config.port < 1 or config.port > 65535):
350
- errors.append('Redis port must be between 1 and 65535')
375
+ errors.append("Redis port must be between 1 and 65535")
351
376
  if config.db and config.db < 0:
352
- errors.append('Redis database index must be non-negative')
377
+ errors.append("Redis database index must be non-negative")
353
378
 
354
- elif config.type == 'postgres':
379
+ elif config.type == "postgres":
355
380
  if config.port and (config.port < 1 or config.port > 65535):
356
- errors.append('PostgreSQL port must be between 1 and 65535')
357
- if hasattr(config, 'max_connections') and config.max_connections <= 0:
358
- errors.append('max_connections must be greater than 0')
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
- isinstance(primary_health.data, dict) and
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 is None or
171
- (isinstance(fallback_health.data, dict) and fallback_health.data.get('healthy', False))
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
- 'healthy': overall_healthy,
178
- 'primary': primary_health.data if isinstance(primary_health.data, dict) else {'healthy': False},
179
- 'fallback': fallback_health.data if fallback_health and isinstance(fallback_health.data, dict) else None
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('close', 'composite', None, error)
193
+ create_a2a_task_storage_error("close", "composite", None, error)
199
194
  )
200
195
 
201
196
  return CompositeA2ATaskProvider()