xpander-sdk 2.0.144__py3-none-any.whl → 2.0.161__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.
@@ -35,6 +35,7 @@ and deleting resources.
35
35
  ListTasks = "/agent-execution/executions/history/{agent_id}"
36
36
  ListUserTasks = "/agent-execution/executions/history/user/{user_id}"
37
37
  GetTask = "/agent-execution/{task_id}/status"
38
+ GetTaskActivityLog = "/activity/{agent_id}/{task_id}"
38
39
  TaskCrud = "/agent-execution/{agent_or_task_id}"
39
40
  UpdateTask = "/agent-execution/{task_id}/update"
40
41
  ReportExternalTask = "/agent-execution/{agent_id}/report_task"
@@ -0,0 +1,65 @@
1
+ from datetime import datetime
2
+ from enum import Enum
3
+ from typing import Any, List, Literal, Optional, Union
4
+
5
+ from xpander_sdk.models.events import ToolCallRequestReasoning
6
+ from xpander_sdk.models.shared import XPanderSharedModel
7
+ from xpander_sdk.models.user import User
8
+ from xpander_sdk.modules.tools_repository.models.mcp import MCPOAuthGetTokenResponse
9
+
10
+ class AgentActivityThreadMessageContent(XPanderSharedModel):
11
+ text: Optional[str] = None
12
+ files: Optional[List[str]] = []
13
+
14
+ class AgentActivityThreadMessage(XPanderSharedModel):
15
+ id: str
16
+ created_at: datetime
17
+ role: Literal["user","agent"]
18
+ content: AgentActivityThreadMessageContent
19
+
20
+ class AgentActivityThreadToolCall(XPanderSharedModel):
21
+ id: str
22
+ created_at: datetime
23
+ tool_name: str
24
+ payload: Any
25
+ is_error: Optional[bool] = False
26
+ reasoning: Optional[ToolCallRequestReasoning] = None
27
+ result: Optional[Any] = None
28
+
29
+ class AgentActivityThreadReasoningType(str, Enum):
30
+ Think = "think"
31
+ Analyze = "analyze"
32
+
33
+ class AgentActivityThreadReasoning(XPanderSharedModel):
34
+ id: str
35
+ created_at: datetime
36
+ type: AgentActivityThreadReasoningType
37
+ title: str
38
+ confidence: float
39
+ thought: Optional[str] = None
40
+ action: Optional[str] = None
41
+ result: Optional[str] = None
42
+ analysis: Optional[str] = None
43
+
44
+ class AgentActivityThreadSubAgentTrigger(XPanderSharedModel):
45
+ id: str
46
+ created_at: datetime
47
+ agent_id: str
48
+ query: Optional[str] = None
49
+ files: Optional[List[str]] = []
50
+ reasoning: ToolCallRequestReasoning
51
+
52
+ class AgentActivityThreadAuth(MCPOAuthGetTokenResponse):
53
+ id: str
54
+ created_at: datetime
55
+
56
+ AgentActivityThreadMessageType = Union[AgentActivityThreadMessage, AgentActivityThreadToolCall, AgentActivityThreadReasoning, AgentActivityThreadSubAgentTrigger, AgentActivityThreadAuth]
57
+ class AgentActivityThread(XPanderSharedModel):
58
+ id: str
59
+ created_at: datetime
60
+ messages: List[AgentActivityThreadMessageType]
61
+ user: Optional[User] = None
62
+
63
+ class AgentActivityThreadListItem(XPanderSharedModel):
64
+ id: str
65
+ created_at: datetime
@@ -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"
@@ -383,7 +383,10 @@ class AgentGraphItem(BaseModel):
383
383
  llm_settings: Optional[List[AgentGraphItemLLMSettings]] = []
384
384
  is_first: Optional[bool] = False
385
385
 
386
-
386
+ class LLMReasoningEffort(str, Enum):
387
+ Low = "low"
388
+ Medium = "medium"
389
+ High = "high"
387
390
 
388
391
  class AIAgentConnectivityDetailsA2AAuthType(str, Enum):
389
392
  NoAuth = "none"
@@ -40,6 +40,7 @@ from xpander_sdk.modules.agents.models.agent import (
40
40
  AgentType,
41
41
  DatabaseConnectionString,
42
42
  LLMCredentials,
43
+ LLMReasoningEffort,
43
44
  )
44
45
  from xpander_sdk.modules.agents.models.knowledge_bases import AgentKnowledgeBase
45
46
  from xpander_sdk.modules.knowledge_bases.knowledge_bases_module import KnowledgeBases
@@ -151,6 +152,9 @@ class Agent(XPanderSharedModel):
151
152
  using_nemo: Optional[bool]
152
153
  model_provider: str
153
154
  model_name: str
155
+ llm_reasoning_effort: Optional[LLMReasoningEffort] = LLMReasoningEffort.Medium
156
+ deep_planning: Optional[bool] = False
157
+ enforce_deep_planning: Optional[bool] = False
154
158
  llm_api_base: Optional[str]
155
159
  webhook_url: Optional[str]
156
160
  created_at: Optional[datetime]
@@ -192,6 +196,9 @@ class Agent(XPanderSharedModel):
192
196
  using_nemo: Optional[bool] = False
193
197
  model_provider: str
194
198
  model_name: str
199
+ llm_reasoning_effort: Optional[LLMReasoningEffort] = LLMReasoningEffort.Medium
200
+ deep_planning: Optional[bool] = False
201
+ enforce_deep_planning: Optional[bool] = False
195
202
  llm_api_base: Optional[str] = None
196
203
  webhook_url: Optional[str] = None
197
204
  created_at: Optional[datetime] = None
@@ -8,7 +8,7 @@ from loguru import logger
8
8
  from xpander_sdk import Configuration
9
9
  from xpander_sdk.models.shared import OutputFormat, ThinkMode
10
10
  from xpander_sdk.modules.agents.agents_module import Agents
11
- from xpander_sdk.modules.agents.models.agent import AgentGraphItemType
11
+ from xpander_sdk.modules.agents.models.agent import AgentGraphItemType, LLMReasoningEffort
12
12
  from xpander_sdk.modules.agents.sub_modules.agent import Agent
13
13
  from xpander_sdk.modules.backend.utils.mcp_oauth import authenticate_mcp_server
14
14
  from xpander_sdk.modules.tasks.sub_modules.task import Task
@@ -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
  """
@@ -278,6 +514,10 @@ def _load_llm_model(agent: Agent, override: Optional[Dict[str, Any]]) -> Any:
278
514
  return env_llm_key or agent.llm_credentials.value
279
515
 
280
516
  llm_args = {}
517
+
518
+ if agent.llm_reasoning_effort and agent.llm_reasoning_effort != LLMReasoningEffort.Medium and agent.model_name and "gpt-5" in agent.model_name.lower():
519
+ llm_args = { "reasoning_effort": agent.llm_reasoning_effort.value }
520
+
281
521
  if agent.llm_api_base and len(agent.llm_api_base) != 0:
282
522
  llm_args["base_url"] = agent.llm_api_base
283
523
 
@@ -579,6 +819,7 @@ async def _resolve_agent_tools(agent: Agent, task: Optional[Task] = None) -> Lis
579
819
  ),
580
820
  include_tools=mcp.allowed_tools or None,
581
821
  timeout_seconds=120,
822
+ tool_name_prefix="mcp_tool"
582
823
  )
583
824
  )
584
825
  elif mcp.url:
@@ -612,10 +853,9 @@ async def _resolve_agent_tools(agent: Agent, task: Optional[Task] = None) -> Lis
612
853
  transport=transport,
613
854
  server_params=params_cls(url=mcp.url, headers=mcp.headers),
614
855
  include_tools=mcp.allowed_tools or None,
615
- timeout_seconds=120
856
+ timeout_seconds=120,
857
+ tool_name_prefix="mcp_tool"
616
858
  )
617
859
  )
618
860
 
619
- return agent.tools.functions + await asyncio.gather(
620
- *[mcp.__aenter__() for mcp in mcp_tools]
621
- )
861
+ return agent.tools.functions + mcp_tools
@@ -8,7 +8,7 @@ from xpander_sdk.modules.tasks.sub_modules.task import Task, TaskUpdateEvent
8
8
  from xpander_sdk.modules.tools_repository.models.mcp import MCPOAuthGetTokenGenericResponse, MCPOAuthGetTokenResponse, MCPOAuthResponseType, MCPServerDetails
9
9
 
10
10
  POLLING_INTERVAL = 1 # every 1s
11
- MAX_WAIT_FOR_LOGIN = 300 # 5 mintutes
11
+ MAX_WAIT_FOR_LOGIN = 600 # 10 mintutes
12
12
 
13
13
  async def push_event(task: Task, event: TaskUpdateEvent):
14
14
  client = APIClient(configuration=task.configuration)
@@ -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)