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,578 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool Module - XPander SDK
|
|
3
|
+
|
|
4
|
+
This module provides the Tool class for managing and invoking tools within the XPander AI system.
|
|
5
|
+
It supports both local and remote tool execution with comprehensive schema validation, error handling,
|
|
6
|
+
and flexible invocation patterns.
|
|
7
|
+
|
|
8
|
+
Recent Enhancements:
|
|
9
|
+
- Added get_invocation_function() factory method for creating pre-configured invocation functions
|
|
10
|
+
- Enhanced payload_schema computed property with comprehensive documentation
|
|
11
|
+
- Improved error handling and validation throughout
|
|
12
|
+
- Added support for standalone tool invocation patterns
|
|
13
|
+
- Enhanced type hints and documentation coverage
|
|
14
|
+
|
|
15
|
+
Key Features:
|
|
16
|
+
- Schema-based payload validation using Pydantic
|
|
17
|
+
- Support for both sync and async invocation patterns
|
|
18
|
+
- Local and remote tool execution capabilities
|
|
19
|
+
- Configurable schema overrides and validation
|
|
20
|
+
- Comprehensive error handling and reporting
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from copy import deepcopy
|
|
24
|
+
from typing import Dict, Any, Literal, Optional, Callable
|
|
25
|
+
from httpx import HTTPStatusError
|
|
26
|
+
from pydantic import BaseModel, computed_field, create_model, model_validator, Field
|
|
27
|
+
from xpander_sdk.consts.api_routes import APIRoute
|
|
28
|
+
from xpander_sdk.core.xpander_api_client import APIClient
|
|
29
|
+
from xpander_sdk.models.configuration import Configuration
|
|
30
|
+
from xpander_sdk.models.shared import XPanderSharedModel
|
|
31
|
+
from xpander_sdk.modules.agents.models.agent import AgentGraphItemSchema
|
|
32
|
+
from xpander_sdk.modules.tools_repository.models.tool_invocation_result import (
|
|
33
|
+
ToolInvocationResult,
|
|
34
|
+
)
|
|
35
|
+
from xpander_sdk.modules.tools_repository.utils.generic import deep_merge, pascal_case
|
|
36
|
+
from xpander_sdk.modules.tools_repository.utils.local_tools import invoke_local_fn
|
|
37
|
+
from xpander_sdk.modules.tools_repository.utils.schemas import (
|
|
38
|
+
apply_permanent_values_to_payload,
|
|
39
|
+
build_model_from_schema,
|
|
40
|
+
enforce_schema_on_response,
|
|
41
|
+
schema_enforcement_block_and_descriptions,
|
|
42
|
+
)
|
|
43
|
+
from xpander_sdk.utils.event_loop import run_sync
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Tool(XPanderSharedModel):
|
|
47
|
+
"""
|
|
48
|
+
Represents a callable tool in the xpander.ai system.
|
|
49
|
+
|
|
50
|
+
A tool can be invoked either locally or remotely. This class handles input validation,
|
|
51
|
+
dynamic schema generation, preflight checks, and invocation logic.
|
|
52
|
+
|
|
53
|
+
Attributes:
|
|
54
|
+
id (str): Unique identifier for the tool.
|
|
55
|
+
name (str): Name of the tool.
|
|
56
|
+
method (str): HTTP method used for remote invocation.
|
|
57
|
+
path (str): Endpoint path for the tool.
|
|
58
|
+
should_add_to_graph (Optional[bool]): Whether the tool should be added to the execution graph.
|
|
59
|
+
is_local (Optional[bool]): Whether the tool is local.
|
|
60
|
+
is_synced (Optional[bool]): Whether the tool is synchronized.
|
|
61
|
+
description (str): Description of the tool.
|
|
62
|
+
parameters (Dict[str, Any]): Parameter schema for the tool.
|
|
63
|
+
configuration (Optional[Configuration]): Configuration for the tool.
|
|
64
|
+
fn (Optional[Callable]): Callable function for local tools.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
configuration: Optional[Configuration] = Configuration()
|
|
68
|
+
|
|
69
|
+
id: str
|
|
70
|
+
name: str
|
|
71
|
+
method: str
|
|
72
|
+
path: str
|
|
73
|
+
should_add_to_graph: Optional[bool] = False
|
|
74
|
+
is_local: Optional[bool] = False
|
|
75
|
+
is_standalone: Optional[bool] = False
|
|
76
|
+
is_synced: Optional[bool] = False
|
|
77
|
+
schema_overrides: Optional[AgentGraphItemSchema] = None
|
|
78
|
+
description: str = ""
|
|
79
|
+
parameters: Dict[str, Any] = {}
|
|
80
|
+
connector_id: Optional[str] = None
|
|
81
|
+
operation_id: Optional[str] = None
|
|
82
|
+
|
|
83
|
+
fn: Optional[Callable] = Field(default=None, exclude=True)
|
|
84
|
+
|
|
85
|
+
def set_configuration(self, configuration: Configuration):
|
|
86
|
+
"""
|
|
87
|
+
Set the configuration object for this tool.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
configuration (Configuration): The configuration instance to associate with this tool.
|
|
91
|
+
"""
|
|
92
|
+
self.configuration = configuration
|
|
93
|
+
|
|
94
|
+
def set_schema_overrides(self, agent_graph: Any):
|
|
95
|
+
"""
|
|
96
|
+
Apply schema overrides from the agent graph item if available.
|
|
97
|
+
|
|
98
|
+
This method retrieves the graph item associated with this tool's ID
|
|
99
|
+
from the provided agent graph. If the item exists and contains valid
|
|
100
|
+
schema configuration, it sets the tool's internal `schema_overrides`
|
|
101
|
+
attribute accordingly.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
agent_graph (AgentGraph): The agent graph containing graph items. Must provide
|
|
105
|
+
a `get_graph_item(key: str, value: Any)` method to retrieve a graph item
|
|
106
|
+
by a key-value match, where the key is 'item_id' and value is `self.id`.
|
|
107
|
+
"""
|
|
108
|
+
if gi := agent_graph.get_graph_item("item_id", self.id):
|
|
109
|
+
if (
|
|
110
|
+
gi.settings
|
|
111
|
+
and gi.settings.schemas
|
|
112
|
+
and isinstance(gi.settings.schemas, AgentGraphItemSchema)
|
|
113
|
+
):
|
|
114
|
+
self.schema_overrides = gi.settings.schemas
|
|
115
|
+
|
|
116
|
+
def has_schema_override(self, type: Literal["input", "output"]) -> bool:
|
|
117
|
+
return (
|
|
118
|
+
self.schema_overrides
|
|
119
|
+
and hasattr(self.schema_overrides, type)
|
|
120
|
+
and isinstance(getattr(self.schema_overrides, type), dict)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
@computed_field
|
|
124
|
+
@property
|
|
125
|
+
def schema(self) -> type[BaseModel]:
|
|
126
|
+
"""
|
|
127
|
+
Generate and return a Pydantic model schema based on the tool's parameters.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
type[BaseModel]: A dynamically constructed Pydantic model class.
|
|
131
|
+
"""
|
|
132
|
+
model_name = f"{pascal_case(self.id)}PayloadSchema"
|
|
133
|
+
|
|
134
|
+
schema = deepcopy(self.parameters)
|
|
135
|
+
|
|
136
|
+
# apply input schema enforcement
|
|
137
|
+
if self.has_schema_override(type="input"):
|
|
138
|
+
schema = schema_enforcement_block_and_descriptions(
|
|
139
|
+
target_schema=schema, reference_schema=self.schema_overrides.input
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return build_model_from_schema(
|
|
143
|
+
model_name=model_name, schema=schema, with_defaults=self.is_local == False
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
@model_validator(mode="before")
|
|
147
|
+
@classmethod
|
|
148
|
+
def set_description_from_function_desc(
|
|
149
|
+
cls, values: Dict[str, Any]
|
|
150
|
+
) -> Dict[str, Any]:
|
|
151
|
+
"""
|
|
152
|
+
Automatically sets the tool's description from the 'function_description' field if not already set.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
values (Dict[str, Any]): Initial model values.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Dict[str, Any]: Updated model values with description set if applicable.
|
|
159
|
+
"""
|
|
160
|
+
if not values.get("description") and values.get("function_description"):
|
|
161
|
+
values["description"] = values["function_description"]
|
|
162
|
+
return values
|
|
163
|
+
|
|
164
|
+
async def acall_remote_tool(
|
|
165
|
+
self,
|
|
166
|
+
agent_id: str,
|
|
167
|
+
payload: Any,
|
|
168
|
+
agent_version: Optional[str] = None,
|
|
169
|
+
payload_extension: Optional[Dict[str, Any]] = {},
|
|
170
|
+
configuration: Optional[Configuration] = None,
|
|
171
|
+
task_id: Optional[str] = None,
|
|
172
|
+
is_preflight: Optional[bool] = False,
|
|
173
|
+
append_payload: Optional[bool] = False,
|
|
174
|
+
) -> Any:
|
|
175
|
+
"""
|
|
176
|
+
Asynchronously invoke the tool remotely using xpander.ai API.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
agent_id (str): The ID of the agent calling the tool.
|
|
180
|
+
payload (Any): The request payload to be sent.
|
|
181
|
+
agent_version (Optional[str]): Optional agent version to use.
|
|
182
|
+
payload_extension (Optional[Dict[str, Any]]): Additional values to merge into the payload.
|
|
183
|
+
configuration (Optional[Configuration]): Optional configuration override.
|
|
184
|
+
task_id (Optional[str]): ID of the execution task.
|
|
185
|
+
is_preflight (Optional[bool]): If True, performs a preflight check only.
|
|
186
|
+
append_payload (Optional[bool]): If True, appends the payload also for preflights.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Any: The response from the remote API.
|
|
190
|
+
"""
|
|
191
|
+
client = APIClient(configuration=configuration or self.configuration)
|
|
192
|
+
headers = {}
|
|
193
|
+
|
|
194
|
+
if agent_version:
|
|
195
|
+
headers["x-agent-version"] = str(agent_version)
|
|
196
|
+
|
|
197
|
+
if task_id:
|
|
198
|
+
headers["x-execution-id"] = task_id
|
|
199
|
+
|
|
200
|
+
if (
|
|
201
|
+
isinstance(payload, dict)
|
|
202
|
+
and isinstance(payload_extension, dict)
|
|
203
|
+
and payload_extension
|
|
204
|
+
):
|
|
205
|
+
payload = deep_merge(a=payload, b=payload_extension)
|
|
206
|
+
|
|
207
|
+
if is_preflight:
|
|
208
|
+
headers["x-preflight-check"] = "true"
|
|
209
|
+
|
|
210
|
+
# apply input schema
|
|
211
|
+
if (
|
|
212
|
+
not is_preflight
|
|
213
|
+
and self.has_schema_override(type="input")
|
|
214
|
+
and payload
|
|
215
|
+
and (isinstance(payload, dict) or isinstance(payload, list))
|
|
216
|
+
):
|
|
217
|
+
payload = apply_permanent_values_to_payload(
|
|
218
|
+
schema=self.schema_overrides.input, payload=payload
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
result = await client.make_request(
|
|
222
|
+
path=APIRoute.InvokeTool.format(agent_id=agent_id, tool_id=self.id),
|
|
223
|
+
method="POST",
|
|
224
|
+
payload=None if is_preflight and not append_payload else payload,
|
|
225
|
+
headers=headers,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# apply output schema
|
|
229
|
+
if (
|
|
230
|
+
not is_preflight
|
|
231
|
+
and self.has_schema_override(type="output")
|
|
232
|
+
and result
|
|
233
|
+
and (isinstance(result, dict) or isinstance(result, list))
|
|
234
|
+
):
|
|
235
|
+
result = enforce_schema_on_response(
|
|
236
|
+
schema=self.schema_overrides.output, response=result
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
return result
|
|
240
|
+
|
|
241
|
+
def call_remote_tool(
|
|
242
|
+
self,
|
|
243
|
+
agent_id: str,
|
|
244
|
+
payload: Any,
|
|
245
|
+
agent_version: Optional[str] = None,
|
|
246
|
+
payload_extension: Optional[Dict[str, Any]] = {},
|
|
247
|
+
configuration: Optional[Configuration] = None,
|
|
248
|
+
task_id: Optional[str] = None,
|
|
249
|
+
is_preflight: Optional[bool] = False,
|
|
250
|
+
) -> Any:
|
|
251
|
+
"""
|
|
252
|
+
Synchronous wrapper for `acall_remote_tool`.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
agent_id (str): The ID of the agent calling the tool.
|
|
256
|
+
payload (Any): The request payload.
|
|
257
|
+
agent_version (Optional[str]): Optional agent version to use.
|
|
258
|
+
payload_extension (Optional[Dict[str, Any]]): Additional payload values.
|
|
259
|
+
configuration (Optional[Configuration]): Configuration override.
|
|
260
|
+
task_id (Optional[str]): Execution task ID.
|
|
261
|
+
is_preflight (Optional[bool]): Preflight mode flag.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Any: The response from the API.
|
|
265
|
+
"""
|
|
266
|
+
return run_sync(
|
|
267
|
+
self.acall_remote_tool(
|
|
268
|
+
agent_id=agent_id,
|
|
269
|
+
payload=payload,
|
|
270
|
+
agent_version=agent_version,
|
|
271
|
+
payload_extension=payload_extension,
|
|
272
|
+
configuration=configuration,
|
|
273
|
+
task_id=task_id,
|
|
274
|
+
is_preflight=is_preflight,
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
async def agraph_preflight_check(
|
|
279
|
+
self,
|
|
280
|
+
agent_id: str,
|
|
281
|
+
agent_version: Optional[str] = None,
|
|
282
|
+
configuration: Optional[Configuration] = None,
|
|
283
|
+
task_id: Optional[str] = None,
|
|
284
|
+
payload: Optional[Any] = None
|
|
285
|
+
):
|
|
286
|
+
"""
|
|
287
|
+
Perform an asynchronous preflight check on the tool execution graph.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
agent_id (str): The ID of the agent.
|
|
291
|
+
agent_version (Optional[str]): Optional agent version to use.
|
|
292
|
+
configuration (Optional[Configuration]): Optional configuration override.
|
|
293
|
+
task_id (Optional[str]): Execution task ID.
|
|
294
|
+
payload (Optional[Any]): Tool call payload.
|
|
295
|
+
|
|
296
|
+
Raises:
|
|
297
|
+
Exception: If the preflight check returns an error.
|
|
298
|
+
"""
|
|
299
|
+
try:
|
|
300
|
+
if not task_id:
|
|
301
|
+
return
|
|
302
|
+
|
|
303
|
+
result = await self.acall_remote_tool(
|
|
304
|
+
agent_id=agent_id,
|
|
305
|
+
configuration=configuration,
|
|
306
|
+
agent_version=agent_version,
|
|
307
|
+
task_id=task_id,
|
|
308
|
+
payload={"body_params": payload} if isinstance(payload, dict) else None,
|
|
309
|
+
is_preflight=True,
|
|
310
|
+
append_payload=True
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
if isinstance(result, dict) and (error := result.get("error")):
|
|
314
|
+
raise Exception(error)
|
|
315
|
+
except Exception:
|
|
316
|
+
raise
|
|
317
|
+
|
|
318
|
+
def graph_preflight_check(
|
|
319
|
+
self,
|
|
320
|
+
agent_id: str,
|
|
321
|
+
agent_version: Optional[str] = None,
|
|
322
|
+
configuration: Optional[Configuration] = None,
|
|
323
|
+
task_id: Optional[str] = None,
|
|
324
|
+
):
|
|
325
|
+
"""
|
|
326
|
+
Synchronous wrapper for `agraph_preflight_check`.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
agent_id (str): The ID of the agent.
|
|
330
|
+
agent_version (Optional[str]): Optional agent version to use.
|
|
331
|
+
configuration (Optional[Configuration]): Optional configuration override.
|
|
332
|
+
task_id (Optional[str]): Execution task ID.
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
Any: Result of the preflight check.
|
|
336
|
+
"""
|
|
337
|
+
return run_sync(
|
|
338
|
+
self.agraph_preflight_check(
|
|
339
|
+
agent_id=agent_id,
|
|
340
|
+
agent_version=agent_version,
|
|
341
|
+
configuration=configuration,
|
|
342
|
+
task_id=task_id,
|
|
343
|
+
)
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
async def ainvoke(
|
|
347
|
+
self,
|
|
348
|
+
agent_id: str,
|
|
349
|
+
payload: Any,
|
|
350
|
+
agent_version: Optional[str] = None,
|
|
351
|
+
payload_extension: Optional[Dict[str, Any]] = {},
|
|
352
|
+
configuration: Optional[Configuration] = None,
|
|
353
|
+
task_id: Optional[str] = None,
|
|
354
|
+
tool_call_id: Optional[str] = None,
|
|
355
|
+
) -> ToolInvocationResult:
|
|
356
|
+
"""
|
|
357
|
+
Asynchronously invoke the tool (local or remote), with schema validation and error handling.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
agent_id (str): ID of the agent making the call.
|
|
361
|
+
payload (Any): The input payload to the tool.
|
|
362
|
+
agent_version (Optional[str]): Optional agent version to use.
|
|
363
|
+
payload_extension (Optional[Dict[str, Any]]): Optional additional payload data.
|
|
364
|
+
configuration (Optional[Configuration]): Optional configuration override.
|
|
365
|
+
task_id (Optional[str]): ID of the current task context.
|
|
366
|
+
tool_call_id (Optional[str]): Unique ID of the tool call.
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
ToolInvocationResult: Result object encapsulating invocation output and status.
|
|
370
|
+
"""
|
|
371
|
+
tool_invocation_result = ToolInvocationResult(
|
|
372
|
+
tool_id=self.id,
|
|
373
|
+
payload=payload,
|
|
374
|
+
is_local=self.is_local,
|
|
375
|
+
tool_call_id=tool_call_id,
|
|
376
|
+
task_id=task_id,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
try:
|
|
380
|
+
if self.schema and payload:
|
|
381
|
+
try:
|
|
382
|
+
self.schema.model_validate(payload)
|
|
383
|
+
except Exception as validation_error:
|
|
384
|
+
raise ValueError(
|
|
385
|
+
f"Invalid payload for tool '{self.name}': {validation_error}"
|
|
386
|
+
) from validation_error
|
|
387
|
+
|
|
388
|
+
if self.is_local:
|
|
389
|
+
await self.agraph_preflight_check(
|
|
390
|
+
agent_id=agent_id,
|
|
391
|
+
agent_version=agent_version,
|
|
392
|
+
configuration=configuration,
|
|
393
|
+
task_id=task_id,
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
if self.fn is None:
|
|
397
|
+
raise RuntimeError(
|
|
398
|
+
f"No local function provided for this tool ({self.id})."
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
result = await invoke_local_fn(fn=self.fn, payload=payload)
|
|
402
|
+
tool_invocation_result.result = result
|
|
403
|
+
tool_invocation_result.is_success = True
|
|
404
|
+
return tool_invocation_result
|
|
405
|
+
|
|
406
|
+
tool_invocation_result.result = await self.acall_remote_tool(
|
|
407
|
+
agent_id=agent_id,
|
|
408
|
+
agent_version=agent_version,
|
|
409
|
+
payload=payload,
|
|
410
|
+
payload_extension=payload_extension,
|
|
411
|
+
configuration=configuration,
|
|
412
|
+
task_id=task_id,
|
|
413
|
+
)
|
|
414
|
+
tool_invocation_result.is_success = True
|
|
415
|
+
|
|
416
|
+
except Exception as e:
|
|
417
|
+
tool_invocation_result.is_error = True
|
|
418
|
+
if isinstance(e, HTTPStatusError):
|
|
419
|
+
tool_invocation_result.status_code = e.response.status_code
|
|
420
|
+
tool_invocation_result.result = e.response.text
|
|
421
|
+
else:
|
|
422
|
+
tool_invocation_result.status_code = 500
|
|
423
|
+
tool_invocation_result.result = str(e)
|
|
424
|
+
|
|
425
|
+
return tool_invocation_result
|
|
426
|
+
|
|
427
|
+
def invoke(
|
|
428
|
+
self,
|
|
429
|
+
agent_id: str,
|
|
430
|
+
payload: Any,
|
|
431
|
+
agent_version: Optional[str] = None,
|
|
432
|
+
payload_extension: Optional[Dict[str, Any]] = {},
|
|
433
|
+
configuration: Optional[Configuration] = None,
|
|
434
|
+
task_id: Optional[str] = None,
|
|
435
|
+
tool_call_id: Optional[str] = None,
|
|
436
|
+
) -> ToolInvocationResult:
|
|
437
|
+
"""
|
|
438
|
+
Synchronous wrapper for `ainvoke`.
|
|
439
|
+
|
|
440
|
+
Args:
|
|
441
|
+
agent_id (str): ID of the agent making the call.
|
|
442
|
+
payload (Any): Payload to pass to the tool.
|
|
443
|
+
agent_version (Optional[str]): Optional agent version to use.
|
|
444
|
+
payload_extension (Optional[Dict[str, Any]]): Optional additional payload.
|
|
445
|
+
configuration (Optional[Configuration]): Optional override configuration.
|
|
446
|
+
task_id (Optional[str]): Optional execution task ID.
|
|
447
|
+
tool_call_id (Optional[str]): Optional tool call ID.
|
|
448
|
+
|
|
449
|
+
Returns:
|
|
450
|
+
ToolInvocationResult: Result of the tool invocation.
|
|
451
|
+
"""
|
|
452
|
+
return run_sync(
|
|
453
|
+
self.ainvoke(
|
|
454
|
+
agent_id=agent_id,
|
|
455
|
+
payload=payload,
|
|
456
|
+
agent_version=agent_version,
|
|
457
|
+
payload_extension=payload_extension,
|
|
458
|
+
configuration=configuration,
|
|
459
|
+
task_id=task_id,
|
|
460
|
+
tool_call_id=tool_call_id,
|
|
461
|
+
)
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
def get_invocation_function(
|
|
465
|
+
self,
|
|
466
|
+
is_async: Optional[bool] = False,
|
|
467
|
+
configuration: Optional[Configuration] = None,
|
|
468
|
+
) -> Callable:
|
|
469
|
+
"""
|
|
470
|
+
Factory method that creates a tool invocation function with bound configuration.
|
|
471
|
+
|
|
472
|
+
This method provides a convenient way to create a pre-configured invocation function
|
|
473
|
+
that can be used without repeatedly passing configuration parameters. The returned
|
|
474
|
+
function will handle schema validation, API communication, and error handling.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
is_async (Optional[bool]): If True, returns an async function. If False, returns
|
|
478
|
+
a synchronous function that wraps the async implementation. Defaults to False.
|
|
479
|
+
configuration (Optional[Configuration]): Configuration to bind to the invocation
|
|
480
|
+
function. If None, uses the tool's default configuration.
|
|
481
|
+
|
|
482
|
+
Returns:
|
|
483
|
+
Callable: A function that takes payload and returns ToolInvocationResult.
|
|
484
|
+
The function signature depends on is_async:
|
|
485
|
+
- If async: async def(payload: schema) -> ToolInvocationResult
|
|
486
|
+
- If sync: def(payload: schema) -> ToolInvocationResult
|
|
487
|
+
|
|
488
|
+
Example:
|
|
489
|
+
>>> # Get async invocation function
|
|
490
|
+
>>> async_invoke = tool.get_invocation_function(is_async=True)
|
|
491
|
+
>>> result = await async_invoke({"param": "value"})
|
|
492
|
+
|
|
493
|
+
>>> # Get sync invocation function
|
|
494
|
+
>>> sync_invoke = tool.get_invocation_function(is_async=False)
|
|
495
|
+
>>> result = sync_invoke({"param": "value"})
|
|
496
|
+
|
|
497
|
+
Note:
|
|
498
|
+
This method is primarily intended for standalone tool invocation where you
|
|
499
|
+
don't have agent context. For agent-based invocations, use the `ainvoke`
|
|
500
|
+
or `invoke` methods instead.
|
|
501
|
+
"""
|
|
502
|
+
bound_config = configuration or self.configuration
|
|
503
|
+
|
|
504
|
+
async def ainvoke_standalone(payload: Any) -> ToolInvocationResult:
|
|
505
|
+
"""Async standalone invocation implementation."""
|
|
506
|
+
tool_invocation_result = ToolInvocationResult(
|
|
507
|
+
tool_id=self.id,
|
|
508
|
+
payload=payload,
|
|
509
|
+
is_local=self.is_local,
|
|
510
|
+
)
|
|
511
|
+
|
|
512
|
+
try:
|
|
513
|
+
# Validate payload against schema if available
|
|
514
|
+
if self.schema and payload:
|
|
515
|
+
try:
|
|
516
|
+
self.schema.model_validate(payload)
|
|
517
|
+
except Exception as validation_error:
|
|
518
|
+
raise ValueError(
|
|
519
|
+
f"Invalid payload for tool '{self.name}': {validation_error}"
|
|
520
|
+
) from validation_error
|
|
521
|
+
|
|
522
|
+
# Make API request to invoke the tool
|
|
523
|
+
client = APIClient(configuration=bound_config)
|
|
524
|
+
tool_invocation_result.result = await client.make_request(
|
|
525
|
+
path=APIRoute.GetOrInvokeToolById.format(
|
|
526
|
+
tool_id=f"{self.connector_id}_{self.operation_id}"
|
|
527
|
+
),
|
|
528
|
+
method="POST",
|
|
529
|
+
payload=payload,
|
|
530
|
+
)
|
|
531
|
+
tool_invocation_result.is_success = True
|
|
532
|
+
|
|
533
|
+
except Exception as e:
|
|
534
|
+
tool_invocation_result.is_error = True
|
|
535
|
+
if isinstance(e, HTTPStatusError):
|
|
536
|
+
tool_invocation_result.status_code = e.response.status_code
|
|
537
|
+
tool_invocation_result.result = e.response.text
|
|
538
|
+
else:
|
|
539
|
+
tool_invocation_result.status_code = 500
|
|
540
|
+
tool_invocation_result.result = str(e)
|
|
541
|
+
|
|
542
|
+
return tool_invocation_result
|
|
543
|
+
|
|
544
|
+
if is_async:
|
|
545
|
+
return ainvoke_standalone
|
|
546
|
+
|
|
547
|
+
def invoke_standalone(payload: Any) -> ToolInvocationResult:
|
|
548
|
+
"""Sync standalone invocation implementation."""
|
|
549
|
+
return run_sync(ainvoke_standalone(payload=payload))
|
|
550
|
+
|
|
551
|
+
return invoke_standalone
|
|
552
|
+
|
|
553
|
+
@computed_field
|
|
554
|
+
@property
|
|
555
|
+
def payload_schema(self) -> type[BaseModel]:
|
|
556
|
+
"""
|
|
557
|
+
Computed property that creates a wrapper schema for the tool's payload.
|
|
558
|
+
|
|
559
|
+
This property dynamically generates a Pydantic model that wraps the tool's
|
|
560
|
+
schema in a 'payload' field. This is useful for APIs or frameworks that
|
|
561
|
+
expect a specific payload structure.
|
|
562
|
+
|
|
563
|
+
Returns:
|
|
564
|
+
type[BaseModel]: A dynamically created Pydantic model class with a single
|
|
565
|
+
'payload' field containing the tool's schema.
|
|
566
|
+
|
|
567
|
+
Example:
|
|
568
|
+
>>> tool = Tool(...)
|
|
569
|
+
>>> PayloadModel = tool.payload_schema
|
|
570
|
+
>>> instance = PayloadModel(payload={"param": "value"})
|
|
571
|
+
>>> print(instance.payload) # Access the actual tool payload
|
|
572
|
+
|
|
573
|
+
Note:
|
|
574
|
+
The generated model class name follows the pattern: {OriginalSchema}Payload
|
|
575
|
+
"""
|
|
576
|
+
return create_model(
|
|
577
|
+
f"{self.schema.__name__}Payload", payload=(self.schema, ...)
|
|
578
|
+
)
|