vectara-agentic 0.3.2__py3-none-any.whl → 0.4.0__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.
Potentially problematic release.
This version of vectara-agentic might be problematic. Click here for more details.
- tests/__init__.py +7 -0
- tests/conftest.py +312 -0
- tests/endpoint.py +54 -17
- tests/run_tests.py +111 -0
- tests/test_agent.py +10 -5
- tests/test_agent_type.py +82 -143
- tests/test_api_endpoint.py +4 -0
- tests/test_bedrock.py +4 -0
- tests/test_fallback.py +4 -0
- tests/test_gemini.py +28 -45
- tests/test_groq.py +4 -0
- tests/test_private_llm.py +11 -2
- tests/test_return_direct.py +6 -2
- tests/test_serialization.py +4 -0
- tests/test_streaming.py +88 -0
- tests/test_tools.py +10 -82
- tests/test_vectara_llms.py +4 -0
- tests/test_vhc.py +66 -0
- tests/test_workflow.py +4 -0
- vectara_agentic/__init__.py +27 -4
- vectara_agentic/_callback.py +65 -67
- vectara_agentic/_observability.py +30 -30
- vectara_agentic/_version.py +1 -1
- vectara_agentic/agent.py +375 -848
- vectara_agentic/agent_config.py +15 -14
- vectara_agentic/agent_core/__init__.py +22 -0
- vectara_agentic/agent_core/factory.py +501 -0
- vectara_agentic/{_prompts.py → agent_core/prompts.py} +3 -35
- vectara_agentic/agent_core/serialization.py +345 -0
- vectara_agentic/agent_core/streaming.py +495 -0
- vectara_agentic/agent_core/utils/__init__.py +34 -0
- vectara_agentic/agent_core/utils/hallucination.py +202 -0
- vectara_agentic/agent_core/utils/logging.py +52 -0
- vectara_agentic/agent_core/utils/prompt_formatting.py +56 -0
- vectara_agentic/agent_core/utils/schemas.py +87 -0
- vectara_agentic/agent_core/utils/tools.py +125 -0
- vectara_agentic/agent_endpoint.py +4 -6
- vectara_agentic/db_tools.py +37 -12
- vectara_agentic/llm_utils.py +41 -42
- vectara_agentic/sub_query_workflow.py +9 -14
- vectara_agentic/tool_utils.py +138 -83
- vectara_agentic/tools.py +43 -21
- vectara_agentic/tools_catalog.py +16 -16
- vectara_agentic/types.py +98 -6
- {vectara_agentic-0.3.2.dist-info → vectara_agentic-0.4.0.dist-info}/METADATA +69 -30
- vectara_agentic-0.4.0.dist-info/RECORD +50 -0
- tests/test_agent_planning.py +0 -64
- tests/test_hhem.py +0 -100
- vectara_agentic/hhem.py +0 -82
- vectara_agentic-0.3.2.dist-info/RECORD +0 -39
- {vectara_agentic-0.3.2.dist-info → vectara_agentic-0.4.0.dist-info}/WHEEL +0 -0
- {vectara_agentic-0.3.2.dist-info → vectara_agentic-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {vectara_agentic-0.3.2.dist-info → vectara_agentic-0.4.0.dist-info}/top_level.txt +0 -0
vectara_agentic/tool_utils.py
CHANGED
|
@@ -4,6 +4,7 @@ This module contains the ToolsFactory class for creating agent tools.
|
|
|
4
4
|
|
|
5
5
|
import inspect
|
|
6
6
|
import re
|
|
7
|
+
import traceback
|
|
7
8
|
|
|
8
9
|
from typing import (
|
|
9
10
|
Callable,
|
|
@@ -17,7 +18,7 @@ from typing import (
|
|
|
17
18
|
get_origin,
|
|
18
19
|
get_args,
|
|
19
20
|
)
|
|
20
|
-
from pydantic import BaseModel, create_model
|
|
21
|
+
from pydantic import BaseModel, create_model, Field
|
|
21
22
|
from pydantic_core import PydanticUndefined
|
|
22
23
|
|
|
23
24
|
from llama_index.core.tools import FunctionTool
|
|
@@ -31,21 +32,26 @@ from .utils import is_float
|
|
|
31
32
|
|
|
32
33
|
class VectaraToolMetadata(ToolMetadata):
|
|
33
34
|
"""
|
|
34
|
-
A subclass of ToolMetadata adding the tool_type
|
|
35
|
+
A subclass of ToolMetadata adding the tool_type and vhc_eligible attributes.
|
|
35
36
|
"""
|
|
36
37
|
|
|
37
38
|
tool_type: ToolType
|
|
39
|
+
vhc_eligible: bool
|
|
38
40
|
|
|
39
|
-
def __init__(self, tool_type: ToolType, **kwargs):
|
|
41
|
+
def __init__(self, tool_type: ToolType, vhc_eligible: bool = True, **kwargs):
|
|
40
42
|
super().__init__(**kwargs)
|
|
41
43
|
self.tool_type = tool_type
|
|
44
|
+
self.vhc_eligible = vhc_eligible
|
|
42
45
|
|
|
43
46
|
def __repr__(self) -> str:
|
|
44
47
|
"""
|
|
45
|
-
Returns a string representation of the VectaraToolMetadata object,
|
|
48
|
+
Returns a string representation of the VectaraToolMetadata object,
|
|
49
|
+
including the tool_type and vhc_eligible attributes.
|
|
46
50
|
"""
|
|
47
51
|
base_repr = super().__repr__()
|
|
48
|
-
return
|
|
52
|
+
return (
|
|
53
|
+
f"{base_repr}, tool_type={self.tool_type}, vhc_eligible={self.vhc_eligible}"
|
|
54
|
+
)
|
|
49
55
|
|
|
50
56
|
|
|
51
57
|
class VectaraTool(FunctionTool):
|
|
@@ -59,11 +65,17 @@ class VectaraTool(FunctionTool):
|
|
|
59
65
|
metadata: ToolMetadata,
|
|
60
66
|
fn: Optional[Callable[..., Any]] = None,
|
|
61
67
|
async_fn: Optional[AsyncCallable] = None,
|
|
68
|
+
vhc_eligible: bool = True,
|
|
62
69
|
) -> None:
|
|
70
|
+
# Use Pydantic v2 compatible method for extracting metadata
|
|
63
71
|
metadata_dict = (
|
|
64
|
-
metadata.
|
|
72
|
+
metadata.model_dump()
|
|
73
|
+
if hasattr(metadata, "model_dump")
|
|
74
|
+
else metadata.dict() if hasattr(metadata, "dict") else metadata.__dict__
|
|
75
|
+
)
|
|
76
|
+
vm = VectaraToolMetadata(
|
|
77
|
+
tool_type=tool_type, vhc_eligible=vhc_eligible, **metadata_dict
|
|
65
78
|
)
|
|
66
|
-
vm = VectaraToolMetadata(tool_type=tool_type, **metadata_dict)
|
|
67
79
|
super().__init__(fn, vm, async_fn)
|
|
68
80
|
|
|
69
81
|
@classmethod
|
|
@@ -80,6 +92,7 @@ class VectaraTool(FunctionTool):
|
|
|
80
92
|
async_callback: Optional[AsyncCallable] = None,
|
|
81
93
|
partial_params: Optional[Dict[str, Any]] = None,
|
|
82
94
|
tool_type: ToolType = ToolType.QUERY,
|
|
95
|
+
vhc_eligible: bool = True,
|
|
83
96
|
) -> "VectaraTool":
|
|
84
97
|
tool = FunctionTool.from_defaults(
|
|
85
98
|
fn,
|
|
@@ -98,6 +111,7 @@ class VectaraTool(FunctionTool):
|
|
|
98
111
|
fn=tool.fn,
|
|
99
112
|
metadata=tool.metadata,
|
|
100
113
|
async_fn=tool.async_fn,
|
|
114
|
+
vhc_eligible=vhc_eligible,
|
|
101
115
|
)
|
|
102
116
|
return vectara_tool
|
|
103
117
|
|
|
@@ -123,94 +137,82 @@ class VectaraTool(FunctionTool):
|
|
|
123
137
|
)
|
|
124
138
|
return is_equal
|
|
125
139
|
|
|
126
|
-
def
|
|
127
|
-
self,
|
|
140
|
+
def _create_tool_error_output(
|
|
141
|
+
self, error: Exception, args: Any, kwargs: Any, include_traceback: bool = False
|
|
128
142
|
) -> ToolOutput:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
except TypeError as e:
|
|
143
|
+
"""Create standardized error output for tool execution failures."""
|
|
144
|
+
if isinstance(error, TypeError):
|
|
145
|
+
# Parameter validation error handling
|
|
133
146
|
sig = inspect.signature(self.metadata.fn_schema)
|
|
134
147
|
valid_parameters = list(sig.parameters.keys())
|
|
135
148
|
params_str = ", ".join(valid_parameters)
|
|
136
|
-
|
|
137
|
-
err_output = ToolOutput(
|
|
149
|
+
return ToolOutput(
|
|
138
150
|
tool_name=self.metadata.name,
|
|
139
151
|
content=(
|
|
140
|
-
f"Wrong argument used when calling {self.metadata.name}: {str(
|
|
141
|
-
f"Valid arguments: {params_str}.
|
|
152
|
+
f"Wrong argument used when calling {self.metadata.name}: {str(error)}. "
|
|
153
|
+
f"Valid arguments: {params_str}. Please call the tool again with the correct arguments."
|
|
142
154
|
),
|
|
143
155
|
raw_input={"args": args, "kwargs": kwargs},
|
|
144
|
-
raw_output={"response": str(
|
|
156
|
+
raw_output={"response": str(error)},
|
|
145
157
|
)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
else:
|
|
159
|
+
# General execution error handling
|
|
160
|
+
content = f"Tool {self.metadata.name} Malfunction: {str(error)}"
|
|
161
|
+
if include_traceback:
|
|
162
|
+
content += f", traceback: {traceback.format_exc()}"
|
|
163
|
+
|
|
164
|
+
return ToolOutput(
|
|
149
165
|
tool_name=self.metadata.name,
|
|
150
|
-
content=
|
|
166
|
+
content=content,
|
|
151
167
|
raw_input={"args": args, "kwargs": kwargs},
|
|
152
|
-
raw_output={"response": str(
|
|
168
|
+
raw_output={"response": str(error)},
|
|
153
169
|
)
|
|
154
|
-
return err_output
|
|
155
170
|
|
|
156
|
-
|
|
171
|
+
def call(
|
|
157
172
|
self, *args: Any, ctx: Optional[Context] = None, **kwargs: Any
|
|
158
173
|
) -> ToolOutput:
|
|
159
174
|
try:
|
|
160
|
-
|
|
175
|
+
# Only pass ctx if it's not None to avoid passing unwanted kwargs to the function
|
|
176
|
+
if ctx is not None:
|
|
177
|
+
result = super().call(*args, ctx=ctx, **kwargs)
|
|
178
|
+
else:
|
|
179
|
+
result = super().call(*args, **kwargs)
|
|
161
180
|
return self._format_tool_output(result)
|
|
162
|
-
except TypeError as e:
|
|
163
|
-
sig = inspect.signature(self.metadata.fn_schema)
|
|
164
|
-
valid_parameters = list(sig.parameters.keys())
|
|
165
|
-
params_str = ", ".join(valid_parameters)
|
|
166
|
-
|
|
167
|
-
err_output = ToolOutput(
|
|
168
|
-
tool_name=self.metadata.name,
|
|
169
|
-
content=(
|
|
170
|
-
f"Wrong argument used when calling {self.metadata.name}: {str(e)}. "
|
|
171
|
-
f"Valid arguments: {params_str}. please call the tool again with the correct arguments."
|
|
172
|
-
),
|
|
173
|
-
raw_input={"args": args, "kwargs": kwargs},
|
|
174
|
-
raw_output={"response": str(e)},
|
|
175
|
-
)
|
|
176
|
-
return err_output
|
|
177
181
|
except Exception as e:
|
|
178
|
-
|
|
182
|
+
return self._create_tool_error_output(e, args, kwargs)
|
|
179
183
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
async def acall(
|
|
185
|
+
self, *args: Any, ctx: Optional[Context] = None, **kwargs: Any
|
|
186
|
+
) -> ToolOutput:
|
|
187
|
+
try:
|
|
188
|
+
# Only pass ctx if it's not None to avoid passing unwanted kwargs to the function
|
|
189
|
+
if ctx is not None:
|
|
190
|
+
result = await super().acall(*args, ctx=ctx, **kwargs)
|
|
191
|
+
else:
|
|
192
|
+
result = await super().acall(*args, **kwargs)
|
|
193
|
+
return self._format_tool_output(result)
|
|
194
|
+
except Exception as e:
|
|
195
|
+
return self._create_tool_error_output(
|
|
196
|
+
e, args, kwargs, include_traceback=True
|
|
185
197
|
)
|
|
186
|
-
return err_output
|
|
187
198
|
|
|
188
199
|
def _format_tool_output(self, result: ToolOutput) -> ToolOutput:
|
|
189
|
-
"""Format tool output
|
|
190
|
-
|
|
200
|
+
"""Format tool output by converting human-readable wrappers to formatted content immediately."""
|
|
201
|
+
import logging
|
|
202
|
+
|
|
203
|
+
# If the raw_output has human-readable formatting, use it for the content
|
|
204
|
+
if hasattr(result, "raw_output") and _is_human_readable_output(
|
|
205
|
+
result.raw_output
|
|
206
|
+
):
|
|
191
207
|
try:
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return ToolOutput(
|
|
196
|
-
tool_name=result.tool_name,
|
|
197
|
-
content=human_readable_content,
|
|
198
|
-
raw_input=result.raw_input,
|
|
199
|
-
raw_output=raw_output,
|
|
200
|
-
)
|
|
208
|
+
formatted_content = result.raw_output.to_human_readable()
|
|
209
|
+
# Replace the content with the formatted version
|
|
210
|
+
result.content = formatted_content
|
|
201
211
|
except Exception as e:
|
|
202
|
-
# If formatting fails, fall back to original content with error info
|
|
203
|
-
import logging
|
|
204
|
-
|
|
205
212
|
logging.warning(
|
|
206
|
-
f"Failed to
|
|
207
|
-
)
|
|
208
|
-
return ToolOutput(
|
|
209
|
-
tool_name=result.tool_name,
|
|
210
|
-
content=f"[Formatting Error] {str(result.content)}",
|
|
211
|
-
raw_input=result.raw_input,
|
|
212
|
-
raw_output={"error": str(e), "original_content": result.content},
|
|
213
|
+
f"{self.metadata.name}: Failed to convert to human-readable: {e}"
|
|
213
214
|
)
|
|
215
|
+
|
|
214
216
|
return result
|
|
215
217
|
|
|
216
218
|
|
|
@@ -257,7 +259,6 @@ def _make_docstring(
|
|
|
257
259
|
tool_description: str,
|
|
258
260
|
fn_schema: Type[BaseModel],
|
|
259
261
|
all_params: List[inspect.Parameter],
|
|
260
|
-
compact_docstring: bool,
|
|
261
262
|
) -> str:
|
|
262
263
|
"""
|
|
263
264
|
Generates a docstring for a function based on its signature, description,
|
|
@@ -269,7 +270,6 @@ def _make_docstring(
|
|
|
269
270
|
tool_description: The main description of the tool/function.
|
|
270
271
|
fn_schema: The Pydantic model representing the function's arguments schema.
|
|
271
272
|
all_params: A list of inspect.Parameter objects for the function signature.
|
|
272
|
-
compact_docstring: If True, omits the signature line in the main description.
|
|
273
273
|
|
|
274
274
|
Returns:
|
|
275
275
|
A formatted docstring string.
|
|
@@ -282,10 +282,7 @@ def _make_docstring(
|
|
|
282
282
|
params_str = ", ".join(params_str_parts)
|
|
283
283
|
signature_line = f"{tool_name}({params_str}) -> dict[str, Any]"
|
|
284
284
|
|
|
285
|
-
|
|
286
|
-
doc_lines = [tool_description.strip()]
|
|
287
|
-
else:
|
|
288
|
-
doc_lines = [signature_line, "", tool_description.strip()]
|
|
285
|
+
doc_lines = [signature_line, "", tool_description.strip()]
|
|
289
286
|
|
|
290
287
|
full_schema = fn_schema.model_json_schema()
|
|
291
288
|
props = full_schema.get("properties", {})
|
|
@@ -339,14 +336,61 @@ def _make_docstring(
|
|
|
339
336
|
return final_docstring
|
|
340
337
|
|
|
341
338
|
|
|
339
|
+
def _auto_fix_field_if_needed(
|
|
340
|
+
field_name: str, field_info, annotation
|
|
341
|
+
) -> Tuple[Any, Any]:
|
|
342
|
+
"""
|
|
343
|
+
Auto-fix problematic Field definitions: convert non-Optional types with any default value to Optional.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
field_name: Name of the field
|
|
347
|
+
field_info: The Pydantic FieldInfo object
|
|
348
|
+
annotation: The type annotation for the field
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Tuple of (possibly_modified_annotation, possibly_modified_field_info)
|
|
352
|
+
"""
|
|
353
|
+
# Check for problematic pattern: non-Optional type with any default value
|
|
354
|
+
if (
|
|
355
|
+
field_info.default is not PydanticUndefined
|
|
356
|
+
and annotation is not None
|
|
357
|
+
and get_origin(annotation) is not Union
|
|
358
|
+
):
|
|
359
|
+
|
|
360
|
+
# Convert to Optional[OriginalType] and keep the original default value
|
|
361
|
+
new_annotation = Union[annotation, type(None)]
|
|
362
|
+
# Create new field_info preserving the original default value
|
|
363
|
+
new_field_info = Field(
|
|
364
|
+
default=field_info.default,
|
|
365
|
+
description=field_info.description,
|
|
366
|
+
examples=getattr(field_info, "examples", None),
|
|
367
|
+
title=getattr(field_info, "title", None),
|
|
368
|
+
alias=getattr(field_info, "alias", None),
|
|
369
|
+
json_schema_extra=getattr(field_info, "json_schema_extra", None),
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
# Optional: Log the auto-fix for debugging
|
|
373
|
+
import logging
|
|
374
|
+
|
|
375
|
+
logging.debug(
|
|
376
|
+
f"Auto-fixed field '{field_name}': "
|
|
377
|
+
f"converted {annotation} with default={field_info.default} to Optional[{annotation.__name__}]"
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
return new_annotation, new_field_info
|
|
381
|
+
else:
|
|
382
|
+
# Keep original field definition
|
|
383
|
+
return annotation, field_info
|
|
384
|
+
|
|
385
|
+
|
|
342
386
|
def create_tool_from_dynamic_function(
|
|
343
387
|
function: Callable[..., ToolOutput],
|
|
344
388
|
tool_name: str,
|
|
345
389
|
tool_description: str,
|
|
346
390
|
base_params_model: Type[BaseModel],
|
|
347
391
|
tool_args_schema: Type[BaseModel],
|
|
348
|
-
compact_docstring: bool = False,
|
|
349
392
|
return_direct: bool = False,
|
|
393
|
+
vhc_eligible: bool = True,
|
|
350
394
|
) -> VectaraTool:
|
|
351
395
|
"""
|
|
352
396
|
Create a VectaraTool from a dynamic function.
|
|
@@ -356,7 +400,7 @@ def create_tool_from_dynamic_function(
|
|
|
356
400
|
tool_description (str): The description of the tool.
|
|
357
401
|
base_params_model (Type[BaseModel]): The Pydantic model for the base parameters.
|
|
358
402
|
tool_args_schema (Type[BaseModel]): The Pydantic model for the tool arguments.
|
|
359
|
-
|
|
403
|
+
return_direct (bool): Whether to return the tool output directly.
|
|
360
404
|
Returns:
|
|
361
405
|
VectaraTool: The created VectaraTool.
|
|
362
406
|
"""
|
|
@@ -371,6 +415,11 @@ def create_tool_from_dynamic_function(
|
|
|
371
415
|
fields: Dict[str, Any] = {}
|
|
372
416
|
base_params = []
|
|
373
417
|
for field_name, field_info in base_params_model.model_fields.items():
|
|
418
|
+
# Apply auto-conversion if needed
|
|
419
|
+
annotation, field_info = _auto_fix_field_if_needed(
|
|
420
|
+
field_name, field_info, field_info.annotation
|
|
421
|
+
)
|
|
422
|
+
|
|
374
423
|
default = (
|
|
375
424
|
Ellipsis if field_info.default is PydanticUndefined else field_info.default
|
|
376
425
|
)
|
|
@@ -378,16 +427,21 @@ def create_tool_from_dynamic_function(
|
|
|
378
427
|
field_name,
|
|
379
428
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
380
429
|
default=default if default is not Ellipsis else inspect.Parameter.empty,
|
|
381
|
-
annotation=
|
|
430
|
+
annotation=annotation,
|
|
382
431
|
)
|
|
383
432
|
base_params.append(param)
|
|
384
|
-
fields[field_name] = (
|
|
433
|
+
fields[field_name] = (annotation, field_info)
|
|
385
434
|
|
|
386
435
|
# Add tool_args_schema fields to the fields dict if not already included.
|
|
387
436
|
for field_name, field_info in tool_args_schema.model_fields.items():
|
|
388
437
|
if field_name in fields:
|
|
389
438
|
continue
|
|
390
439
|
|
|
440
|
+
# Apply auto-conversion if needed
|
|
441
|
+
annotation, field_info = _auto_fix_field_if_needed(
|
|
442
|
+
field_name, field_info, field_info.annotation
|
|
443
|
+
)
|
|
444
|
+
|
|
391
445
|
default = (
|
|
392
446
|
Ellipsis if field_info.default is PydanticUndefined else field_info.default
|
|
393
447
|
)
|
|
@@ -395,12 +449,12 @@ def create_tool_from_dynamic_function(
|
|
|
395
449
|
field_name,
|
|
396
450
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
397
451
|
default=default if default is not Ellipsis else inspect.Parameter.empty,
|
|
398
|
-
annotation=
|
|
452
|
+
annotation=annotation,
|
|
399
453
|
)
|
|
400
454
|
base_params.append(param)
|
|
401
|
-
fields[field_name] = (
|
|
455
|
+
fields[field_name] = (annotation, field_info)
|
|
402
456
|
|
|
403
|
-
# Create the dynamic schema with both base_params_model and tool_args_schema fields
|
|
457
|
+
# Create the dynamic schema with both base_params_model and tool_args_schema fields (auto-fixed)
|
|
404
458
|
fn_schema = create_model(f"{tool_name}_schema", **fields)
|
|
405
459
|
|
|
406
460
|
# Combine parameters into a function signature.
|
|
@@ -414,7 +468,7 @@ def create_tool_from_dynamic_function(
|
|
|
414
468
|
function.__name__ = re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
|
|
415
469
|
|
|
416
470
|
function.__doc__ = _make_docstring(
|
|
417
|
-
function, tool_name, tool_description, fn_schema, all_params
|
|
471
|
+
function, tool_name, tool_description, fn_schema, all_params
|
|
418
472
|
)
|
|
419
473
|
tool = VectaraTool.from_defaults(
|
|
420
474
|
fn=function,
|
|
@@ -423,6 +477,7 @@ def create_tool_from_dynamic_function(
|
|
|
423
477
|
fn_schema=fn_schema,
|
|
424
478
|
tool_type=ToolType.QUERY,
|
|
425
479
|
return_direct=return_direct,
|
|
480
|
+
vhc_eligible=vhc_eligible,
|
|
426
481
|
)
|
|
427
482
|
return tool
|
|
428
483
|
|
vectara_agentic/tools.py
CHANGED
|
@@ -74,20 +74,16 @@ class VectaraToolFactory:
|
|
|
74
74
|
self,
|
|
75
75
|
vectara_corpus_key: str = str(os.environ.get("VECTARA_CORPUS_KEY", "")),
|
|
76
76
|
vectara_api_key: str = str(os.environ.get("VECTARA_API_KEY", "")),
|
|
77
|
-
compact_docstring: bool = False,
|
|
78
77
|
) -> None:
|
|
79
78
|
"""
|
|
80
79
|
Initialize the VectaraToolFactory
|
|
81
80
|
Args:
|
|
82
81
|
vectara_corpus_key (str): The Vectara corpus key (or comma separated list of keys).
|
|
83
82
|
vectara_api_key (str): The Vectara API key.
|
|
84
|
-
compact_docstring (bool): Whether to use a compact docstring format for tools
|
|
85
|
-
This is useful if OpenAI complains on the 1024 token limit.
|
|
86
83
|
"""
|
|
87
84
|
self.vectara_corpus_key = vectara_corpus_key
|
|
88
85
|
self.vectara_api_key = vectara_api_key
|
|
89
86
|
self.num_corpora = len(vectara_corpus_key.split(","))
|
|
90
|
-
self.compact_docstring = compact_docstring
|
|
91
87
|
|
|
92
88
|
def create_search_tool(
|
|
93
89
|
self,
|
|
@@ -116,6 +112,7 @@ class VectaraToolFactory:
|
|
|
116
112
|
verbose: bool = False,
|
|
117
113
|
vectara_base_url: str = "https://api.vectara.io",
|
|
118
114
|
vectara_verify_ssl: bool = True,
|
|
115
|
+
vhc_eligible: bool = True,
|
|
119
116
|
) -> VectaraTool:
|
|
120
117
|
"""
|
|
121
118
|
Creates a Vectara search/retrieval tool
|
|
@@ -280,7 +277,7 @@ class VectaraToolFactory:
|
|
|
280
277
|
# Add all matching text if available
|
|
281
278
|
matches = result["metadata"]["matching_text"]
|
|
282
279
|
if matches:
|
|
283
|
-
result_str +=
|
|
280
|
+
result_str += "".join(
|
|
284
281
|
f"Match #{inx} Text: {match}\n"
|
|
285
282
|
for inx, match in enumerate(matches, 1)
|
|
286
283
|
)
|
|
@@ -312,7 +309,7 @@ class VectaraToolFactory:
|
|
|
312
309
|
description="The search query to perform, in the form of a question.",
|
|
313
310
|
)
|
|
314
311
|
top_k: int = Field(
|
|
315
|
-
10, description="The number of top documents to retrieve."
|
|
312
|
+
default=10, description="The number of top documents to retrieve."
|
|
316
313
|
)
|
|
317
314
|
|
|
318
315
|
search_tool_extra_desc = (
|
|
@@ -331,8 +328,8 @@ class VectaraToolFactory:
|
|
|
331
328
|
else SearchToolBaseParamsWithoutSummarize
|
|
332
329
|
),
|
|
333
330
|
tool_args_schema,
|
|
334
|
-
compact_docstring=self.compact_docstring,
|
|
335
331
|
return_direct=return_direct,
|
|
332
|
+
vhc_eligible=vhc_eligible,
|
|
336
333
|
)
|
|
337
334
|
return tool
|
|
338
335
|
|
|
@@ -367,13 +364,16 @@ class VectaraToolFactory:
|
|
|
367
364
|
frequency_penalty: Optional[float] = None,
|
|
368
365
|
presence_penalty: Optional[float] = None,
|
|
369
366
|
include_citations: bool = True,
|
|
370
|
-
citation_pattern: str =
|
|
367
|
+
citation_pattern: str = None,
|
|
368
|
+
citation_url_pattern: str = "{doc.url}",
|
|
369
|
+
citation_text_pattern: str = "{doc.title}",
|
|
371
370
|
save_history: bool = False,
|
|
372
371
|
fcs_threshold: float = 0.0,
|
|
373
372
|
return_direct: bool = False,
|
|
374
373
|
verbose: bool = False,
|
|
375
374
|
vectara_base_url: str = "https://api.vectara.io",
|
|
376
375
|
vectara_verify_ssl: bool = True,
|
|
376
|
+
vhc_eligible: bool = True,
|
|
377
377
|
) -> VectaraTool:
|
|
378
378
|
"""
|
|
379
379
|
Creates a RAG (Retrieve and Generate) tool.
|
|
@@ -421,9 +421,13 @@ class VectaraToolFactory:
|
|
|
421
421
|
higher values increasing the diversity of topics.
|
|
422
422
|
include_citations (bool, optional): Whether to include citations in the response.
|
|
423
423
|
If True, uses markdown vectara citations that requires the Vectara scale plan.
|
|
424
|
-
|
|
424
|
+
citation_url_pattern (str, optional): The pattern for the citations in the response.
|
|
425
425
|
Default is "{doc.url}" which uses the document URL.
|
|
426
426
|
If include_citations is False, this parameter is ignored.
|
|
427
|
+
citation_pattern (str, optional): old name for citation_url_pattern. Deprecated.
|
|
428
|
+
citation_text_pattern (str, optional): The text pattern for citations in the response.
|
|
429
|
+
Default is "{doc.title}" which uses the title of the document.
|
|
430
|
+
If include_citations is False, this parameter is ignored.
|
|
427
431
|
save_history (bool, optional): Whether to save the query in history.
|
|
428
432
|
fcs_threshold (float, optional): A threshold for factual consistency.
|
|
429
433
|
If set above 0, the tool notifies the calling agent that it "cannot respond" if FCS is too low.
|
|
@@ -469,6 +473,15 @@ class VectaraToolFactory:
|
|
|
469
473
|
)
|
|
470
474
|
return {"text": msg, "metadata": {"args": args, "kwargs": kwargs}}
|
|
471
475
|
|
|
476
|
+
citations_url_pattern = (
|
|
477
|
+
(
|
|
478
|
+
citation_url_pattern
|
|
479
|
+
if citation_url_pattern is not None
|
|
480
|
+
else citation_pattern
|
|
481
|
+
)
|
|
482
|
+
if include_citations
|
|
483
|
+
else None
|
|
484
|
+
)
|
|
472
485
|
vectara_query_engine = vectara.as_query_engine(
|
|
473
486
|
summary_enabled=True,
|
|
474
487
|
similarity_top_k=summary_num_results,
|
|
@@ -501,7 +514,10 @@ class VectaraToolFactory:
|
|
|
501
514
|
frequency_penalty=frequency_penalty,
|
|
502
515
|
presence_penalty=presence_penalty,
|
|
503
516
|
citations_style="markdown" if include_citations else None,
|
|
504
|
-
citations_url_pattern=
|
|
517
|
+
citations_url_pattern=citations_url_pattern,
|
|
518
|
+
citations_text_pattern=(
|
|
519
|
+
citation_text_pattern if include_citations else None
|
|
520
|
+
),
|
|
505
521
|
save_history=save_history,
|
|
506
522
|
x_source_str="vectara-agentic",
|
|
507
523
|
verbose=verbose,
|
|
@@ -513,9 +529,11 @@ class VectaraToolFactory:
|
|
|
513
529
|
"Tool failed to generate a response since no matches were found. "
|
|
514
530
|
"Please check the arguments and try again."
|
|
515
531
|
)
|
|
532
|
+
kwargs["query"] = query
|
|
516
533
|
return {"text": msg, "metadata": {"args": args, "kwargs": kwargs}}
|
|
517
534
|
if str(response) == "None":
|
|
518
535
|
msg = "Tool failed to generate a response."
|
|
536
|
+
kwargs["query"] = query
|
|
519
537
|
return {"text": msg, "metadata": {"args": args, "kwargs": kwargs}}
|
|
520
538
|
|
|
521
539
|
# Extract citation metadata
|
|
@@ -557,11 +575,8 @@ class VectaraToolFactory:
|
|
|
557
575
|
if key.isdigit():
|
|
558
576
|
doc = value.get("document", {})
|
|
559
577
|
doc_metadata = f"{key}: " + "; ".join(
|
|
560
|
-
[
|
|
561
|
-
|
|
562
|
-
for k, v in doc.items()
|
|
563
|
-
] +
|
|
564
|
-
[
|
|
578
|
+
[f"{k}='{v}'" for k, v in doc.items()]
|
|
579
|
+
+ [
|
|
565
580
|
f"{k}='{v}'"
|
|
566
581
|
for k, v in value.items()
|
|
567
582
|
if k not in ["document"] + keys_to_ignore
|
|
@@ -589,8 +604,8 @@ class VectaraToolFactory:
|
|
|
589
604
|
tool_description,
|
|
590
605
|
RagToolBaseParams,
|
|
591
606
|
tool_args_schema,
|
|
592
|
-
compact_docstring=self.compact_docstring,
|
|
593
607
|
return_direct=return_direct,
|
|
608
|
+
vhc_eligible=vhc_eligible,
|
|
594
609
|
)
|
|
595
610
|
return tool
|
|
596
611
|
|
|
@@ -604,7 +619,10 @@ class ToolsFactory:
|
|
|
604
619
|
self.agent_config = agent_config
|
|
605
620
|
|
|
606
621
|
def create_tool(
|
|
607
|
-
self,
|
|
622
|
+
self,
|
|
623
|
+
function: Callable,
|
|
624
|
+
tool_type: ToolType = ToolType.QUERY,
|
|
625
|
+
vhc_eligible: bool = True,
|
|
608
626
|
) -> VectaraTool:
|
|
609
627
|
"""
|
|
610
628
|
Create a tool from a function.
|
|
@@ -616,7 +634,9 @@ class ToolsFactory:
|
|
|
616
634
|
Returns:
|
|
617
635
|
VectaraTool: A VectaraTool object.
|
|
618
636
|
"""
|
|
619
|
-
return VectaraTool.from_defaults(
|
|
637
|
+
return VectaraTool.from_defaults(
|
|
638
|
+
tool_type=tool_type, fn=function, vhc_eligible=vhc_eligible
|
|
639
|
+
)
|
|
620
640
|
|
|
621
641
|
def get_llama_index_tools(
|
|
622
642
|
self,
|
|
@@ -677,7 +697,7 @@ class ToolsFactory:
|
|
|
677
697
|
"""
|
|
678
698
|
tc = ToolsCatalog(self.agent_config)
|
|
679
699
|
return [
|
|
680
|
-
self.create_tool(tool)
|
|
700
|
+
self.create_tool(tool, vhc_eligible=True)
|
|
681
701
|
for tool in [tc.summarize_text, tc.rephrase_text, tc.critique_text]
|
|
682
702
|
]
|
|
683
703
|
|
|
@@ -685,7 +705,7 @@ class ToolsFactory:
|
|
|
685
705
|
"""
|
|
686
706
|
Create a list of guardrail tools to avoid controversial topics.
|
|
687
707
|
"""
|
|
688
|
-
return [self.create_tool(get_bad_topics)]
|
|
708
|
+
return [self.create_tool(get_bad_topics, vhc_eligible=False)]
|
|
689
709
|
|
|
690
710
|
def financial_tools(self):
|
|
691
711
|
"""
|
|
@@ -726,7 +746,8 @@ class ToolsFactory:
|
|
|
726
746
|
)
|
|
727
747
|
|
|
728
748
|
return [
|
|
729
|
-
self.create_tool(tool
|
|
749
|
+
self.create_tool(tool, vhc_eligible=False)
|
|
750
|
+
for tool in [summarize_legal_text, critique_as_judge]
|
|
730
751
|
]
|
|
731
752
|
|
|
732
753
|
def database_tools(
|
|
@@ -801,6 +822,7 @@ class ToolsFactory:
|
|
|
801
822
|
fn=tool.fn,
|
|
802
823
|
async_fn=tool.async_fn,
|
|
803
824
|
metadata=tool.metadata,
|
|
825
|
+
vhc_eligible=True,
|
|
804
826
|
)
|
|
805
827
|
vtools.append(vtool)
|
|
806
828
|
return vtools
|
vectara_agentic/tools_catalog.py
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This module contains the tools catalog for the Vectara Agentic.
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
from typing import List
|
|
5
6
|
from datetime import date
|
|
6
7
|
|
|
7
|
-
import requests
|
|
8
|
-
|
|
9
8
|
from pydantic import Field
|
|
10
9
|
|
|
11
10
|
from .types import LLMRole
|
|
@@ -13,16 +12,6 @@ from .agent_config import AgentConfig
|
|
|
13
12
|
from .llm_utils import get_llm
|
|
14
13
|
from .utils import remove_self_from_signature
|
|
15
14
|
|
|
16
|
-
req_session = requests.Session()
|
|
17
|
-
|
|
18
|
-
get_headers = {
|
|
19
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0",
|
|
20
|
-
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
|
21
|
-
"Accept-Language": "en-US,en;q=0.5",
|
|
22
|
-
"Accept-Encoding": "gzip, deflate",
|
|
23
|
-
"Connection": "keep-alive",
|
|
24
|
-
}
|
|
25
|
-
|
|
26
15
|
def get_current_date() -> str:
|
|
27
16
|
"""
|
|
28
17
|
Returns the current date as a string.
|
|
@@ -35,6 +24,7 @@ class ToolsCatalog:
|
|
|
35
24
|
"""
|
|
36
25
|
A curated set of tools for vectara-agentic
|
|
37
26
|
"""
|
|
27
|
+
|
|
38
28
|
def __init__(self, agent_config: AgentConfig):
|
|
39
29
|
self.agent_config = agent_config
|
|
40
30
|
|
|
@@ -76,7 +66,9 @@ class ToolsCatalog:
|
|
|
76
66
|
def rephrase_text(
|
|
77
67
|
self,
|
|
78
68
|
text: str = Field(description="the original text."),
|
|
79
|
-
instructions: str = Field(
|
|
69
|
+
instructions: str = Field(
|
|
70
|
+
description="the specific instructions for how to rephrase the text."
|
|
71
|
+
),
|
|
80
72
|
) -> str:
|
|
81
73
|
"""
|
|
82
74
|
This is a helper tool.
|
|
@@ -103,8 +95,13 @@ class ToolsCatalog:
|
|
|
103
95
|
def critique_text(
|
|
104
96
|
self,
|
|
105
97
|
text: str = Field(description="the original text."),
|
|
106
|
-
role: str = Field(
|
|
107
|
-
|
|
98
|
+
role: str = Field(
|
|
99
|
+
default=None, description="the role of the person providing critique."
|
|
100
|
+
),
|
|
101
|
+
point_of_view: str = Field(
|
|
102
|
+
default=None,
|
|
103
|
+
description="the point of view with which to provide critique.",
|
|
104
|
+
),
|
|
108
105
|
) -> str:
|
|
109
106
|
"""
|
|
110
107
|
This is a helper tool.
|
|
@@ -121,13 +118,16 @@ class ToolsCatalog:
|
|
|
121
118
|
if role:
|
|
122
119
|
prompt = f"As a {role}, critique the provided text from the point of view of {point_of_view}."
|
|
123
120
|
else:
|
|
124
|
-
prompt =
|
|
121
|
+
prompt = (
|
|
122
|
+
f"Critique the provided text from the point of view of {point_of_view}."
|
|
123
|
+
)
|
|
125
124
|
prompt += "\nStructure the critique as bullet points.\n"
|
|
126
125
|
prompt += f"Original text: {text}\nCritique:"
|
|
127
126
|
llm = get_llm(LLMRole.TOOL, config=self.agent_config)
|
|
128
127
|
response = llm.complete(prompt)
|
|
129
128
|
return response.text
|
|
130
129
|
|
|
130
|
+
|
|
131
131
|
#
|
|
132
132
|
# Guardrails tool: returns list of topics to avoid
|
|
133
133
|
#
|