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,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
+ )