xpander-sdk 2.0.158__tar.gz → 2.0.159__tar.gz

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 (100) hide show
  1. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/PKG-INFO +1 -1
  2. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/setup.py +1 -1
  3. xpander_sdk-2.0.159/src/xpander_sdk/models/deep_planning.py +15 -0
  4. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/models/events.py +3 -0
  5. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/backend/frameworks/agno.py +184 -0
  6. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/events_module.py +20 -0
  7. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tasks/sub_modules/task.py +72 -3
  8. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk.egg-info/PKG-INFO +1 -1
  9. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk.egg-info/SOURCES.txt +1 -0
  10. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/LICENSE +0 -0
  11. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/README.md +0 -0
  12. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/pyproject.toml +0 -0
  13. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/setup.cfg +0 -0
  14. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/__init__.py +0 -0
  15. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/consts/__init__.py +0 -0
  16. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/consts/api_routes.py +0 -0
  17. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/core/__init__.py +0 -0
  18. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/core/module_base.py +0 -0
  19. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/core/state.py +0 -0
  20. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/core/xpander_api_client.py +0 -0
  21. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/exceptions/__init__.py +0 -0
  22. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/exceptions/module_exception.py +0 -0
  23. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/models/__init__.py +0 -0
  24. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/models/activity.py +0 -0
  25. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/models/configuration.py +0 -0
  26. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/models/frameworks.py +0 -0
  27. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/models/shared.py +0 -0
  28. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/models/user.py +0 -0
  29. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/__init__.py +0 -0
  30. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/__init__.py +0 -0
  31. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/agents_module.py +0 -0
  32. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/models/__init__.py +0 -0
  33. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/models/agent.py +0 -0
  34. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/models/agent_list.py +0 -0
  35. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/models/knowledge_bases.py +0 -0
  36. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/sub_modules/__init__.py +0 -0
  37. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/sub_modules/agent.py +0 -0
  38. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/utils/__init__.py +0 -0
  39. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/agents/utils/generic.py +0 -0
  40. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/backend/__init__.py +0 -0
  41. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/backend/backend_module.py +0 -0
  42. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/backend/frameworks/__init__.py +0 -0
  43. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/backend/frameworks/dispatch.py +0 -0
  44. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/backend/utils/__init__.py +0 -0
  45. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/backend/utils/mcp_oauth.py +0 -0
  46. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/__init__.py +0 -0
  47. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/decorators/__init__.py +0 -0
  48. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/decorators/on_boot.py +0 -0
  49. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/decorators/on_shutdown.py +0 -0
  50. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/decorators/on_task.py +0 -0
  51. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/models/__init__.py +0 -0
  52. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/models/deployments.py +0 -0
  53. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/models/events.py +0 -0
  54. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/utils/__init__.py +0 -0
  55. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/utils/generic.py +0 -0
  56. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/events/utils/git_init.py +0 -0
  57. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/knowledge_bases/__init__.py +0 -0
  58. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/knowledge_bases/knowledge_bases_module.py +0 -0
  59. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/knowledge_bases/models/__init__.py +0 -0
  60. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/knowledge_bases/models/knowledge_bases.py +0 -0
  61. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/knowledge_bases/sub_modules/__init__.py +0 -0
  62. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base.py +0 -0
  63. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base_document_item.py +0 -0
  64. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/knowledge_bases/utils/__init__.py +0 -0
  65. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tasks/__init__.py +0 -0
  66. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tasks/models/__init__.py +0 -0
  67. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tasks/models/task.py +0 -0
  68. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tasks/models/tasks_list.py +0 -0
  69. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tasks/sub_modules/__init__.py +0 -0
  70. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tasks/tasks_module.py +0 -0
  71. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tasks/utils/__init__.py +0 -0
  72. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tasks/utils/files.py +0 -0
  73. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/__init__.py +0 -0
  74. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/decorators/__init__.py +0 -0
  75. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/decorators/register_tool.py +0 -0
  76. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/models/__init__.py +0 -0
  77. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/models/mcp.py +0 -0
  78. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/models/tool_invocation_result.py +0 -0
  79. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/sub_modules/__init__.py +0 -0
  80. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/sub_modules/tool.py +0 -0
  81. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/tools_repository_module.py +0 -0
  82. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/utils/__init__.py +0 -0
  83. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/utils/generic.py +0 -0
  84. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/utils/local_tools.py +0 -0
  85. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/modules/tools_repository/utils/schemas.py +0 -0
  86. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/utils/__init__.py +0 -0
  87. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/utils/env.py +0 -0
  88. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/utils/event_loop.py +0 -0
  89. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk/utils/tools.py +0 -0
  90. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk.egg-info/dependency_links.txt +0 -0
  91. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk.egg-info/requires.txt +0 -0
  92. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/src/xpander_sdk.egg-info/top_level.txt +0 -0
  93. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/tests/test_agents_module.py +0 -0
  94. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/tests/test_api_client.py +0 -0
  95. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/tests/test_backend_module.py +0 -0
  96. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/tests/test_boot_shutdown_handlers.py +0 -0
  97. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/tests/test_configuration.py +0 -0
  98. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/tests/test_knowledge_bases_module.py +0 -0
  99. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/tests/test_tasks_module.py +0 -0
  100. {xpander_sdk-2.0.158 → xpander_sdk-2.0.159}/tests/test_tools_repository.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xpander-sdk
3
- Version: 2.0.158
3
+ Version: 2.0.159
4
4
  Summary: xpander.ai Backend-as-a-service for AI Agents - SDK
5
5
  Home-page: https://www.xpander.ai
6
6
  Author: xpanderAI
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="xpander-sdk",
8
- version="2.0.158",
8
+ version="2.0.159",
9
9
  author="xpanderAI",
10
10
  author_email="dev@xpander.ai",
11
11
  description="xpander.ai Backend-as-a-service for AI Agents - SDK",
@@ -0,0 +1,15 @@
1
+ from typing import List, Optional
2
+ from .shared import XPanderSharedModel
3
+
4
+ class DeepPlanningItem(XPanderSharedModel):
5
+ id: str
6
+ title: str
7
+ completed: Optional[bool] = False
8
+
9
+ class DeepPlanning(XPanderSharedModel):
10
+ enabled: Optional[bool] = False
11
+ tasks: Optional[List[DeepPlanningItem]] = []
12
+
13
+ class PlanFollowingStatus(XPanderSharedModel):
14
+ can_finish: bool
15
+ uncompleted_tasks: Optional[List[DeepPlanningItem]] = []
@@ -68,3 +68,6 @@ class TaskUpdateEventType(str, Enum):
68
68
  # reasoning
69
69
  Think = "think"
70
70
  Analyze = "analyze"
71
+
72
+ # deep planning
73
+ PlanUpdated = "plan_updated"
@@ -213,8 +213,192 @@ async def build_agent_args(
213
213
  if args["model"] and args["model"].id and args["model"].id.startswith("gpt-5"):
214
214
  del args["model"].temperature
215
215
 
216
+ # configure deep planning guidance
217
+ _configure_deep_planning_guidance(args=args, agent=xpander_agent, task=task)
216
218
  return args
217
219
 
220
+ def _configure_deep_planning_guidance(args: Dict[str, Any], agent: Agent, task: Optional[Task]) -> None:
221
+ if args and agent and task and agent.deep_planning and task.deep_planning.enabled == True:
222
+ # add instructions guidance
223
+ if not "instructions" in args:
224
+ args["instructions"] = ""
225
+
226
+ args["instructions"] += """\n
227
+ <important_planning_instructions>
228
+ ## **Deep Planning Tools - Essential for Multi-Step Tasks**
229
+
230
+ When handling complex tasks with multiple steps, use these planning tools to track progress systematically.
231
+
232
+ ### **Core Workflow**
233
+ 1. **CREATE** plan at the start (`xpcreate-agent-plan`)
234
+ 2. **CHECK** plan before each action (`xpget-agent-plan`)
235
+ 3. **COMPLETE** task immediately after finishing it (`xpcomplete-agent-plan-item`)
236
+ 4. Repeat steps 2-3 until all tasks are done
237
+
238
+ ---
239
+
240
+ ### **Tool Reference**
241
+
242
+ #### **1. xpcreate-agent-plan** - Create Initial Plan
243
+ **When to use**: At the very start of any multi-step task, ONLY if no plan exists yet
244
+
245
+ **How to use**:
246
+ - Pass an array of task objects (NOT strings)
247
+ - Each task must have a `title` field with short, explanatory description
248
+ - Example format:
249
+ ```json
250
+ {
251
+ "body_params": {
252
+ "tasks": [
253
+ {"title": "Research API requirements"},
254
+ {"title": "Design data schema"},
255
+ {"title": "Implement endpoints"},
256
+ {"title": "Write tests"},
257
+ {"title": "Deploy to staging"}
258
+ ]
259
+ }
260
+ }
261
+ ```
262
+ **Important**:
263
+ - Include ALL steps needed from start to finish
264
+ - Each title should be clear and actionable (3-6 words)
265
+ - Do NOT pass plain strings like `["Task 1", "Task 2"]` - must be objects!
266
+
267
+ ---
268
+
269
+ #### **2. xpget-agent-plan** - View Current Plan
270
+ **When to use**: Before deciding what to do next; to check progress
271
+
272
+ **Returns**:
273
+ - All tasks with their IDs, titles, and completion status
274
+ - Use this to know what's done and what remains
275
+
276
+ **No parameters needed** - just call the tool
277
+
278
+ ---
279
+
280
+ #### **3. xpcomplete-agent-plan-item** - Mark Task Complete
281
+ **When to use**: **IMMEDIATELY** after finishing each task (NOT before!)
282
+
283
+ **How to use**:
284
+ ```json
285
+ {
286
+ "body_params": {
287
+ "id": "task-uuid-from-plan"
288
+ }
289
+ }
290
+ ```
291
+ **CRITICAL**:
292
+ - Only mark complete AFTER work is actually done
293
+ - This is MANDATORY for progress tracking
294
+ - Get the task ID from `xpget-agent-plan` results
295
+
296
+ ---
297
+
298
+ #### **4. xpadd-new-agent-plan-item** - Add Discovered Task
299
+ **When to use**: When you discover additional work needed during execution
300
+
301
+ **How to use**:
302
+ ```json
303
+ {
304
+ "body_params": {
305
+ "title": "Validate input schemas",
306
+ "completed": false
307
+ }
308
+ }
309
+ ```
310
+ ---
311
+
312
+ #### **5. xpupdate-agent-plan-item** - Modify Existing Task
313
+ **When to use**: When task details change or need clarification
314
+
315
+ **How to use**:
316
+ ```json
317
+ {
318
+ "body_params": {
319
+ "id": "task-uuid",
320
+ "title": "Updated description",
321
+ "completed": true
322
+ }
323
+ }
324
+ ```
325
+ (All fields optional except `id`)
326
+
327
+ ---
328
+
329
+ #### **6. xpdelete-agent-plan-item** - Remove Task
330
+ **When to use**: When a task becomes unnecessary or redundant
331
+
332
+ **How to use**:
333
+ ```json
334
+ {
335
+ "body_params": {
336
+ "id": "task-uuid"
337
+ }
338
+ }
339
+ ```
340
+ ---
341
+
342
+ ### **Best Practices**
343
+
344
+ ✅ **DO:**
345
+ - Create comprehensive plans with ALL necessary steps
346
+ - Use descriptive, actionable task titles
347
+ - Check plan before each action to stay oriented
348
+ - Mark tasks complete immediately after finishing them
349
+ - Call plan tools **sequentially** (one at a time, never in parallel)
350
+
351
+ ❌ **DON'T:**
352
+ - Mark tasks complete before they're actually done
353
+ - Pass plain string arrays - must be objects with `title` field
354
+ - Call plan tools in parallel with each other
355
+ - Skip checking the plan between major steps
356
+ - Forget to mark completed tasks
357
+
358
+ ---
359
+
360
+ ### **Example Complete Workflow**
361
+
362
+ ```
363
+ 1. User: "Build a REST API for user management"
364
+
365
+ 2. Call: xpcreate-agent-plan
366
+ tasks: [
367
+ {"title": "Design user schema"},
368
+ {"title": "Create database migration"},
369
+ {"title": "Implement CRUD endpoints"},
370
+ {"title": "Add authentication"},
371
+ {"title": "Write integration tests"}
372
+ ]
373
+
374
+ 3. Call: xpget-agent-plan
375
+ → See: Task 1 (ID: abc-123) - Design user schema - Not complete
376
+
377
+ 4. [Do the work: Design schema]
378
+
379
+ 5. Call: xpcomplete-agent-plan-item
380
+ id: "abc-123"
381
+
382
+ 6. Call: xpget-agent-plan
383
+ → See: Task 1 ✓ complete, Task 2 next...
384
+
385
+ 7. [Continue through remaining tasks]
386
+ ```
387
+ **Remember**: The plan is your roadmap. Check it often, update it as needed, and always mark tasks complete when done!
388
+ </important_planning_instructions>
389
+ """
390
+
391
+ # add the expected output guidance
392
+ if not "expected_output" in args:
393
+ args["expected_output"] = ""
394
+ args["expected_output"] += "\nAll planned tasks completed and marked as done."
395
+
396
+ # add the plan to additional_context
397
+ if not "additional_context" in args:
398
+ args["additional_context"] = ""
399
+
400
+ plan_str = task.deep_planning.model_dump_json() if task.deep_planning and task.deep_planning.enabled and len(task.deep_planning.tasks) != 0 else "No execution plan, please generate"
401
+ args["additional_context"] += f" \n Current execution plan: {plan_str}"
218
402
 
219
403
  def _load_llm_model(agent: Agent, override: Optional[Dict[str, Any]]) -> Any:
220
404
  """
@@ -330,6 +330,7 @@ class Events(ModuleBase):
330
330
  agent_worker: DeployedAsset,
331
331
  task: Task,
332
332
  on_execution_request: ExecutionRequestHandler,
333
+ retry_count: Optional[int] = 0,
333
334
  ) -> None:
334
335
  """
335
336
  Handle an incoming task execution request.
@@ -338,6 +339,7 @@ class Events(ModuleBase):
338
339
  agent_worker (DeployedAsset): The deployed asset (agent) to handle the task.
339
340
  task (Task): The task object containing execution details.
340
341
  on_execution_request (ExecutionRequestHandler): The handler function to process the task.
342
+ retry_count (Optional[int]): Current retry attempt count. Defaults to 0.
341
343
  """
342
344
  error = None
343
345
  try:
@@ -351,6 +353,24 @@ class Events(ModuleBase):
351
353
  on_execution_request,
352
354
  task,
353
355
  )
356
+
357
+ # Check if plan is complete, retry if not
358
+ plan_following_status = await task.aget_plan_following_status()
359
+ if not plan_following_status.can_finish:
360
+ # Check if we've exceeded max retries
361
+ if retry_count >= 2: # 0, 1, 2 = 3 total attempts
362
+ raise Exception(f"Failed to complete plan after {retry_count + 1} attempts. Remaining incomplete tasks.")
363
+
364
+ # Recursively call with incremented retry count
365
+ logger.info(f"Plan not complete, retrying (attempt {retry_count + 2}/3)")
366
+ await self.handle_task_execution_request(
367
+ agent_worker,
368
+ task,
369
+ on_execution_request,
370
+ retry_count=retry_count + 1
371
+ )
372
+ return
373
+
354
374
  except Exception as e:
355
375
  logger.exception(f"Execution handler failed - {str(e)}")
356
376
  error = str(e)
@@ -43,12 +43,14 @@ from httpx import HTTPStatusError
43
43
  import httpx
44
44
  import json
45
45
  from httpx_sse import aconnect_sse
46
+ from pydantic import Field, computed_field
46
47
 
47
48
  from xpander_sdk.consts.api_routes import APIRoute
48
49
  from xpander_sdk.core.xpander_api_client import APIClient
49
50
  from xpander_sdk.exceptions.module_exception import ModuleException
50
51
  from xpander_sdk.models.activity import AgentActivityThread
51
52
  from xpander_sdk.models.configuration import Configuration
53
+ from xpander_sdk.models.deep_planning import PlanFollowingStatus, DeepPlanning
52
54
  from xpander_sdk.models.events import (
53
55
  TaskUpdateEventType,
54
56
  ToolCallRequest,
@@ -85,7 +87,7 @@ from xpander_sdk.utils.event_loop import run_sync
85
87
  T = TypeVar("T", bound="Task")
86
88
 
87
89
  TaskUpdateEventData = Union[
88
- T, ToolCallRequest, ToolCallResult, MCPOAuthGetTokenResponse
90
+ T, ToolCallRequest, ToolCallResult, MCPOAuthGetTokenResponse, DeepPlanning
89
91
  ]
90
92
 
91
93
 
@@ -133,6 +135,7 @@ class Task(XPanderSharedModel):
133
135
  mcp_servers (Optional[List[MCPServerDetails]]): Optional list of mcp servers to use.
134
136
  triggering_agent_id (Optional[str]): Optional triggering agent id.
135
137
  title (Optional[str]): Optional task title.
138
+ deep_planning: Optional[DeepPlanning] = Field(default_factory=DeepPlanning)
136
139
 
137
140
  Example:
138
141
  >>> task = Task.load(task_id="task_123")
@@ -180,6 +183,7 @@ class Task(XPanderSharedModel):
180
183
  title: Optional[str] = (None,)
181
184
  think_mode: Optional[ThinkMode] = ThinkMode.Default
182
185
  disable_attachment_injection: Optional[bool] = False
186
+ deep_planning: Optional[DeepPlanning] = Field(default_factory=DeepPlanning)
183
187
 
184
188
  # metrics
185
189
  tokens: Optional[Tokens] = None
@@ -348,7 +352,7 @@ class Task(XPanderSharedModel):
348
352
  response = await client.make_request(
349
353
  path=APIRoute.UpdateTask.format(task_id=self.id),
350
354
  method="PATCH",
351
- payload=self.model_dump_safe(),
355
+ payload=self.model_dump_safe(exclude={"configuration","deep_planning"}),
352
356
  )
353
357
  updated_task = Task(**response, configuration=self.configuration)
354
358
  for field, value in updated_task.__dict__.items():
@@ -536,6 +540,17 @@ class Task(XPanderSharedModel):
536
540
  for f in readable_files:
537
541
  message += f"\n{json.dumps(f)}"
538
542
 
543
+ if self.deep_planning and self.deep_planning.enabled == True:
544
+ self.reload()
545
+ uncompleted_tasks = [task for task in self.deep_planning.tasks if not task.completed]
546
+ if len(uncompleted_tasks) != 0:
547
+ message = "\n".join([
548
+ "Task not finished, uncompleted tasks detected:",
549
+ f"Uncompleted tasks: {[task.model_dump_json() for task in uncompleted_tasks]}",
550
+ "You must complete tasks if fulfilled",
551
+ f"User's original request: \"{message}\""
552
+ ])
553
+
539
554
  return message
540
555
 
541
556
  async def aget_activity_log(self) -> AgentActivityThread:
@@ -750,6 +765,60 @@ class Task(XPanderSharedModel):
750
765
  None
751
766
  """
752
767
  return run_sync(self.areport_metrics(configuration=configuration))
768
+
769
+ async def aget_plan_following_status(self) -> PlanFollowingStatus:
770
+ """
771
+ Asynchronously check if the task's deep planning is complete.
772
+
773
+ Reloads the task to get the latest deep planning state and checks for
774
+ any uncompleted tasks. If deep planning is disabled or all tasks are
775
+ completed, returns a status indicating the task can finish.
776
+
777
+ Returns:
778
+ PlanFollowingStatus: Status object containing:
779
+ - can_finish (bool): True if all tasks are completed or deep planning is disabled.
780
+ - uncompleted_tasks (List[DeepPlanningItem]): List of tasks not yet completed.
781
+
782
+ Example:
783
+ >>> status = await task.aget_plan_following_status()
784
+ >>> if not status.can_finish:
785
+ ... print(f"Remaining tasks: {len(status.uncompleted_tasks)}")
786
+ """
787
+ try:
788
+ if self.deep_planning and self.deep_planning.enabled:
789
+ await self.areload()
790
+
791
+ uncompleted_tasks = [
792
+ task for task in self.deep_planning.tasks if not task.completed
793
+ ]
794
+ if len(uncompleted_tasks) != 0:
795
+ return PlanFollowingStatus(
796
+ can_finish=False, uncompleted_tasks=uncompleted_tasks
797
+ )
798
+ except Exception:
799
+ pass
800
+
801
+ return PlanFollowingStatus(can_finish=True)
802
+
803
+ def get_plan_following_status(self) -> PlanFollowingStatus:
804
+ """
805
+ Check if the task's deep planning is complete synchronously.
806
+
807
+ This function wraps the asynchronous aget_plan_following_status method.
808
+ Reloads the task to get the latest deep planning state and checks for
809
+ any uncompleted tasks.
810
+
811
+ Returns:
812
+ PlanFollowingStatus: Status object containing:
813
+ - can_finish (bool): True if all tasks are completed or deep planning is disabled.
814
+ - uncompleted_tasks (List[DeepPlanningItem]): List of tasks not yet completed.
815
+
816
+ Example:
817
+ >>> status = task.get_plan_following_status()
818
+ >>> if not status.can_finish:
819
+ ... print(f"Remaining tasks: {len(status.uncompleted_tasks)}")
820
+ """
821
+ return run_sync(self.aget_plan_following_status())
753
822
 
754
823
  @classmethod
755
824
  async def areport_external_task(
@@ -884,4 +953,4 @@ class Task(XPanderSharedModel):
884
953
  duration=duration,
885
954
  used_tools=used_tools,
886
955
  )
887
- )
956
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xpander-sdk
3
- Version: 2.0.158
3
+ Version: 2.0.159
4
4
  Summary: xpander.ai Backend-as-a-service for AI Agents - SDK
5
5
  Home-page: https://www.xpander.ai
6
6
  Author: xpanderAI
@@ -19,6 +19,7 @@ src/xpander_sdk/exceptions/module_exception.py
19
19
  src/xpander_sdk/models/__init__.py
20
20
  src/xpander_sdk/models/activity.py
21
21
  src/xpander_sdk/models/configuration.py
22
+ src/xpander_sdk/models/deep_planning.py
22
23
  src/xpander_sdk/models/events.py
23
24
  src/xpander_sdk/models/frameworks.py
24
25
  src/xpander_sdk/models/shared.py
File without changes
File without changes
File without changes