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.

Files changed (53) hide show
  1. tests/__init__.py +7 -0
  2. tests/conftest.py +312 -0
  3. tests/endpoint.py +54 -17
  4. tests/run_tests.py +111 -0
  5. tests/test_agent.py +10 -5
  6. tests/test_agent_type.py +82 -143
  7. tests/test_api_endpoint.py +4 -0
  8. tests/test_bedrock.py +4 -0
  9. tests/test_fallback.py +4 -0
  10. tests/test_gemini.py +28 -45
  11. tests/test_groq.py +4 -0
  12. tests/test_private_llm.py +11 -2
  13. tests/test_return_direct.py +6 -2
  14. tests/test_serialization.py +4 -0
  15. tests/test_streaming.py +88 -0
  16. tests/test_tools.py +10 -82
  17. tests/test_vectara_llms.py +4 -0
  18. tests/test_vhc.py +66 -0
  19. tests/test_workflow.py +4 -0
  20. vectara_agentic/__init__.py +27 -4
  21. vectara_agentic/_callback.py +65 -67
  22. vectara_agentic/_observability.py +30 -30
  23. vectara_agentic/_version.py +1 -1
  24. vectara_agentic/agent.py +375 -848
  25. vectara_agentic/agent_config.py +15 -14
  26. vectara_agentic/agent_core/__init__.py +22 -0
  27. vectara_agentic/agent_core/factory.py +501 -0
  28. vectara_agentic/{_prompts.py → agent_core/prompts.py} +3 -35
  29. vectara_agentic/agent_core/serialization.py +345 -0
  30. vectara_agentic/agent_core/streaming.py +495 -0
  31. vectara_agentic/agent_core/utils/__init__.py +34 -0
  32. vectara_agentic/agent_core/utils/hallucination.py +202 -0
  33. vectara_agentic/agent_core/utils/logging.py +52 -0
  34. vectara_agentic/agent_core/utils/prompt_formatting.py +56 -0
  35. vectara_agentic/agent_core/utils/schemas.py +87 -0
  36. vectara_agentic/agent_core/utils/tools.py +125 -0
  37. vectara_agentic/agent_endpoint.py +4 -6
  38. vectara_agentic/db_tools.py +37 -12
  39. vectara_agentic/llm_utils.py +41 -42
  40. vectara_agentic/sub_query_workflow.py +9 -14
  41. vectara_agentic/tool_utils.py +138 -83
  42. vectara_agentic/tools.py +43 -21
  43. vectara_agentic/tools_catalog.py +16 -16
  44. vectara_agentic/types.py +98 -6
  45. {vectara_agentic-0.3.2.dist-info → vectara_agentic-0.4.0.dist-info}/METADATA +69 -30
  46. vectara_agentic-0.4.0.dist-info/RECORD +50 -0
  47. tests/test_agent_planning.py +0 -64
  48. tests/test_hhem.py +0 -100
  49. vectara_agentic/hhem.py +0 -82
  50. vectara_agentic-0.3.2.dist-info/RECORD +0 -39
  51. {vectara_agentic-0.3.2.dist-info → vectara_agentic-0.4.0.dist-info}/WHEEL +0 -0
  52. {vectara_agentic-0.3.2.dist-info → vectara_agentic-0.4.0.dist-info}/licenses/LICENSE +0 -0
  53. {vectara_agentic-0.3.2.dist-info → vectara_agentic-0.4.0.dist-info}/top_level.txt +0 -0
@@ -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 attribute.
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, including the tool_type attribute.
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 f"{base_repr}, tool_type={self.tool_type}"
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.dict() if hasattr(metadata, "dict") else metadata.__dict__
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 call(
127
- self, *args: Any, ctx: Optional[Context] = None, **kwargs: Any
140
+ def _create_tool_error_output(
141
+ self, error: Exception, args: Any, kwargs: Any, include_traceback: bool = False
128
142
  ) -> ToolOutput:
129
- try:
130
- result = super().call(*args, ctx=ctx, **kwargs)
131
- return self._format_tool_output(result)
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(e)}."
141
- f"Valid arguments: {params_str}. please call the tool again with the correct arguments."
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(e)},
156
+ raw_output={"response": str(error)},
145
157
  )
146
- return err_output
147
- except Exception as e:
148
- err_output = ToolOutput(
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=f"Tool {self.metadata.name} Malfunction: {str(e)}",
166
+ content=content,
151
167
  raw_input={"args": args, "kwargs": kwargs},
152
- raw_output={"response": str(e)},
168
+ raw_output={"response": str(error)},
153
169
  )
154
- return err_output
155
170
 
156
- async def acall(
171
+ def call(
157
172
  self, *args: Any, ctx: Optional[Context] = None, **kwargs: Any
158
173
  ) -> ToolOutput:
159
174
  try:
160
- result = await super().acall(*args, ctx=ctx, **kwargs)
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
- import traceback
182
+ return self._create_tool_error_output(e, args, kwargs)
179
183
 
180
- err_output = ToolOutput(
181
- tool_name=self.metadata.name,
182
- content=f"Tool {self.metadata.name} Malfunction: {str(e)}, traceback: {traceback.format_exc()}",
183
- raw_input={"args": args, "kwargs": kwargs},
184
- raw_output={"response": str(e)},
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 to use human-readable representation if available."""
190
- if hasattr(result, "content") and _is_human_readable_output(result.content):
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
- # Use human-readable format for content, keep raw output
193
- human_readable_content = result.content.to_human_readable()
194
- raw_output = result.content.get_raw_output()
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 format tool output for {result.tool_name}: {e}"
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
- if compact_docstring:
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
- compact_docstring (bool): Whether to use a compact docstring format.
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=field_info.annotation,
430
+ annotation=annotation,
382
431
  )
383
432
  base_params.append(param)
384
- fields[field_name] = (field_info.annotation, field_info)
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=field_info.annotation,
452
+ annotation=annotation,
399
453
  )
400
454
  base_params.append(param)
401
- fields[field_name] = (field_info.annotation, field_info)
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, compact_docstring
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 += ''.join(
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 = "{doc.url}",
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
- citation_pattern (str, optional): The pattern for the citations in the response.
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=citation_pattern if include_citations else None,
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
- f"{k}='{v}'"
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, function: Callable, tool_type: ToolType = ToolType.QUERY
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(tool_type=tool_type, fn=function)
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) for tool in [summarize_legal_text, critique_as_judge]
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
@@ -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(description="the specific instructions for how to rephrase the text."),
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(default=None, description="the role of the person providing critique."),
107
- point_of_view: str = Field(default=None, description="the point of view with which to provide critique."),
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 = f"Critique the provided text from the point of view of {point_of_view}."
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
  #