dao-ai 0.1.1__py3-none-any.whl → 0.1.3__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.
- dao_ai/agent_as_code.py +2 -5
- dao_ai/cli.py +65 -15
- dao_ai/config.py +672 -218
- dao_ai/genie/cache/core.py +6 -2
- dao_ai/genie/cache/lru.py +29 -11
- dao_ai/genie/cache/semantic.py +95 -44
- dao_ai/hooks/core.py +5 -5
- dao_ai/logging.py +56 -0
- dao_ai/memory/core.py +61 -44
- dao_ai/memory/databricks.py +54 -41
- dao_ai/memory/postgres.py +77 -36
- dao_ai/middleware/assertions.py +45 -17
- dao_ai/middleware/core.py +13 -7
- dao_ai/middleware/guardrails.py +30 -25
- dao_ai/middleware/human_in_the_loop.py +9 -5
- dao_ai/middleware/message_validation.py +61 -29
- dao_ai/middleware/summarization.py +16 -11
- dao_ai/models.py +172 -69
- dao_ai/nodes.py +148 -19
- dao_ai/optimization.py +26 -16
- dao_ai/orchestration/core.py +15 -8
- dao_ai/orchestration/supervisor.py +22 -8
- dao_ai/orchestration/swarm.py +57 -12
- dao_ai/prompts.py +17 -17
- dao_ai/providers/databricks.py +365 -155
- dao_ai/state.py +24 -6
- dao_ai/tools/__init__.py +2 -0
- dao_ai/tools/agent.py +1 -3
- dao_ai/tools/core.py +7 -7
- dao_ai/tools/email.py +29 -77
- dao_ai/tools/genie.py +18 -13
- dao_ai/tools/mcp.py +223 -156
- dao_ai/tools/python.py +5 -2
- dao_ai/tools/search.py +1 -1
- dao_ai/tools/slack.py +21 -9
- dao_ai/tools/sql.py +202 -0
- dao_ai/tools/time.py +30 -7
- dao_ai/tools/unity_catalog.py +129 -86
- dao_ai/tools/vector_search.py +318 -244
- dao_ai/utils.py +15 -10
- dao_ai-0.1.3.dist-info/METADATA +455 -0
- dao_ai-0.1.3.dist-info/RECORD +64 -0
- dao_ai-0.1.1.dist-info/METADATA +0 -1878
- dao_ai-0.1.1.dist-info/RECORD +0 -62
- {dao_ai-0.1.1.dist-info → dao_ai-0.1.3.dist-info}/WHEEL +0 -0
- {dao_ai-0.1.1.dist-info → dao_ai-0.1.3.dist-info}/entry_points.txt +0 -0
- {dao_ai-0.1.1.dist-info → dao_ai-0.1.3.dist-info}/licenses/LICENSE +0 -0
dao_ai/tools/unity_catalog.py
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
from typing import Any, Dict, Optional, Sequence,
|
|
1
|
+
from typing import Any, Dict, Optional, Sequence, Set
|
|
2
2
|
|
|
3
3
|
from databricks.sdk import WorkspaceClient
|
|
4
|
-
from databricks.sdk.service.catalog import PermissionsChange, Privilege
|
|
4
|
+
from databricks.sdk.service.catalog import FunctionInfo, PermissionsChange, Privilege
|
|
5
5
|
from databricks_langchain import DatabricksFunctionClient, UCFunctionToolkit
|
|
6
6
|
from langchain_core.runnables.base import RunnableLike
|
|
7
7
|
from langchain_core.tools import StructuredTool
|
|
8
8
|
from loguru import logger
|
|
9
|
+
from pydantic import BaseModel
|
|
9
10
|
from unitycatalog.ai.core.base import FunctionExecutionResult
|
|
10
11
|
|
|
11
12
|
from dao_ai.config import (
|
|
12
13
|
AnyVariable,
|
|
13
14
|
CompositeVariableModel,
|
|
14
|
-
ToolModel,
|
|
15
15
|
UnityCatalogFunctionModel,
|
|
16
16
|
value_of,
|
|
17
17
|
)
|
|
@@ -34,36 +34,39 @@ def create_uc_tools(
|
|
|
34
34
|
Returns:
|
|
35
35
|
A sequence of BaseTool objects that wrap the specified UC functions
|
|
36
36
|
"""
|
|
37
|
+
original_function_model: UnityCatalogFunctionModel | None = None
|
|
38
|
+
workspace_client: WorkspaceClient | None = None
|
|
39
|
+
function_name: str
|
|
37
40
|
|
|
38
|
-
logger.debug(f"create_uc_tools: {function}")
|
|
39
|
-
|
|
40
|
-
original_function_model = None
|
|
41
41
|
if isinstance(function, UnityCatalogFunctionModel):
|
|
42
42
|
original_function_model = function
|
|
43
|
-
function_name = function.full_name
|
|
43
|
+
function_name = function.resource.full_name
|
|
44
|
+
workspace_client = function.resource.workspace_client
|
|
44
45
|
else:
|
|
45
46
|
function_name = function
|
|
46
47
|
|
|
48
|
+
logger.trace("Creating UC tools", function_name=function_name)
|
|
49
|
+
|
|
47
50
|
# Determine which tools to create
|
|
51
|
+
tools: list[RunnableLike]
|
|
48
52
|
if original_function_model and original_function_model.partial_args:
|
|
49
|
-
logger.debug(
|
|
50
|
-
|
|
51
|
-
tool_model = ToolModel(
|
|
52
|
-
name=original_function_model.name, function=original_function_model
|
|
53
|
+
logger.debug(
|
|
54
|
+
"Creating custom tool with partial arguments", function_name=function_name
|
|
53
55
|
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
tools = [with_partial_args(tool_model, original_function_model.partial_args)]
|
|
56
|
+
# Use with_partial_args directly with UnityCatalogFunctionModel
|
|
57
|
+
tools = [with_partial_args(original_function_model)]
|
|
57
58
|
else:
|
|
58
59
|
# Fallback to standard UC toolkit approach
|
|
59
|
-
client: DatabricksFunctionClient = DatabricksFunctionClient(
|
|
60
|
+
client: DatabricksFunctionClient = DatabricksFunctionClient(
|
|
61
|
+
client=workspace_client
|
|
62
|
+
)
|
|
60
63
|
|
|
61
64
|
toolkit: UCFunctionToolkit = UCFunctionToolkit(
|
|
62
65
|
function_names=[function_name], client=client
|
|
63
66
|
)
|
|
64
67
|
|
|
65
68
|
tools = toolkit.tools or []
|
|
66
|
-
logger.
|
|
69
|
+
logger.trace("Retrieved tools", tools_count=len(tools))
|
|
67
70
|
|
|
68
71
|
# HITL is now handled at middleware level via HumanInTheLoopMiddleware
|
|
69
72
|
return list(tools)
|
|
@@ -72,7 +75,7 @@ def create_uc_tools(
|
|
|
72
75
|
def _execute_uc_function(
|
|
73
76
|
client: DatabricksFunctionClient,
|
|
74
77
|
function_name: str,
|
|
75
|
-
partial_args: Dict[str, str] = None,
|
|
78
|
+
partial_args: Optional[Dict[str, str]] = None,
|
|
76
79
|
**kwargs: Any,
|
|
77
80
|
) -> str:
|
|
78
81
|
"""Execute Unity Catalog function with partial args and provided parameters."""
|
|
@@ -84,7 +87,9 @@ def _execute_uc_function(
|
|
|
84
87
|
all_params.update(kwargs)
|
|
85
88
|
|
|
86
89
|
logger.debug(
|
|
87
|
-
|
|
90
|
+
"Calling UC function",
|
|
91
|
+
function_name=function_name,
|
|
92
|
+
parameters=list(all_params.keys()),
|
|
88
93
|
)
|
|
89
94
|
|
|
90
95
|
result: FunctionExecutionResult = client.execute_function(
|
|
@@ -93,11 +98,19 @@ def _execute_uc_function(
|
|
|
93
98
|
|
|
94
99
|
# Handle errors and extract result
|
|
95
100
|
if result.error:
|
|
96
|
-
logger.error(
|
|
101
|
+
logger.error(
|
|
102
|
+
"Unity Catalog function error",
|
|
103
|
+
function_name=function_name,
|
|
104
|
+
error=result.error,
|
|
105
|
+
)
|
|
97
106
|
raise RuntimeError(f"Function execution failed: {result.error}")
|
|
98
107
|
|
|
99
108
|
result_value: str = result.value if result.value is not None else str(result)
|
|
100
|
-
logger.
|
|
109
|
+
logger.trace(
|
|
110
|
+
"UC function result",
|
|
111
|
+
function_name=function_name,
|
|
112
|
+
result_length=len(str(result_value)),
|
|
113
|
+
)
|
|
101
114
|
return result_value
|
|
102
115
|
|
|
103
116
|
|
|
@@ -116,21 +129,29 @@ def _grant_function_permissions(
|
|
|
116
129
|
"""
|
|
117
130
|
try:
|
|
118
131
|
# Initialize workspace client
|
|
119
|
-
workspace_client
|
|
132
|
+
workspace_client: WorkspaceClient = (
|
|
133
|
+
WorkspaceClient(host=host) if host else WorkspaceClient()
|
|
134
|
+
)
|
|
120
135
|
|
|
121
136
|
# Parse the function name to get catalog and schema
|
|
122
|
-
parts = function_name.split(".")
|
|
137
|
+
parts: list[str] = function_name.split(".")
|
|
123
138
|
if len(parts) != 3:
|
|
124
139
|
logger.warning(
|
|
125
|
-
|
|
140
|
+
"Invalid function name format, expected catalog.schema.function",
|
|
141
|
+
function_name=function_name,
|
|
126
142
|
)
|
|
127
143
|
return
|
|
128
144
|
|
|
145
|
+
catalog_name: str
|
|
146
|
+
schema_name: str
|
|
147
|
+
func_name: str
|
|
129
148
|
catalog_name, schema_name, func_name = parts
|
|
130
|
-
schema_full_name = f"{catalog_name}.{schema_name}"
|
|
149
|
+
schema_full_name: str = f"{catalog_name}.{schema_name}"
|
|
131
150
|
|
|
132
151
|
logger.debug(
|
|
133
|
-
|
|
152
|
+
"Granting comprehensive permissions",
|
|
153
|
+
function_name=function_name,
|
|
154
|
+
principal=client_id,
|
|
134
155
|
)
|
|
135
156
|
|
|
136
157
|
# 1. Grant EXECUTE permission on the function
|
|
@@ -142,9 +163,13 @@ def _grant_function_permissions(
|
|
|
142
163
|
PermissionsChange(principal=client_id, add=[Privilege.EXECUTE])
|
|
143
164
|
],
|
|
144
165
|
)
|
|
145
|
-
logger.
|
|
166
|
+
logger.trace("Granted EXECUTE permission", function_name=function_name)
|
|
146
167
|
except Exception as e:
|
|
147
|
-
logger.warning(
|
|
168
|
+
logger.warning(
|
|
169
|
+
"Failed to grant EXECUTE permission",
|
|
170
|
+
function_name=function_name,
|
|
171
|
+
error=str(e),
|
|
172
|
+
)
|
|
148
173
|
|
|
149
174
|
# 2. Grant USE_SCHEMA permission on the schema
|
|
150
175
|
try:
|
|
@@ -158,10 +183,12 @@ def _grant_function_permissions(
|
|
|
158
183
|
)
|
|
159
184
|
],
|
|
160
185
|
)
|
|
161
|
-
logger.
|
|
186
|
+
logger.trace("Granted USE_SCHEMA permission", schema=schema_full_name)
|
|
162
187
|
except Exception as e:
|
|
163
188
|
logger.warning(
|
|
164
|
-
|
|
189
|
+
"Failed to grant USE_SCHEMA permission",
|
|
190
|
+
schema=schema_full_name,
|
|
191
|
+
error=str(e),
|
|
165
192
|
)
|
|
166
193
|
|
|
167
194
|
# 3. Grant USE_CATALOG and BROWSE permissions on the catalog
|
|
@@ -176,25 +203,34 @@ def _grant_function_permissions(
|
|
|
176
203
|
)
|
|
177
204
|
],
|
|
178
205
|
)
|
|
179
|
-
logger.
|
|
206
|
+
logger.trace(
|
|
207
|
+
"Granted USE_CATALOG and BROWSE permissions", catalog=catalog_name
|
|
208
|
+
)
|
|
180
209
|
except Exception as e:
|
|
181
210
|
logger.warning(
|
|
182
|
-
|
|
211
|
+
"Failed to grant catalog permissions",
|
|
212
|
+
catalog=catalog_name,
|
|
213
|
+
error=str(e),
|
|
183
214
|
)
|
|
184
215
|
|
|
185
216
|
logger.debug(
|
|
186
|
-
|
|
217
|
+
"Successfully granted comprehensive permissions",
|
|
218
|
+
function_name=function_name,
|
|
219
|
+
principal=client_id,
|
|
187
220
|
)
|
|
188
221
|
|
|
189
222
|
except Exception as e:
|
|
190
223
|
logger.warning(
|
|
191
|
-
|
|
224
|
+
"Failed to grant permissions",
|
|
225
|
+
function_name=function_name,
|
|
226
|
+
principal=client_id,
|
|
227
|
+
error=str(e),
|
|
192
228
|
)
|
|
193
229
|
# Don't fail the tool creation if permission granting fails
|
|
194
230
|
pass
|
|
195
231
|
|
|
196
232
|
|
|
197
|
-
def _create_filtered_schema(original_schema: type, exclude_fields:
|
|
233
|
+
def _create_filtered_schema(original_schema: type, exclude_fields: Set[str]) -> type:
|
|
198
234
|
"""
|
|
199
235
|
Create a new Pydantic model that excludes specified fields from the original schema.
|
|
200
236
|
|
|
@@ -205,23 +241,27 @@ def _create_filtered_schema(original_schema: type, exclude_fields: set[str]) ->
|
|
|
205
241
|
Returns:
|
|
206
242
|
A new Pydantic model class with the specified fields removed
|
|
207
243
|
"""
|
|
208
|
-
from pydantic import
|
|
209
|
-
from pydantic.fields import PydanticUndefined
|
|
244
|
+
from pydantic import Field, create_model
|
|
245
|
+
from pydantic.fields import FieldInfo, PydanticUndefined
|
|
210
246
|
|
|
211
247
|
try:
|
|
212
248
|
# Get the original model's fields (Pydantic v2)
|
|
213
|
-
original_fields = original_schema.model_fields
|
|
214
|
-
filtered_field_definitions = {}
|
|
249
|
+
original_fields: dict[str, FieldInfo] = original_schema.model_fields
|
|
250
|
+
filtered_field_definitions: dict[str, tuple[type, FieldInfo]] = {}
|
|
215
251
|
|
|
216
|
-
|
|
217
|
-
|
|
252
|
+
field_name: str
|
|
253
|
+
field: FieldInfo
|
|
254
|
+
for field_name, field in original_fields.items():
|
|
255
|
+
if field_name not in exclude_fields:
|
|
218
256
|
# Reconstruct the field definition for create_model
|
|
219
|
-
field_type = field.annotation
|
|
220
|
-
field_default = (
|
|
257
|
+
field_type: type = field.annotation
|
|
258
|
+
field_default: Any = (
|
|
221
259
|
field.default if field.default is not PydanticUndefined else ...
|
|
222
260
|
)
|
|
223
|
-
field_info = Field(
|
|
224
|
-
|
|
261
|
+
field_info: FieldInfo = Field(
|
|
262
|
+
default=field_default, description=field.description
|
|
263
|
+
)
|
|
264
|
+
filtered_field_definitions[field_name] = (field_type, field_info)
|
|
225
265
|
|
|
226
266
|
# If no fields remain after filtering, return a generic empty schema
|
|
227
267
|
if not filtered_field_definitions:
|
|
@@ -234,18 +274,18 @@ def _create_filtered_schema(original_schema: type, exclude_fields: set[str]) ->
|
|
|
234
274
|
return EmptySchema
|
|
235
275
|
|
|
236
276
|
# Create the new model dynamically
|
|
237
|
-
model_name = f"Filtered{original_schema.__name__}"
|
|
238
|
-
docstring = getattr(
|
|
277
|
+
model_name: str = f"Filtered{original_schema.__name__}"
|
|
278
|
+
docstring: str = getattr(
|
|
239
279
|
original_schema, "__doc__", "Filtered Unity Catalog function parameters."
|
|
240
280
|
)
|
|
241
281
|
|
|
242
|
-
filtered_model = create_model(
|
|
282
|
+
filtered_model: type[BaseModel] = create_model(
|
|
243
283
|
model_name, __doc__=docstring, **filtered_field_definitions
|
|
244
284
|
)
|
|
245
285
|
return filtered_model
|
|
246
286
|
|
|
247
287
|
except Exception as e:
|
|
248
|
-
logger.warning(
|
|
288
|
+
logger.warning("Failed to create filtered schema", error=str(e))
|
|
249
289
|
|
|
250
290
|
# Fallback to generic schema
|
|
251
291
|
class GenericFilteredSchema(BaseModel):
|
|
@@ -257,8 +297,7 @@ def _create_filtered_schema(original_schema: type, exclude_fields: set[str]) ->
|
|
|
257
297
|
|
|
258
298
|
|
|
259
299
|
def with_partial_args(
|
|
260
|
-
|
|
261
|
-
partial_args: dict[str, AnyVariable] = {},
|
|
300
|
+
uc_function: UnityCatalogFunctionModel,
|
|
262
301
|
) -> StructuredTool:
|
|
263
302
|
"""
|
|
264
303
|
Create a Unity Catalog tool with partial arguments pre-filled.
|
|
@@ -267,12 +306,8 @@ def with_partial_args(
|
|
|
267
306
|
already resolved, so the caller only needs to provide the remaining parameters.
|
|
268
307
|
|
|
269
308
|
Args:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
Supports:
|
|
273
|
-
- client_id, client_secret: OAuth credentials directly
|
|
274
|
-
- service_principal: ServicePrincipalModel with client_id and client_secret
|
|
275
|
-
- host or workspace_host: Databricks workspace host
|
|
309
|
+
uc_function: UnityCatalogFunctionModel containing the function configuration
|
|
310
|
+
and partial_args to pre-fill.
|
|
276
311
|
|
|
277
312
|
Returns:
|
|
278
313
|
StructuredTool: A LangChain tool with partial arguments pre-filled
|
|
@@ -281,10 +316,12 @@ def with_partial_args(
|
|
|
281
316
|
|
|
282
317
|
from dao_ai.config import ServicePrincipalModel
|
|
283
318
|
|
|
284
|
-
|
|
319
|
+
partial_args: dict[str, AnyVariable] = uc_function.partial_args or {}
|
|
285
320
|
|
|
286
321
|
# Convert dict-based variables to CompositeVariableModel and resolve their values
|
|
287
322
|
resolved_args: dict[str, Any] = {}
|
|
323
|
+
k: str
|
|
324
|
+
v: AnyVariable
|
|
288
325
|
for k, v in partial_args.items():
|
|
289
326
|
if isinstance(v, dict):
|
|
290
327
|
resolved_args[k] = value_of(CompositeVariableModel(**v))
|
|
@@ -293,7 +330,7 @@ def with_partial_args(
|
|
|
293
330
|
|
|
294
331
|
# Handle service_principal - expand into client_id and client_secret
|
|
295
332
|
if "service_principal" in resolved_args:
|
|
296
|
-
sp = resolved_args.pop("service_principal")
|
|
333
|
+
sp: Any = resolved_args.pop("service_principal")
|
|
297
334
|
if isinstance(sp, dict):
|
|
298
335
|
sp = ServicePrincipalModel(**sp)
|
|
299
336
|
if isinstance(sp, ServicePrincipalModel):
|
|
@@ -316,17 +353,17 @@ def with_partial_args(
|
|
|
316
353
|
if host:
|
|
317
354
|
resolved_args["host"] = host
|
|
318
355
|
|
|
319
|
-
|
|
356
|
+
# Get function info from the resource
|
|
357
|
+
function_name: str = uc_function.resource.full_name
|
|
358
|
+
tool_name: str = uc_function.resource.name or function_name.replace(".", "_")
|
|
359
|
+
workspace_client: WorkspaceClient = uc_function.resource.workspace_client
|
|
320
360
|
|
|
321
|
-
|
|
322
|
-
tool
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
function_name: str = unity_catalog_function.full_name
|
|
329
|
-
logger.debug(f"Creating UC tool with partial args for: {function_name}")
|
|
361
|
+
logger.debug(
|
|
362
|
+
"Creating UC tool with partial args",
|
|
363
|
+
function_name=function_name,
|
|
364
|
+
tool_name=tool_name,
|
|
365
|
+
partial_args=list(resolved_args.keys()),
|
|
366
|
+
)
|
|
330
367
|
|
|
331
368
|
# Grant permissions if we have credentials
|
|
332
369
|
if "client_id" in resolved_args:
|
|
@@ -335,36 +372,44 @@ def with_partial_args(
|
|
|
335
372
|
try:
|
|
336
373
|
_grant_function_permissions(function_name, client_id, host)
|
|
337
374
|
except Exception as e:
|
|
338
|
-
logger.warning(
|
|
375
|
+
logger.warning(
|
|
376
|
+
"Failed to grant permissions", function_name=function_name, error=str(e)
|
|
377
|
+
)
|
|
339
378
|
|
|
340
|
-
# Create the client for function execution
|
|
341
|
-
client: DatabricksFunctionClient = DatabricksFunctionClient()
|
|
379
|
+
# Create the client for function execution using the resource's workspace client
|
|
380
|
+
client: DatabricksFunctionClient = DatabricksFunctionClient(client=workspace_client)
|
|
342
381
|
|
|
343
382
|
# Try to get the function schema for better tool definition
|
|
383
|
+
schema_model: type[BaseModel]
|
|
384
|
+
tool_description: str
|
|
344
385
|
try:
|
|
345
|
-
function_info = client.get_function(function_name)
|
|
386
|
+
function_info: FunctionInfo = client.get_function(function_name)
|
|
346
387
|
schema_info = generate_function_input_params_schema(function_info)
|
|
347
388
|
tool_description = (
|
|
348
389
|
function_info.comment or f"Unity Catalog function: {function_name}"
|
|
349
390
|
)
|
|
350
391
|
|
|
351
|
-
logger.
|
|
352
|
-
|
|
392
|
+
logger.trace(
|
|
393
|
+
"Generated function schema",
|
|
394
|
+
function_name=function_name,
|
|
395
|
+
schema=schema_info.pydantic_model.__name__,
|
|
353
396
|
)
|
|
354
|
-
logger.debug(f"Tool description: {tool_description}")
|
|
355
397
|
|
|
356
398
|
# Create a modified schema that excludes partial args
|
|
357
|
-
original_schema = schema_info.pydantic_model
|
|
399
|
+
original_schema: type = schema_info.pydantic_model
|
|
358
400
|
schema_model = _create_filtered_schema(original_schema, resolved_args.keys())
|
|
359
|
-
logger.
|
|
360
|
-
|
|
401
|
+
logger.trace(
|
|
402
|
+
"Filtered schema to exclude partial args",
|
|
403
|
+
function_name=function_name,
|
|
404
|
+
excluded_args=list(resolved_args.keys()),
|
|
361
405
|
)
|
|
362
406
|
|
|
363
407
|
except Exception as e:
|
|
364
|
-
logger.warning(
|
|
365
|
-
|
|
366
|
-
|
|
408
|
+
logger.warning(
|
|
409
|
+
"Could not introspect function", function_name=function_name, error=str(e)
|
|
410
|
+
)
|
|
367
411
|
|
|
412
|
+
# Fallback to a generic schema
|
|
368
413
|
class GenericUCParams(BaseModel):
|
|
369
414
|
"""Generic parameters for Unity Catalog function."""
|
|
370
415
|
|
|
@@ -384,14 +429,12 @@ def with_partial_args(
|
|
|
384
429
|
)
|
|
385
430
|
|
|
386
431
|
# Set the function name for the decorator
|
|
387
|
-
uc_function_wrapper.__name__ =
|
|
432
|
+
uc_function_wrapper.__name__ = tool_name
|
|
388
433
|
|
|
389
434
|
# Create the tool using LangChain's StructuredTool
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
partial_tool = StructuredTool.from_function(
|
|
435
|
+
partial_tool: StructuredTool = StructuredTool.from_function(
|
|
393
436
|
func=uc_function_wrapper,
|
|
394
|
-
name=
|
|
437
|
+
name=tool_name,
|
|
395
438
|
description=tool_description,
|
|
396
439
|
args_schema=schema_model,
|
|
397
440
|
)
|