xpander-sdk 1.60.8__py3-none-any.whl → 2.0.155__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. xpander_sdk/__init__.py +76 -7793
  2. xpander_sdk/consts/__init__.py +0 -0
  3. xpander_sdk/consts/api_routes.py +63 -0
  4. xpander_sdk/core/__init__.py +0 -0
  5. xpander_sdk/core/module_base.py +164 -0
  6. xpander_sdk/core/state.py +10 -0
  7. xpander_sdk/core/xpander_api_client.py +119 -0
  8. xpander_sdk/exceptions/__init__.py +0 -0
  9. xpander_sdk/exceptions/module_exception.py +45 -0
  10. xpander_sdk/models/__init__.py +0 -0
  11. xpander_sdk/models/activity.py +65 -0
  12. xpander_sdk/models/configuration.py +92 -0
  13. xpander_sdk/models/events.py +70 -0
  14. xpander_sdk/models/frameworks.py +64 -0
  15. xpander_sdk/models/shared.py +102 -0
  16. xpander_sdk/models/user.py +21 -0
  17. xpander_sdk/modules/__init__.py +0 -0
  18. xpander_sdk/modules/agents/__init__.py +0 -0
  19. xpander_sdk/modules/agents/agents_module.py +164 -0
  20. xpander_sdk/modules/agents/models/__init__.py +0 -0
  21. xpander_sdk/modules/agents/models/agent.py +477 -0
  22. xpander_sdk/modules/agents/models/agent_list.py +107 -0
  23. xpander_sdk/modules/agents/models/knowledge_bases.py +33 -0
  24. xpander_sdk/modules/agents/sub_modules/__init__.py +0 -0
  25. xpander_sdk/modules/agents/sub_modules/agent.py +953 -0
  26. xpander_sdk/modules/agents/utils/__init__.py +0 -0
  27. xpander_sdk/modules/agents/utils/generic.py +2 -0
  28. xpander_sdk/modules/backend/__init__.py +0 -0
  29. xpander_sdk/modules/backend/backend_module.py +425 -0
  30. xpander_sdk/modules/backend/frameworks/__init__.py +0 -0
  31. xpander_sdk/modules/backend/frameworks/agno.py +627 -0
  32. xpander_sdk/modules/backend/frameworks/dispatch.py +36 -0
  33. xpander_sdk/modules/backend/utils/__init__.py +0 -0
  34. xpander_sdk/modules/backend/utils/mcp_oauth.py +95 -0
  35. xpander_sdk/modules/events/__init__.py +0 -0
  36. xpander_sdk/modules/events/decorators/__init__.py +0 -0
  37. xpander_sdk/modules/events/decorators/on_boot.py +94 -0
  38. xpander_sdk/modules/events/decorators/on_shutdown.py +94 -0
  39. xpander_sdk/modules/events/decorators/on_task.py +203 -0
  40. xpander_sdk/modules/events/events_module.py +629 -0
  41. xpander_sdk/modules/events/models/__init__.py +0 -0
  42. xpander_sdk/modules/events/models/deployments.py +25 -0
  43. xpander_sdk/modules/events/models/events.py +57 -0
  44. xpander_sdk/modules/events/utils/__init__.py +0 -0
  45. xpander_sdk/modules/events/utils/generic.py +56 -0
  46. xpander_sdk/modules/events/utils/git_init.py +32 -0
  47. xpander_sdk/modules/knowledge_bases/__init__.py +0 -0
  48. xpander_sdk/modules/knowledge_bases/knowledge_bases_module.py +217 -0
  49. xpander_sdk/modules/knowledge_bases/models/__init__.py +0 -0
  50. xpander_sdk/modules/knowledge_bases/models/knowledge_bases.py +11 -0
  51. xpander_sdk/modules/knowledge_bases/sub_modules/__init__.py +0 -0
  52. xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base.py +107 -0
  53. xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base_document_item.py +40 -0
  54. xpander_sdk/modules/knowledge_bases/utils/__init__.py +0 -0
  55. xpander_sdk/modules/tasks/__init__.py +0 -0
  56. xpander_sdk/modules/tasks/models/__init__.py +0 -0
  57. xpander_sdk/modules/tasks/models/task.py +153 -0
  58. xpander_sdk/modules/tasks/models/tasks_list.py +107 -0
  59. xpander_sdk/modules/tasks/sub_modules/__init__.py +0 -0
  60. xpander_sdk/modules/tasks/sub_modules/task.py +887 -0
  61. xpander_sdk/modules/tasks/tasks_module.py +492 -0
  62. xpander_sdk/modules/tasks/utils/__init__.py +0 -0
  63. xpander_sdk/modules/tasks/utils/files.py +114 -0
  64. xpander_sdk/modules/tools_repository/__init__.py +0 -0
  65. xpander_sdk/modules/tools_repository/decorators/__init__.py +0 -0
  66. xpander_sdk/modules/tools_repository/decorators/register_tool.py +108 -0
  67. xpander_sdk/modules/tools_repository/models/__init__.py +0 -0
  68. xpander_sdk/modules/tools_repository/models/mcp.py +68 -0
  69. xpander_sdk/modules/tools_repository/models/tool_invocation_result.py +14 -0
  70. xpander_sdk/modules/tools_repository/sub_modules/__init__.py +0 -0
  71. xpander_sdk/modules/tools_repository/sub_modules/tool.py +578 -0
  72. xpander_sdk/modules/tools_repository/tools_repository_module.py +259 -0
  73. xpander_sdk/modules/tools_repository/utils/__init__.py +0 -0
  74. xpander_sdk/modules/tools_repository/utils/generic.py +57 -0
  75. xpander_sdk/modules/tools_repository/utils/local_tools.py +52 -0
  76. xpander_sdk/modules/tools_repository/utils/schemas.py +308 -0
  77. xpander_sdk/utils/__init__.py +0 -0
  78. xpander_sdk/utils/env.py +44 -0
  79. xpander_sdk/utils/event_loop.py +67 -0
  80. xpander_sdk/utils/tools.py +32 -0
  81. xpander_sdk-2.0.155.dist-info/METADATA +538 -0
  82. xpander_sdk-2.0.155.dist-info/RECORD +85 -0
  83. {xpander_sdk-1.60.8.dist-info → xpander_sdk-2.0.155.dist-info}/WHEEL +1 -1
  84. {xpander_sdk-1.60.8.dist-info → xpander_sdk-2.0.155.dist-info/licenses}/LICENSE +0 -1
  85. xpander_sdk/_jsii/__init__.py +0 -39
  86. xpander_sdk/_jsii/xpander-sdk@1.60.8.jsii.tgz +0 -0
  87. xpander_sdk/py.typed +0 -1
  88. xpander_sdk-1.60.8.dist-info/METADATA +0 -368
  89. xpander_sdk-1.60.8.dist-info/RECORD +0 -9
  90. {xpander_sdk-1.60.8.dist-info → xpander_sdk-2.0.155.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,492 @@
1
+ """
2
+ Tasks module for managing tasks in the xpander.ai platform.
3
+
4
+ This module provides functionality to create, list, retrieve, update,
5
+ and stop tasks within the xpander.ai Backend-as-a-Service platform.
6
+ """
7
+
8
+ from typing import Dict, List, Optional
9
+
10
+ from httpx import HTTPStatusError
11
+
12
+ from xpander_sdk.consts.api_routes import APIRoute
13
+ from xpander_sdk.core.module_base import ModuleBase
14
+ from xpander_sdk.core.xpander_api_client import APIClient
15
+ from xpander_sdk.exceptions.module_exception import ModuleException
16
+ from xpander_sdk.models.configuration import Configuration
17
+ from xpander_sdk.models.shared import OutputFormat, ThinkMode
18
+ from xpander_sdk.models.user import User
19
+ from xpander_sdk.modules.tasks.models.task import (
20
+ AgentExecutionInput,
21
+ AgentExecutionStatus,
22
+ )
23
+ from xpander_sdk.modules.tasks.models.tasks_list import TasksListItem
24
+ from xpander_sdk.modules.tasks.sub_modules.task import Task
25
+ from xpander_sdk.modules.tools_repository.models.mcp import MCPServerDetails
26
+ from xpander_sdk.utils.event_loop import run_sync
27
+
28
+
29
+ class Tasks(ModuleBase):
30
+ """
31
+ Main module for managing tasks in xpander.ai.
32
+
33
+ This class provides methods for creating, listing, retrieving, updating,
34
+ and stopping tasks related to AI agents within xpander.ai. It offers both
35
+ asynchronous and synchronous versions for flexibility in different runtime
36
+ environments.
37
+
38
+ The module uses the singleton pattern inherited from ModuleBase to ensure
39
+ consistent configuration across all task operations.
40
+
41
+ Example:
42
+ >>> tasks = Tasks()
43
+ >>> task_list = tasks.list(agent_id="agent123")
44
+ >>> specific_task = tasks.get(task_id="task456")
45
+ """
46
+
47
+ def __init__(self, configuration: Optional[Configuration] = None):
48
+ """
49
+ Initialize the Tasks module.
50
+
51
+ Args:
52
+ configuration (Optional[Configuration]): SDK configuration. If None,
53
+ will use default configuration from environment variables.
54
+ """
55
+ super().__init__(configuration)
56
+
57
+ async def alist(self, agent_id: str, filters: Optional[Dict] = None) -> List[TasksListItem]:
58
+ """
59
+ Asynchronously list all tasks for a specific agent.
60
+
61
+ Retrieves a list of all tasks associated with a given agent, providing
62
+ basic information for display and selection purposes.
63
+
64
+ Args:
65
+ agent_id (str): The unique identifier of the agent whose tasks should be listed.
66
+ filters (Optional[Dict]): Optional filters to be used on the query. supported filters: user_id, parent_task_id, triggering_agent_id, status, internal_status
67
+
68
+ Returns:
69
+ List[TasksListItem]: List of task summary objects related to the agent.
70
+
71
+ Raises:
72
+ ModuleException: If the API request fails or returns an error.
73
+
74
+ Example:
75
+ >>> tasks = Tasks()
76
+ >>> task_list = await tasks.alist(agent_id="agent123")
77
+ >>> for task in task_list:
78
+ ... print(f"Task: {task.id} - Status: {task.status}")
79
+ """
80
+ try:
81
+ client = APIClient(configuration=self.configuration)
82
+ tasks = await client.make_request(
83
+ path=APIRoute.ListTasks.format(agent_id=agent_id),
84
+ query=filters or {}
85
+ )
86
+ return [TasksListItem(**task) for task in tasks]
87
+ except Exception as e:
88
+ if isinstance(e, HTTPStatusError):
89
+ raise ModuleException(e.response.status_code, e.response.text)
90
+ raise ModuleException(500, f"Failed to list tasks - {str(e)}")
91
+
92
+ async def alist_user_tasks(self, user_id: str, filters: Optional[Dict] = None) -> List[TasksListItem]:
93
+ """
94
+ Asynchronously list all tasks for a specific user.
95
+
96
+ Retrieves a list of all tasks associated with a given user, providing
97
+ basic information for display and selection purposes.
98
+
99
+ Args:
100
+ user_id (str): The unique identifier of the user whose tasks should be listed.
101
+ filters (Optional[Dict]): Optional filters to be used on the query. supported filters: parent_task_id, triggering_agent_id, status, internal_status
102
+
103
+ Returns:
104
+ List[TasksListItem]: List of task summary objects related to the user.
105
+
106
+ Raises:
107
+ ModuleException: If the API request fails or returns an error.
108
+
109
+ Example:
110
+ >>> tasks = Tasks()
111
+ >>> task_list = await tasks.alist(user_id="user123")
112
+ >>> for task in task_list:
113
+ ... print(f"Task: {task.id} - Status: {task.status}")
114
+ """
115
+ try:
116
+ client = APIClient(configuration=self.configuration)
117
+ tasks = await client.make_request(
118
+ path=APIRoute.ListUserTasks.format(user_id=user_id),
119
+ query=filters or {}
120
+ )
121
+ return [TasksListItem(**task) for task in tasks]
122
+ except Exception as e:
123
+ if isinstance(e, HTTPStatusError):
124
+ raise ModuleException(e.response.status_code, e.response.text)
125
+ raise ModuleException(500, f"Failed to list user tasks - {str(e)}")
126
+
127
+ def list(self, agent_id: str, filters: Optional[Dict] = None) -> List[TasksListItem]:
128
+ """
129
+ Synchronously list all tasks for a specific agent.
130
+
131
+ This is the synchronous version of alist(). It internally calls the
132
+ asynchronous method and waits for completion.
133
+
134
+ Args:
135
+ agent_id (str): The unique identifier of the agent whose tasks should be listed.
136
+ filters (Optional[Dict]): Optional filters to be used on the query. supported filters: user_id, parent_task_id, triggering_agent_id, status, internal_status
137
+
138
+ Returns:
139
+ List[TasksListItem]: List of task summary objects related to the agent.
140
+
141
+ Raises:
142
+ ModuleException: If the API request fails or returns an error.
143
+
144
+ Example:
145
+ >>> tasks = Tasks()
146
+ >>> task_list = tasks.list(agent_id="agent123")
147
+ >>> print(f"Found {len(task_list)} tasks")
148
+ """
149
+ return run_sync(self.alist(agent_id=agent_id,filters=filters))
150
+
151
+ def list_user_tasks(self, user_id: str, filters: Optional[Dict] = None) -> List[TasksListItem]:
152
+ """
153
+ Synchronously list all tasks for a specific user.
154
+
155
+ This is the synchronous version of alist(). It internally calls the
156
+ asynchronous method and waits for completion.
157
+
158
+ Args:
159
+ user_id (str): The unique identifier of the user whose tasks should be listed.
160
+ filters (Optional[Dict]): Optional filters to be used on the query. supported filters: parent_task_id, triggering_agent_id, status, internal_status
161
+
162
+ Returns:
163
+ List[TasksListItem]: List of task summary objects related to the user.
164
+
165
+ Raises:
166
+ ModuleException: If the API request fails or returns an error.
167
+
168
+ Example:
169
+ >>> tasks = Tasks()
170
+ >>> task_list = tasks.list(user_id="user123")
171
+ >>> print(f"Found {len(task_list)} tasks")
172
+ """
173
+ return run_sync(self.alist_user_tasks(user_id=user_id, filters=filters))
174
+
175
+ async def aget(self, task_id: str) -> Task:
176
+ """
177
+ Asynchronously retrieve a specific task by its unique ID.
178
+
179
+ Loads and returns a full Task object with complete configuration and
180
+ execution details based on the task's unique identifier.
181
+
182
+ Args:
183
+ task_id (str): Unique identifier of the task to retrieve.
184
+
185
+ Returns:
186
+ Task: Complete task object with all associated metadata.
187
+
188
+ Raises:
189
+ ModuleException: If the task is not found or access is denied.
190
+
191
+ Example:
192
+ >>> tasks = Tasks()
193
+ >>> task = await tasks.aget(task_id="task456")
194
+ >>> print(f"Loaded task: {task.name}")
195
+ """
196
+ return await Task.aload(task_id=task_id, configuration=self.configuration)
197
+
198
+ def get(self, task_id: str) -> Task:
199
+ """
200
+ Synchronously retrieve a specific task by its unique ID.
201
+
202
+ This is the synchronous version of aget(). It internally calls the
203
+ asynchronous method and waits for completion.
204
+
205
+ Args:
206
+ task_id (str): Unique identifier of the task to retrieve.
207
+
208
+ Returns:
209
+ Task: Complete task object with all associated metadata.
210
+
211
+ Raises:
212
+ ModuleException: If the task is not found or access is denied.
213
+
214
+ Example:
215
+ >>> tasks = Tasks()
216
+ >>> task = tasks.get(task_id="task456")
217
+ >>> result = task.execute(parameters={"param": "value"})
218
+ """
219
+ return run_sync(self.aget(task_id=task_id))
220
+
221
+ async def acreate(
222
+ self,
223
+ agent_id: str,
224
+ existing_task_id: Optional[str] = None,
225
+ prompt: Optional[str] = "",
226
+ file_urls: Optional[List[str]] = [],
227
+ user_details: Optional[User] = None,
228
+ agent_version: Optional[str] = None,
229
+ tool_call_payload_extension: Optional[dict] = None,
230
+ source: Optional[str] = None,
231
+ worker_id: Optional[str] = None,
232
+ run_locally: Optional[bool] = False,
233
+ output_format: Optional[OutputFormat] = None,
234
+ output_schema: Optional[Dict] = None,
235
+ events_streaming: Optional[bool] = False,
236
+ additional_context: Optional[str] = None,
237
+ expected_output: Optional[str] = None,
238
+ mcp_servers: Optional[List[MCPServerDetails]] = [],
239
+ triggering_agent_id: Optional[str] = None,
240
+ title: Optional[str] = None,
241
+ think_mode: Optional[ThinkMode] = ThinkMode.Default,
242
+ disable_attachment_injection: Optional[bool] = False,
243
+ ) -> Task:
244
+ """
245
+ Asynchronously create a new task for a specific agent.
246
+
247
+ Initiates a new task using the xpander.ai platform, linking it to the specified
248
+ agent and using provided input and configuration parameters.
249
+
250
+ Args:
251
+ agent_id (str): The unique identifier of the agent responsible for executing the task.
252
+ existing_task_id (Optional[str]): Existing task id if exists.
253
+ prompt (Optional[str]): Textual input for task initiation.
254
+ file_urls (Optional[List[str]]): List URLs pointing to task-related files.
255
+ user_details (Optional[User]): User information to pass with the task.
256
+ agent_version (Optional[str]): Specific agent version to target.
257
+ tool_call_payload_extension (Optional[dict]): Extension details for tool call payload.
258
+ source (Optional[str]): Information about the task's origin.
259
+ worker_id (Optional[str]): Identifier for the worker handling the task.
260
+ run_locally (Optional[bool]): Whether to execute the task locally.
261
+ output_format (Optional[OutputFormat]): Format of the task output.
262
+ output_schema (Optional[Dict]): Schema defining the expected output structure.
263
+ events_streaming (Optional[bool]): Flag idicating for events are required for this task.
264
+ additional_context (Optional[str]): Additional context to be passed to the agent.
265
+ expected_output (Optional[str]): Expected output of the execution.
266
+ mcp_servers (Optional[List[MCPServerDetails]]): Optional list of mcp servers to use.
267
+ triggering_agent_id (Optional[str]): Optional triggering agent id.
268
+ title (Optional[str]): Optional task title.
269
+ think_mode (Optional[ThinkMode]): Optional task think mode, defaults to "default".
270
+ disable_attachment_injection (Optional[bool]): Optional selection if to disable attachment injection to the context window.
271
+
272
+ Returns:
273
+ Task: Newly created task object containing all initial configuration data.
274
+
275
+ Raises:
276
+ ModuleException: If task creation fails due to API problems or configuration errors.
277
+
278
+ Example:
279
+ >>> tasks = Tasks()
280
+ >>> new_task = await tasks.acreate(
281
+ ... agent_id="agent123",
282
+ ... prompt="Analyze the provided dataset",
283
+ ... file_urls=["https://data.source/file.csv"]
284
+ ... )
285
+ >>> print(f"Created task: {new_task.id}")
286
+ """
287
+ try:
288
+ headers = {}
289
+ if agent_version:
290
+ headers['x-agent-version'] = str(agent_version)
291
+
292
+ client = APIClient(configuration=self.configuration)
293
+ created_task = await client.make_request(
294
+ path=APIRoute.TaskCrud.format(agent_or_task_id=agent_id),
295
+ method="POST",
296
+ headers=headers,
297
+ payload={
298
+ "id": existing_task_id,
299
+ "input": AgentExecutionInput(
300
+ text=prompt, files=file_urls, user=user_details
301
+ ).model_dump(),
302
+ "payload_extension": tool_call_payload_extension,
303
+ "source": source,
304
+ "worker_id": worker_id,
305
+ "output_format": output_format,
306
+ "output_schema": output_schema,
307
+ "run_locally": run_locally,
308
+ "events_streaming": events_streaming,
309
+ "additional_context": additional_context,
310
+ "expected_output": expected_output,
311
+ "mcp_servers": [server.model_dump() for server in mcp_servers],
312
+ "triggering_agent_id": triggering_agent_id,
313
+ "title": title,
314
+ "think_mode": think_mode.value,
315
+ "disable_attachment_injection": disable_attachment_injection,
316
+ },
317
+ )
318
+ return Task(**created_task, configuration=self.configuration)
319
+ except Exception as e:
320
+ if isinstance(e, HTTPStatusError):
321
+ raise ModuleException(e.response.status_code, e.response.text)
322
+ raise ModuleException(500, f"Failed to create task - {str(e)}")
323
+
324
+ def create(self, *args, **kwargs) -> Task:
325
+ """
326
+ Synchronously create a new task for a specific agent.
327
+
328
+ This is the synchronous version of acreate(). It internally calls the
329
+ asynchronous method and waits for completion.
330
+
331
+ Args:
332
+ *args, **kwargs: Arguments and keyword arguments matching those of acreate().
333
+
334
+ Returns:
335
+ Task: Newly created task object containing all initial configuration data.
336
+
337
+ Raises:
338
+ ModuleException: If task creation fails due to API problems or configuration errors.
339
+
340
+ Example:
341
+ >>> tasks = Tasks()
342
+ >>> new_task = tasks.create(
343
+ ... agent_id="agent123",
344
+ ... prompt="Analyze the provided dataset",
345
+ ... file_urls=["https://data.source/file.csv"]
346
+ ... )
347
+ >>> print(f"Created task: {new_task.id}")
348
+ """
349
+ return run_sync(self.acreate(*args, **kwargs))
350
+
351
+ async def aupdate(
352
+ self,
353
+ task_id: str,
354
+ tool_call_payload_extension: Optional[dict] = None,
355
+ source: Optional[str] = None,
356
+ status: Optional[AgentExecutionStatus] = None,
357
+ last_executed_node_id: Optional[str] = None,
358
+ result: Optional[str] = None,
359
+ ) -> Task:
360
+ """
361
+ Asynchronously update an existing task by its unique ID.
362
+
363
+ Modifies task attributes, statuses, or results on the xpander.ai platform.
364
+
365
+ Args:
366
+ task_id (str): Unique identifier of the task to update.
367
+ tool_call_payload_extension (Optional[dict]): Details to extend the payload.
368
+ source (Optional[str]): Information about the task's origin.
369
+ status (Optional[AgentExecutionStatus]): New status for the task.
370
+ last_executed_node_id (Optional[str]): Identifier of the last executed node.
371
+ result (Optional[str]): Result data or message to associate with the task.
372
+
373
+ Returns:
374
+ Task: Updated task object reflecting the changes made.
375
+
376
+ Raises:
377
+ ModuleException: If task update fails due to API problems or configuration errors.
378
+
379
+ Example:
380
+ >>> tasks = Tasks()
381
+ >>> updated_task = await tasks.aupdate(
382
+ ... task_id="task456",
383
+ ... status=AgentExecutionStatus.COMPLETED,
384
+ ... result="Task successfully executed"
385
+ ... )
386
+ >>> print(f"Updated task: {updated_task.id} - {updated_task.status}")
387
+ """
388
+ client = APIClient(configuration=self.configuration)
389
+
390
+ payload: dict = {
391
+ "tool_call_payload_extension": tool_call_payload_extension,
392
+ "source": source,
393
+ "status": status.value if status else None,
394
+ "last_executed_node_id": last_executed_node_id,
395
+ "result": result,
396
+ }
397
+ payload = {k: v for k, v in payload.items() if v is not None}
398
+
399
+ try:
400
+ response = await client.make_request(
401
+ path=APIRoute.UpdateTask.format(task_id=task_id),
402
+ method="PATCH",
403
+ payload=payload,
404
+ )
405
+ return Task(**response, configuration=self.configuration)
406
+ except HTTPStatusError as e:
407
+ raise ModuleException(e.response.status_code, e.response.text)
408
+ except Exception as e:
409
+ raise ModuleException(500, f"Failed to update task: {str(e)}")
410
+
411
+ def update(self, *args, **kwargs) -> Task:
412
+ """
413
+ Synchronously update an existing task by its unique ID.
414
+
415
+ This is the synchronous version of aupdate(). It internally calls the
416
+ asynchronous method and waits for completion.
417
+
418
+ Args:
419
+ *args, **kwargs: Arguments and keyword arguments matching those of aupdate().
420
+
421
+ Returns:
422
+ Task: Updated task object reflecting the changes made.
423
+
424
+ Raises:
425
+ ModuleException: If task update fails due to API problems or configuration errors.
426
+
427
+ Example:
428
+ >>> tasks = Tasks()
429
+ >>> updated_task = tasks.update(
430
+ ... task_id="task456",
431
+ ... status=AgentExecutionStatus.COMPLETED,
432
+ ... result="Task successfully executed"
433
+ ... )
434
+ >>> print(f"Updated task: {updated_task.id} - {updated_task.status}")
435
+ """
436
+ return run_sync(self.aupdate(*args, **kwargs))
437
+
438
+ async def astop(self, task_id: str) -> Task:
439
+ """
440
+ Asynchronously stop a specific task by its unique ID.
441
+
442
+ Terminates the running task on the xpander.ai platform gracefully
443
+ by using its unique identifier.
444
+
445
+ Args:
446
+ task_id (str): Unique identifier of the task to stop.
447
+
448
+ Returns:
449
+ Task: Task object reflecting the task's stopped state.
450
+
451
+ Raises:
452
+ ModuleException: If task stopping fails due to API problems or configuration errors.
453
+
454
+ Example:
455
+ >>> tasks = Tasks()
456
+ >>> stopped_task = await tasks.astop(task_id="task456")
457
+ >>> print(f"Stopped task: {stopped_task.id}")
458
+ """
459
+ try:
460
+ client = APIClient(configuration=self.configuration)
461
+ created_task = await client.make_request(
462
+ path=APIRoute.TaskCrud.format(agent_or_task_id=task_id),
463
+ method="DELETE",
464
+ )
465
+ return Task(**created_task, configuration=self.configuration)
466
+ except Exception as e:
467
+ if isinstance(e, HTTPStatusError):
468
+ raise ModuleException(e.response.status_code, e.response.text)
469
+ raise ModuleException(500, f"Failed to stop task - {str(e)}")
470
+
471
+ def stop(self, task_id: str) -> Task:
472
+ """
473
+ Synchronously stop a specific task by its unique ID.
474
+
475
+ This is the synchronous version of astop(). It internally calls the
476
+ asynchronous method and waits for completion.
477
+
478
+ Args:
479
+ task_id (str): Unique identifier of the task to stop.
480
+
481
+ Returns:
482
+ Task: Task object reflecting the task's stopped state.
483
+
484
+ Raises:
485
+ ModuleException: If task stopping fails due to API problems or configuration errors.
486
+
487
+ Example:
488
+ >>> tasks = Tasks()
489
+ >>> stopped_task = tasks.stop(task_id="task456")
490
+ >>> print(f"Stopped task: {stopped_task.id}")
491
+ """
492
+ return run_sync(self.astop(task_id=task_id))
File without changes
@@ -0,0 +1,114 @@
1
+ import base64
2
+ import mimetypes
3
+ from typing import List, Optional
4
+ from urllib.parse import urlparse
5
+ import os
6
+ from pydantic import BaseModel
7
+ import httpx
8
+ import asyncio
9
+
10
+ class FileCategorization(BaseModel):
11
+ images: List[str]
12
+ pdfs: List[str]
13
+ files: List[str]
14
+
15
+
16
+ def categorize_files(file_urls: list[str]) -> FileCategorization:
17
+ """
18
+ Categorize a list of file URLs into images, PDFs, and human-readable files
19
+ based on file extensions. Does not load the files.
20
+
21
+ Args:
22
+ file_urls (list[str]): List of file URLs
23
+
24
+ Returns:
25
+ FileCategorization: Pydantic model with categorized URLs
26
+ """
27
+ image_exts = {".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".webp", ".svg"}
28
+ pdf_exts = {".pdf"}
29
+ human_readable_exts = {
30
+ ".txt", ".csv", ".json", ".xml", ".html", ".htm",
31
+ ".md", ".rst", ".yaml", ".yml",
32
+ ".py", ".js", ".ts", ".java", ".c", ".cpp", ".h", ".cs", ".go", ".rb", ".php", ".sh"
33
+ }
34
+
35
+ result = {"images": [], "pdfs": [], "files": []}
36
+
37
+ for url in file_urls:
38
+ path = urlparse(url).path
39
+ _, ext = os.path.splitext(path.lower())
40
+
41
+ if ext in image_exts:
42
+ result["images"].append(url)
43
+ elif ext in pdf_exts:
44
+ result["pdfs"].append(url)
45
+ elif ext in human_readable_exts:
46
+ result["files"].append(url)
47
+
48
+ return FileCategorization(**result)
49
+
50
+ async def fetch_urls(urls: list[str], disable_attachment_injection: Optional[bool] = False) -> list[dict[str, str]]:
51
+ """
52
+ Fetches the content of multiple URLs asynchronously.
53
+
54
+ Args:
55
+ urls (list[str]): List of URLs to fetch.
56
+
57
+ Returns:
58
+ list[dict[str, str]]: A list of dictionaries containing the URL and its content.
59
+ Example: [{"url": "...", "content": "..."}]
60
+ disable_attachment_injection (Optional[bool]): Optional selection if to disable attachment injection to the context window.
61
+ """
62
+ async def fetch(client: httpx.AsyncClient, url: str) -> dict[str, str]:
63
+ try:
64
+ if disable_attachment_injection:
65
+ return {"url": url}
66
+ response = await client.get(url, timeout=10.0)
67
+ response.raise_for_status()
68
+ return {"url": url, "content": response.text}
69
+ except Exception as e:
70
+ return {"url": url, "content": f"Error: {str(e)}"}
71
+
72
+ async with httpx.AsyncClient() as client:
73
+ tasks = [fetch(client, url) for url in urls]
74
+ return await asyncio.gather(*tasks)
75
+
76
+ def fetch_file(url: str):
77
+ """
78
+ Fetch a remote file from URL and wrap it as a File object.
79
+ Automatically derives filename, name, format, and mime type.
80
+
81
+ Args:
82
+ url (str): Remote file URL.
83
+
84
+ Returns:
85
+ File: Wrapped File object with base64 content.
86
+ """
87
+ # Fetch the file
88
+ with httpx.Client() as client:
89
+ response = client.get(url)
90
+ response.raise_for_status()
91
+ content = response.content
92
+
93
+ # Derive filename from URL
94
+ filename = os.path.basename(url.split("?")[0])
95
+
96
+ # Human-friendly name (strip extension, replace underscores)
97
+ name = os.path.splitext(filename)[0].replace("_", " ")
98
+
99
+ # Guess format and mime type
100
+ ext = os.path.splitext(filename)[1].lstrip(".").lower()
101
+ mime, _ = mimetypes.guess_type(filename)
102
+ mime = mime or "application/octet-stream"
103
+
104
+ # Encode content
105
+ content_b64 = base64.b64encode(content).decode("utf-8")
106
+ from agno.media import File
107
+
108
+ return File.from_base64(
109
+ base64_content=content_b64,
110
+ filename=filename,
111
+ name=name,
112
+ format=ext,
113
+ mime_type=mime
114
+ )
File without changes