xpander-sdk 2.0.157__tar.gz → 2.0.161__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.157 → xpander_sdk-2.0.161}/PKG-INFO +1 -1
  2. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/setup.py +1 -1
  3. xpander_sdk-2.0.161/src/xpander_sdk/models/deep_planning.py +18 -0
  4. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/models/events.py +3 -0
  5. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/sub_modules/agent.py +4 -2
  6. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/backend/frameworks/agno.py +236 -0
  7. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/events_module.py +21 -0
  8. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tasks/sub_modules/task.py +76 -3
  9. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk.egg-info/PKG-INFO +1 -1
  10. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk.egg-info/SOURCES.txt +1 -0
  11. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/LICENSE +0 -0
  12. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/README.md +0 -0
  13. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/pyproject.toml +0 -0
  14. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/setup.cfg +0 -0
  15. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/__init__.py +0 -0
  16. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/consts/__init__.py +0 -0
  17. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/consts/api_routes.py +0 -0
  18. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/core/__init__.py +0 -0
  19. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/core/module_base.py +0 -0
  20. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/core/state.py +0 -0
  21. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/core/xpander_api_client.py +0 -0
  22. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/exceptions/__init__.py +0 -0
  23. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/exceptions/module_exception.py +0 -0
  24. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/models/__init__.py +0 -0
  25. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/models/activity.py +0 -0
  26. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/models/configuration.py +0 -0
  27. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/models/frameworks.py +0 -0
  28. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/models/shared.py +0 -0
  29. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/models/user.py +0 -0
  30. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/__init__.py +0 -0
  31. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/__init__.py +0 -0
  32. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/agents_module.py +0 -0
  33. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/models/__init__.py +0 -0
  34. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/models/agent.py +0 -0
  35. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/models/agent_list.py +0 -0
  36. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/models/knowledge_bases.py +0 -0
  37. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/sub_modules/__init__.py +0 -0
  38. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/utils/__init__.py +0 -0
  39. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/agents/utils/generic.py +0 -0
  40. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/backend/__init__.py +0 -0
  41. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/backend/backend_module.py +0 -0
  42. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/backend/frameworks/__init__.py +0 -0
  43. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/backend/frameworks/dispatch.py +0 -0
  44. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/backend/utils/__init__.py +0 -0
  45. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/backend/utils/mcp_oauth.py +0 -0
  46. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/__init__.py +0 -0
  47. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/decorators/__init__.py +0 -0
  48. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/decorators/on_boot.py +0 -0
  49. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/decorators/on_shutdown.py +0 -0
  50. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/decorators/on_task.py +0 -0
  51. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/models/__init__.py +0 -0
  52. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/models/deployments.py +0 -0
  53. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/models/events.py +0 -0
  54. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/utils/__init__.py +0 -0
  55. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/utils/generic.py +0 -0
  56. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/events/utils/git_init.py +0 -0
  57. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/knowledge_bases/__init__.py +0 -0
  58. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/knowledge_bases/knowledge_bases_module.py +0 -0
  59. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/knowledge_bases/models/__init__.py +0 -0
  60. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/knowledge_bases/models/knowledge_bases.py +0 -0
  61. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/knowledge_bases/sub_modules/__init__.py +0 -0
  62. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base.py +0 -0
  63. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base_document_item.py +0 -0
  64. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/knowledge_bases/utils/__init__.py +0 -0
  65. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tasks/__init__.py +0 -0
  66. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tasks/models/__init__.py +0 -0
  67. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tasks/models/task.py +0 -0
  68. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tasks/models/tasks_list.py +0 -0
  69. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tasks/sub_modules/__init__.py +0 -0
  70. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tasks/tasks_module.py +0 -0
  71. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tasks/utils/__init__.py +0 -0
  72. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tasks/utils/files.py +0 -0
  73. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/__init__.py +0 -0
  74. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/decorators/__init__.py +0 -0
  75. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/decorators/register_tool.py +0 -0
  76. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/models/__init__.py +0 -0
  77. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/models/mcp.py +0 -0
  78. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/models/tool_invocation_result.py +0 -0
  79. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/sub_modules/__init__.py +0 -0
  80. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/sub_modules/tool.py +0 -0
  81. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/tools_repository_module.py +0 -0
  82. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/utils/__init__.py +0 -0
  83. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/utils/generic.py +0 -0
  84. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/utils/local_tools.py +0 -0
  85. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/modules/tools_repository/utils/schemas.py +0 -0
  86. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/utils/__init__.py +0 -0
  87. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/utils/env.py +0 -0
  88. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/utils/event_loop.py +0 -0
  89. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk/utils/tools.py +0 -0
  90. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk.egg-info/dependency_links.txt +0 -0
  91. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk.egg-info/requires.txt +0 -0
  92. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/src/xpander_sdk.egg-info/top_level.txt +0 -0
  93. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/tests/test_agents_module.py +0 -0
  94. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/tests/test_api_client.py +0 -0
  95. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/tests/test_backend_module.py +0 -0
  96. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/tests/test_boot_shutdown_handlers.py +0 -0
  97. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/tests/test_configuration.py +0 -0
  98. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/tests/test_knowledge_bases_module.py +0 -0
  99. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/tests/test_tasks_module.py +0 -0
  100. {xpander_sdk-2.0.157 → xpander_sdk-2.0.161}/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.157
3
+ Version: 2.0.161
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.157",
8
+ version="2.0.161",
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,18 @@
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
+ enforce: Optional[bool] = False
12
+ started: Optional[bool] = False
13
+ question_raised: Optional[bool] = False
14
+ tasks: Optional[List[DeepPlanningItem]] = []
15
+
16
+ class PlanFollowingStatus(XPanderSharedModel):
17
+ can_finish: bool
18
+ 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"
@@ -153,7 +153,8 @@ class Agent(XPanderSharedModel):
153
153
  model_provider: str
154
154
  model_name: str
155
155
  llm_reasoning_effort: Optional[LLMReasoningEffort] = LLMReasoningEffort.Medium
156
- deep_plan: Optional[bool] = False
156
+ deep_planning: Optional[bool] = False
157
+ enforce_deep_planning: Optional[bool] = False
157
158
  llm_api_base: Optional[str]
158
159
  webhook_url: Optional[str]
159
160
  created_at: Optional[datetime]
@@ -196,7 +197,8 @@ class Agent(XPanderSharedModel):
196
197
  model_provider: str
197
198
  model_name: str
198
199
  llm_reasoning_effort: Optional[LLMReasoningEffort] = LLMReasoningEffort.Medium
199
- deep_plan: Optional[bool] = False
200
+ deep_planning: Optional[bool] = False
201
+ enforce_deep_planning: Optional[bool] = False
200
202
  llm_api_base: Optional[str] = None
201
203
  webhook_url: Optional[str] = None
202
204
  created_at: Optional[datetime] = None
@@ -213,8 +213,244 @@ 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. **START** plan execution (`xpstart-execution-plan`) - MANDATORY to enable enforcement
235
+ 3. **CHECK** plan before each action (`xpget-agent-plan`)
236
+ 4. **COMPLETE** task immediately after finishing it (`xpcomplete-agent-plan-item`)
237
+ 5. **ASK** user for info if needed (`xpask-for-information`)
238
+ 6. Repeat steps 3-5 until all tasks are done
239
+
240
+ ---
241
+
242
+ ### **Tool Reference**
243
+
244
+ #### **1. xpcreate-agent-plan** - Create Initial Plan
245
+ **When to use**: At the very start of any multi-step task, ONLY if no plan exists yet
246
+ **Note**: After creating a plan, you MUST call `xpstart-execution-plan` to begin enforcement
247
+
248
+ **How to use**:
249
+ - Pass an array of task objects (NOT strings)
250
+ - Each task must have a `title` field with short, explanatory description
251
+ - Example format:
252
+ ```json
253
+ {
254
+ "body_params": {
255
+ "tasks": [
256
+ {"title": "Research API requirements"},
257
+ {"title": "Design data schema"},
258
+ {"title": "Implement endpoints"},
259
+ {"title": "Write tests"},
260
+ {"title": "Deploy to staging"}
261
+ ]
262
+ }
263
+ }
264
+ ```
265
+ **Important**:
266
+ - Include ALL steps needed from start to finish
267
+ - Each title should be clear and actionable (3-6 words)
268
+ - Do NOT pass plain strings like `["Task 1", "Task 2"]` - must be objects!
269
+
270
+ ---
271
+
272
+ #### **2. xpget-agent-plan** - View Current Plan
273
+ **When to use**: Before deciding what to do next; to check progress
274
+
275
+ **Returns**:
276
+ - All tasks with their IDs, titles, and completion status
277
+ - Use this to know what's done and what remains
278
+
279
+ **No parameters needed** - just call the tool
280
+
281
+ ---
282
+
283
+ #### **3. xpcomplete-agent-plan-item** - Mark Task Complete
284
+ **When to use**: **IMMEDIATELY** after finishing each task (NOT before!)
285
+
286
+ **How to use**:
287
+ ```json
288
+ {
289
+ "body_params": {
290
+ "id": "task-uuid-from-plan"
291
+ }
292
+ }
293
+ ```
294
+ **CRITICAL**:
295
+ - Only mark complete AFTER work is actually done
296
+ - This is MANDATORY for progress tracking
297
+ - Get the task ID from `xpget-agent-plan` results
298
+
299
+ ---
300
+
301
+ #### **4. xpadd-new-agent-plan-item** - Add Discovered Task
302
+ **When to use**: When you discover additional work needed during execution
303
+
304
+ **How to use**:
305
+ ```json
306
+ {
307
+ "body_params": {
308
+ "title": "Validate input schemas",
309
+ "completed": false
310
+ }
311
+ }
312
+ ```
313
+ ---
314
+
315
+ #### **5. xpupdate-agent-plan-item** - Modify Existing Task
316
+ **When to use**: When task details change or need clarification
317
+
318
+ **How to use**:
319
+ ```json
320
+ {
321
+ "body_params": {
322
+ "id": "task-uuid",
323
+ "title": "Updated description",
324
+ "completed": true
325
+ }
326
+ }
327
+ ```
328
+ (All fields optional except `id`)
329
+
330
+ ---
331
+
332
+ #### **6. xpdelete-agent-plan-item** - Remove Task
333
+ **When to use**: When a task becomes unnecessary or redundant
334
+
335
+ **How to use**:
336
+ ```json
337
+ {
338
+ "body_params": {
339
+ "id": "task-uuid"
340
+ }
341
+ }
342
+ ```
343
+ ---
344
+
345
+ #### **7. xpstart-execution-plan** - Start Plan Execution
346
+ **When to use**: Immediately after creating a plan with `xpcreate-agent-plan`
347
+ **CRITICAL**: Must be called to enable enforcement mode before executing tasks
348
+
349
+ **How to use**:
350
+ ```json
351
+ {
352
+ "body_params": {}
353
+ }
354
+ ```
355
+ **No parameters needed** - just call after plan creation
356
+
357
+ **What it does**:
358
+ - Marks plan as "started"
359
+ - Enables enforcement mode if `enforce` flag is true
360
+ - Allows plan execution to proceed
361
+
362
+ ---
363
+
364
+ #### **8. xpask-for-information** - Ask User a Question
365
+ **When to use**: When you need information from the user during plan execution
366
+ **PREREQUISITE**: Plan must be started (running) first
367
+
368
+ **How to use**:
369
+ ```json
370
+ {
371
+ "body_params": {
372
+ "question": "What is the customer email address?"
373
+ }
374
+ }
375
+ ```
376
+
377
+ **What it does**:
378
+ - Sets `question_raised` flag to true
379
+ - Prints the question for the user
380
+ - Keeps enforcement active (does NOT pause execution)
381
+ - Returns waiting status
382
+
383
+ ---
384
+
385
+ ### **Best Practices**
386
+
387
+ ✅ **DO:**
388
+ - Create comprehensive plans with ALL necessary steps
389
+ - **START** the plan with `xpstart-execution-plan` after creating it
390
+ - Use descriptive, actionable task titles
391
+ - Check plan before each action to stay oriented
392
+ - Mark tasks complete immediately after finishing them
393
+ - Ask for user information when needed with `xpask-for-information`
394
+ - Call plan tools **sequentially** (one at a time, never in parallel)
395
+
396
+ ❌ **DON'T:**
397
+ - Mark tasks complete before they're actually done
398
+ - Pass plain string arrays - must be objects with `title` field
399
+ - Call plan tools in parallel with each other
400
+ - Skip checking the plan between major steps
401
+ - Forget to mark completed tasks
402
+
403
+ ---
404
+
405
+ ### **Example Complete Workflow**
406
+
407
+ ```
408
+ 1. User: "Build a REST API for user management"
409
+
410
+ 2. Call: xpcreate-agent-plan
411
+ tasks: [
412
+ {"title": "Design user schema"},
413
+ {"title": "Create database migration"},
414
+ {"title": "Implement CRUD endpoints"},
415
+ {"title": "Add authentication"},
416
+ {"title": "Write integration tests"}
417
+ ]
418
+
419
+ 3. Call: xpstart-execution-plan
420
+ → Plan now started, enforcement enabled (if enforce=true)
421
+
422
+ 4. Call: xpget-agent-plan
423
+ → See: Task 1 (ID: abc-123) - Design user schema - Not complete
424
+
425
+ 5. [Realize need user input] Call: xpask-for-information
426
+ question: "Which database should we use - PostgreSQL or MySQL?"
427
+ → question_raised=true, waiting for response
428
+
429
+ 6. [After user responds, do the work: Design schema]
430
+
431
+ 7. Call: xpcomplete-agent-plan-item
432
+ id: "abc-123"
433
+
434
+ 8. Call: xpget-agent-plan
435
+ → See: Task 1 ✓ complete, Task 2 next...
436
+
437
+ 9. [Continue through remaining tasks]
438
+ ```
439
+ **Remember**: The plan is your roadmap. Check it often, update it as needed, and always mark tasks complete when done!
440
+ </important_planning_instructions>
441
+ """
442
+
443
+ # add the expected output guidance
444
+ if not "expected_output" in args:
445
+ args["expected_output"] = ""
446
+ args["expected_output"] += "\nAll planned tasks completed and marked as done."
447
+
448
+ # add the plan to additional_context
449
+ if not "additional_context" in args:
450
+ args["additional_context"] = ""
451
+
452
+ 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"
453
+ args["additional_context"] += f" \n Current execution plan: {plan_str}"
218
454
 
219
455
  def _load_llm_model(agent: Agent, override: Optional[Dict[str, Any]]) -> Any:
220
456
  """
@@ -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,25 @@ 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
+ logger.warning(f"Failed to complete plan after {retry_count + 1} attempts. Remaining incomplete tasks.")
363
+ return
364
+
365
+ # Recursively call with incremented retry count
366
+ logger.info(f"Plan not complete, retrying (attempt {retry_count + 2}/3)")
367
+ await self.handle_task_execution_request(
368
+ agent_worker,
369
+ task,
370
+ on_execution_request,
371
+ retry_count=retry_count + 1
372
+ )
373
+ return
374
+
354
375
  except Exception as e:
355
376
  logger.exception(f"Execution handler failed - {str(e)}")
356
377
  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,64 @@ 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 and self.deep_planning.started and self.deep_planning.enforce:
789
+ await self.areload()
790
+
791
+ # allow early exit to ask question
792
+ if self.deep_planning.question_raised:
793
+ return PlanFollowingStatus(can_finish=True)
794
+
795
+ uncompleted_tasks = [
796
+ task for task in self.deep_planning.tasks if not task.completed
797
+ ]
798
+ if len(uncompleted_tasks) != 0:
799
+ return PlanFollowingStatus(
800
+ can_finish=False, uncompleted_tasks=uncompleted_tasks
801
+ )
802
+ except Exception:
803
+ pass
804
+
805
+ return PlanFollowingStatus(can_finish=True)
806
+
807
+ def get_plan_following_status(self) -> PlanFollowingStatus:
808
+ """
809
+ Check if the task's deep planning is complete synchronously.
810
+
811
+ This function wraps the asynchronous aget_plan_following_status method.
812
+ Reloads the task to get the latest deep planning state and checks for
813
+ any uncompleted tasks.
814
+
815
+ Returns:
816
+ PlanFollowingStatus: Status object containing:
817
+ - can_finish (bool): True if all tasks are completed or deep planning is disabled.
818
+ - uncompleted_tasks (List[DeepPlanningItem]): List of tasks not yet completed.
819
+
820
+ Example:
821
+ >>> status = task.get_plan_following_status()
822
+ >>> if not status.can_finish:
823
+ ... print(f"Remaining tasks: {len(status.uncompleted_tasks)}")
824
+ """
825
+ return run_sync(self.aget_plan_following_status())
753
826
 
754
827
  @classmethod
755
828
  async def areport_external_task(
@@ -884,4 +957,4 @@ class Task(XPanderSharedModel):
884
957
  duration=duration,
885
958
  used_tools=used_tools,
886
959
  )
887
- )
960
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xpander-sdk
3
- Version: 2.0.157
3
+ Version: 2.0.161
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