vectara-agentic 0.2.10__py3-none-any.whl → 0.2.11__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.

@@ -154,7 +154,7 @@ class AgentCallbackHandler(BaseCallbackHandler):
154
154
  elif event_type == CBEventType.AGENT_STEP:
155
155
  self._handle_agent_step(payload, event_id)
156
156
  elif event_type == CBEventType.EXCEPTION:
157
- print(f"Exception: {payload.get(EventPayload.EXCEPTION)}")
157
+ print(f"Exception in handle_event: {payload.get(EventPayload.EXCEPTION)}")
158
158
  else:
159
159
  print(f"Unknown event type: {event_type}, payload={payload}")
160
160
 
@@ -168,7 +168,7 @@ class AgentCallbackHandler(BaseCallbackHandler):
168
168
  elif event_type == CBEventType.AGENT_STEP:
169
169
  await self._ahandle_agent_step(payload, event_id)
170
170
  elif event_type == CBEventType.EXCEPTION:
171
- print(f"Exception: {payload.get(EventPayload.EXCEPTION)}")
171
+ print(f"Exception in ahandle_event: {payload.get(EventPayload.EXCEPTION)}")
172
172
  else:
173
173
  print(f"Unknown event type: {event_type}, payload={payload}")
174
174
 
@@ -37,10 +37,11 @@ GENERAL_INSTRUCTIONS = """
37
37
  - If including latex equations in the markdown response, make sure the equations are on a separate line and enclosed in double dollar signs.
38
38
  - Always respond in the language of the question, and in text (no images, videos or code).
39
39
  - If you are provided with database tools use them for analytical queries (such as counting, calculating max, min, average, sum, or other statistics).
40
- For each database, the database tools include: x_list_tables, x_load_data, x_describe_tables, and x_load_sample_data, where 'x' in the database name.
41
- The x_list_tables tool provides a list of available tables in the x database. Always use x_list_tables before using other database tools, to understand valid table names.
42
- Before using the x_load_data with a SQL query, always follow these steps:
43
- - Use the x_describe_tables tool to understand the schema of each table.
40
+ For each database, the database tools include: x_list_tables, x_load_data, x_describe_tables, x_load_unique_values, and x_load_sample_data, where 'x' in the database name.
41
+ for example, if the database name is "ev", the tools are: ev_list_tables, ev_load_data, ev_describe_tables, ev_load_unique_values, and ev_load_sample_data.
42
+ Before using the x_load_data with a SQL query, always follow these discovery steps:
43
+ - call the x_list_tables tool to list of available tables in the x database.
44
+ - Call the x_describe_tables tool to understand the schema of each table you want to query data from.
44
45
  - Use the x_load_unique_values tool to understand the unique values in each column.
45
46
  Sometimes the user may ask for a specific column value, but the actual value in the table may be different, and you will need to use the correct value.
46
47
  - Use the x_load_sample_data tool to understand the column names, and typical values in each column.
@@ -1,4 +1,4 @@
1
1
  """
2
2
  Define the version of the package.
3
3
  """
4
- __version__ = "0.2.10"
4
+ __version__ = "0.2.11"
@@ -15,7 +15,6 @@ from sqlalchemy.engine import Engine
15
15
  from sqlalchemy.exc import NoSuchTableError
16
16
  from sqlalchemy.schema import CreateTable
17
17
 
18
- from llama_index.core.readers.base import BaseReader
19
18
  from llama_index.core.utilities.sql_wrapper import SQLDatabase
20
19
  from llama_index.core.schema import Document
21
20
  from llama_index.core.tools.function_tool import FunctionTool
@@ -25,7 +24,7 @@ from llama_index.core.tools.utils import create_schema_from_function
25
24
 
26
25
  AsyncCallable = Callable[..., Awaitable[Any]]
27
26
 
28
- class DatabaseTools(BaseReader):
27
+ class DatabaseTools:
29
28
  """Database tools for vectara-agentic
30
29
  This class provides a set of tools to interact with a database.
31
30
  It allows you to load data, list tables, describe tables, and load unique values.
@@ -49,9 +48,11 @@ class DatabaseTools(BaseReader):
49
48
  user: Optional[str] = None,
50
49
  password: Optional[str] = None,
51
50
  dbname: Optional[str] = None,
51
+ tool_name_prefix: str = "db",
52
52
  **kwargs: Any,
53
53
  ) -> None:
54
54
  self.max_rows = max_rows
55
+ self.tool_name_prefix = tool_name_prefix
55
56
 
56
57
  if sql_database:
57
58
  self.sql_database = sql_database
@@ -86,35 +87,34 @@ class DatabaseTools(BaseReader):
86
87
  func = getattr(self, fn_name)
87
88
  except AttributeError:
88
89
  return None
89
- name = fn_name
90
+ name = self.tool_name_prefix + "_" + fn_name if self.tool_name_prefix else fn_name
90
91
  docstring = func.__doc__ or ""
91
92
  description = f"{name}{signature(func)}\n{docstring}"
92
93
  fn_schema = create_schema_from_function(fn_name, getattr(self, fn_name))
93
94
  return ToolMetadata(name=name, description=description, fn_schema=fn_schema)
94
95
 
95
- def _load_data(self, query: str) -> List[Document]:
96
+ def _load_data(self, sql_query: str) -> List[Document]:
96
97
  documents = []
97
98
  with self.sql_database.engine.connect() as connection:
98
- if query is None:
99
+ if sql_query is None:
99
100
  raise ValueError("A query parameter is necessary to filter the data")
100
- result = connection.execute(text(query))
101
+ result = connection.execute(text(sql_query))
101
102
  for item in result.fetchall():
102
103
  doc_str = ", ".join([str(entry) for entry in item])
103
104
  documents.append(Document(text=doc_str))
104
105
  return documents
105
106
 
106
- def load_data(self, *args: Any, **load_kwargs: Any) -> List[str]:
107
+ def load_data(self, sql_query: str) -> List[str]:
107
108
  """Query and load data from the Database, returning a list of Documents.
108
109
  Args:
109
- query (str): an SQL query to filter tables and rows.
110
+ sql_query (str): an SQL query to filter tables and rows.
110
111
  Returns:
111
- List[Document]: a list of Document objects from the database.
112
+ List[str]: a list of Document objects from the database.
112
113
  """
113
- query = args[0] if args else load_kwargs.get("args",{}).get("query")
114
- if query is None:
114
+ if sql_query is None:
115
115
  raise ValueError("A query parameter is necessary to filter the data")
116
116
 
117
- count_query = f"SELECT COUNT(*) FROM ({query})"
117
+ count_query = f"SELECT COUNT(*) FROM ({sql_query})"
118
118
  try:
119
119
  count_rows = self._load_data(count_query)
120
120
  except Exception as e:
@@ -126,9 +126,9 @@ class DatabaseTools(BaseReader):
126
126
  "Please refactor your query to make it return less rows. "
127
127
  ]
128
128
  try:
129
- res = self._load_data(query)
129
+ res = self._load_data(sql_query)
130
130
  except Exception as e:
131
- return [f"Error ({str(e)}) occurred while executing the query {query}"]
131
+ return [f"Error ({str(e)}) occurred while executing the query {sql_query}"]
132
132
  return [d.text for d in res]
133
133
 
134
134
  def load_sample_data(self, table_name: str, num_rows: int = 25) -> Any:
@@ -141,6 +141,11 @@ class DatabaseTools(BaseReader):
141
141
  Returns:
142
142
  Any: The result of the database query.
143
143
  """
144
+ if table_name not in self.list_tables():
145
+ return (
146
+ f"Table {table_name} does not exist in the database."
147
+ f"Valid table names are: {self.list_tables()}"
148
+ )
144
149
  try:
145
150
  res = self._load_data(f"SELECT * FROM {table_name} LIMIT {num_rows}")
146
151
  except Exception as e:
@@ -162,6 +167,15 @@ class DatabaseTools(BaseReader):
162
167
  str: A string representation of the table schemas.
163
168
  """
164
169
  table_names = tables or [table.name for table in self._metadata.sorted_tables]
170
+ if len(table_names) == 0:
171
+ return "You must specify at least one table name to describe."
172
+ for table_name in table_names:
173
+ if table_name not in self.list_tables():
174
+ return (
175
+ f"Table {table_name} does not exist in the database."
176
+ f"Valid table names are: {self.list_tables()}"
177
+ )
178
+
165
179
  table_schemas = []
166
180
  for table_name in table_names:
167
181
  table = next(
@@ -186,6 +200,12 @@ class DatabaseTools(BaseReader):
186
200
  Returns:
187
201
  Any: the result of the database query
188
202
  """
203
+ if table_name not in self.list_tables():
204
+ return (
205
+ f"Table {table_name} does not exist in the database."
206
+ f"Valid table names are: {self.list_tables()}"
207
+ )
208
+
189
209
  res = {}
190
210
  try:
191
211
  for column in columns:
@@ -3,6 +3,7 @@ This module contains the SubQuestionQueryEngine workflow, which is a workflow
3
3
  that takes a user question and a list of tools, and outputs a list of sub-questions.
4
4
  """
5
5
 
6
+ import re
6
7
  import json
7
8
  from pydantic import BaseModel
8
9
 
@@ -109,17 +110,27 @@ class SubQuestionQueryWorkflow(Workflow):
109
110
  if await ctx.get("verbose"):
110
111
  print(f"Sub-questions are {response}")
111
112
 
112
- if not str(response):
113
+ response_str = str(response)
114
+ if not response_str:
113
115
  raise ValueError(
114
116
  f"No response from LLM when generating sub-questions for query {original_query}"
115
117
  )
116
-
117
118
  try:
118
- sub_questions = json.loads(str(response))["sub_questions"]
119
- if not sub_questions:
120
- raise ValueError("LLM returned empty sub-questions list")
121
- except (json.JSONDecodeError, KeyError) as e:
122
- raise ValueError(f"Invalid LLM response format: {response}") from e
119
+ data = json.loads(response_str)
120
+ except json.JSONDecodeError as e1:
121
+ match = re.search(r"\{.*\}", response_str, re.DOTALL)
122
+ if not match:
123
+ raise ValueError(f"Invalid LLM response format: {response_str}") from e1
124
+ try:
125
+ data = json.loads(match.group(0))
126
+ except json.JSONDecodeError as e2:
127
+ raise ValueError(f"Invalid LLM response format: {response_str}") from e2
128
+
129
+ sub_questions = data.get("sub_questions")
130
+ if sub_questions is None:
131
+ raise ValueError(f"Invalid LLM response format: {response_str}")
132
+ if not sub_questions:
133
+ raise ValueError("LLM returned empty sub-questions list")
123
134
 
124
135
  await ctx.set("sub_question_count", len(sub_questions))
125
136
  for question in sub_questions:
@@ -273,8 +284,23 @@ class SequentialSubQuestionsWorkflow(Workflow):
273
284
  if not str(response):
274
285
  raise ValueError(f"No response from LLM for query {original_query}")
275
286
 
276
- response_obj = json.loads(str(response))
277
- sub_questions = response_obj["sub_questions"]
287
+ response_str = str(response)
288
+ try:
289
+ response_obj = json.loads(response_str)
290
+ except json.JSONDecodeError as e1:
291
+ match = re.search(r"\{.*\}", response_str, re.DOTALL)
292
+ if not match:
293
+ raise ValueError(
294
+ f"Failed to extract JSON object with subquestions from LLM response: {response_str}"
295
+ ) from e1
296
+ try:
297
+ response_obj = json.loads(match.group(0))
298
+ except json.JSONDecodeError as e2:
299
+ raise ValueError(
300
+ f"Failed to extract JSON object with subquestions from LLM response: {response_str}"
301
+ ) from e2
302
+
303
+ sub_questions = response_obj.get("sub_questions")
278
304
 
279
305
  await ctx.set("sub_questions", sub_questions)
280
306
  if await ctx.get("verbose"):
@@ -305,7 +331,6 @@ class SequentialSubQuestionsWorkflow(Workflow):
305
331
  if await ctx.get("verbose"):
306
332
  print(f"Answer is {response}")
307
333
 
308
- sub_questions = await ctx.get("sub_questions")
309
334
  if ev.num + 1 < len(sub_questions):
310
335
  return self.QueryEvent(
311
336
  question=sub_questions[ev.num + 1],
vectara_agentic/tools.py CHANGED
@@ -55,13 +55,15 @@ LI_packages = {
55
55
  "send_message": ToolType.ACTION,
56
56
  "fetch_channel": ToolType.QUERY,
57
57
  }
58
- }
58
+ },
59
59
  }
60
60
 
61
+
61
62
  class VectaraToolMetadata(ToolMetadata):
62
63
  """
63
64
  A subclass of ToolMetadata adding the tool_type attribute.
64
65
  """
66
+
65
67
  tool_type: ToolType
66
68
 
67
69
  def __init__(self, tool_type: ToolType, **kwargs):
@@ -88,7 +90,9 @@ class VectaraTool(FunctionTool):
88
90
  fn: Optional[Callable[..., Any]] = None,
89
91
  async_fn: Optional[AsyncCallable] = None,
90
92
  ) -> None:
91
- metadata_dict = metadata.dict() if hasattr(metadata, 'dict') else metadata.__dict__
93
+ metadata_dict = (
94
+ metadata.dict() if hasattr(metadata, "dict") else metadata.__dict__
95
+ )
92
96
  vm = VectaraToolMetadata(tool_type=tool_type, **metadata_dict)
93
97
  super().__init__(fn, vm, async_fn)
94
98
 
@@ -107,19 +111,26 @@ class VectaraTool(FunctionTool):
107
111
  tool_type: ToolType = ToolType.QUERY,
108
112
  ) -> "VectaraTool":
109
113
  tool = FunctionTool.from_defaults(
110
- fn, name, description, return_direct, fn_schema, async_fn, tool_metadata,
111
- callback, async_callback
114
+ fn,
115
+ name,
116
+ description,
117
+ return_direct,
118
+ fn_schema,
119
+ async_fn,
120
+ tool_metadata,
121
+ callback,
122
+ async_callback,
112
123
  )
113
124
  vectara_tool = cls(
114
- tool_type=tool_type, fn=tool.fn, metadata=tool.metadata, async_fn=tool.async_fn,
125
+ tool_type=tool_type,
126
+ fn=tool.fn,
127
+ metadata=tool.metadata,
128
+ async_fn=tool.async_fn,
115
129
  )
116
130
  return vectara_tool
117
131
 
118
132
  def __str__(self) -> str:
119
- return (
120
- f"Tool(name={self.metadata.name}, "
121
- f"Tool metadata={self.metadata})"
122
- )
133
+ return f"Tool(name={self.metadata.name}, " f"Tool metadata={self.metadata})"
123
134
 
124
135
  def __repr__(self) -> str:
125
136
  return str(self)
@@ -137,19 +148,20 @@ class VectaraTool(FunctionTool):
137
148
  # If schema is a dict-like object, compare the dict representation
138
149
  try:
139
150
  # Try to get schema as dict if possible
140
- if hasattr(self.metadata.fn_schema, 'schema'):
151
+ if hasattr(self.metadata.fn_schema, "schema"):
141
152
  self_schema = self.metadata.fn_schema.schema
142
153
  other_schema = other.metadata.fn_schema.schema
143
154
 
144
155
  # Compare only properties and required fields
145
- self_props = self_schema.get('properties', {})
146
- other_props = other_schema.get('properties', {})
156
+ self_props = self_schema.get("properties", {})
157
+ other_props = other_schema.get("properties", {})
147
158
 
148
- self_required = self_schema.get('required', [])
149
- other_required = other_schema.get('required', [])
159
+ self_required = self_schema.get("required", [])
160
+ other_required = other_schema.get("required", [])
150
161
 
151
- return (self_props.keys() == other_props.keys() and
152
- set(self_required) == set(other_required))
162
+ return self_props.keys() == other_props.keys() and set(
163
+ self_required
164
+ ) == set(other_required)
153
165
  except Exception:
154
166
  # If any exception occurs during schema comparison, fall back to name comparison
155
167
  pass
@@ -184,6 +196,7 @@ class VectaraTool(FunctionTool):
184
196
  )
185
197
  return err_output
186
198
 
199
+
187
200
  def _create_tool_from_dynamic_function(
188
201
  function: Callable[..., ToolOutput],
189
202
  tool_name: str,
@@ -197,11 +210,17 @@ def _create_tool_from_dynamic_function(
197
210
  """
198
211
  fields = {}
199
212
  for param in base_params:
200
- default_value = param.default if param.default != inspect.Parameter.empty else ...
213
+ default_value = (
214
+ param.default if param.default != inspect.Parameter.empty else ...
215
+ )
201
216
  fields[param.name] = (param.annotation, default_value)
202
217
  for field_name, field_info in tool_args_schema.model_fields.items():
203
218
  if field_name not in fields:
204
- default_value = field_info.default if field_info.default is not PydanticUndefined else ...
219
+ default_value = (
220
+ field_info.default
221
+ if field_info.default is not PydanticUndefined
222
+ else ...
223
+ )
205
224
  fields[field_name] = (field_info.annotation, default_value)
206
225
  fn_schema = create_model(f"{tool_name}", **fields)
207
226
 
@@ -209,8 +228,16 @@ def _create_tool_from_dynamic_function(
209
228
  inspect.Parameter(
210
229
  name=field_name,
211
230
  kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
212
- default=field_info.default if field_info.default is not PydanticUndefined else inspect.Parameter.empty,
213
- annotation=field_info.annotation if hasattr(field_info, 'annotation') else field_info,
231
+ default=(
232
+ field_info.default
233
+ if field_info.default is not PydanticUndefined
234
+ else inspect.Parameter.empty
235
+ ),
236
+ annotation=(
237
+ field_info.annotation
238
+ if hasattr(field_info, "annotation")
239
+ else field_info
240
+ ),
214
241
  )
215
242
  for field_name, field_info in tool_args_schema.model_fields.items()
216
243
  if field_name not in [p.name for p in base_params]
@@ -218,17 +245,21 @@ def _create_tool_from_dynamic_function(
218
245
  all_params = base_params + schema_params
219
246
 
220
247
  required_params = [p for p in all_params if p.default is inspect.Parameter.empty]
221
- optional_params = [p for p in all_params if p.default is not inspect.Parameter.empty]
248
+ optional_params = [
249
+ p for p in all_params if p.default is not inspect.Parameter.empty
250
+ ]
222
251
  sig = inspect.Signature(required_params + optional_params)
223
252
  function.__signature__ = sig
224
253
  function.__annotations__["return"] = dict[str, Any]
225
- function.__name__ = "_" + re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
254
+ function.__name__ = re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
226
255
 
227
256
  # Create the tool function signature string
228
257
  param_strs = []
229
258
  for param in all_params:
230
259
  annotation = param.annotation
231
- type_name = annotation.__name__ if hasattr(annotation, '__name__') else str(annotation)
260
+ type_name = (
261
+ annotation.__name__ if hasattr(annotation, "__name__") else str(annotation)
262
+ )
232
263
  param_strs.append(f"{param.name}: {type_name}")
233
264
  args_str = ", ".join(param_strs)
234
265
  function_str = f"{tool_name}({args_str}) -> str"
@@ -243,7 +274,10 @@ def _create_tool_from_dynamic_function(
243
274
  )
244
275
  return tool
245
276
 
246
- def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict], fixed_filter: str) -> str:
277
+
278
+ def _build_filter_string(
279
+ kwargs: Dict[str, Any], tool_args_type: Dict[str, dict], fixed_filter: str
280
+ ) -> str:
247
281
  """
248
282
  Build filter string for Vectara from kwargs
249
283
  """
@@ -257,9 +291,9 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
257
291
 
258
292
  # Determine the prefix for the key. Valid values are "doc" or "part"
259
293
  # default to 'doc' if not specified
260
- tool_args_dict = tool_args_type.get(key, {'type': 'doc', 'is_list': False})
294
+ tool_args_dict = tool_args_type.get(key, {"type": "doc", "is_list": False})
261
295
  prefix = tool_args_dict.get(key, "doc")
262
- is_list = tool_args_dict.get('is_list', False)
296
+ is_list = tool_args_dict.get("is_list", False)
263
297
 
264
298
  if prefix not in ["doc", "part"]:
265
299
  raise ValueError(
@@ -312,12 +346,10 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
312
346
  range_conditions.append(f"{prefix}.{key} {operator} {end_val}")
313
347
 
314
348
  # Join the range conditions with AND
315
- filter_parts.append('( ' + " AND ".join(range_conditions) + ' )')
349
+ filter_parts.append("( " + " AND ".join(range_conditions) + " )")
316
350
  continue
317
351
 
318
- raise ValueError(
319
- f"Range operator requires two values for {key}: {value}"
320
- )
352
+ raise ValueError(f"Range operator requires two values for {key}: {value}")
321
353
 
322
354
  # Check if value contains a known comparison operator at the start
323
355
  matched_operator = None
@@ -329,7 +361,7 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
329
361
  # Break down operator from value
330
362
  # e.g. val_str = ">2022" --> operator = ">", rhs = "2022"
331
363
  if matched_operator:
332
- rhs = val_str[len(matched_operator):].strip()
364
+ rhs = val_str[len(matched_operator) :].strip()
333
365
 
334
366
  if matched_operator in numeric_only_ops:
335
367
  # Must be numeric
@@ -343,7 +375,9 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
343
375
  if rhs.isdigit() or is_float(rhs):
344
376
  filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs}")
345
377
  elif rhs.lower() in ["true", "false"]:
346
- filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs.lower()}")
378
+ filter_parts.append(
379
+ f"{prefix}.{key}{matched_operator}{rhs.lower()}"
380
+ )
347
381
  else:
348
382
  # For string operands, wrap them in quotes
349
383
  filter_parts.append(f"{prefix}.{key}{matched_operator}'{rhs}'")
@@ -373,6 +407,7 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
373
407
  else:
374
408
  return fixed_filter or filter_str
375
409
 
410
+
376
411
  class VectaraToolFactory:
377
412
  """
378
413
  A factory class for creating Vectara RAG tools.
@@ -480,7 +515,9 @@ class VectaraToolFactory:
480
515
  top_k = kwargs.pop("top_k", 10)
481
516
  summarize = kwargs.pop("summarize", True)
482
517
  try:
483
- filter_string = _build_filter_string(kwargs, tool_args_type, fixed_filter)
518
+ filter_string = _build_filter_string(
519
+ kwargs, tool_args_type, fixed_filter
520
+ )
484
521
  except ValueError as e:
485
522
  return ToolOutput(
486
523
  tool_name=search_function.__name__,
@@ -493,7 +530,11 @@ class VectaraToolFactory:
493
530
  summary_enabled=False,
494
531
  similarity_top_k=top_k,
495
532
  reranker=reranker,
496
- rerank_k=rerank_k if rerank_k * self.num_corpora <= 100 else int(100 / self.num_corpora),
533
+ rerank_k=(
534
+ rerank_k
535
+ if rerank_k * self.num_corpora <= 100
536
+ else int(100 / self.num_corpora)
537
+ ),
497
538
  rerank_limit=rerank_limit,
498
539
  rerank_cutoff=rerank_cutoff,
499
540
  mmr_diversity_bias=mmr_diversity_bias,
@@ -531,9 +572,7 @@ class VectaraToolFactory:
531
572
  if summarize:
532
573
  summaries_dict = asyncio.run(
533
574
  summarize_documents(
534
- self.vectara_corpus_key,
535
- self.vectara_api_key,
536
- list(unique_ids)
575
+ self.vectara_corpus_key, self.vectara_api_key, list(unique_ids)
537
576
  )
538
577
  )
539
578
  for doc_id, metadata in docs:
@@ -541,7 +580,9 @@ class VectaraToolFactory:
541
580
  tool_output += f"document_id: '{doc_id}'\nmetadata: '{metadata}'\nsummary: '{summary}'\n\n"
542
581
  else:
543
582
  for doc_id, metadata in docs:
544
- tool_output += f"document_id: '{doc_id}'\nmetadata: '{metadata}'\n\n"
583
+ tool_output += (
584
+ f"document_id: '{doc_id}'\nmetadata: '{metadata}'\n\n"
585
+ )
545
586
 
546
587
  out = ToolOutput(
547
588
  tool_name=search_function.__name__,
@@ -552,16 +593,29 @@ class VectaraToolFactory:
552
593
  return out
553
594
 
554
595
  base_params = [
555
- inspect.Parameter("query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str),
556
- inspect.Parameter("top_k", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=int),
557
- inspect.Parameter("summarize", inspect.Parameter.POSITIONAL_OR_KEYWORD, default=True, annotation=bool),
596
+ inspect.Parameter(
597
+ "query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str
598
+ ),
599
+ inspect.Parameter(
600
+ "top_k", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=int
601
+ ),
602
+ inspect.Parameter(
603
+ "summarize",
604
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
605
+ default=True,
606
+ annotation=bool,
607
+ ),
558
608
  ]
559
- search_tool_extra_desc = tool_description + "\n" + """
609
+ search_tool_extra_desc = (
610
+ tool_description
611
+ + "\n"
612
+ + """
560
613
  This tool is meant to perform a search for relevant documents, it is not meant for asking questions.
561
614
  The response includes metadata about each relevant document.
562
615
  If summarize=True, it also includes a summary of each document, but takes a lot longer to respond,
563
616
  so avoid using it unless necessary.
564
617
  """
618
+ )
565
619
 
566
620
  tool = _create_tool_from_dynamic_function(
567
621
  search_function,
@@ -684,7 +738,9 @@ class VectaraToolFactory:
684
738
 
685
739
  query = kwargs.pop("query")
686
740
  try:
687
- filter_string = _build_filter_string(kwargs, tool_args_type, fixed_filter)
741
+ filter_string = _build_filter_string(
742
+ kwargs, tool_args_type, fixed_filter
743
+ )
688
744
  except ValueError as e:
689
745
  return ToolOutput(
690
746
  tool_name=rag_function.__name__,
@@ -701,7 +757,11 @@ class VectaraToolFactory:
701
757
  summary_prompt_name=vectara_summarizer,
702
758
  prompt_text=vectara_prompt_text,
703
759
  reranker=reranker,
704
- rerank_k=rerank_k if rerank_k * self.num_corpora <= 100 else int(100 / self.num_corpora),
760
+ rerank_k=(
761
+ rerank_k
762
+ if rerank_k * self.num_corpora <= 100
763
+ else int(100 / self.num_corpora)
764
+ ),
705
765
  rerank_limit=rerank_limit,
706
766
  rerank_cutoff=rerank_cutoff,
707
767
  mmr_diversity_bias=mmr_diversity_bias,
@@ -793,7 +853,9 @@ class VectaraToolFactory:
793
853
  return out
794
854
 
795
855
  base_params = [
796
- inspect.Parameter("query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str),
856
+ inspect.Parameter(
857
+ "query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str
858
+ ),
797
859
  ]
798
860
  tool = _create_tool_from_dynamic_function(
799
861
  rag_function,
@@ -804,6 +866,7 @@ class VectaraToolFactory:
804
866
  )
805
867
  return tool
806
868
 
869
+
807
870
  class ToolsFactory:
808
871
  """
809
872
  A factory class for creating agent tools.
@@ -812,7 +875,9 @@ class ToolsFactory:
812
875
  def __init__(self, agent_config: AgentConfig = None) -> None:
813
876
  self.agent_config = agent_config
814
877
 
815
- def create_tool(self, function: Callable, tool_type: ToolType = ToolType.QUERY) -> VectaraTool:
878
+ def create_tool(
879
+ self, function: Callable, tool_type: ToolType = ToolType.QUERY
880
+ ) -> VectaraTool:
816
881
  """
817
882
  Create a tool from a function.
818
883
 
@@ -846,7 +911,9 @@ class ToolsFactory:
846
911
  """
847
912
  # Dynamically install and import the module
848
913
  if tool_package_name not in LI_packages:
849
- raise ValueError(f"Tool package {tool_package_name} from LlamaIndex not supported by Vectara-agentic.")
914
+ raise ValueError(
915
+ f"Tool package {tool_package_name} from LlamaIndex not supported by Vectara-agentic."
916
+ )
850
917
 
851
918
  module_name = f"llama_index.tools.{tool_package_name}"
852
919
  module = importlib.import_module(module_name)
@@ -861,11 +928,18 @@ class ToolsFactory:
861
928
  tool.metadata.name = tool_name_prefix + "_" + tool.metadata.name
862
929
  if isinstance(func_type, dict):
863
930
  if tool_spec_name not in func_type.keys():
864
- raise ValueError(f"Tool spec {tool_spec_name} not found in package {tool_package_name}.")
931
+ raise ValueError(
932
+ f"Tool spec {tool_spec_name} not found in package {tool_package_name}."
933
+ )
865
934
  tool_type = func_type[tool_spec_name]
866
935
  else:
867
936
  tool_type = func_type
868
- vtool = VectaraTool(tool_type=tool_type, fn=tool.fn, metadata=tool.metadata, async_fn=tool.async_fn)
937
+ vtool = VectaraTool(
938
+ tool_type=tool_type,
939
+ fn=tool.fn,
940
+ metadata=tool.metadata,
941
+ async_fn=tool.async_fn,
942
+ )
869
943
  vtools.append(vtool)
870
944
  return vtools
871
945
 
@@ -874,7 +948,10 @@ class ToolsFactory:
874
948
  Create a list of standard tools.
875
949
  """
876
950
  tc = ToolsCatalog(self.agent_config)
877
- return [self.create_tool(tool) for tool in [tc.summarize_text, tc.rephrase_text, tc.critique_text]]
951
+ return [
952
+ self.create_tool(tool)
953
+ for tool in [tc.summarize_text, tc.rephrase_text, tc.critique_text]
954
+ ]
878
955
 
879
956
  def guardrail_tools(self) -> List[FunctionTool]:
880
957
  """
@@ -886,7 +963,9 @@ class ToolsFactory:
886
963
  """
887
964
  Create a list of financial tools.
888
965
  """
889
- return self.get_llama_index_tools(tool_package_name="yahoo_finance", tool_spec_name="YahooFinanceToolSpec")
966
+ return self.get_llama_index_tools(
967
+ tool_package_name="yahoo_finance", tool_spec_name="YahooFinanceToolSpec"
968
+ )
890
969
 
891
970
  def legal_tools(self) -> List[FunctionTool]:
892
971
  """
@@ -918,7 +997,9 @@ class ToolsFactory:
918
997
  """,
919
998
  )
920
999
 
921
- return [self.create_tool(tool) for tool in [summarize_legal_text, critique_as_judge]]
1000
+ return [
1001
+ self.create_tool(tool) for tool in [summarize_legal_text, critique_as_judge]
1002
+ ]
922
1003
 
923
1004
  def database_tools(
924
1005
  self,
@@ -955,16 +1036,22 @@ class ToolsFactory:
955
1036
  List[VectaraTool]: A list of VectaraTool objects.
956
1037
  """
957
1038
  if sql_database:
958
- dbt = DatabaseTools(sql_database=sql_database)
1039
+ dbt = DatabaseTools(
1040
+ tool_name_prefix=tool_name_prefix,
1041
+ sql_database=sql_database,
1042
+ max_rows=max_rows,
1043
+ )
959
1044
  else:
960
1045
  if scheme in ["postgresql", "mysql", "sqlite", "mssql", "oracle"]:
961
1046
  dbt = DatabaseTools(
1047
+ tool_name_prefix=tool_name_prefix,
962
1048
  scheme=scheme,
963
1049
  host=host,
964
1050
  port=port,
965
1051
  user=user,
966
1052
  password=password,
967
1053
  dbname=dbname,
1054
+ max_rows=max_rows,
968
1055
  )
969
1056
  else:
970
1057
  raise ValueError(
@@ -978,14 +1065,14 @@ class ToolsFactory:
978
1065
  for tool in tools:
979
1066
  if content_description:
980
1067
  tool.metadata.description = (
981
- tool.metadata.description + f"The database tables include data about {content_description}."
1068
+ tool.metadata.description
1069
+ + f"The database tables include data about {content_description}."
982
1070
  )
983
- if len(tool_name_prefix) > 0:
984
- tool.metadata.name = tool_name_prefix + "_" + tool.metadata.name
985
1071
  vtool = VectaraTool(
986
1072
  tool_type=ToolType.QUERY,
987
- fn=tool.fn, async_fn=tool.async_fn,
988
- metadata=tool.metadata
1073
+ fn=tool.fn,
1074
+ async_fn=tool.async_fn,
1075
+ metadata=tool.metadata,
989
1076
  )
990
1077
  vtools.append(vtool)
991
1078
  return vtools
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vectara_agentic
3
- Version: 0.2.10
3
+ Version: 0.2.11
4
4
  Summary: A Python package for creating AI Assistants and AI Agents with Vectara
5
5
  Home-page: https://github.com/vectara/py-vectara-agentic
6
6
  Author: Ofer Mendelevitch
@@ -9,21 +9,21 @@ tests/test_serialization.py,sha256=Ed23GN2zhSJNdPFrVK4aqLkOhJKviczR_o0t-r9TuRI,4
9
9
  tests/test_tools.py,sha256=as0rEAKAs6ekvqFDCcq1smRWKhQm5EaH2PUWT8hg1qQ,5726
10
10
  tests/test_workflow.py,sha256=lVyrVHdRO5leYNbYtHTmKqMX0c8_xehCpUA7cXQKVsc,2175
11
11
  vectara_agentic/__init__.py,sha256=2GLDS3U6KckK-dBRl9v_x1kSV507gEhjOfuMmmu0Qxg,850
12
- vectara_agentic/_callback.py,sha256=lU35-Pxp-fsMpOi4woY6oLECAhO1nSmLIy3b8fbgT54,13029
12
+ vectara_agentic/_callback.py,sha256=ron49t1t-ox-736WaXzrZ99vhN4NI9bMiHFyj0iIPqg,13062
13
13
  vectara_agentic/_observability.py,sha256=BA2zhwa5930aaDUJxHefPlmIPt8kZOuLHVBc9PtYNuU,3839
14
- vectara_agentic/_prompts.py,sha256=LYyiOAiC8imz3U7MSJiuCYAP39afsp7ycXY7-9biyJI,9314
15
- vectara_agentic/_version.py,sha256=-h5PMZplLmgj04BNOpvmJ0dvWk8SQqCU6tP_hnySaFE,66
14
+ vectara_agentic/_prompts.py,sha256=CKbsFrosoM6bPH02t2R5_K3jzVzaxJAl85qO3mEAQ3U,9439
15
+ vectara_agentic/_version.py,sha256=uneBdCHiroBsYz5R-8jYqrJU6UuA9Nu4vCqLtO0VSsE,66
16
16
  vectara_agentic/agent.py,sha256=KX0VYQuGFkK_CELjUFdxXWYHng32GFjsLdRdH-gR7aM,43970
17
17
  vectara_agentic/agent_config.py,sha256=E-rtYMcpoGxnEAyy8231bizo2n0uGQ2qWxuSgTEfwdQ,4327
18
18
  vectara_agentic/agent_endpoint.py,sha256=QIMejCLlpW2qzXxeDAxv3anF46XMDdVMdKGWhJh3azY,1996
19
- vectara_agentic/db_tools.py,sha256=Go03bzma9m-qDH0CPP8hWhf1nu_4S6s7ke0jGqz58Pk,10296
20
- vectara_agentic/sub_query_workflow.py,sha256=rwiS4e-k75LQvT_WdwoRI8sAJRsL9kiDZlrPwcjYUAE,11120
21
- vectara_agentic/tools.py,sha256=EC4NAhJy_SB62c2e-fmalWLXtTt6Pgjfi0qE22KxRco,42650
19
+ vectara_agentic/db_tools.py,sha256=zhP1KIRNiE6BKD69VGmUdcjeKSZ6g0kTIsJdTDNCuv4,11141
20
+ vectara_agentic/sub_query_workflow.py,sha256=xjySd2qjLAKwK6XuS0R0PTyk2uXraHCgCbDP1xDoFVI,12175
21
+ vectara_agentic/tools.py,sha256=n06CwlEqOHlawEJj6BX8xHM5-kMrBQO48Jo68GKRKes,43874
22
22
  vectara_agentic/tools_catalog.py,sha256=oiw3wAfbpFhh0_6rMvZsyPqWV6QIzHqhZCNzqRxuyV8,4818
23
23
  vectara_agentic/types.py,sha256=HcS7vR8P2v2xQTlOc6ZFV2vvlr3OpzSNWhtcLMxqUZc,1792
24
24
  vectara_agentic/utils.py,sha256=4vA5MyNoG47_7eHuLFQByiG_FHWbrQ6ZJDsdqHUwiJA,7720
25
- vectara_agentic-0.2.10.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
- vectara_agentic-0.2.10.dist-info/METADATA,sha256=LjjT33W4ISkAKJIk0_tJSxYuF39Z5EY355qrz6dCbR4,25088
27
- vectara_agentic-0.2.10.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
28
- vectara_agentic-0.2.10.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
29
- vectara_agentic-0.2.10.dist-info/RECORD,,
25
+ vectara_agentic-0.2.11.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
26
+ vectara_agentic-0.2.11.dist-info/METADATA,sha256=c0ue2vnkkIwgNueoHQILYkui5eTDbXB7-SnBBnbWK0A,25088
27
+ vectara_agentic-0.2.11.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
28
+ vectara_agentic-0.2.11.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
29
+ vectara_agentic-0.2.11.dist-info/RECORD,,