xpander-sdk 2.0.143__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.
- xpander_sdk/consts/api_routes.py +1 -0
- xpander_sdk/models/activity.py +65 -0
- xpander_sdk/models/deep_planning.py +18 -0
- xpander_sdk/models/events.py +3 -0
- xpander_sdk/modules/agents/models/agent.py +4 -1
- xpander_sdk/modules/agents/sub_modules/agent.py +7 -0
- xpander_sdk/modules/backend/frameworks/agno.py +245 -5
- xpander_sdk/modules/backend/utils/mcp_oauth.py +1 -1
- xpander_sdk/modules/events/events_module.py +34 -0
- xpander_sdk/modules/tasks/sub_modules/task.py +235 -77
- xpander_sdk/modules/tools_repository/models/mcp.py +1 -0
- xpander_sdk/modules/tools_repository/tools_repository_module.py +6 -2
- {xpander_sdk-2.0.143.dist-info → xpander_sdk-2.0.161.dist-info}/METADATA +130 -4
- {xpander_sdk-2.0.143.dist-info → xpander_sdk-2.0.161.dist-info}/RECORD +17 -15
- {xpander_sdk-2.0.143.dist-info → xpander_sdk-2.0.161.dist-info}/WHEEL +0 -0
- {xpander_sdk-2.0.143.dist-info → xpander_sdk-2.0.161.dist-info}/licenses/LICENSE +0 -0
- {xpander_sdk-2.0.143.dist-info → xpander_sdk-2.0.161.dist-info}/top_level.txt +0 -0
xpander_sdk/consts/api_routes.py
CHANGED
|
@@ -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]] = []
|
xpander_sdk/models/events.py
CHANGED
|
@@ -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 +
|
|
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 =
|
|
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)
|
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
|
|
10
10
|
import asyncio
|
|
11
11
|
import json
|
|
12
|
+
import json as py_json
|
|
12
13
|
import os
|
|
13
14
|
import signal
|
|
14
15
|
import sys
|
|
@@ -19,10 +20,12 @@ from typing import Any, Awaitable, Callable, Optional, Set, Union, List
|
|
|
19
20
|
import httpx
|
|
20
21
|
from httpx_sse import aconnect_sse
|
|
21
22
|
from loguru import logger
|
|
23
|
+
from pydantic import BaseModel
|
|
22
24
|
|
|
23
25
|
from xpander_sdk.core.module_base import ModuleBase
|
|
24
26
|
from xpander_sdk.exceptions.module_exception import ModuleException
|
|
25
27
|
from xpander_sdk.models.configuration import Configuration
|
|
28
|
+
from xpander_sdk.models.shared import OutputFormat
|
|
26
29
|
from xpander_sdk.modules.agents.models.agent import SourceNodeType
|
|
27
30
|
from xpander_sdk.modules.tasks.tasks_module import Tasks
|
|
28
31
|
|
|
@@ -327,6 +330,7 @@ class Events(ModuleBase):
|
|
|
327
330
|
agent_worker: DeployedAsset,
|
|
328
331
|
task: Task,
|
|
329
332
|
on_execution_request: ExecutionRequestHandler,
|
|
333
|
+
retry_count: Optional[int] = 0,
|
|
330
334
|
) -> None:
|
|
331
335
|
"""
|
|
332
336
|
Handle an incoming task execution request.
|
|
@@ -335,6 +339,7 @@ class Events(ModuleBase):
|
|
|
335
339
|
agent_worker (DeployedAsset): The deployed asset (agent) to handle the task.
|
|
336
340
|
task (Task): The task object containing execution details.
|
|
337
341
|
on_execution_request (ExecutionRequestHandler): The handler function to process the task.
|
|
342
|
+
retry_count (Optional[int]): Current retry attempt count. Defaults to 0.
|
|
338
343
|
"""
|
|
339
344
|
error = None
|
|
340
345
|
try:
|
|
@@ -348,6 +353,25 @@ class Events(ModuleBase):
|
|
|
348
353
|
on_execution_request,
|
|
349
354
|
task,
|
|
350
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
|
+
|
|
351
375
|
except Exception as e:
|
|
352
376
|
logger.exception(f"Execution handler failed - {str(e)}")
|
|
353
377
|
error = str(e)
|
|
@@ -364,6 +388,16 @@ class Events(ModuleBase):
|
|
|
364
388
|
): # let the handler set the status, if not set - mark as completed
|
|
365
389
|
task.status = AgentExecutionStatus.Completed
|
|
366
390
|
|
|
391
|
+
# in case of structured output, return as stringified json
|
|
392
|
+
try:
|
|
393
|
+
if task.output_format == OutputFormat.Json:
|
|
394
|
+
if isinstance(task.result, BaseModel):
|
|
395
|
+
task.result = task.result.model_dump_json()
|
|
396
|
+
if isinstance(task.result, dict) or isinstance(task.result, list):
|
|
397
|
+
task.result = py_json.dumps(task.result)
|
|
398
|
+
except Exception:
|
|
399
|
+
pass
|
|
400
|
+
|
|
367
401
|
await task.asave()
|
|
368
402
|
task.tokens = task_used_tokens
|
|
369
403
|
task.used_tools = task_used_tools
|