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

@@ -7,3 +7,10 @@ from .tools import VectaraToolFactory, VectaraTool
7
7
 
8
8
  # Define the __all__ variable for wildcard imports
9
9
  __all__ = ['Agent', 'VectaraToolFactory', 'VectaraTool']
10
+
11
+ # Ensure package version is available
12
+ try:
13
+ import importlib.metadata
14
+ __version__ = importlib.metadata.version("vectara_agentic")
15
+ except Exception:
16
+ __version__ = "0.0.0" # fallback if not installed
@@ -5,6 +5,7 @@ This file contains the prompt templates for the different types of agents.
5
5
  # General (shared) instructions
6
6
  GENERAL_INSTRUCTIONS = """
7
7
  - Use tools as your main source of information, do not respond without using a tool. Do not respond based on pre-trained knowledge.
8
+ - Always call the 'get_current_date' tool to ensure you know the exact date when a user asks a question.
8
9
  - When using a tool with arguments, simplify the query as much as possible if you use the tool with arguments.
9
10
  For example, if the original query is "revenue for apple in 2021", you can use the tool with a query "revenue" with arguments year=2021 and company=apple.
10
11
  - If a tool responds with "I do not have enough information", try one of the following:
@@ -43,7 +44,7 @@ GENERAL_PROMPT_TEMPLATE = """
43
44
  You are a helpful chatbot in conversation with a user, with expertise in {chat_topic}.
44
45
 
45
46
  ## Date
46
- Today's date is {today}.
47
+ Your birth date is {today}.
47
48
 
48
49
  ## INSTRUCTIONS:
49
50
  IMPORTANT - FOLLOW THESE INSTRUCTIONS CAREFULLY:
@@ -63,7 +64,7 @@ You are designed to help with a variety of tasks, from answering questions to pr
63
64
  You have expertise in {chat_topic}.
64
65
 
65
66
  ## Date
66
- Today's date is {today}.
67
+ Your birth date is {today}.
67
68
 
68
69
  ## Tools
69
70
  You have access to a wide variety of tools.
@@ -1,4 +1,4 @@
1
1
  """
2
2
  Define the version of the package.
3
3
  """
4
- __version__ = "0.1.24"
4
+ __version__ = "0.1.25"
vectara_agentic/agent.py CHANGED
@@ -8,6 +8,7 @@ import time
8
8
  import json
9
9
  import logging
10
10
  import traceback
11
+ import asyncio
11
12
 
12
13
  import dill
13
14
  from dotenv import load_dotenv
@@ -25,12 +26,13 @@ from llama_index.core.callbacks.base_handler import BaseCallbackHandler
25
26
  from llama_index.agent.openai import OpenAIAgent
26
27
  from llama_index.core.memory import ChatMemoryBuffer
27
28
 
28
- from .types import AgentType, AgentStatusType, LLMRole, ToolType
29
+ from .types import AgentType, AgentStatusType, LLMRole, ToolType, AgentResponse, AgentStreamingResponse
29
30
  from .utils import get_llm, get_tokenizer_for_model
30
31
  from ._prompts import REACT_PROMPT_TEMPLATE, GENERAL_PROMPT_TEMPLATE, GENERAL_INSTRUCTIONS
31
32
  from ._callback import AgentCallbackHandler
32
33
  from ._observability import setup_observer, eval_fcs
33
- from .tools import VectaraToolFactory, VectaraTool
34
+ from .tools import VectaraToolFactory, VectaraTool, ToolsFactory
35
+ from .tools_catalog import get_current_date
34
36
  from .agent_config import AgentConfig
35
37
 
36
38
  logger = logging.getLogger("opentelemetry.exporter.otlp.proto.http.trace_exporter")
@@ -92,6 +94,7 @@ class Agent:
92
94
  verbose: bool = True,
93
95
  update_func: Optional[Callable[[AgentStatusType, str], None]] = None,
94
96
  agent_progress_callback: Optional[Callable[[AgentStatusType, str], None]] = None,
97
+ query_logging_callback: Optional[Callable[[str, str], None]] = None,
95
98
  agent_config: Optional[AgentConfig] = None,
96
99
  ) -> None:
97
100
  """
@@ -105,16 +108,18 @@ class Agent:
105
108
  verbose (bool, optional): Whether the agent should print its steps. Defaults to True.
106
109
  agent_progress_callback (Callable): A callback function the code calls on any agent updates.
107
110
  update_func (Callable): old name for agent_progress_callback. Will be deprecated in future.
111
+ query_logging_callback (Callable): A callback function the code calls upon completion of a query
108
112
  agent_config (AgentConfig, optional): The configuration of the agent.
109
113
  Defaults to AgentConfig(), which reads from environment variables.
110
114
  """
111
115
  self.agent_config = agent_config or AgentConfig()
112
116
  self.agent_type = self.agent_config.agent_type
113
- self.tools = tools
117
+ self.tools = tools + [ToolsFactory().create_tool(get_current_date)]
114
118
  self.llm = get_llm(LLMRole.MAIN, config=self.agent_config)
115
119
  self._custom_instructions = custom_instructions
116
120
  self._topic = topic
117
121
  self.agent_progress_callback = agent_progress_callback if agent_progress_callback else update_func
122
+ self.query_logging_callback = query_logging_callback
118
123
 
119
124
  main_tok = get_tokenizer_for_model(role=LLMRole.MAIN)
120
125
  self.main_token_counter = TokenCountingHandler(tokenizer=main_tok) if main_tok else None
@@ -134,7 +139,7 @@ class Agent:
134
139
  if self.agent_type == AgentType.REACT:
135
140
  prompt = _get_prompt(REACT_PROMPT_TEMPLATE, topic, custom_instructions)
136
141
  self.agent = ReActAgent.from_tools(
137
- tools=tools,
142
+ tools=self.tools,
138
143
  llm=self.llm,
139
144
  memory=self.memory,
140
145
  verbose=verbose,
@@ -145,7 +150,7 @@ class Agent:
145
150
  elif self.agent_type == AgentType.OPENAI:
146
151
  prompt = _get_prompt(GENERAL_PROMPT_TEMPLATE, topic, custom_instructions)
147
152
  self.agent = OpenAIAgent.from_tools(
148
- tools=tools,
153
+ tools=self.tools,
149
154
  llm=self.llm,
150
155
  memory=self.memory,
151
156
  verbose=verbose,
@@ -154,23 +159,24 @@ class Agent:
154
159
  system_prompt=prompt,
155
160
  )
156
161
  elif self.agent_type == AgentType.LLMCOMPILER:
157
- self.agent = LLMCompilerAgentWorker.from_tools(
158
- tools=tools,
162
+ agent_worker = LLMCompilerAgentWorker.from_tools(
163
+ tools=self.tools,
159
164
  llm=self.llm,
160
165
  verbose=verbose,
161
166
  callable_manager=callback_manager,
162
- ).as_agent()
163
- self.agent.agent_worker.system_prompt = _get_prompt(
164
- _get_llm_compiler_prompt(self.agent.agent_worker.system_prompt, topic, custom_instructions),
167
+ )
168
+ agent_worker.system_prompt = _get_prompt(
169
+ _get_llm_compiler_prompt(agent_worker.system_prompt, topic, custom_instructions),
165
170
  topic, custom_instructions
166
171
  )
167
- self.agent.agent_worker.system_prompt_replan = _get_prompt(
168
- _get_llm_compiler_prompt(self.agent.agent_worker.system_prompt_replan, topic, custom_instructions),
172
+ agent_worker.system_prompt_replan = _get_prompt(
173
+ _get_llm_compiler_prompt(agent_worker.system_prompt_replan, topic, custom_instructions),
169
174
  topic, custom_instructions
170
175
  )
176
+ self.agent = agent_worker.as_agent()
171
177
  elif self.agent_type == AgentType.LATS:
172
178
  agent_worker = LATSAgentWorker.from_tools(
173
- tools=tools,
179
+ tools=self.tools,
174
180
  llm=self.llm,
175
181
  num_expansions=3,
176
182
  max_rollouts=-1,
@@ -255,6 +261,7 @@ class Agent:
255
261
  verbose: bool = True,
256
262
  update_func: Optional[Callable[[AgentStatusType, str], None]] = None,
257
263
  agent_progress_callback: Optional[Callable[[AgentStatusType, str], None]] = None,
264
+ query_logging_callback: Optional[Callable[[str, str], None]] = None,
258
265
  agent_config: AgentConfig = AgentConfig(),
259
266
  ) -> "Agent":
260
267
  """
@@ -268,6 +275,7 @@ class Agent:
268
275
  verbose (bool, optional): Whether the agent should print its steps. Defaults to True.
269
276
  agent_progress_callback (Callable): A callback function the code calls on any agent updates.
270
277
  update_func (Callable): old name for agent_progress_callback. Will be deprecated in future.
278
+ query_logging_callback (Callable): A callback function the code calls upon completion of a query
271
279
  agent_config (AgentConfig, optional): The configuration of the agent.
272
280
 
273
281
  Returns:
@@ -276,6 +284,7 @@ class Agent:
276
284
  return cls(
277
285
  tools=tools, topic=topic, custom_instructions=custom_instructions,
278
286
  verbose=verbose, agent_progress_callback=agent_progress_callback,
287
+ query_logging_callback=query_logging_callback,
279
288
  update_func=update_func, agent_config=agent_config
280
289
  )
281
290
 
@@ -289,6 +298,7 @@ class Agent:
289
298
  vectara_corpus_id: str = str(os.environ.get("VECTARA_CORPUS_ID", "")),
290
299
  vectara_api_key: str = str(os.environ.get("VECTARA_API_KEY", "")),
291
300
  agent_progress_callback: Optional[Callable[[AgentStatusType, str], None]] = None,
301
+ query_logging_callback: Optional[Callable[[str, str], None]] = None,
292
302
  verbose: bool = False,
293
303
  vectara_filter_fields: list[dict] = [],
294
304
  vectara_lambda_val: float = 0.005,
@@ -308,6 +318,7 @@ class Agent:
308
318
  vectara_corpus_id (str): The Vectara corpus ID (or comma separated list of IDs).
309
319
  vectara_api_key (str): The Vectara API key.
310
320
  agent_progress_callback (Callable): A callback function the code calls on any agent updates.
321
+ query_logging_callback (Callable): A callback function the code calls upon completion of a query
311
322
  data_description (str): The description of the data.
312
323
  assistant_specialty (str): The specialty of the assistant.
313
324
  verbose (bool, optional): Whether to print verbose output.
@@ -367,6 +378,7 @@ class Agent:
367
378
  custom_instructions=assistant_instructions,
368
379
  verbose=verbose,
369
380
  agent_progress_callback=agent_progress_callback,
381
+ query_logging_callback=query_logging_callback,
370
382
  )
371
383
 
372
384
  def report(self) -> None:
@@ -400,12 +412,32 @@ class Agent:
400
412
  "tool token count": self.tool_token_counter.total_llm_token_count if self.tool_token_counter else -1,
401
413
  }
402
414
 
415
+ async def _aformat_for_lats(self, prompt, agent_response):
416
+ llm_prompt = f"""
417
+ Given the question '{prompt}', and agent response '{agent_response.response}',
418
+ Please provide a well formatted final response to the query.
419
+ final response:
420
+ """
421
+ agent_response.response = str(self.llm.acomplete(llm_prompt))
422
+
423
+ def chat(self, prompt: str) -> AgentResponse: # type: ignore
424
+ """
425
+ Interact with the agent using a chat prompt.
426
+
427
+ Args:
428
+ prompt (str): The chat prompt.
429
+
430
+ Returns:
431
+ AgentResponse: The response from the agent.
432
+ """
433
+ return asyncio.run(self.achat(prompt))
434
+
403
435
  @retry(
404
436
  retry_on_exception=_retry_if_exception,
405
437
  stop_max_attempt_number=3,
406
438
  wait_fixed=2000,
407
439
  )
408
- def chat(self, prompt: str) -> str:
440
+ async def achat(self, prompt: str) -> AgentResponse: # type: ignore
409
441
  """
410
442
  Interact with the agent using a chat prompt.
411
443
 
@@ -413,32 +445,79 @@ class Agent:
413
445
  prompt (str): The chat prompt.
414
446
 
415
447
  Returns:
416
- str: The response from the agent.
448
+ AgentResponse: The response from the agent.
417
449
  """
418
450
 
419
451
  try:
420
452
  st = time.time()
421
- agent_response = self.agent.chat(prompt)
453
+ agent_response = await self.agent.achat(prompt)
422
454
  if self.agent_type == AgentType.LATS:
423
- prompt = f"""
424
- Given the question '{prompt}', and agent response '{agent_response.response}',
425
- Please provide a well formatted final response to the query.
426
- final response:
427
- """
428
- final_response = str(self.llm.complete(prompt))
429
- else:
430
- final_response = agent_response.response
431
-
455
+ await self._aformat_for_lats(prompt, agent_response)
432
456
  if self.verbose:
433
457
  print(f"Time taken: {time.time() - st}")
434
458
  if self.observability_enabled:
435
459
  eval_fcs()
436
- return final_response
460
+ if self.query_logging_callback:
461
+ self.query_logging_callback(prompt, agent_response.response)
462
+ return agent_response
437
463
  except Exception as e:
438
- return f"Vectara Agentic: encountered an exception ({e}) at ({traceback.format_exc()}), and can't respond."
464
+ return AgentResponse(
465
+ response = (
466
+ f"Vectara Agentic: encountered an exception ({e}) at ({traceback.format_exc()})"
467
+ ", and can't respond."
468
+ )
469
+ )
439
470
 
440
- # Serialization methods
471
+ def stream_chat(self, prompt: str) -> AgentStreamingResponse: # type: ignore
472
+ """
473
+ Interact with the agent using a chat prompt with streaming.
474
+ Args:
475
+ prompt (str): The chat prompt.
476
+ Returns:
477
+ AgentStreamingResponse: The streaming response from the agent.
478
+ """
479
+ return asyncio.run(self.astream_chat(prompt))
480
+
481
+ @retry(
482
+ retry_on_exception=_retry_if_exception,
483
+ stop_max_attempt_number=3,
484
+ wait_fixed=2000,
485
+ )
486
+ async def astream_chat(self, prompt: str) -> AgentStreamingResponse: # type: ignore
487
+ """
488
+ Interact with the agent using a chat prompt asynchronously with streaming.
489
+ Args:
490
+ prompt (str): The chat prompt.
491
+ Returns:
492
+ AgentStreamingResponse: The streaming response from the agent.
493
+ """
494
+ try:
495
+ agent_response = await self.agent.astream_chat(prompt)
496
+ original_async_response_gen = agent_response.async_response_gen
497
+
498
+ # Wrap async_response_gen
499
+ async def _stream_response_wrapper():
500
+ async for token in original_async_response_gen():
501
+ yield token # Yield async token to keep streaming behavior
502
+
503
+ # After streaming completes, execute additional logic
504
+ if self.agent_type == AgentType.LATS:
505
+ await self._aformat_for_lats(prompt, agent_response)
506
+ if self.query_logging_callback:
507
+ self.query_logging_callback(prompt, agent_response.response)
508
+ if self.observability_enabled:
509
+ eval_fcs()
510
+
511
+ agent_response.async_response_gen = _stream_response_wrapper # Override method
512
+ return agent_response
513
+ except Exception as e:
514
+ raise ValueError(
515
+ f"Vectara Agentic: encountered an exception ({e}) at ({traceback.format_exc()}), and can't respond."
516
+ ) from e
441
517
 
518
+ #
519
+ # Serialization methods
520
+ #
442
521
  def dumps(self) -> str:
443
522
  """Serialize the Agent instance to a JSON string."""
444
523
  return json.dumps(self.to_dict())
@@ -31,7 +31,7 @@ class AgentConfig:
31
31
  )
32
32
 
33
33
  main_llm_model_name: str = field(
34
- default_factory=lambda: os.getenv("VECTARA_AGENTIC_MAIN_LLM_MODEL_NAME", "")
34
+ default_factory=lambda: os.getenv("VECTARA_AGENTIC_MAIN_MODEL_NAME", "")
35
35
  )
36
36
 
37
37
  # Tool LLM provider & model name
@@ -41,7 +41,7 @@ class AgentConfig:
41
41
  )
42
42
  )
43
43
  tool_llm_model_name: str = field(
44
- default_factory=lambda: os.getenv("VECTARA_AGENTIC_TOOL_LLM_MODEL_NAME", "")
44
+ default_factory=lambda: os.getenv("VECTARA_AGENTIC_TOOL_MODEL_NAME", "")
45
45
  )
46
46
 
47
47
  # Observer
vectara_agentic/tools.py CHANGED
@@ -126,6 +126,122 @@ class VectaraTool(FunctionTool):
126
126
  break
127
127
  return is_equal
128
128
 
129
+ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, str], fixed_filter: str) -> str:
130
+ """
131
+ Build filter string for Vectara from kwargs
132
+ """
133
+ filter_parts = []
134
+ comparison_operators = [">=", "<=", "!=", ">", "<", "="]
135
+ numeric_only_ops = {">", "<", ">=", "<="}
136
+
137
+ for key, value in kwargs.items():
138
+ if value is None or value == "":
139
+ continue
140
+
141
+ # Determine the prefix for the key. Valid values are "doc" or "part"
142
+ # default to 'doc' if not specified
143
+ prefix = tool_args_type.get(key, "doc")
144
+
145
+ if prefix not in ["doc", "part"]:
146
+ raise ValueError(
147
+ f'Unrecognized prefix {prefix}. Please make sure to use either "doc" or "part" for the prefix.'
148
+ )
149
+
150
+ if value is PydanticUndefined:
151
+ raise ValueError(
152
+ f"Value of argument {key} is undefined, and this is invalid. "
153
+ "Please form proper arguments and try again."
154
+ )
155
+
156
+ # value of the argument
157
+ val_str = str(value).strip()
158
+
159
+ # Special handling for range operator
160
+ if val_str.startswith(("[", "(")) and val_str.endswith(("]", ")")):
161
+ # Extract the boundary types
162
+ start_inclusive = val_str.startswith("[")
163
+ end_inclusive = val_str.endswith("]")
164
+
165
+ # Remove the boundaries and strip whitespace
166
+ val_str = val_str[1:-1].strip()
167
+
168
+ if "," in val_str:
169
+ val_str = val_str.split(",")
170
+ if len(val_str) != 2:
171
+ raise ValueError(
172
+ f"Range operator requires two values for {key}: {value}"
173
+ )
174
+
175
+ # Validate both bounds as numeric or empty (for unbounded ranges)
176
+ start_val, end_val = val_str[0].strip(), val_str[1].strip()
177
+ if start_val and not (start_val.isdigit() or is_float(start_val)):
178
+ raise ValueError(
179
+ f"Range operator requires numeric operands for {key}: {value}"
180
+ )
181
+ if end_val and not (end_val.isdigit() or is_float(end_val)):
182
+ raise ValueError(
183
+ f"Range operator requires numeric operands for {key}: {value}"
184
+ )
185
+
186
+ # Build the SQL condition
187
+ range_conditions = []
188
+ if start_val:
189
+ operator = ">=" if start_inclusive else ">"
190
+ range_conditions.append(f"{prefix}.{key} {operator} {start_val}")
191
+ if end_val:
192
+ operator = "<=" if end_inclusive else "<"
193
+ range_conditions.append(f"{prefix}.{key} {operator} {end_val}")
194
+
195
+ # Join the range conditions with AND
196
+ filter_parts.append('( ' + " AND ".join(range_conditions) + ' )')
197
+ continue
198
+
199
+ raise ValueError(
200
+ f"Range operator requires two values for {key}: {value}"
201
+ )
202
+
203
+ # Check if value contains a known comparison operator at the start
204
+ matched_operator = None
205
+ for op in comparison_operators:
206
+ if val_str.startswith(op):
207
+ matched_operator = op
208
+ break
209
+
210
+ # Break down operator from value
211
+ # e.g. val_str = ">2022" --> operator = ">", rhs = "2022"
212
+ if matched_operator:
213
+ rhs = val_str[len(matched_operator):].strip()
214
+
215
+ if matched_operator in numeric_only_ops:
216
+ # Must be numeric
217
+ if not (rhs.isdigit() or is_float(rhs)):
218
+ raise ValueError(
219
+ f"Operator {matched_operator} requires a numeric operand for {key}: {val_str}"
220
+ )
221
+ filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs}")
222
+ else:
223
+ # = and != operators can be numeric or string
224
+ if rhs.isdigit() or is_float(rhs):
225
+ filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs}")
226
+ elif rhs.lower() in ["true", "false"]:
227
+ filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs.lower()}")
228
+ else:
229
+ # For string operands, wrap them in quotes
230
+ filter_parts.append(f"{prefix}.{key}{matched_operator}'{rhs}'")
231
+ else:
232
+ if val_str.isdigit() or is_float(val_str):
233
+ filter_parts.append(f"{prefix}.{key}={val_str}")
234
+ elif val_str.lower() in ["true", "false"]:
235
+ # This is to handle boolean values.
236
+ # This is not complete solution - the best solution would be to test if the field is boolean
237
+ # That can be done after we move to APIv2
238
+ filter_parts.append(f"{prefix}.{key}={val_str.lower()}")
239
+ else:
240
+ filter_parts.append(f"{prefix}.{key}='{val_str}'")
241
+
242
+ filter_str = " AND ".join(filter_parts)
243
+ return f"({fixed_filter}) AND ({filter_str})" if fixed_filter else filter_str
244
+
129
245
  class VectaraToolFactory:
130
246
  """
131
247
  A factory class for creating Vectara RAG tools.
@@ -149,13 +265,159 @@ class VectaraToolFactory:
149
265
  self.vectara_api_key = vectara_api_key
150
266
  self.num_corpora = len(vectara_corpus_id.split(","))
151
267
 
268
+ def create_search_tool(
269
+ self,
270
+ tool_name: str,
271
+ tool_description: str,
272
+ tool_args_schema: type[BaseModel],
273
+ tool_args_type: Dict[str, str] = {},
274
+ fixed_filter: str = "",
275
+ lambda_val: float = 0.005,
276
+ reranker: str = "mmr",
277
+ rerank_k: int = 50,
278
+ mmr_diversity_bias: float = 0.2,
279
+ udf_expression: str = None,
280
+ rerank_chain: List[Dict] = None,
281
+ verbose: bool = False,
282
+ ) -> VectaraTool:
283
+ """
284
+ Creates a Vectara search/retrieval tool
285
+
286
+ Args:
287
+ tool_name (str): The name of the tool.
288
+ tool_description (str): The description of the tool.
289
+ tool_args_schema (BaseModel): The schema for the tool arguments.
290
+ tool_args_type (Dict[str, str], optional): The type of each argument (doc or part).
291
+ fixed_filter (str, optional): A fixed Vectara filter condition to apply to all queries.
292
+ lambda_val (float, optional): Lambda value for the Vectara query.
293
+ reranker (str, optional): The reranker mode.
294
+ rerank_k (int, optional): Number of top-k documents for reranking.
295
+ mmr_diversity_bias (float, optional): MMR diversity bias.
296
+ udf_expression (str, optional): the user defined expression for reranking results.
297
+ rerank_chain (List[Dict], optional): A list of rerankers to be applied sequentially.
298
+ Each dictionary should specify the "type" of reranker (mmr, slingshot, udf)
299
+ and any other parameters (e.g. "limit" or "cutoff" for any type,
300
+ "diversity_bias" for mmr, and "user_function" for udf).
301
+ If using slingshot/multilingual_reranker_v1, it must be first in the list.
302
+ verbose (bool, optional): Whether to print verbose output.
303
+
304
+ Returns:
305
+ VectaraTool: A VectaraTool object.
306
+ """
307
+
308
+ vectara = VectaraIndex(
309
+ vectara_api_key=self.vectara_api_key,
310
+ vectara_customer_id=self.vectara_customer_id,
311
+ vectara_corpus_id=self.vectara_corpus_id,
312
+ x_source_str="vectara-agentic",
313
+ )
314
+
315
+ # Dynamically generate the search function
316
+ def search_function(*args, **kwargs) -> ToolOutput:
317
+ """
318
+ Dynamically generated function for semantic search Vectara.
319
+ """
320
+ # Convert args to kwargs using the function signature
321
+ sig = inspect.signature(search_function)
322
+ bound_args = sig.bind_partial(*args, **kwargs)
323
+ bound_args.apply_defaults()
324
+ kwargs = bound_args.arguments
325
+
326
+ query = kwargs.pop("query")
327
+ top_k = kwargs.pop("top_k", 10)
328
+ try:
329
+ filter_string = _build_filter_string(kwargs, tool_args_type, fixed_filter)
330
+ except ValueError as e:
331
+ return ToolOutput(
332
+ tool_name=search_function.__name__,
333
+ content=str(e),
334
+ raw_input={"args": args, "kwargs": kwargs},
335
+ raw_output={"response": str(e)},
336
+ )
337
+
338
+ vectara_retriever = vectara.as_retriever(
339
+ summary_enabled=False,
340
+ similarity_top_k=top_k,
341
+ reranker=reranker,
342
+ rerank_k=rerank_k if rerank_k * self.num_corpora <= 100 else int(100 / self.num_corpora),
343
+ mmr_diversity_bias=mmr_diversity_bias,
344
+ udf_expression=udf_expression,
345
+ rerank_chain=rerank_chain,
346
+ lambda_val=lambda_val,
347
+ filter=filter_string,
348
+ x_source_str="vectara-agentic",
349
+ verbose=verbose,
350
+ )
351
+ response = vectara_retriever.retrieve(query)
352
+
353
+ if len(response) == 0:
354
+ msg = "Vectara Tool failed to retreive any results for the query."
355
+ return ToolOutput(
356
+ tool_name=search_function.__name__,
357
+ content=msg,
358
+ raw_input={"args": args, "kwargs": kwargs},
359
+ raw_output={"response": msg},
360
+ )
361
+ tool_output = "Matching documents:\n"
362
+ unique_ids = set()
363
+ for doc in response:
364
+ if doc.id_ in unique_ids:
365
+ continue
366
+ unique_ids.add(doc.id_)
367
+ tool_output += f"document '{doc.id_}' metadata: {doc.metadata}\n"
368
+ out = ToolOutput(
369
+ tool_name=search_function.__name__,
370
+ content=tool_output,
371
+ raw_input={"args": args, "kwargs": kwargs},
372
+ raw_output=response,
373
+ )
374
+ return out
375
+
376
+ fields = tool_args_schema.model_fields
377
+ params = [
378
+ inspect.Parameter(
379
+ name=field_name,
380
+ kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
381
+ default=field_info.default,
382
+ annotation=field_info,
383
+ )
384
+ for field_name, field_info in fields.items()
385
+ ]
386
+
387
+ # Create a new signature using the extracted parameters
388
+ sig = inspect.Signature(params)
389
+ search_function.__signature__ = sig
390
+ search_function.__annotations__["return"] = dict[str, Any]
391
+ search_function.__name__ = "_" + re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
392
+
393
+ # Create the tool function signature string
394
+ fields = []
395
+ for name, field in tool_args_schema.__fields__.items():
396
+ annotation = field.annotation
397
+ type_name = annotation.__name__ if hasattr(annotation, '__name__') else str(annotation)
398
+ fields.append(f"{name}: {type_name}")
399
+ args_str = ", ".join(fields)
400
+ function_str = f"{tool_name}({args_str}) -> str"
401
+
402
+ # Create the tool
403
+ tool = VectaraTool.from_defaults(
404
+ fn=search_function,
405
+ name=tool_name,
406
+ description=function_str + ". " + tool_description,
407
+ fn_schema=tool_args_schema,
408
+ tool_type=ToolType.QUERY,
409
+ )
410
+ return tool
411
+
152
412
  def create_rag_tool(
153
413
  self,
154
414
  tool_name: str,
155
415
  tool_description: str,
156
416
  tool_args_schema: type[BaseModel],
157
417
  tool_args_type: Dict[str, str] = {},
418
+ fixed_filter: str = "",
158
419
  vectara_summarizer: str = "vectara-summary-ext-24-05-sml",
420
+ vectara_prompt_text: str = None,
159
421
  summary_num_results: int = 5,
160
422
  summary_response_lang: str = "eng",
161
423
  n_sentences_before: int = 2,
@@ -178,7 +440,9 @@ class VectaraToolFactory:
178
440
  tool_description (str): The description of the tool.
179
441
  tool_args_schema (BaseModel): The schema for the tool arguments.
180
442
  tool_args_type (Dict[str, str], optional): The type of each argument (doc or part).
443
+ fixed_filter (str, optional): A fixed Vectara filter condition to apply to all queries.
181
444
  vectara_summarizer (str, optional): The Vectara summarizer to use.
445
+ vectara_prompt_text (str, optional): The prompt text for the Vectara summarizer.
182
446
  summary_num_results (int, optional): The number of summary results.
183
447
  summary_response_lang (str, optional): The response language for the summary.
184
448
  n_sentences_before (int, optional): Number of sentences before the summary.
@@ -210,118 +474,6 @@ class VectaraToolFactory:
210
474
  x_source_str="vectara-agentic",
211
475
  )
212
476
 
213
- def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, str]) -> str:
214
- filter_parts = []
215
- comparison_operators = [">=", "<=", "!=", ">", "<", "="]
216
- numeric_only_ops = {">", "<", ">=", "<="}
217
-
218
- for key, value in kwargs.items():
219
- if value is None or value == "":
220
- continue
221
-
222
- # Determine the prefix for the key. Valid values are "doc" or "part"
223
- # default to 'doc' if not specified
224
- prefix = tool_args_type.get(key, "doc")
225
-
226
- if prefix not in ["doc", "part"]:
227
- raise ValueError(
228
- f'Unrecognized prefix {prefix}. Please make sure to use either "doc" or "part" for the prefix.'
229
- )
230
-
231
- if value is PydanticUndefined:
232
- raise ValueError(
233
- f"Value of argument {key} is undefined, and this is invalid. "
234
- "Please form proper arguments and try again."
235
- )
236
-
237
- # value of the arrgument
238
- val_str = str(value).strip()
239
-
240
- # Special handling for range operator
241
- if val_str.startswith(("[", "(")) and val_str.endswith(("]", ")")):
242
- # Extract the boundary types
243
- start_inclusive = val_str.startswith("[")
244
- end_inclusive = val_str.endswith("]")
245
-
246
- # Remove the boundaries and strip whitespace
247
- val_str = val_str[1:-1].strip()
248
-
249
- if "," in val_str:
250
- val_str = val_str.split(",")
251
- if len(val_str) != 2:
252
- raise ValueError(
253
- f"Range operator requires two values for {key}: {value}"
254
- )
255
-
256
- # Validate both bounds as numeric or empty (for unbounded ranges)
257
- start_val, end_val = val_str[0].strip(), val_str[1].strip()
258
- if start_val and not (start_val.isdigit() or is_float(start_val)):
259
- raise ValueError(
260
- f"Range operator requires numeric operands for {key}: {value}"
261
- )
262
- if end_val and not (end_val.isdigit() or is_float(end_val)):
263
- raise ValueError(
264
- f"Range operator requires numeric operands for {key}: {value}"
265
- )
266
-
267
- # Build the SQL condition
268
- range_conditions = []
269
- if start_val:
270
- operator = ">=" if start_inclusive else ">"
271
- range_conditions.append(f"{prefix}.{key} {operator} {start_val}")
272
- if end_val:
273
- operator = "<=" if end_inclusive else "<"
274
- range_conditions.append(f"{prefix}.{key} {operator} {end_val}")
275
-
276
- # Join the range conditions with AND
277
- filter_parts.append('( ' + " AND ".join(range_conditions) + ' )')
278
- continue
279
-
280
- raise ValueError(
281
- f"Range operator requires two values for {key}: {value}"
282
- )
283
-
284
- # Check if value contains a known comparison operator at the start
285
- matched_operator = None
286
- for op in comparison_operators:
287
- if val_str.startswith(op):
288
- matched_operator = op
289
- break
290
-
291
- # Break down operator from value
292
- # e.g. val_str = ">2022" --> operator = ">", rhs = "2022"
293
- if matched_operator:
294
- rhs = val_str[len(matched_operator):].strip()
295
-
296
- if matched_operator in numeric_only_ops:
297
- # Must be numeric
298
- if not (rhs.isdigit() or is_float(rhs)):
299
- raise ValueError(
300
- f"Operator {matched_operator} requires a numeric operand for {key}: {val_str}"
301
- )
302
- filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs}")
303
- else:
304
- # = and != operators can be numeric or string
305
- if rhs.isdigit() or is_float(rhs):
306
- filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs}")
307
- elif rhs.lower() in ["true", "false"]:
308
- filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs.lower()}")
309
- else:
310
- # For string operands, wrap them in quotes
311
- filter_parts.append(f"{prefix}.{key}{matched_operator}'{rhs}'")
312
- else:
313
- if val_str.isdigit() or is_float(val_str):
314
- filter_parts.append(f"{prefix}.{key}={val_str}")
315
- elif val_str.lower() in ["true", "false"]:
316
- # This is to handle boolean values.
317
- # This is not complete solution - the best solution would be to test if the field is boolean
318
- # That can be done after we move to APIv2
319
- filter_parts.append(f"{prefix}.{key}={val_str.lower()}")
320
- else:
321
- filter_parts.append(f"{prefix}.{key}='{val_str}'")
322
-
323
- return " AND ".join(filter_parts)
324
-
325
477
  # Dynamically generate the RAG function
326
478
  def rag_function(*args, **kwargs) -> ToolOutput:
327
479
  """
@@ -335,7 +487,7 @@ class VectaraToolFactory:
335
487
 
336
488
  query = kwargs.pop("query")
337
489
  try:
338
- filter_string = _build_filter_string(kwargs, tool_args_type)
490
+ filter_string = _build_filter_string(kwargs, tool_args_type, fixed_filter)
339
491
  except ValueError as e:
340
492
  return ToolOutput(
341
493
  tool_name=rag_function.__name__,
@@ -349,6 +501,7 @@ class VectaraToolFactory:
349
501
  summary_num_results=summary_num_results,
350
502
  summary_response_lang=summary_response_lang,
351
503
  summary_prompt_name=vectara_summarizer,
504
+ prompt_text=vectara_prompt_text,
352
505
  reranker=reranker,
353
506
  rerank_k=rerank_k if rerank_k * self.num_corpora <= 100 else int(100 / self.num_corpora),
354
507
  mmr_diversity_bias=mmr_diversity_bias,
@@ -3,9 +3,11 @@ This module contains the tools catalog for the Vectara Agentic.
3
3
  """
4
4
  from typing import List
5
5
  from functools import lru_cache
6
- from pydantic import Field
6
+ from datetime import date
7
7
  import requests
8
8
 
9
+ from pydantic import Field
10
+
9
11
  from .types import LLMRole
10
12
  from .utils import get_llm
11
13
 
@@ -19,6 +21,11 @@ get_headers = {
19
21
  "Connection": "keep-alive",
20
22
  }
21
23
 
24
+ def get_current_date() -> str:
25
+ """
26
+ Returns: the current date.
27
+ """
28
+ return date.today().strftime("%A, %B %d, %Y")
22
29
 
23
30
  #
24
31
  # Standard Tools
vectara_agentic/types.py CHANGED
@@ -3,6 +3,9 @@ This module contains the types used in the Vectara Agentic.
3
3
  """
4
4
  from enum import Enum
5
5
 
6
+ from llama_index.core.tools.types import ToolOutput as LI_ToolOutput
7
+ from llama_index.core.chat_engine.types import AgentChatResponse as LI_AgentChatResponse
8
+ from llama_index.core.chat_engine.types import StreamingAgentChatResponse as LI_StreamingAgentChatResponse
6
9
 
7
10
  class AgentType(Enum):
8
11
  """Enumeration for different types of agents."""
@@ -29,6 +32,7 @@ class ModelProvider(Enum):
29
32
  FIREWORKS = "FIREWORKS"
30
33
  COHERE = "COHERE"
31
34
  GEMINI = "GEMINI"
35
+ BEDROCK = "BEDROCK"
32
36
 
33
37
 
34
38
  class AgentStatusType(Enum):
@@ -51,3 +55,9 @@ class ToolType(Enum):
51
55
  """Enumeration for different types of tools."""
52
56
  QUERY = "query"
53
57
  ACTION = "action"
58
+
59
+
60
+ # classes for Agent responses
61
+ ToolOutput = LI_ToolOutput
62
+ AgentResponse = LI_AgentChatResponse
63
+ AgentStreamingResponse = LI_StreamingAgentChatResponse
vectara_agentic/utils.py CHANGED
@@ -20,6 +20,7 @@ provider_to_default_model_name = {
20
20
  ModelProvider.TOGETHER: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
21
21
  ModelProvider.GROQ: "llama-3.3-70b-versatile",
22
22
  ModelProvider.FIREWORKS: "accounts/fireworks/models/firefunction-v2",
23
+ ModelProvider.BEDROCK: "anthropic.claude-3-5-sonnet-20241022-v2:0",
23
24
  ModelProvider.COHERE: "command-r-plus",
24
25
  ModelProvider.GEMINI: "models/gemini-1.5-flash",
25
26
  }
@@ -105,6 +106,9 @@ def get_llm(
105
106
  elif model_provider == ModelProvider.FIREWORKS:
106
107
  from llama_index.llms.fireworks import Fireworks
107
108
  llm = Fireworks(model=model_name, temperature=0)
109
+ elif model_provider == ModelProvider.BEDROCK:
110
+ from llama_index.llms.bedrock import Bedrock
111
+ llm = Bedrock(model=model_name, temperature=0)
108
112
  elif model_provider == ModelProvider.COHERE:
109
113
  from llama_index.llms.cohere import Cohere
110
114
  llm = Cohere(model=model_name, temperature=0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: vectara_agentic
3
- Version: 0.1.24
3
+ Version: 0.1.25
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
@@ -16,39 +16,39 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
16
16
  Requires-Python: >=3.10
17
17
  Description-Content-Type: text/markdown
18
18
  License-File: LICENSE
19
- Requires-Dist: llama-index==0.12.7
19
+ Requires-Dist: llama-index==0.12.11
20
20
  Requires-Dist: llama-index-indices-managed-vectara==0.3.1
21
21
  Requires-Dist: llama-index-agent-llm-compiler==0.3.0
22
22
  Requires-Dist: llama-index-agent-lats==0.3.0
23
- Requires-Dist: llama-index-agent-openai==0.4.1
24
- Requires-Dist: llama-index-llms-openai==0.3.12
25
- Requires-Dist: llama-index-llms-anthropic==0.6.3
23
+ Requires-Dist: llama-index-agent-openai==0.4.3
24
+ Requires-Dist: llama-index-llms-openai==0.3.16
25
+ Requires-Dist: llama-index-llms-anthropic==0.6.4
26
26
  Requires-Dist: llama-index-llms-together==0.3.1
27
27
  Requires-Dist: llama-index-llms-groq==0.3.1
28
- Requires-Dist: llama-index-llms-fireworks==0.3.0
28
+ Requires-Dist: llama-index-llms-fireworks==0.3.1
29
29
  Requires-Dist: llama-index-llms-cohere==0.4.0
30
- Requires-Dist: llama-index-llms-gemini==0.4.2
30
+ Requires-Dist: llama-index-llms-gemini==0.4.4
31
+ Requires-Dist: llama-index-llms-bedrock==0.3.3
31
32
  Requires-Dist: llama-index-tools-yahoo-finance==0.3.0
32
33
  Requires-Dist: llama-index-tools-arxiv==0.3.0
33
34
  Requires-Dist: llama-index-tools-database==0.3.0
34
35
  Requires-Dist: llama-index-tools-google==0.3.0
35
36
  Requires-Dist: llama-index-tools-tavily_research==0.3.0
36
37
  Requires-Dist: llama-index-tools-neo4j==0.3.0
37
- Requires-Dist: llama-index-graph-stores-kuzu==0.5.0
38
+ Requires-Dist: llama-index-graph-stores-kuzu==0.6.0
38
39
  Requires-Dist: llama-index-tools-slack==0.3.0
39
40
  Requires-Dist: llama-index-tools-exa==0.3.0
40
41
  Requires-Dist: tavily-python==0.5.0
41
- Requires-Dist: exa-py==1.7.0
42
+ Requires-Dist: exa-py==1.8.5
42
43
  Requires-Dist: yahoo-finance==1.4.0
43
- Requires-Dist: openinference-instrumentation-llama-index==3.0.3
44
+ Requires-Dist: openinference-instrumentation-llama-index==3.1.4
44
45
  Requires-Dist: opentelemetry-proto==1.26.0
45
- Requires-Dist: arize-phoenix==5.7.0
46
+ Requires-Dist: arize-phoenix==7.11.0
46
47
  Requires-Dist: arize-phoenix-otel==0.6.1
47
48
  Requires-Dist: protobuf==4.25.5
48
49
  Requires-Dist: tokenizers>=0.20
49
- Requires-Dist: pydantic==2.9.2
50
+ Requires-Dist: pydantic==2.10.3
50
51
  Requires-Dist: retrying==1.3.4
51
- Requires-Dist: pymongo==4.10.1
52
52
  Requires-Dist: python-dotenv==1.0.1
53
53
  Requires-Dist: tiktoken==0.8.0
54
54
  Requires-Dist: dill>=0.3.7
@@ -87,7 +87,7 @@ Dynamic: summary
87
87
 
88
88
  ## ✨ Overview
89
89
 
90
- `vectara-agentic` is a Python library for developing powerful AI assistants and agents using Vectara and Agentic-RAG. It leverages the LlamaIndex Agent framework, customized for use with Vectara.
90
+ `vectara-agentic` is a Python library for developing powerful AI assistants and agents using Vectara and Agentic-RAG. It leverages the LlamaIndex Agent framework and provides helper functions to quickly create tools that connect to Vectara corpora.
91
91
 
92
92
  <p align="center">
93
93
  <img src="https://raw.githubusercontent.com/vectara/py-vectara-agentic/main/.github/assets/diagram1.png" alt="Agentic RAG diagram" width="100%" style="vertical-align: middle;">
@@ -96,10 +96,10 @@ Dynamic: summary
96
96
  ### Features
97
97
 
98
98
  - Enables easy creation of custom AI assistants and agents.
99
- - Create a Vectara RAG tool with a single line of code.
100
- - Supports `ReAct`, `OpenAIAgent`, `LATS' and `LLMCompiler` agent types.
99
+ - Create a Vectara RAG tool or search tool with a single line of code.
100
+ - Supports `ReAct`, `OpenAIAgent`, `LATS` and `LLMCompiler` agent types.
101
101
  - Includes pre-built tools for various domains (e.g., finance, legal).
102
- - Integrates with various LLM inference services like OpenAI, Anthropic, Gemini, GROQ, Together.AI, Cohere and Fireworks
102
+ - Integrates with various LLM inference services like OpenAI, Anthropic, Gemini, GROQ, Together.AI, Cohere, Bedrock and Fireworks
103
103
  - Built-in support for observability with Arize Phoenix
104
104
 
105
105
  ### 📚 Example AI Assistants
@@ -109,14 +109,14 @@ Check out our example AI assistants:
109
109
  - [Financial Assistant](https://huggingface.co/spaces/vectara/finance-chat)
110
110
  - [Justice Harvard Teaching Assistant](https://huggingface.co/spaces/vectara/Justice-Harvard)
111
111
  - [Legal Assistant](https://huggingface.co/spaces/vectara/legal-agent)
112
-
112
+ - [EV Assistant](https://huggingface.co/spaces/vectara/ev-assistant)
113
113
 
114
114
  ### Prerequisites
115
115
 
116
116
  - [Vectara account](https://console.vectara.com/signup/?utm_source=github&utm_medium=code&utm_term=DevRel&utm_content=vectara-agentic&utm_campaign=github-code-DevRel-vectara-agentic)
117
117
  - A Vectara corpus with an [API key](https://docs.vectara.com/docs/api-keys)
118
118
  - [Python 3.10 or higher](https://www.python.org/downloads/)
119
- - OpenAI API key (or API keys for Anthropic, TOGETHER.AI, Fireworks AI, Cohere, GEMINI or GROQ, if you choose to use them)
119
+ - OpenAI API key (or API keys for Anthropic, TOGETHER.AI, Fireworks AI, Bedrock, Cohere, GEMINI or GROQ, if you choose to use them)
120
120
 
121
121
  ### Installation
122
122
 
@@ -126,18 +126,25 @@ pip install vectara-agentic
126
126
 
127
127
  ## 🚀 Quick Start
128
128
 
129
- ### 1. Create a Vectara RAG tool
129
+ ### 1. Initialize the Vectara tool factory
130
130
 
131
131
  ```python
132
132
  import os
133
133
  from vectara_agentic.tools import VectaraToolFactory
134
- from pydantic import BaseModel, Field
135
134
 
136
135
  vec_factory = VectaraToolFactory(
137
136
  vectara_api_key=os.environ['VECTARA_API_KEY'],
138
137
  vectara_customer_id=os.environ['VECTARA_CUSTOMER_ID'],
139
138
  vectara_corpus_id=os.environ['VECTARA_CORPUS_ID']
140
139
  )
140
+ ```
141
+
142
+ ### 2. Create a Vectara RAG Tool
143
+
144
+ A RAG tool calls the full Vectara RAG pipeline to provide summarized responses to queries grounded in data.
145
+
146
+ ```python
147
+ from pydantic import BaseModel, Field
141
148
 
142
149
  years = list(range(2020, 2024))
143
150
  tickers = {
@@ -156,17 +163,22 @@ query_financial_reports_tool = vec_factory.create_rag_tool(
156
163
  tool_name="query_financial_reports",
157
164
  tool_description="Query financial reports for a company and year",
158
165
  tool_args_schema=QueryFinancialReportsArgs,
166
+ lambda_val=0.005,
167
+ summary_num_results=7,
168
+ # Additional arguments
159
169
  )
160
170
  ```
161
171
 
162
- ### 2. Create other tools (optional)
172
+ See the [docs](https://vectara.github.io/vectara-agentic-docs/) for additional arguments to customize your Vectara RAG tool.
173
+
174
+ ### 3. Create other tools (optional)
163
175
 
164
176
  In addition to RAG tools, you can generate a lot of other types of tools the agent can use. These could be mathematical tools, tools
165
177
  that call other APIs to get more information, or any other type of tool.
166
178
 
167
179
  See [Agent Tools](#agent-tools) for more information.
168
180
 
169
- ### 3. Create your agent
181
+ ### 4. Create your agent
170
182
 
171
183
  ```python
172
184
  from vectara_agentic import Agent
@@ -186,14 +198,72 @@ agent = Agent(
186
198
  )
187
199
  ```
188
200
 
189
- ### 4. Run your agent
201
+ See the [docs](https://vectara.github.io/vectara-agentic-docs/) for additional arguments, including `agent_progress_callback` and `query_logging_callback`.
202
+
203
+ ### 5. Run your agent
190
204
 
191
205
  ```python
192
- response = agent.chat("What was the revenue for Apple in 2021?")
193
- print(response)
206
+ res = agent.chat("What was the revenue for Apple in 2021?")
207
+ print(res.response)
194
208
  ```
195
209
 
196
- ## 🛠️ Agent Tools
210
+ Note that:
211
+ 1. `vectara-agentic` also supports `achat()` and two streaming variants `stream_chat()` and `astream_chat()`.
212
+ 2. The response types from `chat()` and `achat()` are of type `AgentResponse`. If you just need the actual string
213
+ response it's available as the `response` variable, or just use `str()`. For advanced use-cases you can look
214
+ at other `AgentResponse` variables [such as `sources`](https://github.com/run-llama/llama_index/blob/659f9faaafbecebb6e6c65f42143c0bf19274a37/llama-index-core/llama_index/core/chat_engine/types.py#L53).
215
+
216
+ ## 🧰 Vectara tools
217
+
218
+ `vectara-agentic` provides two helper functions to connect with Vectara RAG
219
+ * `create_rag_tool()` to create an agent tool that connects with a Vectara corpus for querying.
220
+ * `create_search_tool()` to create a tool to search a Vectara corpus and return a list of matching documents.
221
+
222
+ See the documentation for the full list of arguments for `create_rag_tool()` and `create_search_tool()`,
223
+ to understand how to configure Vectara query performed by those tools.
224
+
225
+ ### Creating a Vectara RAG tool
226
+
227
+ A Vectara RAG tool is often the main workhorse for any Agentic RAG application, and enables the agent to query
228
+ one or more Vectara RAG corpora.
229
+
230
+ The tool generated always includes the `query` argument, followed by 1 or more optional arguments used for
231
+ metadata filtering, defined by `tool_args_schema`.
232
+
233
+ For example, in the quickstart example the schema is:
234
+
235
+ ```
236
+ class QueryFinancialReportsArgs(BaseModel):
237
+ query: str = Field(..., description="The user query.")
238
+ year: int | str = Field(..., description=f"The year this query relates to. An integer between {min(years)} and {max(years)} or a string specifying a condition on the year (example: '>2020').")
239
+ ticker: str = Field(..., description=f"The company ticker. Must be a valid ticket symbol from the list {tickers.keys()}.")
240
+ ```
241
+
242
+ The `query` is required and is always the query string.
243
+ The other arguments are optional and will be interpreted as Vectara metadata filters.
244
+
245
+ For example, in the example above, the agent may call the `query_financial_reports_tool` tool with
246
+ query='what is the revenue?', year=2022 and ticker='AAPL'. Subsequently the RAG tool will issue
247
+ a Vectara RAG query with the same query, but with metadata filtering (doc.year=2022 and doc.ticker='AAPL').
248
+
249
+ There are also additional cool features supported here:
250
+ * An argument can be a condition, for example year='>2022' translates to the correct metadata
251
+ filtering condition doc.year>2022
252
+ * if `fixed_filter` is defined in the RAG tool, it provides a constant metadata filtering that is always applied.
253
+ For example, if fixed_filter=`doc.filing_type='10K'` then a query with query='what is the reveue', year=2022
254
+ and ticker='AAPL' would translate into query='what is the revenue' with metadata filtering condition of
255
+ "doc.year=2022 AND doc.ticker='AAPL' and doc.filing_type='10K'"
256
+
257
+ Note that `tool_args_type` is an optional dictionary that indicates the level at which metadata filtering
258
+ is applied for each argument (`doc` or `part`)
259
+
260
+ ### Creating a Vectara search tool
261
+
262
+ The Vectara search tool allows the agent to list documents that match a query.
263
+ This can be helpful to the agent to answer queries like "how many documents discuss the iPhone?" or other
264
+ similar queries that require a response in terms of a list of matching documents.
265
+
266
+ ## 🛠️ Agent Tools at a Glance
197
267
 
198
268
  `vectara-agentic` provides a few tools out of the box:
199
269
  1. **Standard tools**:
@@ -217,10 +287,9 @@ print(response)
217
287
  - `load_unique_values`: returns the top unique values for a given column
218
288
 
219
289
  In addition, we include various other tools from LlamaIndex ToolSpecs:
220
- * Tavily search
221
- * EXA.AI
290
+ * Tavily search and EXA.AI
222
291
  * arxiv
223
- * neo4j & Kuzu for Graph integration
292
+ * neo4j & Kuzu for Graph DB integration
224
293
  * Google tools (including gmail, calendar, and search)
225
294
  * Slack
226
295
 
@@ -240,7 +309,7 @@ mult_tool = ToolsFactory().create_tool(mult_func)
240
309
  The main way to control the behavior of `vectara-agentic` is by passing an `AgentConfig` object to your `Agent` when creating it.
241
310
  This object will include the following items:
242
311
  - `VECTARA_AGENTIC_AGENT_TYPE`: valid values are `REACT`, `LLMCOMPILER`, `LATS` or `OPENAI` (default: `OPENAI`)
243
- - `VECTARA_AGENTIC_MAIN_LLM_PROVIDER`: valid values are `OPENAI`, `ANTHROPIC`, `TOGETHER`, `GROQ`, `COHERE`, `GEMINI` or `FIREWORKS` (default: `OPENAI`)
312
+ - `VECTARA_AGENTIC_MAIN_LLM_PROVIDER`: valid values are `OPENAI`, `ANTHROPIC`, `TOGETHER`, `GROQ`, `COHERE`, `BEDROCK`, `GEMINI` or `FIREWORKS` (default: `OPENAI`)
244
313
  - `VECTARA_AGENTIC_MAIN_MODEL_NAME`: agent model name (default depends on provider)
245
314
  - `VECTARA_AGENTIC_TOOL_LLM_PROVIDER`: tool LLM provider (default: `OPENAI`)
246
315
  - `VECTARA_AGENTIC_TOOL_MODEL_NAME`: tool model name (default depends on provider)
@@ -0,0 +1,21 @@
1
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ tests/test_agent.py,sha256=8LQlny0rIuVa13LGtebG0vatES6Ln7gmenbSwX-ctrY,4260
3
+ tests/test_tools.py,sha256=2ofZYz3Q-YMSxjpiEg1VNUlA9gMjvNAcJAO2Ucd0eVE,2969
4
+ vectara_agentic/__init__.py,sha256=ADH4fPKLbpGNYYYszv3c3QDOjPToPE_qh3LpkH_seCU,430
5
+ vectara_agentic/_callback.py,sha256=OBiHk_OJZTS3iKz_LigfEjnl_p5V90XYsQC0vEYVSPo,8782
6
+ vectara_agentic/_observability.py,sha256=HeQYJIkqPLW3EWHiXHatkaJzo08IQGESKujdeWTuRgk,3805
7
+ vectara_agentic/_prompts.py,sha256=mgGx3zUJPtpS1epBLtl0BnoLeE7wb6AX1SX6dFNaTTQ,6368
8
+ vectara_agentic/_version.py,sha256=90Cr_yLyeFeoJ2YK_LHwWc15gk11Emy9cx1xS0hlwtc,66
9
+ vectara_agentic/agent.py,sha256=1-dno6FrC5sMuJ2nei9o2r3E53sg0dM6p-zn25hxRN0,25979
10
+ vectara_agentic/agent_config.py,sha256=9P9lyFAAXLX1ft2dBQ6tYN7dKzp7SC7B-h-DnhUHsSg,2941
11
+ vectara_agentic/agent_endpoint.py,sha256=QIMejCLlpW2qzXxeDAxv3anF46XMDdVMdKGWhJh3azY,1996
12
+ vectara_agentic/db_tools.py,sha256=kCEENzmnorm8i-k4Kpd4KLJt1QWh_ZlAyX1aG-tzET0,3619
13
+ vectara_agentic/tools.py,sha256=8FNnlTj57VTCYCaUfNWCongg38_lwnEoWdc2idCX6Jw,34087
14
+ vectara_agentic/tools_catalog.py,sha256=DDHoPuA88jiixZjNSuGIuZCB7NSsQXdbbhrbVrtR2Ec,4141
15
+ vectara_agentic/types.py,sha256=00wm3YsJRyddvIBzeVjF3qklA38NuTkCvy_B-e68v6c,1589
16
+ vectara_agentic/utils.py,sha256=eDEOUpCkb0r216LxSLkamll2NmJhu76Z1TgQFbAVq7A,4690
17
+ vectara_agentic-0.1.25.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
18
+ vectara_agentic-0.1.25.dist-info/METADATA,sha256=TtClmE0CpgC9qcJlSWxFnTywur0fFK3UkIKnmyZIkGs,19469
19
+ vectara_agentic-0.1.25.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
20
+ vectara_agentic-0.1.25.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
21
+ vectara_agentic-0.1.25.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- tests/test_agent.py,sha256=8LQlny0rIuVa13LGtebG0vatES6Ln7gmenbSwX-ctrY,4260
3
- tests/test_tools.py,sha256=2ofZYz3Q-YMSxjpiEg1VNUlA9gMjvNAcJAO2Ucd0eVE,2969
4
- vectara_agentic/__init__.py,sha256=FUWwh37ia9AduK4YDo_TCD52A09ocbYo49oYyBJtyqY,219
5
- vectara_agentic/_callback.py,sha256=OBiHk_OJZTS3iKz_LigfEjnl_p5V90XYsQC0vEYVSPo,8782
6
- vectara_agentic/_observability.py,sha256=HeQYJIkqPLW3EWHiXHatkaJzo08IQGESKujdeWTuRgk,3805
7
- vectara_agentic/_prompts.py,sha256=ITHWQQ4oSmRhqBwRcheYC_nMdZZielUOjfVIbDYi9rw,6257
8
- vectara_agentic/_version.py,sha256=3P-p27Ccx3RQ_PJQDAu-lIwPrPQ8S780FnRmeItjqNg,66
9
- vectara_agentic/agent.py,sha256=35dc7RBYevvX9XGz1WoPRQL6N8NnWxL_Eac56e1JpFo,22427
10
- vectara_agentic/agent_config.py,sha256=8q_eRPURAZYHUXu_rxD2eO1XHC9jNGe_d9ytPgXbS7g,2949
11
- vectara_agentic/agent_endpoint.py,sha256=QIMejCLlpW2qzXxeDAxv3anF46XMDdVMdKGWhJh3azY,1996
12
- vectara_agentic/db_tools.py,sha256=kCEENzmnorm8i-k4Kpd4KLJt1QWh_ZlAyX1aG-tzET0,3619
13
- vectara_agentic/tools.py,sha256=Qhql8IA_CGvsZ3ErOFQH2y-sxxHXvL3GUDH6shgxmzQ,28262
14
- vectara_agentic/tools_catalog.py,sha256=5NlJypdu0IKa7mODxVOwo05lw3PqQJtSl_ZOsUDH_TA,3986
15
- vectara_agentic/types.py,sha256=siRh9VmFt3jhTu4uJzYpvNlLi60lyIH5_xqYHKpB24Q,1149
16
- vectara_agentic/utils.py,sha256=-hnILxUAAtcFRlupeNEVBwHpm-EMF98iLV1Z_hjVrNA,4460
17
- vectara_agentic-0.1.24.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
18
- vectara_agentic-0.1.24.dist-info/METADATA,sha256=VtSVDzA0zHVWq5C8fNqjATQCOpF_zXIAsWiPYd2_Y5E,15379
19
- vectara_agentic-0.1.24.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
20
- vectara_agentic-0.1.24.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
21
- vectara_agentic-0.1.24.dist-info/RECORD,,