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.
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 +361 -280
  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.9.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.9.dist-info/RECORD +0 -96
  89. {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/WHEEL +0 -0
  90. {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/entry_points.txt +0 -0
  91. {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/licenses/LICENSE +0 -0
  92. {jaf_py-2.5.9.dist-info → jaf_py-2.5.11.dist-info}/top_level.txt +0 -0
jaf/a2a/types.py CHANGED
@@ -11,6 +11,7 @@ from pydantic import BaseModel, Field
11
11
 
12
12
  class A2AErrorCodes(Enum):
13
13
  """A2A protocol error codes"""
14
+
14
15
  PARSE_ERROR = -32700
15
16
  INVALID_REQUEST = -32600
16
17
  METHOD_NOT_FOUND = -32601
@@ -26,6 +27,7 @@ class A2AErrorCodes(Enum):
26
27
 
27
28
  class A2AFile(BaseModel):
28
29
  """A2A file representation"""
30
+
29
31
  model_config = {"frozen": True}
30
32
 
31
33
  bytes: Optional[str] = None
@@ -36,6 +38,7 @@ class A2AFile(BaseModel):
36
38
 
37
39
  class A2ATextPart(BaseModel):
38
40
  """A2A text part"""
41
+
39
42
  model_config = {"frozen": True, "extra": "forbid"}
40
43
 
41
44
  kind: Literal["text"]
@@ -45,6 +48,7 @@ class A2ATextPart(BaseModel):
45
48
 
46
49
  class A2ADataPart(BaseModel):
47
50
  """A2A data part"""
51
+
48
52
  model_config = {"frozen": True, "extra": "forbid"}
49
53
 
50
54
  kind: Literal["data"]
@@ -54,6 +58,7 @@ class A2ADataPart(BaseModel):
54
58
 
55
59
  class A2AFilePart(BaseModel):
56
60
  """A2A file part"""
61
+
57
62
  model_config = {"frozen": True}
58
63
 
59
64
  kind: Literal["file"]
@@ -66,6 +71,7 @@ A2APart = Union[A2ATextPart, A2ADataPart, A2AFilePart]
66
71
 
67
72
  class A2AMessage(BaseModel):
68
73
  """Core A2A message type"""
74
+
69
75
  model_config = {"frozen": True}
70
76
 
71
77
  role: Literal["user", "agent"]
@@ -81,6 +87,7 @@ class A2AMessage(BaseModel):
81
87
 
82
88
  class TaskState(str, Enum):
83
89
  """Task state enumeration"""
90
+
84
91
  SUBMITTED = "submitted"
85
92
  WORKING = "working"
86
93
  INPUT_REQUIRED = "input-required"
@@ -94,6 +101,7 @@ class TaskState(str, Enum):
94
101
 
95
102
  class A2ATaskStatus(BaseModel):
96
103
  """Task status information"""
104
+
97
105
  model_config = {"frozen": True}
98
106
 
99
107
  state: TaskState
@@ -103,6 +111,7 @@ class A2ATaskStatus(BaseModel):
103
111
 
104
112
  class A2AArtifact(BaseModel):
105
113
  """A2A artifact type"""
114
+
106
115
  model_config = {"frozen": True}
107
116
 
108
117
  artifact_id: str = Field(alias="artifactId")
@@ -115,6 +124,7 @@ class A2AArtifact(BaseModel):
115
124
 
116
125
  class A2ATask(BaseModel):
117
126
  """A2A task representation"""
127
+
118
128
  model_config = {"frozen": True}
119
129
 
120
130
  id: str
@@ -128,6 +138,7 @@ class A2ATask(BaseModel):
128
138
 
129
139
  class AgentSkill(BaseModel):
130
140
  """Agent skill definition"""
141
+
131
142
  model_config = {"frozen": True}
132
143
 
133
144
  id: str
@@ -141,6 +152,7 @@ class AgentSkill(BaseModel):
141
152
 
142
153
  class AgentCapabilities(BaseModel):
143
154
  """Agent capabilities definition"""
155
+
144
156
  model_config = {"frozen": True}
145
157
 
146
158
  streaming: Optional[bool] = None
@@ -150,6 +162,7 @@ class AgentCapabilities(BaseModel):
150
162
 
151
163
  class AgentProvider(BaseModel):
152
164
  """Agent provider information"""
165
+
153
166
  model_config = {"frozen": True}
154
167
 
155
168
  organization: str
@@ -158,6 +171,7 @@ class AgentProvider(BaseModel):
158
171
 
159
172
  class AgentCard(BaseModel):
160
173
  """A2A agent card for discovery"""
174
+
161
175
  model_config = {"frozen": True}
162
176
 
163
177
  protocol_version: str = Field(alias="protocolVersion")
@@ -177,6 +191,7 @@ class AgentCard(BaseModel):
177
191
 
178
192
  class JSONRPCError(BaseModel):
179
193
  """JSON-RPC error format"""
194
+
180
195
  model_config = {"frozen": True}
181
196
 
182
197
  code: int
@@ -186,6 +201,7 @@ class JSONRPCError(BaseModel):
186
201
 
187
202
  class JSONRPCRequest(BaseModel):
188
203
  """JSON-RPC request format"""
204
+
189
205
  model_config = {"frozen": True}
190
206
 
191
207
  jsonrpc: Literal["2.0"]
@@ -196,6 +212,7 @@ class JSONRPCRequest(BaseModel):
196
212
 
197
213
  class JSONRPCSuccessResponse(BaseModel):
198
214
  """JSON-RPC success response"""
215
+
199
216
  model_config = {"frozen": True}
200
217
 
201
218
  jsonrpc: Literal["2.0"]
@@ -205,6 +222,7 @@ class JSONRPCSuccessResponse(BaseModel):
205
222
 
206
223
  class JSONRPCErrorResponse(BaseModel):
207
224
  """JSON-RPC error response"""
225
+
208
226
  model_config = {"frozen": True}
209
227
 
210
228
  jsonrpc: Literal["2.0"]
@@ -217,6 +235,7 @@ JSONRPCResponse = Union[JSONRPCSuccessResponse, JSONRPCErrorResponse]
217
235
 
218
236
  class MessageSendConfiguration(BaseModel):
219
237
  """Configuration for message sending"""
238
+
220
239
  model_config = {"frozen": True}
221
240
 
222
241
  model: Optional[str] = None
@@ -228,6 +247,7 @@ class MessageSendConfiguration(BaseModel):
228
247
 
229
248
  class SendMessageParams(BaseModel):
230
249
  """Parameters for message/send method"""
250
+
231
251
  model_config = {"frozen": True}
232
252
 
233
253
  message: A2AMessage
@@ -237,6 +257,7 @@ class SendMessageParams(BaseModel):
237
257
 
238
258
  class SendMessageRequest(JSONRPCRequest):
239
259
  """A2A message/send request"""
260
+
240
261
  model_config = {"frozen": True}
241
262
 
242
263
  method: Literal["message/send"]
@@ -245,6 +266,7 @@ class SendMessageRequest(JSONRPCRequest):
245
266
 
246
267
  class SendStreamingMessageRequest(JSONRPCRequest):
247
268
  """A2A message/stream request"""
269
+
248
270
  model_config = {"frozen": True}
249
271
 
250
272
  method: Literal["message/stream"]
@@ -253,6 +275,7 @@ class SendStreamingMessageRequest(JSONRPCRequest):
253
275
 
254
276
  class GetTaskParams(BaseModel):
255
277
  """Parameters for tasks/get method"""
278
+
256
279
  model_config = {"frozen": True}
257
280
 
258
281
  id: str
@@ -262,6 +285,7 @@ class GetTaskParams(BaseModel):
262
285
 
263
286
  class GetTaskRequest(JSONRPCRequest):
264
287
  """A2A tasks/get request"""
288
+
265
289
  model_config = {"frozen": True}
266
290
 
267
291
  method: Literal["tasks/get"]
@@ -270,6 +294,7 @@ class GetTaskRequest(JSONRPCRequest):
270
294
 
271
295
  class A2AError(BaseModel):
272
296
  """A2A error format"""
297
+
273
298
  model_config = {"frozen": True}
274
299
 
275
300
  code: int
@@ -279,6 +304,7 @@ class A2AError(BaseModel):
279
304
 
280
305
  class StreamEvent(BaseModel):
281
306
  """Base stream event"""
307
+
282
308
  model_config = {"frozen": True}
283
309
 
284
310
  isTaskComplete: bool
@@ -290,6 +316,7 @@ class StreamEvent(BaseModel):
290
316
 
291
317
  class A2AStatusUpdateEvent(BaseModel):
292
318
  """A2A status update stream event"""
319
+
293
320
  model_config = {"frozen": True}
294
321
 
295
322
  kind: Literal["status-update"]
@@ -301,6 +328,7 @@ class A2AStatusUpdateEvent(BaseModel):
301
328
 
302
329
  class A2AArtifactUpdateEvent(BaseModel):
303
330
  """A2A artifact update stream event"""
331
+
304
332
  model_config = {"frozen": True}
305
333
 
306
334
  kind: Literal["artifact-update"]
@@ -313,6 +341,7 @@ class A2AArtifactUpdateEvent(BaseModel):
313
341
 
314
342
  class A2AMessageEvent(BaseModel):
315
343
  """A2A message stream event"""
344
+
316
345
  model_config = {"frozen": True}
317
346
 
318
347
  kind: Literal["message"]
@@ -324,6 +353,7 @@ A2AStreamEvent = Union[A2AStatusUpdateEvent, A2AArtifactUpdateEvent, A2AMessageE
324
353
 
325
354
  class AgentState(BaseModel):
326
355
  """Agent state representation"""
356
+
327
357
  model_config = {"frozen": True}
328
358
 
329
359
  sessionId: str
@@ -335,6 +365,7 @@ class AgentState(BaseModel):
335
365
 
336
366
  class ToolContext(BaseModel):
337
367
  """Tool execution context"""
368
+
338
369
  model_config = {"frozen": True}
339
370
 
340
371
  actions: Dict[str, bool]
@@ -343,6 +374,7 @@ class ToolContext(BaseModel):
343
374
 
344
375
  class A2AToolResult(BaseModel):
345
376
  """A2A tool execution result"""
377
+
346
378
  model_config = {"frozen": True}
347
379
 
348
380
  status: str
@@ -354,6 +386,7 @@ class A2AToolResult(BaseModel):
354
386
 
355
387
  class A2AAgentTool(BaseModel):
356
388
  """A2A agent tool definition"""
389
+
357
390
  model_config = {"frozen": True}
358
391
 
359
392
  name: str
@@ -364,6 +397,7 @@ class A2AAgentTool(BaseModel):
364
397
 
365
398
  class A2AAgent(BaseModel):
366
399
  """A2A agent definition"""
400
+
367
401
  model_config = {"frozen": True}
368
402
 
369
403
  name: str
@@ -375,6 +409,7 @@ class A2AAgent(BaseModel):
375
409
 
376
410
  class A2AServerConfig(BaseModel):
377
411
  """A2A server configuration"""
412
+
378
413
  model_config = {"frozen": True}
379
414
 
380
415
  agents: Dict[str, A2AAgent]
@@ -386,6 +421,7 @@ class A2AServerConfig(BaseModel):
386
421
 
387
422
  class A2AClientConfig(BaseModel):
388
423
  """A2A client configuration"""
424
+
389
425
  model_config = {"frozen": True}
390
426
 
391
427
  base_url: str = Field(alias="baseUrl")
@@ -394,6 +430,7 @@ class A2AClientConfig(BaseModel):
394
430
 
395
431
  class A2AClientState(BaseModel):
396
432
  """A2A client state"""
433
+
397
434
  model_config = {"frozen": True}
398
435
 
399
436
  config: A2AClientConfig
@@ -402,6 +439,7 @@ class A2AClientState(BaseModel):
402
439
 
403
440
  # Factory functions for creating instances
404
441
 
442
+
405
443
  def create_a2a_text_part(text: str, metadata: Optional[Dict[str, Any]] = None) -> A2ATextPart:
406
444
  """Create an A2A text part"""
407
445
  if metadata is None:
@@ -409,7 +447,9 @@ def create_a2a_text_part(text: str, metadata: Optional[Dict[str, Any]] = None) -
409
447
  return A2ATextPart(kind="text", text=text, metadata=metadata)
410
448
 
411
449
 
412
- def create_a2a_data_part(data: Dict[str, Any], metadata: Optional[Dict[str, Any]] = None) -> A2ADataPart:
450
+ def create_a2a_data_part(
451
+ data: Dict[str, Any], metadata: Optional[Dict[str, Any]] = None
452
+ ) -> A2ADataPart:
413
453
  """Create an A2A data part"""
414
454
  if metadata is None:
415
455
  return A2ADataPart(kind="data", data=data)
@@ -421,12 +461,16 @@ def create_a2a_file_part(file: A2AFile, metadata: Optional[Dict[str, Any]] = Non
421
461
  return A2AFilePart(kind="file", file=file, metadata=metadata)
422
462
 
423
463
 
424
- def create_jsonrpc_success_response(id: Union[str, int, None], result: Any) -> JSONRPCSuccessResponse:
464
+ def create_jsonrpc_success_response(
465
+ id: Union[str, int, None], result: Any
466
+ ) -> JSONRPCSuccessResponse:
425
467
  """Create a JSON-RPC success response"""
426
468
  return JSONRPCSuccessResponse(jsonrpc="2.0", id=id, result=result)
427
469
 
428
470
 
429
- def create_jsonrpc_error_response(id: Union[str, int, None], error: Union[JSONRPCError, A2AError]) -> JSONRPCErrorResponse:
471
+ def create_jsonrpc_error_response(
472
+ id: Union[str, int, None], error: Union[JSONRPCError, A2AError]
473
+ ) -> JSONRPCErrorResponse:
430
474
  """Create a JSON-RPC error response"""
431
475
  if isinstance(error, A2AError):
432
476
  jsonrpc_error = JSONRPCError(code=error.code, message=error.message, data=error.data)
@@ -444,7 +488,7 @@ def create_a2a_message(
444
488
  role: Literal["user", "agent"],
445
489
  parts: List[A2APart],
446
490
  context_id: str,
447
- task_id: Optional[str] = None
491
+ task_id: Optional[str] = None,
448
492
  ) -> A2AMessage:
449
493
  """Create an A2A message"""
450
494
  import time
@@ -456,14 +500,11 @@ def create_a2a_message(
456
500
  messageId=f"msg_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}",
457
501
  contextId=context_id,
458
502
  taskId=task_id,
459
- kind="message"
503
+ kind="message",
460
504
  )
461
505
 
462
506
 
463
- def create_a2a_task(
464
- initial_message: A2AMessage,
465
- context_id: str
466
- ) -> A2ATask:
507
+ def create_a2a_task(initial_message: A2AMessage, context_id: str) -> A2ATask:
467
508
  """Create an A2A task"""
468
509
  import time
469
510
  import uuid
@@ -472,20 +513,14 @@ def create_a2a_task(
472
513
  return A2ATask(
473
514
  id=f"task_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}",
474
515
  contextId=context_id,
475
- status=A2ATaskStatus(
476
- state="submitted",
477
- timestamp=datetime.now().isoformat()
478
- ),
516
+ status=A2ATaskStatus(state="submitted", timestamp=datetime.now().isoformat()),
479
517
  history=[initial_message],
480
518
  artifacts=[],
481
- kind="task"
519
+ kind="task",
482
520
  )
483
521
 
484
522
 
485
- def create_a2a_artifact(
486
- name: str,
487
- parts: List[A2APart]
488
- ) -> A2AArtifact:
523
+ def create_a2a_artifact(name: str, parts: List[A2APart]) -> A2AArtifact:
489
524
  """Create an A2A artifact"""
490
525
  import time
491
526
  import uuid
@@ -495,29 +530,23 @@ def create_a2a_artifact(
495
530
  artifactId=f"artifact_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}",
496
531
  name=name,
497
532
  parts=parts,
498
- timestamp=datetime.now().isoformat()
533
+ timestamp=datetime.now().isoformat(),
499
534
  )
500
535
 
501
536
 
502
537
  def create_a2a_agent_tool(
503
- name: str,
504
- description: str,
505
- parameters: Dict[str, Any],
506
- execute_func: Any
538
+ name: str, description: str, parameters: Dict[str, Any], execute_func: Any
507
539
  ) -> A2AAgentTool:
508
540
  """Create an A2A agent tool"""
509
541
  return A2AAgentTool(
510
- name=name,
511
- description=description,
512
- parameters=parameters,
513
- execute=execute_func
542
+ name=name, description=description, parameters=parameters, execute=execute_func
514
543
  )
515
544
 
516
545
 
517
546
  def create_jsonrpc_request(
518
547
  method: str,
519
548
  params: Optional[Dict[str, Any]] = None,
520
- request_id: Optional[Union[str, int]] = None
549
+ request_id: Optional[Union[str, int]] = None,
521
550
  ) -> JSONRPCRequest:
522
551
  """Create a JSON-RPC request"""
523
552
  import time
@@ -526,9 +555,4 @@ def create_jsonrpc_request(
526
555
  if request_id is None:
527
556
  request_id = f"req_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
528
557
 
529
- return JSONRPCRequest(
530
- jsonrpc="2.0",
531
- method=method,
532
- params=params,
533
- id=request_id
534
- )
558
+ return JSONRPCRequest(jsonrpc="2.0", method=method, params=params, id=request_id)
jaf/cli.py CHANGED
@@ -30,27 +30,21 @@ def create_default_config() -> ServerConfig:
30
30
  tools=None,
31
31
  output_codec=None,
32
32
  handoffs=None,
33
- model_config=None
33
+ model_config=None,
34
34
  )
35
35
 
36
36
  # Create model provider
37
37
  model_provider = make_litellm_provider(
38
- base_url="https://api.openai.com/v1",
39
- api_key="your-api-key-here"
38
+ base_url="https://api.openai.com/v1", api_key="your-api-key-here"
40
39
  )
41
40
 
42
41
  # Create run config
43
42
  run_config = RunConfig(
44
- agent_registry={"echo": echo_agent},
45
- model_provider=model_provider,
46
- max_turns=10
43
+ agent_registry={"echo": echo_agent}, model_provider=model_provider, max_turns=10
47
44
  )
48
45
 
49
46
  return ServerConfig(
50
- host="0.0.0.0",
51
- port=8000,
52
- agent_registry={"echo": echo_agent},
53
- run_config=run_config
47
+ host="0.0.0.0", port=8000, agent_registry={"echo": echo_agent}, run_config=run_config
54
48
  )
55
49
 
56
50
 
@@ -63,17 +57,15 @@ async def run_server_command(args: argparse.Namespace) -> None:
63
57
 
64
58
  # Override with command line arguments
65
59
  if args.host:
66
- config = config.__class__(
67
- **{**config.__dict__, 'host': args.host}
68
- )
60
+ config = config.__class__(**{**config.__dict__, "host": args.host})
69
61
 
70
62
  if args.port:
71
- config = config.__class__(
72
- **{**config.__dict__, 'port': args.port}
73
- )
63
+ config = config.__class__(**{**config.__dict__, "port": args.port})
74
64
 
75
65
  print(f"📍 Server will run on {config.host}:{config.port}")
76
- print("💡 This is a default configuration. For production use, provide your own agents and model provider.")
66
+ print(
67
+ "💡 This is a default configuration. For production use, provide your own agents and model provider."
68
+ )
77
69
  print("📚 See documentation for how to create custom agents and configurations.")
78
70
 
79
71
  try:
@@ -88,8 +80,7 @@ async def run_server_command(args: argparse.Namespace) -> None:
88
80
  def create_parser() -> argparse.ArgumentParser:
89
81
  """Create the argument parser."""
90
82
  parser = argparse.ArgumentParser(
91
- prog="jaf",
92
- description="JAF (Juspay Agent Framework) - Command Line Interface"
83
+ prog="jaf", description="JAF (Juspay Agent Framework) - Command Line Interface"
93
84
  )
94
85
 
95
86
  subparsers = parser.add_subparsers(dest="command", help="Available commands")
@@ -97,21 +88,13 @@ def create_parser() -> argparse.ArgumentParser:
97
88
  # Server command
98
89
  server_parser = subparsers.add_parser("server", help="Run the JAF server")
99
90
  server_parser.add_argument(
100
- "--host",
101
- type=str,
102
- default="0.0.0.0",
103
- help="Host to bind the server to (default: 0.0.0.0)"
91
+ "--host", type=str, default="0.0.0.0", help="Host to bind the server to (default: 0.0.0.0)"
104
92
  )
105
93
  server_parser.add_argument(
106
- "--port",
107
- type=int,
108
- default=8000,
109
- help="Port to bind the server to (default: 8000)"
94
+ "--port", type=int, default=8000, help="Port to bind the server to (default: 8000)"
110
95
  )
111
96
  server_parser.add_argument(
112
- "--config",
113
- type=str,
114
- help="Path to configuration file (not implemented yet)"
97
+ "--config", type=str, help="Path to configuration file (not implemented yet)"
115
98
  )
116
99
 
117
100
  # Version command
@@ -119,11 +102,7 @@ def create_parser() -> argparse.ArgumentParser:
119
102
 
120
103
  # Init command for creating project templates
121
104
  init_parser = subparsers.add_parser("init", help="Initialize a new JAF project")
122
- init_parser.add_argument(
123
- "project_name",
124
- type=str,
125
- help="Name of the project to create"
126
- )
105
+ init_parser.add_argument("project_name", type=str, help="Name of the project to create")
127
106
 
128
107
  return parser
129
108
 
@@ -132,6 +111,7 @@ def show_version() -> None:
132
111
  """Show version information."""
133
112
  try:
134
113
  from . import __version__
114
+
135
115
  print(f"JAF (Juspay Agent Framework) version {__version__}")
136
116
  except ImportError:
137
117
  print("JAF (Juspay Agent Framework) version unknown")
@@ -216,13 +196,13 @@ if __name__ == "__main__":
216
196
  (project_path / "main.py").write_text(main_py_content)
217
197
 
218
198
  # Create requirements.txt
219
- requirements_content = '''jaf-py>=2.0.0
199
+ requirements_content = """jaf-py>=2.0.0
220
200
  python-dotenv>=1.0.0
221
- '''
201
+ """
222
202
  (project_path / "requirements.txt").write_text(requirements_content)
223
203
 
224
204
  # Create .env.example
225
- env_example_content = '''# OpenAI API Configuration
205
+ env_example_content = """# OpenAI API Configuration
226
206
  OPENAI_API_KEY=your_openai_api_key_here
227
207
 
228
208
  # Server Configuration
@@ -233,11 +213,11 @@ JAF_PORT=8000
233
213
  JAF_MODEL=gpt-4o
234
214
  JAF_TEMPERATURE=0.7
235
215
  JAF_MAX_TOKENS=1000
236
- '''
216
+ """
237
217
  (project_path / ".env.example").write_text(env_example_content)
238
218
 
239
219
  # Create README.md
240
- readme_content = f'''# {project_name}
220
+ readme_content = f"""# {project_name}
241
221
 
242
222
  A JAF (Juspay Agent Framework) project.
243
223
 
@@ -282,7 +262,7 @@ Edit `main.py` to:
282
262
  - Configure model providers
283
263
  - Add tools and capabilities
284
264
  - Customize server settings
285
- '''
265
+ """
286
266
 
287
267
  (project_path / "README.md").write_text(readme_content)
288
268
 
jaf/core/__init__.py CHANGED
@@ -22,7 +22,13 @@ from .parallel_agents import (
22
22
  create_domain_experts_tool,
23
23
  )
24
24
  from .proxy import ProxyConfig, ProxyAuth, create_proxy_config, get_default_proxy_config
25
- from .handoff import handoff_tool, handoff, create_handoff_tool, is_handoff_request, extract_handoff_target
25
+ from .handoff import (
26
+ handoff_tool,
27
+ handoff,
28
+ create_handoff_tool,
29
+ is_handoff_request,
30
+ extract_handoff_target,
31
+ )
26
32
 
27
33
  __all__ = [
28
34
  "Agent",