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/server.py
CHANGED
|
@@ -20,30 +20,19 @@ from .types import A2AAgent
|
|
|
20
20
|
def create_a2a_server_config(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
21
21
|
"""Pure function to create A2A server configuration"""
|
|
22
22
|
host = config.get("host", "localhost")
|
|
23
|
-
capabilities = config.get(
|
|
24
|
-
"
|
|
25
|
-
"pushNotifications": False,
|
|
26
|
-
|
|
27
|
-
})
|
|
23
|
+
capabilities = config.get(
|
|
24
|
+
"capabilities",
|
|
25
|
+
{"streaming": True, "pushNotifications": False, "stateTransitionHistory": True},
|
|
26
|
+
)
|
|
28
27
|
|
|
29
28
|
agent_card = generate_agent_card(
|
|
30
|
-
config["agentCard"],
|
|
31
|
-
config["agents"],
|
|
32
|
-
f"http://{host}:{config['port']}"
|
|
29
|
+
config["agentCard"], config["agents"], f"http://{host}:{config['port']}"
|
|
33
30
|
)
|
|
34
31
|
|
|
35
32
|
# Override the capabilities in the generated agent card
|
|
36
|
-
updated_agent_card = {
|
|
37
|
-
**agent_card,
|
|
38
|
-
"capabilities": capabilities
|
|
39
|
-
}
|
|
33
|
+
updated_agent_card = {**agent_card, "capabilities": capabilities}
|
|
40
34
|
|
|
41
|
-
return {
|
|
42
|
-
**config,
|
|
43
|
-
"host": host,
|
|
44
|
-
"capabilities": capabilities,
|
|
45
|
-
"agentCard": updated_agent_card
|
|
46
|
-
}
|
|
35
|
+
return {**config, "host": host, "capabilities": capabilities, "agentCard": updated_agent_card}
|
|
47
36
|
|
|
48
37
|
|
|
49
38
|
def create_fastapi_app() -> FastAPI:
|
|
@@ -53,7 +42,7 @@ def create_fastapi_app() -> FastAPI:
|
|
|
53
42
|
description="Agent-to-Agent protocol server for JAF",
|
|
54
43
|
version="1.0.0",
|
|
55
44
|
docs_url="/docs",
|
|
56
|
-
redoc_url="/redoc"
|
|
45
|
+
redoc_url="/redoc",
|
|
57
46
|
)
|
|
58
47
|
|
|
59
48
|
# Add CORS middleware
|
|
@@ -93,8 +82,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
93
82
|
"error": {
|
|
94
83
|
"code": -32700, # Parse Error
|
|
95
84
|
"message": "Parse error",
|
|
96
|
-
"data": {"details": str(e)}
|
|
97
|
-
}
|
|
85
|
+
"data": {"details": str(e)},
|
|
86
|
+
},
|
|
98
87
|
}
|
|
99
88
|
except Exception as e:
|
|
100
89
|
return {
|
|
@@ -103,8 +92,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
103
92
|
"error": {
|
|
104
93
|
"code": -32700, # Parse Error
|
|
105
94
|
"message": "Parse error",
|
|
106
|
-
"data": {"details": str(e)}
|
|
107
|
-
}
|
|
95
|
+
"data": {"details": str(e)},
|
|
96
|
+
},
|
|
108
97
|
}
|
|
109
98
|
|
|
110
99
|
# Handle batch requests
|
|
@@ -116,8 +105,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
116
105
|
"error": {
|
|
117
106
|
"code": -32600, # Invalid Request
|
|
118
107
|
"message": "Invalid Request",
|
|
119
|
-
"data": {"details": "Batch request cannot be empty"}
|
|
120
|
-
}
|
|
108
|
+
"data": {"details": "Batch request cannot be empty"},
|
|
109
|
+
},
|
|
121
110
|
}
|
|
122
111
|
|
|
123
112
|
# Process batch requests functionally
|
|
@@ -129,8 +118,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
129
118
|
"error": {
|
|
130
119
|
"code": -32600, # Invalid Request
|
|
131
120
|
"message": "Invalid Request",
|
|
132
|
-
"data": {"details": "Each batch item must be an object"}
|
|
133
|
-
}
|
|
121
|
+
"data": {"details": "Each batch item must be an object"},
|
|
122
|
+
},
|
|
134
123
|
}
|
|
135
124
|
|
|
136
125
|
single_result = await handle_a2a_request_internal(config, single_request)
|
|
@@ -143,8 +132,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
143
132
|
"id": single_request.get("id"),
|
|
144
133
|
"error": {
|
|
145
134
|
"code": -32005, # Content Type Not Supported
|
|
146
|
-
"message": "Streaming not supported in batch requests"
|
|
147
|
-
}
|
|
135
|
+
"message": "Streaming not supported in batch requests",
|
|
136
|
+
},
|
|
148
137
|
}
|
|
149
138
|
|
|
150
139
|
# Process all batch items
|
|
@@ -160,8 +149,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
160
149
|
"error": {
|
|
161
150
|
"code": -32600, # Invalid Request
|
|
162
151
|
"message": "Invalid Request",
|
|
163
|
-
"data": {"details": "Request must be an object"}
|
|
164
|
-
}
|
|
152
|
+
"data": {"details": "Request must be an object"},
|
|
153
|
+
},
|
|
165
154
|
}
|
|
166
155
|
|
|
167
156
|
result = await handle_a2a_request_internal(config, body)
|
|
@@ -171,6 +160,7 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
171
160
|
if method == "message/stream":
|
|
172
161
|
# For streaming, result should be an async generator
|
|
173
162
|
if hasattr(result, "__aiter__"):
|
|
163
|
+
|
|
174
164
|
async def generate_sse():
|
|
175
165
|
async for chunk in result:
|
|
176
166
|
yield f"data: {json.dumps(chunk)}\n\n"
|
|
@@ -178,10 +168,7 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
178
168
|
return StreamingResponse(
|
|
179
169
|
generate_sse(),
|
|
180
170
|
media_type="text/event-stream",
|
|
181
|
-
headers={
|
|
182
|
-
"Cache-Control": "no-cache",
|
|
183
|
-
"Connection": "keep-alive"
|
|
184
|
-
}
|
|
171
|
+
headers={"Cache-Control": "no-cache", "Connection": "keep-alive"},
|
|
185
172
|
)
|
|
186
173
|
else:
|
|
187
174
|
# If it's not iterable, wrap single response
|
|
@@ -191,10 +178,7 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
191
178
|
return StreamingResponse(
|
|
192
179
|
generate_sse(),
|
|
193
180
|
media_type="text/event-stream",
|
|
194
|
-
headers={
|
|
195
|
-
"Cache-Control": "no-cache",
|
|
196
|
-
"Connection": "keep-alive"
|
|
197
|
-
}
|
|
181
|
+
headers={"Cache-Control": "no-cache", "Connection": "keep-alive"},
|
|
198
182
|
)
|
|
199
183
|
|
|
200
184
|
return result
|
|
@@ -202,6 +186,7 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
202
186
|
except Exception as error:
|
|
203
187
|
# Log the error for debugging but don't expose internal details
|
|
204
188
|
import logging
|
|
189
|
+
|
|
205
190
|
logging.error(f"Internal server error: {error!s}")
|
|
206
191
|
|
|
207
192
|
return {
|
|
@@ -210,8 +195,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
210
195
|
"error": {
|
|
211
196
|
"code": -32603, # Internal Error
|
|
212
197
|
"message": "Internal error",
|
|
213
|
-
"data": {"type": "server_error"}
|
|
214
|
-
}
|
|
198
|
+
"data": {"type": "server_error"},
|
|
199
|
+
},
|
|
215
200
|
}
|
|
216
201
|
|
|
217
202
|
# Agent-specific endpoints
|
|
@@ -233,8 +218,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
233
218
|
"error": {
|
|
234
219
|
"code": -32700, # Parse Error
|
|
235
220
|
"message": "Parse error",
|
|
236
|
-
"data": {"details": str(e)}
|
|
237
|
-
}
|
|
221
|
+
"data": {"details": str(e)},
|
|
222
|
+
},
|
|
238
223
|
}
|
|
239
224
|
except Exception as e:
|
|
240
225
|
return {
|
|
@@ -243,8 +228,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
243
228
|
"error": {
|
|
244
229
|
"code": -32700, # Parse Error
|
|
245
230
|
"message": "Parse error",
|
|
246
|
-
"data": {"details": str(e)}
|
|
247
|
-
}
|
|
231
|
+
"data": {"details": str(e)},
|
|
232
|
+
},
|
|
248
233
|
}
|
|
249
234
|
|
|
250
235
|
# Validate request structure
|
|
@@ -255,8 +240,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
255
240
|
"error": {
|
|
256
241
|
"code": -32600, # Invalid Request
|
|
257
242
|
"message": "Invalid Request",
|
|
258
|
-
"data": {"details": "Request must be an object"}
|
|
259
|
-
}
|
|
243
|
+
"data": {"details": "Request must be an object"},
|
|
244
|
+
},
|
|
260
245
|
}
|
|
261
246
|
|
|
262
247
|
result = await handle_a2a_request_for_agent(config, body, agent_name)
|
|
@@ -266,6 +251,7 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
266
251
|
if method == "message/stream":
|
|
267
252
|
# For streaming, result should be an async generator
|
|
268
253
|
if hasattr(result, "__aiter__"):
|
|
254
|
+
|
|
269
255
|
async def generate_sse():
|
|
270
256
|
async for chunk in result:
|
|
271
257
|
yield f"data: {json.dumps(chunk)}\n\n"
|
|
@@ -273,10 +259,7 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
273
259
|
return StreamingResponse(
|
|
274
260
|
generate_sse(),
|
|
275
261
|
media_type="text/event-stream",
|
|
276
|
-
headers={
|
|
277
|
-
"Cache-Control": "no-cache",
|
|
278
|
-
"Connection": "keep-alive"
|
|
279
|
-
}
|
|
262
|
+
headers={"Cache-Control": "no-cache", "Connection": "keep-alive"},
|
|
280
263
|
)
|
|
281
264
|
else:
|
|
282
265
|
# If it's not iterable, wrap single response
|
|
@@ -286,10 +269,7 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
286
269
|
return StreamingResponse(
|
|
287
270
|
generate_sse(),
|
|
288
271
|
media_type="text/event-stream",
|
|
289
|
-
headers={
|
|
290
|
-
"Cache-Control": "no-cache",
|
|
291
|
-
"Connection": "keep-alive"
|
|
292
|
-
}
|
|
272
|
+
headers={"Cache-Control": "no-cache", "Connection": "keep-alive"},
|
|
293
273
|
)
|
|
294
274
|
|
|
295
275
|
return result
|
|
@@ -297,6 +277,7 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
297
277
|
except Exception as error:
|
|
298
278
|
# Log the error for debugging but don't expose internal details
|
|
299
279
|
import logging
|
|
280
|
+
|
|
300
281
|
logging.error(f"Internal server error: {error!s}")
|
|
301
282
|
|
|
302
283
|
return {
|
|
@@ -305,8 +286,8 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
305
286
|
"error": {
|
|
306
287
|
"code": -32603, # Internal Error
|
|
307
288
|
"message": "Internal error",
|
|
308
|
-
"data": {"type": "server_error"}
|
|
309
|
-
}
|
|
289
|
+
"data": {"type": "server_error"},
|
|
290
|
+
},
|
|
310
291
|
}
|
|
311
292
|
|
|
312
293
|
# Agent-specific card endpoint - fix closure issue
|
|
@@ -318,16 +299,16 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
318
299
|
"name": current_agent.name,
|
|
319
300
|
"description": current_agent.description,
|
|
320
301
|
"version": "1.0.0",
|
|
321
|
-
"provider": config["agentCard"].get(
|
|
322
|
-
"organization": "Unknown",
|
|
323
|
-
|
|
324
|
-
})
|
|
302
|
+
"provider": config["agentCard"].get(
|
|
303
|
+
"provider", {"organization": "Unknown", "url": ""}
|
|
304
|
+
),
|
|
325
305
|
},
|
|
326
306
|
{current_agent_name: current_agent},
|
|
327
|
-
f"http://{config.get('host', 'localhost')}:{config['port']}"
|
|
307
|
+
f"http://{config.get('host', 'localhost')}:{config['port']}",
|
|
328
308
|
)
|
|
329
309
|
|
|
330
310
|
return agent_card
|
|
311
|
+
|
|
331
312
|
return get_agent_card_specific
|
|
332
313
|
|
|
333
314
|
# Create the endpoint with proper closure
|
|
@@ -341,7 +322,7 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
341
322
|
"protocol": "A2A",
|
|
342
323
|
"version": "0.3.0",
|
|
343
324
|
"agents": list(config["agents"].keys()),
|
|
344
|
-
"timestamp": None # Would be set by the system
|
|
325
|
+
"timestamp": None, # Would be set by the system
|
|
345
326
|
}
|
|
346
327
|
|
|
347
328
|
# A2A capabilities endpoint
|
|
@@ -353,19 +334,16 @@ def setup_a2a_routes(app: FastAPI, config: Dict[str, Any]) -> None:
|
|
|
353
334
|
"message/stream",
|
|
354
335
|
"tasks/get",
|
|
355
336
|
"tasks/cancel",
|
|
356
|
-
"agent/getAuthenticatedExtendedCard"
|
|
337
|
+
"agent/getAuthenticatedExtendedCard",
|
|
357
338
|
],
|
|
358
339
|
"supportedTransports": ["JSONRPC"],
|
|
359
340
|
"capabilities": config["agentCard"]["capabilities"],
|
|
360
341
|
"inputModes": config["agentCard"]["defaultInputModes"],
|
|
361
|
-
"outputModes": config["agentCard"]["defaultOutputModes"]
|
|
342
|
+
"outputModes": config["agentCard"]["defaultOutputModes"],
|
|
362
343
|
}
|
|
363
344
|
|
|
364
345
|
|
|
365
|
-
async def handle_a2a_request_internal(
|
|
366
|
-
config: Dict[str, Any],
|
|
367
|
-
request: Dict[str, Any]
|
|
368
|
-
) -> Any:
|
|
346
|
+
async def handle_a2a_request_internal(config: Dict[str, Any], request: Dict[str, Any]) -> Any:
|
|
369
347
|
"""Pure function to handle A2A requests"""
|
|
370
348
|
# Validate JSON-RPC request structure first
|
|
371
349
|
if not validate_jsonrpc_request(request):
|
|
@@ -375,15 +353,18 @@ async def handle_a2a_request_internal(
|
|
|
375
353
|
"error": {
|
|
376
354
|
"code": -32600, # Invalid Request
|
|
377
355
|
"message": "Invalid Request",
|
|
378
|
-
"data": {"details": "Missing required JSON-RPC fields"}
|
|
379
|
-
}
|
|
356
|
+
"data": {"details": "Missing required JSON-RPC fields"},
|
|
357
|
+
},
|
|
380
358
|
}
|
|
381
359
|
|
|
382
360
|
# Check if method is supported
|
|
383
361
|
method = request.get("method")
|
|
384
362
|
supported_methods = [
|
|
385
|
-
"message/send",
|
|
386
|
-
"
|
|
363
|
+
"message/send",
|
|
364
|
+
"message/stream",
|
|
365
|
+
"tasks/get",
|
|
366
|
+
"tasks/cancel",
|
|
367
|
+
"agent/getAuthenticatedExtendedCard",
|
|
387
368
|
]
|
|
388
369
|
|
|
389
370
|
if method not in supported_methods:
|
|
@@ -393,8 +374,8 @@ async def handle_a2a_request_internal(
|
|
|
393
374
|
"error": {
|
|
394
375
|
"code": -32601, # Method Not Found
|
|
395
376
|
"message": f"Method '{method}' not found",
|
|
396
|
-
"data": {"supported_methods": supported_methods}
|
|
397
|
-
}
|
|
377
|
+
"data": {"supported_methods": supported_methods},
|
|
378
|
+
},
|
|
398
379
|
}
|
|
399
380
|
|
|
400
381
|
# Use the first available agent by default
|
|
@@ -403,10 +384,7 @@ async def handle_a2a_request_internal(
|
|
|
403
384
|
return {
|
|
404
385
|
"jsonrpc": "2.0",
|
|
405
386
|
"id": request.get("id"),
|
|
406
|
-
"error": {
|
|
407
|
-
"code": -32001,
|
|
408
|
-
"message": "No agents available"
|
|
409
|
-
}
|
|
387
|
+
"error": {"code": -32001, "message": "No agents available"},
|
|
410
388
|
}
|
|
411
389
|
|
|
412
390
|
first_agent = next(iter(agents.values()))
|
|
@@ -414,9 +392,7 @@ async def handle_a2a_request_internal(
|
|
|
414
392
|
|
|
415
393
|
|
|
416
394
|
async def handle_a2a_request_for_agent(
|
|
417
|
-
config: Dict[str, Any],
|
|
418
|
-
request: Dict[str, Any],
|
|
419
|
-
agent_name: str
|
|
395
|
+
config: Dict[str, Any], request: Dict[str, Any], agent_name: str
|
|
420
396
|
) -> Any:
|
|
421
397
|
"""Pure function to handle agent-specific A2A requests"""
|
|
422
398
|
# Validate JSON-RPC request structure first
|
|
@@ -427,15 +403,18 @@ async def handle_a2a_request_for_agent(
|
|
|
427
403
|
"error": {
|
|
428
404
|
"code": -32600, # Invalid Request
|
|
429
405
|
"message": "Invalid Request",
|
|
430
|
-
"data": {"details": "Missing required JSON-RPC fields"}
|
|
431
|
-
}
|
|
406
|
+
"data": {"details": "Missing required JSON-RPC fields"},
|
|
407
|
+
},
|
|
432
408
|
}
|
|
433
409
|
|
|
434
410
|
# Check if method is supported
|
|
435
411
|
method = request.get("method")
|
|
436
412
|
supported_methods = [
|
|
437
|
-
"message/send",
|
|
438
|
-
"
|
|
413
|
+
"message/send",
|
|
414
|
+
"message/stream",
|
|
415
|
+
"tasks/get",
|
|
416
|
+
"tasks/cancel",
|
|
417
|
+
"agent/getAuthenticatedExtendedCard",
|
|
439
418
|
]
|
|
440
419
|
|
|
441
420
|
if method not in supported_methods:
|
|
@@ -445,8 +424,8 @@ async def handle_a2a_request_for_agent(
|
|
|
445
424
|
"error": {
|
|
446
425
|
"code": -32601, # Method Not Found
|
|
447
426
|
"message": f"Method '{method}' not found",
|
|
448
|
-
"data": {"supported_methods": supported_methods}
|
|
449
|
-
}
|
|
427
|
+
"data": {"supported_methods": supported_methods},
|
|
428
|
+
},
|
|
450
429
|
}
|
|
451
430
|
|
|
452
431
|
agent = config["agents"].get(agent_name)
|
|
@@ -454,19 +433,14 @@ async def handle_a2a_request_for_agent(
|
|
|
454
433
|
return {
|
|
455
434
|
"jsonrpc": "2.0",
|
|
456
435
|
"id": request.get("id"),
|
|
457
|
-
"error": {
|
|
458
|
-
"code": -32001,
|
|
459
|
-
"message": f"Agent {agent_name} not found"
|
|
460
|
-
}
|
|
436
|
+
"error": {"code": -32001, "message": f"Agent {agent_name} not found"},
|
|
461
437
|
}
|
|
462
438
|
|
|
463
439
|
return await route_a2a_request_wrapper(config, request, agent)
|
|
464
440
|
|
|
465
441
|
|
|
466
442
|
async def route_a2a_request_wrapper(
|
|
467
|
-
config: Dict[str, Any],
|
|
468
|
-
request: Dict[str, Any],
|
|
469
|
-
agent: A2AAgent
|
|
443
|
+
config: Dict[str, Any], request: Dict[str, Any], agent: A2AAgent
|
|
470
444
|
) -> Any:
|
|
471
445
|
"""Wrapper for route_a2a_request to provide required dependencies"""
|
|
472
446
|
try:
|
|
@@ -482,7 +456,7 @@ async def route_a2a_request_wrapper(
|
|
|
482
456
|
task_storage,
|
|
483
457
|
agent_card,
|
|
484
458
|
execute_a2a_agent,
|
|
485
|
-
execute_a2a_agent_with_streaming
|
|
459
|
+
execute_a2a_agent_with_streaming,
|
|
486
460
|
)
|
|
487
461
|
|
|
488
462
|
# Check if result is a coroutine/awaitable
|
|
@@ -499,6 +473,7 @@ async def route_a2a_request_wrapper(
|
|
|
499
473
|
except Exception as e:
|
|
500
474
|
# Return proper JSON-RPC error
|
|
501
475
|
import logging
|
|
476
|
+
|
|
502
477
|
logging.error(f"Route wrapper error: {e!s}")
|
|
503
478
|
|
|
504
479
|
return {
|
|
@@ -507,8 +482,8 @@ async def route_a2a_request_wrapper(
|
|
|
507
482
|
"error": {
|
|
508
483
|
"code": -32603, # Internal Error
|
|
509
484
|
"message": "Internal error",
|
|
510
|
-
"data": {"type": "route_error"}
|
|
511
|
-
}
|
|
485
|
+
"data": {"type": "route_error"},
|
|
486
|
+
},
|
|
512
487
|
}
|
|
513
488
|
|
|
514
489
|
|
|
@@ -523,7 +498,7 @@ def create_a2a_server(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
523
498
|
app,
|
|
524
499
|
host=server_config.get("host", "localhost"),
|
|
525
500
|
port=server_config["port"],
|
|
526
|
-
log_level="info"
|
|
501
|
+
log_level="info",
|
|
527
502
|
)
|
|
528
503
|
server = uvicorn.Server(uvicorn_config)
|
|
529
504
|
|
|
@@ -561,14 +536,13 @@ def create_a2a_server(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
561
536
|
"agentCard": generate_agent_card(
|
|
562
537
|
{
|
|
563
538
|
**server_config["agentCard"],
|
|
564
|
-
"provider": server_config["agentCard"].get(
|
|
565
|
-
"organization": "Unknown",
|
|
566
|
-
|
|
567
|
-
})
|
|
539
|
+
"provider": server_config["agentCard"].get(
|
|
540
|
+
"provider", {"organization": "Unknown", "url": ""}
|
|
541
|
+
),
|
|
568
542
|
},
|
|
569
543
|
new_agents,
|
|
570
|
-
server_config["agentCard"]["url"].replace("/a2a", "")
|
|
571
|
-
)
|
|
544
|
+
server_config["agentCard"]["url"].replace("/a2a", ""),
|
|
545
|
+
),
|
|
572
546
|
}
|
|
573
547
|
|
|
574
548
|
def remove_agent(name: str) -> Dict[str, Any]:
|
|
@@ -581,14 +555,13 @@ def create_a2a_server(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
581
555
|
"agentCard": generate_agent_card(
|
|
582
556
|
{
|
|
583
557
|
**server_config["agentCard"],
|
|
584
|
-
"provider": server_config["agentCard"].get(
|
|
585
|
-
"organization": "Unknown",
|
|
586
|
-
|
|
587
|
-
})
|
|
558
|
+
"provider": server_config["agentCard"].get(
|
|
559
|
+
"provider", {"organization": "Unknown", "url": ""}
|
|
560
|
+
),
|
|
588
561
|
},
|
|
589
562
|
new_agents,
|
|
590
|
-
server_config["agentCard"]["url"].replace("/a2a", "")
|
|
591
|
-
)
|
|
563
|
+
server_config["agentCard"]["url"].replace("/a2a", ""),
|
|
564
|
+
),
|
|
592
565
|
}
|
|
593
566
|
|
|
594
567
|
return {
|
|
@@ -598,7 +571,7 @@ def create_a2a_server(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
598
571
|
"stop": stop_server,
|
|
599
572
|
"add_agent": add_agent,
|
|
600
573
|
"remove_agent": remove_agent,
|
|
601
|
-
"get_agent_card": lambda: server_config["agentCard"]
|
|
574
|
+
"get_agent_card": lambda: server_config["agentCard"],
|
|
602
575
|
}
|
|
603
576
|
|
|
604
577
|
|
|
@@ -611,6 +584,7 @@ async def start_a2a_server(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
611
584
|
|
|
612
585
|
# Utility functions for configuration
|
|
613
586
|
|
|
587
|
+
|
|
614
588
|
def create_server_config(
|
|
615
589
|
agents: Dict[str, A2AAgent],
|
|
616
590
|
name: str,
|
|
@@ -618,7 +592,7 @@ def create_server_config(
|
|
|
618
592
|
port: int,
|
|
619
593
|
host: str = "localhost",
|
|
620
594
|
version: str = "1.0.0",
|
|
621
|
-
provider: Optional[Dict[str, str]] = None
|
|
595
|
+
provider: Optional[Dict[str, str]] = None,
|
|
622
596
|
) -> Dict[str, Any]:
|
|
623
597
|
"""Utility function to create server configuration"""
|
|
624
598
|
return {
|
|
@@ -627,11 +601,9 @@ def create_server_config(
|
|
|
627
601
|
"name": name,
|
|
628
602
|
"description": description,
|
|
629
603
|
"version": version,
|
|
630
|
-
"provider": provider
|
|
631
|
-
|
|
632
|
-
"url": "https://functional-agent-framework.com"
|
|
633
|
-
}
|
|
604
|
+
"provider": provider
|
|
605
|
+
or {"organization": "JAF Framework", "url": "https://functional-agent-framework.com"},
|
|
634
606
|
},
|
|
635
607
|
"port": port,
|
|
636
|
-
"host": host
|
|
608
|
+
"host": host,
|
|
637
609
|
}
|