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.
- xpander_sdk/__init__.py +76 -7793
- xpander_sdk/consts/__init__.py +0 -0
- xpander_sdk/consts/api_routes.py +63 -0
- xpander_sdk/core/__init__.py +0 -0
- xpander_sdk/core/module_base.py +164 -0
- xpander_sdk/core/state.py +10 -0
- xpander_sdk/core/xpander_api_client.py +119 -0
- xpander_sdk/exceptions/__init__.py +0 -0
- xpander_sdk/exceptions/module_exception.py +45 -0
- xpander_sdk/models/__init__.py +0 -0
- xpander_sdk/models/activity.py +65 -0
- xpander_sdk/models/configuration.py +92 -0
- xpander_sdk/models/events.py +70 -0
- xpander_sdk/models/frameworks.py +64 -0
- xpander_sdk/models/shared.py +102 -0
- xpander_sdk/models/user.py +21 -0
- xpander_sdk/modules/__init__.py +0 -0
- xpander_sdk/modules/agents/__init__.py +0 -0
- xpander_sdk/modules/agents/agents_module.py +164 -0
- xpander_sdk/modules/agents/models/__init__.py +0 -0
- xpander_sdk/modules/agents/models/agent.py +477 -0
- xpander_sdk/modules/agents/models/agent_list.py +107 -0
- xpander_sdk/modules/agents/models/knowledge_bases.py +33 -0
- xpander_sdk/modules/agents/sub_modules/__init__.py +0 -0
- xpander_sdk/modules/agents/sub_modules/agent.py +953 -0
- xpander_sdk/modules/agents/utils/__init__.py +0 -0
- xpander_sdk/modules/agents/utils/generic.py +2 -0
- xpander_sdk/modules/backend/__init__.py +0 -0
- xpander_sdk/modules/backend/backend_module.py +425 -0
- xpander_sdk/modules/backend/frameworks/__init__.py +0 -0
- xpander_sdk/modules/backend/frameworks/agno.py +627 -0
- xpander_sdk/modules/backend/frameworks/dispatch.py +36 -0
- xpander_sdk/modules/backend/utils/__init__.py +0 -0
- xpander_sdk/modules/backend/utils/mcp_oauth.py +95 -0
- xpander_sdk/modules/events/__init__.py +0 -0
- xpander_sdk/modules/events/decorators/__init__.py +0 -0
- xpander_sdk/modules/events/decorators/on_boot.py +94 -0
- xpander_sdk/modules/events/decorators/on_shutdown.py +94 -0
- xpander_sdk/modules/events/decorators/on_task.py +203 -0
- xpander_sdk/modules/events/events_module.py +629 -0
- xpander_sdk/modules/events/models/__init__.py +0 -0
- xpander_sdk/modules/events/models/deployments.py +25 -0
- xpander_sdk/modules/events/models/events.py +57 -0
- xpander_sdk/modules/events/utils/__init__.py +0 -0
- xpander_sdk/modules/events/utils/generic.py +56 -0
- xpander_sdk/modules/events/utils/git_init.py +32 -0
- xpander_sdk/modules/knowledge_bases/__init__.py +0 -0
- xpander_sdk/modules/knowledge_bases/knowledge_bases_module.py +217 -0
- xpander_sdk/modules/knowledge_bases/models/__init__.py +0 -0
- xpander_sdk/modules/knowledge_bases/models/knowledge_bases.py +11 -0
- xpander_sdk/modules/knowledge_bases/sub_modules/__init__.py +0 -0
- xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base.py +107 -0
- xpander_sdk/modules/knowledge_bases/sub_modules/knowledge_base_document_item.py +40 -0
- xpander_sdk/modules/knowledge_bases/utils/__init__.py +0 -0
- xpander_sdk/modules/tasks/__init__.py +0 -0
- xpander_sdk/modules/tasks/models/__init__.py +0 -0
- xpander_sdk/modules/tasks/models/task.py +153 -0
- xpander_sdk/modules/tasks/models/tasks_list.py +107 -0
- xpander_sdk/modules/tasks/sub_modules/__init__.py +0 -0
- xpander_sdk/modules/tasks/sub_modules/task.py +887 -0
- xpander_sdk/modules/tasks/tasks_module.py +492 -0
- xpander_sdk/modules/tasks/utils/__init__.py +0 -0
- xpander_sdk/modules/tasks/utils/files.py +114 -0
- xpander_sdk/modules/tools_repository/__init__.py +0 -0
- xpander_sdk/modules/tools_repository/decorators/__init__.py +0 -0
- xpander_sdk/modules/tools_repository/decorators/register_tool.py +108 -0
- xpander_sdk/modules/tools_repository/models/__init__.py +0 -0
- xpander_sdk/modules/tools_repository/models/mcp.py +68 -0
- xpander_sdk/modules/tools_repository/models/tool_invocation_result.py +14 -0
- xpander_sdk/modules/tools_repository/sub_modules/__init__.py +0 -0
- xpander_sdk/modules/tools_repository/sub_modules/tool.py +578 -0
- xpander_sdk/modules/tools_repository/tools_repository_module.py +259 -0
- xpander_sdk/modules/tools_repository/utils/__init__.py +0 -0
- xpander_sdk/modules/tools_repository/utils/generic.py +57 -0
- xpander_sdk/modules/tools_repository/utils/local_tools.py +52 -0
- xpander_sdk/modules/tools_repository/utils/schemas.py +308 -0
- xpander_sdk/utils/__init__.py +0 -0
- xpander_sdk/utils/env.py +44 -0
- xpander_sdk/utils/event_loop.py +67 -0
- xpander_sdk/utils/tools.py +32 -0
- xpander_sdk-2.0.155.dist-info/METADATA +538 -0
- xpander_sdk-2.0.155.dist-info/RECORD +85 -0
- {xpander_sdk-1.60.8.dist-info → xpander_sdk-2.0.155.dist-info}/WHEEL +1 -1
- {xpander_sdk-1.60.8.dist-info → xpander_sdk-2.0.155.dist-info/licenses}/LICENSE +0 -1
- xpander_sdk/_jsii/__init__.py +0 -39
- xpander_sdk/_jsii/xpander-sdk@1.60.8.jsii.tgz +0 -0
- xpander_sdk/py.typed +0 -1
- xpander_sdk-1.60.8.dist-info/METADATA +0 -368
- xpander_sdk-1.60.8.dist-info/RECORD +0 -9
- {xpander_sdk-1.60.8.dist-info → xpander_sdk-2.0.155.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,953 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent management and execution models for xpander.ai SDK.
|
|
3
|
+
|
|
4
|
+
This module contains data models and methods for managing and executing
|
|
5
|
+
agents in the xpander.ai Backend-as-a-Service platform.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
import heapq
|
|
11
|
+
import re
|
|
12
|
+
from typing import Any, Callable, Dict, List, Optional, Type, TypeVar, Union
|
|
13
|
+
from httpx import HTTPStatusError
|
|
14
|
+
from loguru import logger
|
|
15
|
+
from pydantic import ConfigDict, computed_field
|
|
16
|
+
from strands import tool as strands_tool
|
|
17
|
+
from xpander_sdk.consts.api_routes import APIRoute
|
|
18
|
+
from xpander_sdk.core.xpander_api_client import APIClient
|
|
19
|
+
from xpander_sdk.exceptions.module_exception import ModuleException
|
|
20
|
+
from xpander_sdk.models.configuration import Configuration
|
|
21
|
+
from xpander_sdk.models.frameworks import AgnoSettings, Framework
|
|
22
|
+
from xpander_sdk.modules.agents.utils.generic import get_db_schema_name
|
|
23
|
+
from xpander_sdk.modules.knowledge_bases.models.knowledge_bases import (
|
|
24
|
+
KnowledgeBaseSearchResult,
|
|
25
|
+
)
|
|
26
|
+
from xpander_sdk.modules.tools_repository.models.mcp import MCPServerDetails
|
|
27
|
+
from xpander_sdk.models.shared import LLMModelT, OutputFormat, ThinkMode, XPanderSharedModel
|
|
28
|
+
from xpander_sdk.models.user import User
|
|
29
|
+
from xpander_sdk.modules.agents.models.agent import (
|
|
30
|
+
AIAgentConnectivityDetailsA2A,
|
|
31
|
+
AIAgentConnectivityDetailsCurl,
|
|
32
|
+
AgentAccessScope,
|
|
33
|
+
AgentDeploymentType,
|
|
34
|
+
AgentGraphItem,
|
|
35
|
+
AgentGraphItemType,
|
|
36
|
+
AgentInstructions,
|
|
37
|
+
AgentOutput,
|
|
38
|
+
AgentSourceNode,
|
|
39
|
+
AgentStatus,
|
|
40
|
+
AgentType,
|
|
41
|
+
DatabaseConnectionString,
|
|
42
|
+
LLMCredentials,
|
|
43
|
+
LLMReasoningEffort,
|
|
44
|
+
)
|
|
45
|
+
from xpander_sdk.modules.agents.models.knowledge_bases import AgentKnowledgeBase
|
|
46
|
+
from xpander_sdk.modules.knowledge_bases.knowledge_bases_module import KnowledgeBases
|
|
47
|
+
from xpander_sdk.modules.knowledge_bases.sub_modules.knowledge_base import KnowledgeBase
|
|
48
|
+
from xpander_sdk.modules.tasks.models.task import AgentExecutionInput
|
|
49
|
+
from xpander_sdk.modules.tasks.sub_modules.task import Task
|
|
50
|
+
from xpander_sdk.modules.tools_repository.models.tool_invocation_result import (
|
|
51
|
+
ToolInvocationResult,
|
|
52
|
+
)
|
|
53
|
+
from xpander_sdk.modules.tools_repository.sub_modules.tool import Tool
|
|
54
|
+
from xpander_sdk.modules.tools_repository.tools_repository_module import ToolsRepository
|
|
55
|
+
from xpander_sdk.modules.tools_repository.utils.schemas import build_model_from_schema
|
|
56
|
+
from xpander_sdk.utils.event_loop import run_sync
|
|
57
|
+
from xpander_sdk.utils.tools import get_openai_agents_sdk_tools
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class AgentGraph(XPanderSharedModel):
|
|
61
|
+
"""
|
|
62
|
+
Model representing the graph structure of an agent's execution flow.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
items (List[AgentGraphItem]): List of all items in the agent's execution graph.
|
|
66
|
+
|
|
67
|
+
Methods:
|
|
68
|
+
__init__: Initialize with a list of graph items.
|
|
69
|
+
get_graph_item: Retrieve a specific graph item by attribute.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
items: List[AgentGraphItem] = []
|
|
73
|
+
|
|
74
|
+
def __init__(self, graph: list[AgentGraphItem]):
|
|
75
|
+
"""
|
|
76
|
+
Initialize the agent graph with provided graph items.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
graph (list[AgentGraphItem]): List of graph item definitions.
|
|
80
|
+
"""
|
|
81
|
+
super().__init__()
|
|
82
|
+
self.items = [AgentGraphItem(**item) for item in graph]
|
|
83
|
+
|
|
84
|
+
def get_graph_item(self, attr: str, value: str):
|
|
85
|
+
"""
|
|
86
|
+
Retrieve a specific item from the agent's graph based on a matching attribute.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
attr (str): Attribute name to match.
|
|
90
|
+
value (str): Value of the attribute to find.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Optional[AgentGraphItem]: The graph item if found, otherwise None.
|
|
94
|
+
"""
|
|
95
|
+
for gi in self.items:
|
|
96
|
+
if getattr(gi, attr, None) == value:
|
|
97
|
+
return gi
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
@computed_field
|
|
101
|
+
@property
|
|
102
|
+
def sub_agents(self) -> List[str]:
|
|
103
|
+
"""
|
|
104
|
+
Retrieve the list of sub-agent IDs associated with this agent.
|
|
105
|
+
|
|
106
|
+
This property returns the IDs of all agents that are nested under the current agent.
|
|
107
|
+
A sub-agent is identified when the `type` of the graph item matches `AgentGraphItemType.AGENT`.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
List[str]:
|
|
111
|
+
A list of unique string IDs representing the agents nested under this agent.
|
|
112
|
+
"""
|
|
113
|
+
return [
|
|
114
|
+
gi.item_id
|
|
115
|
+
for gi in self.items
|
|
116
|
+
if gi.type == AgentGraphItemType.AGENT
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
T = TypeVar("T", bound="Agent")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class Agent(XPanderSharedModel):
|
|
125
|
+
"""
|
|
126
|
+
Main class for managing agent configuration and execution in xpander.ai.
|
|
127
|
+
|
|
128
|
+
This class captures the complete state and configuration of an agent within
|
|
129
|
+
the xpander.ai platform, including handling task creation, tool invocation,
|
|
130
|
+
and knowledge base interactions.
|
|
131
|
+
|
|
132
|
+
Attributes:
|
|
133
|
+
configuration: Optional[Configuration]
|
|
134
|
+
id: str
|
|
135
|
+
organization_id: str
|
|
136
|
+
name: str
|
|
137
|
+
description: Optional[str]
|
|
138
|
+
unique_name: str
|
|
139
|
+
origin_template: Optional[str]
|
|
140
|
+
environment_id: str
|
|
141
|
+
tools: Optional[ToolsRepository]
|
|
142
|
+
icon: Optional[str]
|
|
143
|
+
source_nodes: Optional[List[AgentSourceNode]]
|
|
144
|
+
access_scope: Optional[AgentAccessScope]
|
|
145
|
+
instructions: Optional[AgentInstructions]
|
|
146
|
+
framework: Framework
|
|
147
|
+
graph: Optional[AgentGraph]
|
|
148
|
+
status: Optional[AgentStatus]
|
|
149
|
+
knowledge_bases: Optional[List[AgentKnowledgeBase]]
|
|
150
|
+
version: Optional[int]
|
|
151
|
+
created_by: Optional[str]
|
|
152
|
+
using_nemo: Optional[bool]
|
|
153
|
+
model_provider: str
|
|
154
|
+
model_name: str
|
|
155
|
+
llm_reasoning_effort: Optional[LLMReasoningEffort] = LLMReasoningEffort.Medium
|
|
156
|
+
llm_api_base: Optional[str]
|
|
157
|
+
webhook_url: Optional[str]
|
|
158
|
+
created_at: Optional[datetime]
|
|
159
|
+
type: Optional[AgentType]
|
|
160
|
+
connectivity_details: Optional[Union[AIAgentConnectivityDetailsA2A,AIAgentConnectivityDetailsCurl, Dict]] = {}
|
|
161
|
+
output_format: Optional[OutputFormat]
|
|
162
|
+
output_schema: Optional[Dict]
|
|
163
|
+
llm_credentials: Optional[LLMCredentials]
|
|
164
|
+
expected_output: Optional[str]
|
|
165
|
+
agno_settings: Optional[AgnoSettings]
|
|
166
|
+
|
|
167
|
+
Example:
|
|
168
|
+
>>> agent = Agent(id="agent123", name="Example Agent")
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
configuration: Optional[Configuration] = None
|
|
172
|
+
id: str
|
|
173
|
+
organization_id: str
|
|
174
|
+
name: str
|
|
175
|
+
description: Optional[str] = None
|
|
176
|
+
unique_name: str
|
|
177
|
+
origin_template: Optional[str] = None
|
|
178
|
+
environment_id: str = None
|
|
179
|
+
tools: Optional[ToolsRepository] = None
|
|
180
|
+
icon: Optional[str] = "🚀"
|
|
181
|
+
connectivity_details: Optional[Union[AIAgentConnectivityDetailsA2A,AIAgentConnectivityDetailsCurl, Dict]] = {}
|
|
182
|
+
deployment_type: Optional[AgentDeploymentType] = AgentDeploymentType.Serverless
|
|
183
|
+
source_nodes: Optional[List[AgentSourceNode]] = []
|
|
184
|
+
access_scope: Optional[AgentAccessScope] = AgentAccessScope.Organizational
|
|
185
|
+
instructions: Optional[AgentInstructions] = AgentInstructions(
|
|
186
|
+
role=[], goal=[], general=""
|
|
187
|
+
)
|
|
188
|
+
framework: Framework # agents framework
|
|
189
|
+
graph: Optional[AgentGraph] = None
|
|
190
|
+
status: Optional[AgentStatus] = AgentStatus.ACTIVE
|
|
191
|
+
knowledge_bases: Optional[List[AgentKnowledgeBase]] = []
|
|
192
|
+
version: Optional[int] = 1
|
|
193
|
+
created_by: Optional[str] = None
|
|
194
|
+
using_nemo: Optional[bool] = False
|
|
195
|
+
model_provider: str
|
|
196
|
+
model_name: str
|
|
197
|
+
llm_reasoning_effort: Optional[LLMReasoningEffort] = LLMReasoningEffort.Medium
|
|
198
|
+
llm_api_base: Optional[str] = None
|
|
199
|
+
webhook_url: Optional[str] = None
|
|
200
|
+
created_at: Optional[datetime] = None
|
|
201
|
+
type: Optional[AgentType] = None
|
|
202
|
+
output_format: Optional[OutputFormat] = OutputFormat.Markdown
|
|
203
|
+
output_schema: Optional[Dict] = None
|
|
204
|
+
|
|
205
|
+
llm_credentials: Optional[LLMCredentials] = None
|
|
206
|
+
expected_output: Optional[str] = ""
|
|
207
|
+
agno_settings: Optional[AgnoSettings] = AgnoSettings()
|
|
208
|
+
|
|
209
|
+
_connection_string: Optional[DatabaseConnectionString] = None
|
|
210
|
+
|
|
211
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
212
|
+
|
|
213
|
+
def model_post_init(self, context):
|
|
214
|
+
"""
|
|
215
|
+
Post-initialization hook for the model.
|
|
216
|
+
|
|
217
|
+
This method sets the current agent in the global state and then calls the
|
|
218
|
+
parent class's `model_post_init` method.
|
|
219
|
+
|
|
220
|
+
Parameters:
|
|
221
|
+
context (Any): Context object provided during model initialization.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Any: The result from the superclass `model_post_init` method.
|
|
225
|
+
|
|
226
|
+
Note:
|
|
227
|
+
This method uses `self.configuration.state.agent = self` to register the current agent
|
|
228
|
+
in the global state.
|
|
229
|
+
|
|
230
|
+
Powered by xpander.ai
|
|
231
|
+
"""
|
|
232
|
+
self.configuration.state.agent = self
|
|
233
|
+
return super().model_post_init(context)
|
|
234
|
+
|
|
235
|
+
@computed_field
|
|
236
|
+
@property
|
|
237
|
+
def mcp_servers(self) -> List[MCPServerDetails]:
|
|
238
|
+
"""
|
|
239
|
+
List MCP server details configured in the agent's graph.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
List[MCPServerDetails]: Details of MCP servers configured for this agent.
|
|
243
|
+
"""
|
|
244
|
+
return [
|
|
245
|
+
gi.settings.mcp_settings
|
|
246
|
+
for gi in self.graph.items
|
|
247
|
+
if gi.type == AgentGraphItemType.MCP
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
@computed_field
|
|
251
|
+
@property
|
|
252
|
+
def output(self) -> AgentOutput:
|
|
253
|
+
"""
|
|
254
|
+
Construct the output settings for this agent.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
AgentOutput: Output configuration based on schema and format.
|
|
258
|
+
"""
|
|
259
|
+
return AgentOutput(
|
|
260
|
+
output_schema=(
|
|
261
|
+
build_model_from_schema(
|
|
262
|
+
model_name="StructuredOutput", schema=self.output_schema
|
|
263
|
+
)
|
|
264
|
+
if self.output_schema and self.output_format == OutputFormat.Json
|
|
265
|
+
else None
|
|
266
|
+
),
|
|
267
|
+
is_markdown=self.output_format == OutputFormat.Markdown,
|
|
268
|
+
use_json_mode=self.output_format == OutputFormat.Json,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
@classmethod
|
|
272
|
+
async def aload(
|
|
273
|
+
cls: Type[T],
|
|
274
|
+
agent_id: str,
|
|
275
|
+
configuration: Optional[Configuration] = None,
|
|
276
|
+
version: Optional[int] = None,
|
|
277
|
+
) -> T:
|
|
278
|
+
"""
|
|
279
|
+
Asynchronously load an agent's configuration and settings by ID.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
agent_id (str): Unique identifier of the agent.
|
|
283
|
+
configuration (Optional[Configuration]): SDK configuration to use.
|
|
284
|
+
version (Optional[int]): Specific agent version to load.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
T: Loaded agent object.
|
|
288
|
+
|
|
289
|
+
Example:
|
|
290
|
+
>>> agent = await Agent.aload(agent_id="agent123")
|
|
291
|
+
"""
|
|
292
|
+
try:
|
|
293
|
+
client = APIClient(configuration=configuration)
|
|
294
|
+
headers = {}
|
|
295
|
+
if version:
|
|
296
|
+
headers["x-agent-version"] = str(version)
|
|
297
|
+
|
|
298
|
+
response_data: dict = await client.make_request(
|
|
299
|
+
path=APIRoute.GetAgent.format(agent_id=agent_id), headers=headers
|
|
300
|
+
)
|
|
301
|
+
agent = cls.model_validate({**response_data, "graph": None, "tools": None, "configuration": configuration or Configuration()})
|
|
302
|
+
agent.graph = AgentGraph(response_data.get("graph", []))
|
|
303
|
+
agent.tools = ToolsRepository(
|
|
304
|
+
configuration=agent.configuration, tools=response_data.get("tools", []), agent_graph=agent.graph
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
if agent.tools.should_sync_local_tools():
|
|
308
|
+
asyncio.create_task(
|
|
309
|
+
agent.sync_local_tools(tools=agent.tools.get_local_tools_for_sync())
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
return agent
|
|
313
|
+
except HTTPStatusError as e:
|
|
314
|
+
raise ModuleException(
|
|
315
|
+
status_code=e.response.status_code, description=e.response.text
|
|
316
|
+
)
|
|
317
|
+
except Exception as e:
|
|
318
|
+
raise ModuleException(
|
|
319
|
+
status_code=500, description=f"Failed to load agent - {str(e)}"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
@classmethod
|
|
323
|
+
def load(
|
|
324
|
+
cls: Type[T],
|
|
325
|
+
agent_id: str,
|
|
326
|
+
configuration: Optional[Configuration] = None,
|
|
327
|
+
version: Optional[int] = None,
|
|
328
|
+
) -> T:
|
|
329
|
+
"""
|
|
330
|
+
Synchronously load an agent's configuration using its unique ID.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
agent_id (str): Unique identifier of the agent.
|
|
334
|
+
configuration (Optional[Configuration]): SDK configuration to use.
|
|
335
|
+
version (Optional[int]): Specific agent version to load.
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
T: Loaded agent object.
|
|
339
|
+
|
|
340
|
+
Example:
|
|
341
|
+
>>> agent = Agent.load(agent_id="agent123")
|
|
342
|
+
"""
|
|
343
|
+
return run_sync(
|
|
344
|
+
cls.aload(agent_id=agent_id, configuration=configuration, version=version)
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
async def aget_connection_string(self):
|
|
348
|
+
"""
|
|
349
|
+
Asynchronously retrieve the agent's connection string from the platform.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
DatabaseConnectionString: The connection string details.
|
|
353
|
+
"""
|
|
354
|
+
try:
|
|
355
|
+
if self._connection_string:
|
|
356
|
+
return self._connection_string
|
|
357
|
+
|
|
358
|
+
client = APIClient(configuration=self.configuration)
|
|
359
|
+
connection_string = await client.make_request(
|
|
360
|
+
path=APIRoute.GetAgentConnectionString.format(agent_id=self.id)
|
|
361
|
+
)
|
|
362
|
+
self._connection_string = DatabaseConnectionString(**connection_string)
|
|
363
|
+
return self._connection_string
|
|
364
|
+
except HTTPStatusError as e:
|
|
365
|
+
raise ModuleException(
|
|
366
|
+
status_code=e.response.status_code, description=e.response.text
|
|
367
|
+
)
|
|
368
|
+
except Exception as e:
|
|
369
|
+
raise ModuleException(
|
|
370
|
+
status_code=500,
|
|
371
|
+
description=f"Failed to get agent connection string - {str(e)}",
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
def get_connection_string(self):
|
|
375
|
+
"""
|
|
376
|
+
Synchronously retrieve the agent's connection string from the platform.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
DatabaseConnectionString: The connection string details.
|
|
380
|
+
|
|
381
|
+
Example:
|
|
382
|
+
>>> connection_string = agent.get_connection_string()
|
|
383
|
+
"""
|
|
384
|
+
return run_sync(self.aget_connection_string())
|
|
385
|
+
|
|
386
|
+
async def acreate_task(
|
|
387
|
+
self,
|
|
388
|
+
prompt: Optional[str] = "",
|
|
389
|
+
existing_task_id: Optional[str] = None,
|
|
390
|
+
file_urls: Optional[List[str]] = [],
|
|
391
|
+
user_details: Optional[User] = None,
|
|
392
|
+
agent_version: Optional[str] = None,
|
|
393
|
+
tool_call_payload_extension: Optional[dict] = None,
|
|
394
|
+
source: Optional[str] = "sdk",
|
|
395
|
+
worker_id: Optional[str] = None,
|
|
396
|
+
run_locally: Optional[bool] = False,
|
|
397
|
+
output_format: Optional[OutputFormat] = None,
|
|
398
|
+
output_schema: Optional[Dict] = None,
|
|
399
|
+
events_streaming: Optional[bool] = False,
|
|
400
|
+
additional_context: Optional[str] = None,
|
|
401
|
+
expected_output: Optional[str] = None,
|
|
402
|
+
mcp_servers: Optional[List[MCPServerDetails]] = [],
|
|
403
|
+
triggering_agent_id: Optional[str] = None,
|
|
404
|
+
title: Optional[str] = None,
|
|
405
|
+
think_mode: Optional[ThinkMode] = ThinkMode.Default,
|
|
406
|
+
disable_attachment_injection: Optional[bool] = False,
|
|
407
|
+
) -> Task:
|
|
408
|
+
"""
|
|
409
|
+
Asynchronously create a new task and link it to this agent.
|
|
410
|
+
|
|
411
|
+
Args:
|
|
412
|
+
prompt (Optional[str]): Task initiation prompt.
|
|
413
|
+
existing_task_id (Optional[str]): Existing task id if exists.
|
|
414
|
+
file_urls (Optional[List[str]]): URLs of files related to the task.
|
|
415
|
+
user_details (Optional[User]): User linked to this task context.
|
|
416
|
+
agent_version (Optional[str]): Optional agent version to use.
|
|
417
|
+
tool_call_payload_extension (Optional[dict]): Extend payload with additional information.
|
|
418
|
+
source (Optional[str]): Origin or source of the request. default = "sdk".
|
|
419
|
+
worker_id (Optional[str]): Worker identifier if applicable.
|
|
420
|
+
run_locally (Optional[bool]): Indicates if task should run locally.
|
|
421
|
+
output_format (Optional[OutputFormat]): Format for output response.
|
|
422
|
+
output_schema (Optional[Dict]): Schema defining structure of output.
|
|
423
|
+
events_streaming (Optional[bool]): Flag idicating for events are required for this task.
|
|
424
|
+
additional_context (Optional[str]): Additional context to be passed to the agent.
|
|
425
|
+
expected_output (Optional[str]): Expected output of the execution.
|
|
426
|
+
mcp_servers (Optional[List[MCPServerDetails]]): Optional list of mcp servers to use.
|
|
427
|
+
triggering_agent_id (Optional[str]): Optional triggering agent id.
|
|
428
|
+
title (Optional[str]): Optional task title.
|
|
429
|
+
think_mode (Optional[ThinkMode]): Optional task think mode, defaults to "default".
|
|
430
|
+
disable_attachment_injection (Optional[bool]): Optional selection if to disable attachment injection to the context window.
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
Task: Created Task object linked to this agent.
|
|
434
|
+
"""
|
|
435
|
+
try:
|
|
436
|
+
|
|
437
|
+
headers = {}
|
|
438
|
+
if agent_version:
|
|
439
|
+
headers['x-agent-version'] = str(agent_version)
|
|
440
|
+
|
|
441
|
+
client = APIClient(configuration=self.configuration)
|
|
442
|
+
created_task = await client.make_request(
|
|
443
|
+
path=APIRoute.TaskCrud.format(agent_or_task_id=self.id),
|
|
444
|
+
method="POST",
|
|
445
|
+
headers=headers,
|
|
446
|
+
payload={
|
|
447
|
+
"id": existing_task_id,
|
|
448
|
+
"input": AgentExecutionInput(
|
|
449
|
+
text=prompt, files=file_urls, user=user_details
|
|
450
|
+
).model_dump(),
|
|
451
|
+
"payload_extension": tool_call_payload_extension,
|
|
452
|
+
"source": source,
|
|
453
|
+
"worker_id": worker_id,
|
|
454
|
+
"output_format": output_format,
|
|
455
|
+
"output_schema": output_schema,
|
|
456
|
+
"run_locally": run_locally,
|
|
457
|
+
"events_streaming": events_streaming,
|
|
458
|
+
"additional_context": additional_context,
|
|
459
|
+
"expected_output": expected_output,
|
|
460
|
+
"mcp_servers": [server.model_dump() for server in mcp_servers],
|
|
461
|
+
"triggering_agent_id": triggering_agent_id,
|
|
462
|
+
"title": title,
|
|
463
|
+
"think_mode": think_mode.value,
|
|
464
|
+
"disable_attachment_injection": disable_attachment_injection,
|
|
465
|
+
},
|
|
466
|
+
)
|
|
467
|
+
return Task(**created_task, configuration=self.configuration)
|
|
468
|
+
except Exception as e:
|
|
469
|
+
if isinstance(e, HTTPStatusError):
|
|
470
|
+
raise ModuleException(e.response.status_code, e.response.text)
|
|
471
|
+
raise ModuleException(500, f"Failed to create task - {str(e)}")
|
|
472
|
+
|
|
473
|
+
def create_task(self, *args, **kwargs) -> Task:
|
|
474
|
+
"""
|
|
475
|
+
Synchronously create a new task for this agent.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
*args, **kwargs: Arguments matching acreate_task.
|
|
479
|
+
|
|
480
|
+
Returns:
|
|
481
|
+
Task: Created Task object linked to this agent.
|
|
482
|
+
|
|
483
|
+
Example:
|
|
484
|
+
>>> task = agent.create_task(prompt="Analyze data files")
|
|
485
|
+
"""
|
|
486
|
+
return run_sync(self.acreate_task(*args, **kwargs))
|
|
487
|
+
|
|
488
|
+
async def ainvoke_tool(
|
|
489
|
+
self,
|
|
490
|
+
tool: Tool,
|
|
491
|
+
payload: Any,
|
|
492
|
+
payload_extension: Optional[Dict] = {},
|
|
493
|
+
task_id: Optional[str] = None,
|
|
494
|
+
tool_call_id: Optional[str] = None,
|
|
495
|
+
) -> ToolInvocationResult:
|
|
496
|
+
"""
|
|
497
|
+
Asynchronously invoke a specific tool linked to the agent.
|
|
498
|
+
|
|
499
|
+
Args:
|
|
500
|
+
tool (Tool): Tool to be invoked during the execution.
|
|
501
|
+
payload (Any): Data payload to be passed to the tool.
|
|
502
|
+
payload_extension (Optional[Dict]): Optional payload extensions.
|
|
503
|
+
task_id (Optional[str]): Related task ID if linked.
|
|
504
|
+
tool_call_id (Optional[str]): Optional tool call identifier.
|
|
505
|
+
|
|
506
|
+
Returns:
|
|
507
|
+
ToolInvocationResult: Result object with execution details.
|
|
508
|
+
"""
|
|
509
|
+
return await tool.ainvoke(
|
|
510
|
+
agent_id=self.id,
|
|
511
|
+
payload=payload,
|
|
512
|
+
agent_version=self.version,
|
|
513
|
+
payload_extension=payload_extension,
|
|
514
|
+
task_id=task_id,
|
|
515
|
+
tool_call_id=tool_call_id,
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
def invoke_tool(self, *args, **kwargs) -> ToolInvocationResult:
|
|
519
|
+
"""
|
|
520
|
+
Synchronously invoke a specific tool linked to the agent.
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
*args, **kwargs: Arguments matching ainvoke_tool.
|
|
524
|
+
|
|
525
|
+
Returns:
|
|
526
|
+
ToolInvocationResult: Result object with execution details.
|
|
527
|
+
|
|
528
|
+
Example:
|
|
529
|
+
>>> result = agent.invoke_tool(my_tool, payload={"data": "value"})
|
|
530
|
+
"""
|
|
531
|
+
return run_sync(self.ainvoke_tool(*args, **kwargs))
|
|
532
|
+
|
|
533
|
+
async def aget_knowledge_bases(self) -> List[KnowledgeBase]:
|
|
534
|
+
"""
|
|
535
|
+
Asynchronously retrieve all linked knowledge bases for this agent.
|
|
536
|
+
|
|
537
|
+
Returns:
|
|
538
|
+
List[KnowledgeBase]: List of linked knowledge bases.
|
|
539
|
+
|
|
540
|
+
Example:
|
|
541
|
+
>>> knowledge_bases = await agent.aget_knowledge_bases()
|
|
542
|
+
"""
|
|
543
|
+
kb_modules = KnowledgeBases(configuration=self.configuration)
|
|
544
|
+
tasks = [
|
|
545
|
+
kb_modules.aget(knowledge_base_id=kb.id) for kb in self.knowledge_bases
|
|
546
|
+
]
|
|
547
|
+
return await asyncio.gather(*tasks)
|
|
548
|
+
|
|
549
|
+
def get_knowledge_bases(self) -> List[KnowledgeBase]:
|
|
550
|
+
"""
|
|
551
|
+
Synchronously retrieve all linked knowledge bases for this agent.
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
List[KnowledgeBase]: List of linked knowledge bases.
|
|
555
|
+
|
|
556
|
+
Example:
|
|
557
|
+
>>> knowledge_bases = agent.get_knowledge_bases()
|
|
558
|
+
"""
|
|
559
|
+
return run_sync(self.aget_knowledge_bases())
|
|
560
|
+
|
|
561
|
+
async def sync_local_tools(self, tools: List[Tool]):
|
|
562
|
+
"""
|
|
563
|
+
Asynchronously sync local tools with the backend graph representation.
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
tools (List[Tool]): List of local tools to sync.
|
|
567
|
+
"""
|
|
568
|
+
try:
|
|
569
|
+
client = APIClient(configuration=self.configuration)
|
|
570
|
+
response_data: dict = await client.make_request(
|
|
571
|
+
path=APIRoute.SyncLocalTools.format(agent_id=self.id),
|
|
572
|
+
method="PATCH",
|
|
573
|
+
payload=[
|
|
574
|
+
tool.model_dump(exclude={"fn", "schema", "configuration"})
|
|
575
|
+
for tool in tools
|
|
576
|
+
],
|
|
577
|
+
)
|
|
578
|
+
self.graph = AgentGraph(response_data.get("graph", []))
|
|
579
|
+
|
|
580
|
+
# set all local tools as synced
|
|
581
|
+
for tool in tools:
|
|
582
|
+
tool.is_synced = True
|
|
583
|
+
except Exception as e:
|
|
584
|
+
logger.warning(f"Failed to sync local tools - {str(e)}")
|
|
585
|
+
|
|
586
|
+
async def aget_db(self, async_db: Optional[bool] = True):
|
|
587
|
+
"""
|
|
588
|
+
Asynchronously retrieve the db for this agent.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
async_db (Optional[bool]): Should return async database client.
|
|
592
|
+
|
|
593
|
+
Returns:
|
|
594
|
+
AsyncPostgresDb|PostgresDb: Initialized (async?) db (Agno PG) for agent sessions.
|
|
595
|
+
|
|
596
|
+
Raises:
|
|
597
|
+
NotImplementedError: If the framework does not support storage.
|
|
598
|
+
ImportError: If required dependencies are missing.
|
|
599
|
+
ValueError: If the connection string for storage is invalid.
|
|
600
|
+
"""
|
|
601
|
+
framework = Framework.Agno # will be removed
|
|
602
|
+
if self.framework != framework:
|
|
603
|
+
raise NotImplementedError(
|
|
604
|
+
f"Storage for framework '{self.framework}' is not supported."
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
if not self.agno_settings.session_storage:
|
|
608
|
+
raise LookupError("Session storage is not enabled for this agent.")
|
|
609
|
+
|
|
610
|
+
try:
|
|
611
|
+
from agno.db.postgres import AsyncPostgresDb, PostgresDb
|
|
612
|
+
except ImportError as e:
|
|
613
|
+
raise ImportError(
|
|
614
|
+
"The 'agno' extras must be installed to use this db. "
|
|
615
|
+
"Run `pip install xpander-sdk[agno]`."
|
|
616
|
+
) from e
|
|
617
|
+
|
|
618
|
+
connection_string = await self.aget_connection_string()
|
|
619
|
+
if not connection_string or not connection_string.connection_uri.uri:
|
|
620
|
+
raise ValueError(
|
|
621
|
+
"Invalid connection string provided for Agno db."
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
schema = get_db_schema_name(agent_id=self.id)
|
|
625
|
+
|
|
626
|
+
client_type = AsyncPostgresDb if async_db else PostgresDb
|
|
627
|
+
|
|
628
|
+
return client_type(
|
|
629
|
+
db_schema=schema,
|
|
630
|
+
db_url=connection_string.connection_uri.uri.replace("postgresql", "postgresql+psycopg"+("_async" if async_db else "")),
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
def get_db(self) -> Any:
|
|
634
|
+
"""
|
|
635
|
+
Synchronously retrieve the db for this agent.
|
|
636
|
+
|
|
637
|
+
Returns:
|
|
638
|
+
Any: Initialized db for agent sessions.
|
|
639
|
+
|
|
640
|
+
Example:
|
|
641
|
+
>>> db = agent.get_db()
|
|
642
|
+
"""
|
|
643
|
+
return run_sync(self.aget_db(async_db=False))
|
|
644
|
+
|
|
645
|
+
@computed_field
|
|
646
|
+
@property
|
|
647
|
+
def search_knowledge(self) -> bool:
|
|
648
|
+
"""
|
|
649
|
+
Check if any knowledge bases are linked to this agent.
|
|
650
|
+
|
|
651
|
+
Returns:
|
|
652
|
+
bool: True if one or more knowledge bases are linked, otherwise False.
|
|
653
|
+
"""
|
|
654
|
+
return len(self.knowledge_bases) != 0
|
|
655
|
+
|
|
656
|
+
@computed_field
|
|
657
|
+
@property
|
|
658
|
+
def is_a_team(self) -> bool:
|
|
659
|
+
"""
|
|
660
|
+
Check if this agent run in Team mode
|
|
661
|
+
|
|
662
|
+
Returns:
|
|
663
|
+
bool: True if has sub agents, otherwise False.
|
|
664
|
+
"""
|
|
665
|
+
return self.graph.sub_agents and len(self.graph.sub_agents) != 0
|
|
666
|
+
|
|
667
|
+
def knowledge_bases_retriever(
|
|
668
|
+
self,
|
|
669
|
+
) -> Callable[[str, Optional[Any], int], asyncio.Future]:
|
|
670
|
+
"""
|
|
671
|
+
Retrieve callable to perform search within linked knowledge bases.
|
|
672
|
+
|
|
673
|
+
Returns:
|
|
674
|
+
Callable[[str, Optional[Any], int], asyncio.Future]: Function to execute a search query.
|
|
675
|
+
"""
|
|
676
|
+
|
|
677
|
+
def search(
|
|
678
|
+
query: str, agent: Optional[Any] = None, num_documents: int = 5, **kwargs
|
|
679
|
+
) -> Optional[List[dict]]:
|
|
680
|
+
"""
|
|
681
|
+
Perform search across all linked knowledge bases for a query.
|
|
682
|
+
|
|
683
|
+
Args:
|
|
684
|
+
query (str): Search query string.
|
|
685
|
+
agent (Optional[Any]): Optional agent context to refine query.
|
|
686
|
+
num_documents (int): Number of top documents to retrieve.
|
|
687
|
+
|
|
688
|
+
Returns:
|
|
689
|
+
Optional[List[dict]]: Top matching documents from knowledge bases.
|
|
690
|
+
"""
|
|
691
|
+
try:
|
|
692
|
+
num_documents = num_documents or 10
|
|
693
|
+
all_results: List[KnowledgeBaseSearchResult] = []
|
|
694
|
+
# Retrieve all the agent's knowledge bases
|
|
695
|
+
agents_kbs = self.get_knowledge_bases()
|
|
696
|
+
|
|
697
|
+
# Launch concurrent searches
|
|
698
|
+
for kb in agents_kbs:
|
|
699
|
+
all_results.extend(
|
|
700
|
+
kb.search(search_query=query, top_k=num_documents)
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
# Sort by score descending and return top N
|
|
704
|
+
sorted_results = heapq.nlargest(
|
|
705
|
+
num_documents, all_results, key=lambda x: x.score
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
return [result.model_dump() for result in sorted_results]
|
|
709
|
+
|
|
710
|
+
except Exception as e:
|
|
711
|
+
logger.error(f"Error during vector database search: {str(e)}")
|
|
712
|
+
return []
|
|
713
|
+
|
|
714
|
+
return search
|
|
715
|
+
|
|
716
|
+
@computed_field
|
|
717
|
+
@property
|
|
718
|
+
def is_active(self) -> bool:
|
|
719
|
+
"""
|
|
720
|
+
Check if the agent is active.
|
|
721
|
+
|
|
722
|
+
Returns:
|
|
723
|
+
bool: True if the agent is active, False if not.
|
|
724
|
+
"""
|
|
725
|
+
return self.status == AgentStatus.ACTIVE
|
|
726
|
+
|
|
727
|
+
async def aget_user_sessions(self, user_id: str):
|
|
728
|
+
"""
|
|
729
|
+
Asynchronously retrieve all user sessions associated with this agent.
|
|
730
|
+
|
|
731
|
+
This method loads all saved session records linked to the specified user ID from
|
|
732
|
+
the agent's db. It is only supported for agents using the Agno framework
|
|
733
|
+
with session storage enabled.
|
|
734
|
+
|
|
735
|
+
Args:
|
|
736
|
+
user_id (str): Identifier of the user whose sessions are to be retrieved.
|
|
737
|
+
|
|
738
|
+
Returns:
|
|
739
|
+
Any: A list of session records associated with the user.
|
|
740
|
+
|
|
741
|
+
Raises:
|
|
742
|
+
NotImplementedError: If the agent framework does not support session storage.
|
|
743
|
+
LookupError: If session storage is not enabled for this agent.
|
|
744
|
+
ImportError: If required dependencies for Agno storage are not installed.
|
|
745
|
+
ValueError: If the agent connection string is invalid.
|
|
746
|
+
|
|
747
|
+
Example:
|
|
748
|
+
>>> sessions = await agent.aget_user_sessions(user_id="user_123")
|
|
749
|
+
"""
|
|
750
|
+
db = await self.aget_db()
|
|
751
|
+
from agno.db import SessionType
|
|
752
|
+
sessions = await asyncio.to_thread(db.get_sessions, user_id=user_id, limit=50, session_type = SessionType.TEAM if self.is_a_team else SessionType.AGENT)
|
|
753
|
+
return sessions
|
|
754
|
+
|
|
755
|
+
def get_user_sessions(self, user_id: str):
|
|
756
|
+
"""
|
|
757
|
+
Synchronously retrieve all user sessions associated with this agent.
|
|
758
|
+
|
|
759
|
+
This method wraps the asynchronous `aget_user_sessions` method and returns the result
|
|
760
|
+
in a synchronous context. It loads session data for a given user ID from the agent's db.
|
|
761
|
+
|
|
762
|
+
Args:
|
|
763
|
+
user_id (str): Identifier of the user whose sessions are to be retrieved.
|
|
764
|
+
|
|
765
|
+
Returns:
|
|
766
|
+
Any: A list of sessions related to the given user.
|
|
767
|
+
|
|
768
|
+
Example:
|
|
769
|
+
>>> sessions = agent.get_user_sessions(user_id="user_123")
|
|
770
|
+
"""
|
|
771
|
+
return run_sync(self.aget_user_sessions(user_id=user_id))
|
|
772
|
+
|
|
773
|
+
async def aget_session(self, session_id: str):
|
|
774
|
+
"""
|
|
775
|
+
Asynchronously retrieve a single session by its session ID.
|
|
776
|
+
|
|
777
|
+
This method accesses the agent's db and loads the session record
|
|
778
|
+
corresponding to the given session ID. It is only supported for agents using
|
|
779
|
+
the Agno framework with session storage enabled.
|
|
780
|
+
|
|
781
|
+
Args:
|
|
782
|
+
session_id (str): Unique identifier of the session to retrieve.
|
|
783
|
+
|
|
784
|
+
Returns:
|
|
785
|
+
Any: A single session record if found, or None if the session does not exist.
|
|
786
|
+
|
|
787
|
+
Raises:
|
|
788
|
+
NotImplementedError: If the agent framework does not support session storage.
|
|
789
|
+
LookupError: If session storage is not enabled for this agent.
|
|
790
|
+
ImportError: If required dependencies for Agno storage are not installed.
|
|
791
|
+
ValueError: If the agent connection string is invalid.
|
|
792
|
+
|
|
793
|
+
Example:
|
|
794
|
+
>>> session = await agent.aget_session(session_id="sess_456")
|
|
795
|
+
"""
|
|
796
|
+
db = await self.aget_db()
|
|
797
|
+
from agno.db import SessionType
|
|
798
|
+
session = await asyncio.to_thread(db.get_session, session_id=session_id, session_type = SessionType.TEAM if self.is_a_team else SessionType.AGENT)
|
|
799
|
+
return await session
|
|
800
|
+
|
|
801
|
+
def get_session(self, session_id: str):
|
|
802
|
+
"""
|
|
803
|
+
Synchronously retrieve a single session by its session ID.
|
|
804
|
+
|
|
805
|
+
This method wraps the asynchronous `aget_session` and returns the result
|
|
806
|
+
in a synchronous context. It retrieves the session record from the agent's
|
|
807
|
+
db using the given session ID.
|
|
808
|
+
|
|
809
|
+
Args:
|
|
810
|
+
session_id (str): Unique identifier of the session to retrieve.
|
|
811
|
+
|
|
812
|
+
Returns:
|
|
813
|
+
Any: A single session record if found, or None if the session does not exist.
|
|
814
|
+
|
|
815
|
+
Example:
|
|
816
|
+
>>> session = agent.get_session(session_id="sess_456")
|
|
817
|
+
"""
|
|
818
|
+
return run_sync(self.aget_session(session_id=session_id))
|
|
819
|
+
|
|
820
|
+
async def adelete_session(self, session_id: str):
|
|
821
|
+
"""
|
|
822
|
+
Asynchronously delete a session by its session ID.
|
|
823
|
+
|
|
824
|
+
This method removes a specific session record from the agent's db
|
|
825
|
+
based on the provided session ID. It is only supported for agents using the
|
|
826
|
+
Agno framework with session storage enabled.
|
|
827
|
+
|
|
828
|
+
Args:
|
|
829
|
+
session_id (str): Unique identifier of the session to delete.
|
|
830
|
+
|
|
831
|
+
Raises:
|
|
832
|
+
NotImplementedError: If the agent framework does not support session storage.
|
|
833
|
+
LookupError: If session storage is not enabled for this agent.
|
|
834
|
+
ImportError: If required dependencies for Agno storage are not installed.
|
|
835
|
+
ValueError: If the agent connection string is invalid.
|
|
836
|
+
|
|
837
|
+
Example:
|
|
838
|
+
>>> await agent.adelete_session(session_id="sess_456")
|
|
839
|
+
"""
|
|
840
|
+
db = await self.aget_db(async_db=False)
|
|
841
|
+
await asyncio.to_thread(db.delete_session, session_id=session_id)
|
|
842
|
+
|
|
843
|
+
def delete_session(self, session_id: str):
|
|
844
|
+
"""
|
|
845
|
+
Synchronously delete a session by its session ID.
|
|
846
|
+
|
|
847
|
+
This method wraps the asynchronous `adelete_session` and removes the session
|
|
848
|
+
record from the agent's db in a synchronous context.
|
|
849
|
+
|
|
850
|
+
Args:
|
|
851
|
+
session_id (str): Unique identifier of the session to delete.
|
|
852
|
+
|
|
853
|
+
Example:
|
|
854
|
+
>>> agent.delete_session(session_id="sess_456")
|
|
855
|
+
"""
|
|
856
|
+
return run_sync(self.adelete_session(session_id=session_id))
|
|
857
|
+
|
|
858
|
+
def attach_knowledge_base(
|
|
859
|
+
self,
|
|
860
|
+
knowledge_base: Optional[KnowledgeBase] = None,
|
|
861
|
+
knowledge_base_id: Optional[str] = None
|
|
862
|
+
) -> None:
|
|
863
|
+
"""
|
|
864
|
+
Attach a knowledge base to the agent if it is not already linked.
|
|
865
|
+
|
|
866
|
+
This method ensures that a knowledge base is associated with the agent, either
|
|
867
|
+
via a `KnowledgeBase` instance or a raw ID. It avoids duplicate links by checking
|
|
868
|
+
for existing associations.
|
|
869
|
+
|
|
870
|
+
Args:
|
|
871
|
+
knowledge_base (Optional[KnowledgeBase]): The KnowledgeBase instance to attach.
|
|
872
|
+
knowledge_base_id (Optional[str]): The unique identifier of the knowledge base.
|
|
873
|
+
|
|
874
|
+
Raises:
|
|
875
|
+
ValueError: If neither a knowledge base nor an ID is provided.
|
|
876
|
+
TypeError: If a provided knowledge base is not a valid `KnowledgeBase` instance.
|
|
877
|
+
|
|
878
|
+
Example:
|
|
879
|
+
>>> agent.attach_knowledge_base(knowledge_base_id="kb_12345")
|
|
880
|
+
>>> agent.attach_knowledge_base(knowledge_base=my_kb_instance)
|
|
881
|
+
|
|
882
|
+
Note:
|
|
883
|
+
This change only affects the runtime instance of the agent.
|
|
884
|
+
To persist changes, an explicit save or sync must be called.
|
|
885
|
+
"""
|
|
886
|
+
if not knowledge_base and not knowledge_base_id:
|
|
887
|
+
raise ValueError("You must provide either a knowledge_base instance or a knowledge_base_id.")
|
|
888
|
+
|
|
889
|
+
if knowledge_base:
|
|
890
|
+
if not isinstance(knowledge_base, KnowledgeBase):
|
|
891
|
+
raise TypeError("Expected 'knowledge_base' to be an instance of KnowledgeBase.")
|
|
892
|
+
knowledge_base_id = knowledge_base.id
|
|
893
|
+
|
|
894
|
+
if not any(kb.id == knowledge_base_id for kb in self.knowledge_bases):
|
|
895
|
+
self.knowledge_bases.append(AgentKnowledgeBase(id=knowledge_base_id))
|
|
896
|
+
|
|
897
|
+
@computed_field
|
|
898
|
+
@property
|
|
899
|
+
def strands_tools(self) -> List[Any]:
|
|
900
|
+
tools = []
|
|
901
|
+
for _tool in self.tools.list:
|
|
902
|
+
def make_tool(_tool_def: Tool):
|
|
903
|
+
async def invoke(payload: dict):
|
|
904
|
+
return await _tool_def.ainvoke(
|
|
905
|
+
task_id=self.configuration.state.task.id
|
|
906
|
+
if self.configuration.state.task
|
|
907
|
+
else None,
|
|
908
|
+
agent_id=self.id,
|
|
909
|
+
agent_version=self.version,
|
|
910
|
+
payload=payload,
|
|
911
|
+
configuration=_tool.configuration
|
|
912
|
+
)
|
|
913
|
+
return invoke
|
|
914
|
+
|
|
915
|
+
tools.append(
|
|
916
|
+
strands_tool(
|
|
917
|
+
func=make_tool(_tool),
|
|
918
|
+
name=_tool.id,
|
|
919
|
+
description=_tool.description,
|
|
920
|
+
inputSchema={
|
|
921
|
+
"json": {
|
|
922
|
+
"type": "object",
|
|
923
|
+
"properties":{
|
|
924
|
+
"payload": _tool.parameters
|
|
925
|
+
},
|
|
926
|
+
"required": ["payload"]
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
)
|
|
930
|
+
)
|
|
931
|
+
|
|
932
|
+
return tools
|
|
933
|
+
|
|
934
|
+
@computed_field
|
|
935
|
+
@property
|
|
936
|
+
def sanitized_name(self) -> str:
|
|
937
|
+
# Replace invalid characters with underscores
|
|
938
|
+
sanitized = re.sub(r'[^A-Za-z0-9_]', '_', self.name)
|
|
939
|
+
|
|
940
|
+
# If the first character is invalid, prefix with underscore
|
|
941
|
+
if not re.match(r'[A-Za-z_]', sanitized):
|
|
942
|
+
sanitized = '_' + sanitized
|
|
943
|
+
|
|
944
|
+
# Prevent empty result
|
|
945
|
+
if not sanitized:
|
|
946
|
+
sanitized = '_agent'
|
|
947
|
+
|
|
948
|
+
return sanitized
|
|
949
|
+
|
|
950
|
+
@computed_field
|
|
951
|
+
@property
|
|
952
|
+
def openai_agents_sdk_tools(self) -> List[Any]:
|
|
953
|
+
return get_openai_agents_sdk_tools(self)
|