vectara-agentic 0.1.22__py3-none-any.whl → 0.1.24__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of vectara-agentic might be problematic. Click here for more details.

tests/test_agent.py CHANGED
@@ -2,7 +2,8 @@ import unittest
2
2
  from datetime import date
3
3
 
4
4
  from vectara_agentic.agent import _get_prompt, Agent, AgentType, FunctionTool
5
-
5
+ from vectara_agentic.agent_config import AgentConfig
6
+ from vectara_agentic.types import ModelProvider, ObserverType
6
7
 
7
8
  class TestAgentPackage(unittest.TestCase):
8
9
  def test_get_prompt(self):
@@ -43,6 +44,48 @@ class TestAgentPackage(unittest.TestCase):
43
44
  "50",
44
45
  )
45
46
 
47
+ def test_agent_config(self):
48
+ def mult(x, y):
49
+ return x * y
50
+
51
+ tools = [
52
+ FunctionTool.from_defaults(
53
+ fn=mult, name="mult", description="Multiplication functions"
54
+ )
55
+ ]
56
+ topic = "AI topic"
57
+ instructions = "Always do as your father tells you, if your mother agrees!"
58
+ config = AgentConfig(
59
+ agent_type=AgentType.REACT,
60
+ main_llm_provider=ModelProvider.ANTHROPIC,
61
+ main_llm_model_name="claude-3-5-sonnet-20241022",
62
+ tool_llm_provider=ModelProvider.TOGETHER,
63
+ tool_llm_model_name="meta-llama/Llama-3.3-70B-Instruct-Turbo",
64
+ observer=ObserverType.ARIZE_PHOENIX
65
+ )
66
+
67
+ agent = Agent(
68
+ tools=tools,
69
+ topic=topic,
70
+ custom_instructions=instructions,
71
+ agent_config=config
72
+ )
73
+ self.assertEqual(agent.tools, tools)
74
+ self.assertEqual(agent._topic, topic)
75
+ self.assertEqual(agent._custom_instructions, instructions)
76
+ self.assertEqual(agent.agent_type, AgentType.REACT)
77
+ self.assertEqual(agent.agent_config.observer, ObserverType.ARIZE_PHOENIX)
78
+ self.assertEqual(agent.agent_config.main_llm_provider, ModelProvider.ANTHROPIC)
79
+ self.assertEqual(agent.agent_config.tool_llm_provider, ModelProvider.TOGETHER)
80
+
81
+ # To run this test, you must have OPENAI_API_KEY in your environment
82
+ self.assertEqual(
83
+ agent.chat(
84
+ "What is 5 times 10. Only give the answer, nothing else"
85
+ ).replace("$", "\\$"),
86
+ "50",
87
+ )
88
+
46
89
  def test_from_corpus(self):
47
90
  agent = Agent.from_corpus(
48
91
  tool_name="RAG Tool",
tests/test_tools.py CHANGED
@@ -32,7 +32,7 @@ class TestToolsPackage(unittest.TestCase):
32
32
 
33
33
  self.assertIsInstance(query_tool, VectaraTool)
34
34
  self.assertIsInstance(query_tool, FunctionTool)
35
- self.assertEqual(query_tool.tool_type, ToolType.QUERY)
35
+ self.assertEqual(query_tool.metadata.tool_type, ToolType.QUERY)
36
36
 
37
37
  def test_tool_factory(self):
38
38
  def mult(x, y):
@@ -42,7 +42,7 @@ class TestToolsPackage(unittest.TestCase):
42
42
  other_tool = tools_factory.create_tool(mult)
43
43
  self.assertIsInstance(other_tool, VectaraTool)
44
44
  self.assertIsInstance(other_tool, FunctionTool)
45
- self.assertEqual(other_tool.tool_type, ToolType.QUERY)
45
+ self.assertEqual(other_tool.metadata.tool_type, ToolType.QUERY)
46
46
 
47
47
  def test_llama_index_tools(self):
48
48
  tools_factory = ToolsFactory()
@@ -56,7 +56,7 @@ class TestToolsPackage(unittest.TestCase):
56
56
 
57
57
  self.assertIsInstance(arxiv_tool, VectaraTool)
58
58
  self.assertIsInstance(arxiv_tool, FunctionTool)
59
- self.assertEqual(arxiv_tool.tool_type, ToolType.QUERY)
59
+ self.assertEqual(arxiv_tool.metadata.tool_type, ToolType.QUERY)
60
60
 
61
61
  def test_public_repo(self):
62
62
  vectara_customer_id = "1366999410"
@@ -2,22 +2,8 @@
2
2
  vectara_agentic package.
3
3
  """
4
4
 
5
- # Define the package version
6
- __version__ = "0.1.22"
5
+ from .agent import Agent
6
+ from .tools import VectaraToolFactory, VectaraTool
7
7
 
8
- # Import classes and functions from modules
9
- # from .module1 import Class1, function1
10
- # from .module2 import Class2, function2
11
-
12
-
13
- # Any initialization code
14
- def initialize_package():
15
- """print a message when the package is initialized."""
16
- print(f"Initializing vectara-agentic version {__version__}...")
17
-
18
-
19
- initialize_package()
20
-
21
-
22
- # Define the __all__ variable
23
- # __all__ = ['Class1', 'function1', 'Class2', 'function2']
8
+ # Define the __all__ variable for wildcard imports
9
+ __all__ = ['Agent', 'VectaraToolFactory', 'VectaraTool']
@@ -6,16 +6,16 @@ import json
6
6
  from typing import Optional, Union
7
7
  import pandas as pd
8
8
  from .types import ObserverType
9
+ from .agent_config import AgentConfig
9
10
 
10
- def setup_observer() -> bool:
11
+ def setup_observer(config: AgentConfig) -> bool:
11
12
  '''
12
13
  Setup the observer.
13
14
  '''
14
15
  import phoenix as px
15
16
  from openinference.instrumentation.llama_index import LlamaIndexInstrumentor
16
17
  from phoenix.otel import register
17
- observer = ObserverType(os.getenv("VECTARA_AGENTIC_OBSERVER_TYPE", "NO_OBSERVER"))
18
- if observer == ObserverType.ARIZE_PHOENIX:
18
+ if config.observer == ObserverType.ARIZE_PHOENIX:
19
19
  phoenix_endpoint = os.getenv("PHOENIX_ENDPOINT", None)
20
20
  if not phoenix_endpoint:
21
21
  px.launch_app()
@@ -31,7 +31,8 @@ GENERAL_INSTRUCTIONS = """
31
31
  - Use the x_load_unique_values tool to understand the unique values in each column.
32
32
  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.
33
33
  - Use the x_load_sample_data tool to understand the column names, and typical values in each column.
34
- - For tool arguments that support conditional logic (such as year='>2022'), use only one of these operators: [">=", "<=", "!=", ">", "<", "="].
34
+ - For tool arguments that support conditional logic (such as year='>2022'), use one of these operators: [">=", "<=", "!=", ">", "<", "="],
35
+ or a range operator, with inclusive or exclusive brackets (such as '[2021,2022]' or '[2021,2023)').
35
36
  - Do not mention table names or database names in your response.
36
37
  """
37
38
 
@@ -0,0 +1,4 @@
1
+ """
2
+ Define the version of the package.
3
+ """
4
+ __version__ = "0.1.24"
vectara_agentic/agent.py CHANGED
@@ -31,6 +31,7 @@ from ._prompts import REACT_PROMPT_TEMPLATE, GENERAL_PROMPT_TEMPLATE, GENERAL_IN
31
31
  from ._callback import AgentCallbackHandler
32
32
  from ._observability import setup_observer, eval_fcs
33
33
  from .tools import VectaraToolFactory, VectaraTool
34
+ from .agent_config import AgentConfig
34
35
 
35
36
  logger = logging.getLogger("opentelemetry.exporter.otlp.proto.http.trace_exporter")
36
37
  logger.setLevel(logging.CRITICAL)
@@ -91,7 +92,7 @@ class Agent:
91
92
  verbose: bool = True,
92
93
  update_func: Optional[Callable[[AgentStatusType, str], None]] = None,
93
94
  agent_progress_callback: Optional[Callable[[AgentStatusType, str], None]] = None,
94
- agent_type: AgentType = None,
95
+ agent_config: Optional[AgentConfig] = None,
95
96
  ) -> None:
96
97
  """
97
98
  Initialize the agent with the specified type, tools, topic, and system message.
@@ -104,11 +105,13 @@ class Agent:
104
105
  verbose (bool, optional): Whether the agent should print its steps. Defaults to True.
105
106
  agent_progress_callback (Callable): A callback function the code calls on any agent updates.
106
107
  update_func (Callable): old name for agent_progress_callback. Will be deprecated in future.
107
- agent_type (AgentType, optional): The type of agent to be used. Defaults to None.
108
+ agent_config (AgentConfig, optional): The configuration of the agent.
109
+ Defaults to AgentConfig(), which reads from environment variables.
108
110
  """
109
- self.agent_type = agent_type or AgentType(os.getenv("VECTARA_AGENTIC_AGENT_TYPE", "OPENAI"))
111
+ self.agent_config = agent_config or AgentConfig()
112
+ self.agent_type = self.agent_config.agent_type
110
113
  self.tools = tools
111
- self.llm = get_llm(LLMRole.MAIN)
114
+ self.llm = get_llm(LLMRole.MAIN, config=self.agent_config)
112
115
  self._custom_instructions = custom_instructions
113
116
  self._topic = topic
114
117
  self.agent_progress_callback = agent_progress_callback if agent_progress_callback else update_func
@@ -181,7 +184,7 @@ class Agent:
181
184
  raise ValueError(f"Unknown agent type: {self.agent_type}")
182
185
 
183
186
  try:
184
- self.observability_enabled = setup_observer()
187
+ self.observability_enabled = setup_observer(self.agent_config)
185
188
  except Exception as e:
186
189
  print(f"Failed to set up observer ({e}), ignoring")
187
190
  self.observability_enabled = False
@@ -252,7 +255,7 @@ class Agent:
252
255
  verbose: bool = True,
253
256
  update_func: Optional[Callable[[AgentStatusType, str], None]] = None,
254
257
  agent_progress_callback: Optional[Callable[[AgentStatusType, str], None]] = None,
255
- agent_type: AgentType = None,
258
+ agent_config: AgentConfig = AgentConfig(),
256
259
  ) -> "Agent":
257
260
  """
258
261
  Create an agent from tools, agent type, and language model.
@@ -265,7 +268,7 @@ class Agent:
265
268
  verbose (bool, optional): Whether the agent should print its steps. Defaults to True.
266
269
  agent_progress_callback (Callable): A callback function the code calls on any agent updates.
267
270
  update_func (Callable): old name for agent_progress_callback. Will be deprecated in future.
268
- agent_type (AgentType, optional): The type of agent to be used. Defaults to None.
271
+ agent_config (AgentConfig, optional): The configuration of the agent.
269
272
 
270
273
  Returns:
271
274
  Agent: An instance of the Agent class.
@@ -273,7 +276,7 @@ class Agent:
273
276
  return cls(
274
277
  tools=tools, topic=topic, custom_instructions=custom_instructions,
275
278
  verbose=verbose, agent_progress_callback=agent_progress_callback,
276
- update_func=update_func, agent_type=agent_type
279
+ update_func=update_func, agent_config=agent_config
277
280
  )
278
281
 
279
282
  @classmethod
@@ -378,7 +381,10 @@ class Agent:
378
381
  print(f"Topic = {self._topic}")
379
382
  print("Tools:")
380
383
  for tool in self.tools:
381
- print(f"- {tool.metadata.name}")
384
+ if hasattr(tool, 'metadata'):
385
+ print(f"- {tool.metadata.name}")
386
+ else:
387
+ print("- tool without metadata")
382
388
  print(f"Agent LLM = {get_llm(LLMRole.MAIN).metadata.model_name}")
383
389
  print(f"Tool LLM = {get_llm(LLMRole.TOOL).metadata.model_name}")
384
390
 
@@ -449,7 +455,7 @@ class Agent:
449
455
  for tool in self.tools:
450
456
  # Serialize each tool's metadata, function, and dynamic model schema (QueryArgs)
451
457
  tool_dict = {
452
- "tool_type": tool.tool_type.value,
458
+ "tool_type": tool.metadata.tool_type.value,
453
459
  "name": tool.metadata.name,
454
460
  "description": tool.metadata.description,
455
461
  "fn": dill.dumps(tool.fn).decode("latin-1") if tool.fn else None, # Serialize fn
@@ -469,12 +475,13 @@ class Agent:
469
475
  "topic": self._topic,
470
476
  "custom_instructions": self._custom_instructions,
471
477
  "verbose": self.verbose,
478
+ "agent_config": self.agent_config.to_dict(),
472
479
  }
473
480
 
474
481
  @classmethod
475
482
  def from_dict(cls, data: Dict[str, Any]) -> "Agent":
476
483
  """Create an Agent instance from a dictionary."""
477
- agent_type = AgentType(data["agent_type"])
484
+ agent_config = AgentConfig.from_dict(data["agent_config"])
478
485
  tools = []
479
486
 
480
487
  json_type_to_python = {
@@ -523,7 +530,7 @@ class Agent:
523
530
 
524
531
  agent = cls(
525
532
  tools=tools,
526
- agent_type=agent_type,
533
+ agent_config=agent_config,
527
534
  topic=data["topic"],
528
535
  custom_instructions=data["custom_instructions"],
529
536
  verbose=data["verbose"],
@@ -0,0 +1,86 @@
1
+ """
2
+ Define the AgentConfig dataclass for the Vectara Agentic utilities.
3
+ """
4
+
5
+ import os
6
+ from dataclasses import dataclass, field
7
+ from .types import ModelProvider, AgentType, ObserverType
8
+
9
+ @dataclass(eq=True, frozen=True)
10
+ class AgentConfig:
11
+ """
12
+ Centralized configuration for the Vectara Agentic utilities.
13
+
14
+ Each field can default to either a hard-coded value or an environment
15
+ variable. For example, if you have environment variables you want to
16
+ fall back on, you can default to them here.
17
+ """
18
+
19
+ # Agent type
20
+ agent_type: AgentType = field(
21
+ default_factory=lambda: AgentType(
22
+ os.getenv("VECTARA_AGENTIC_AGENT_TYPE", AgentType.OPENAI.value)
23
+ )
24
+ )
25
+
26
+ # Main LLM provider & model name
27
+ main_llm_provider: ModelProvider = field(
28
+ default_factory=lambda: ModelProvider(
29
+ os.getenv("VECTARA_AGENTIC_MAIN_LLM_PROVIDER", ModelProvider.OPENAI.value)
30
+ )
31
+ )
32
+
33
+ main_llm_model_name: str = field(
34
+ default_factory=lambda: os.getenv("VECTARA_AGENTIC_MAIN_LLM_MODEL_NAME", "")
35
+ )
36
+
37
+ # Tool LLM provider & model name
38
+ tool_llm_provider: ModelProvider = field(
39
+ default_factory=lambda: ModelProvider(
40
+ os.getenv("VECTARA_AGENTIC_TOOL_LLM_PROVIDER", ModelProvider.OPENAI.value)
41
+ )
42
+ )
43
+ tool_llm_model_name: str = field(
44
+ default_factory=lambda: os.getenv("VECTARA_AGENTIC_TOOL_LLM_MODEL_NAME", "")
45
+ )
46
+
47
+ # Observer
48
+ observer: ObserverType = field(
49
+ default_factory=lambda: ObserverType(
50
+ os.getenv("VECTARA_AGENTIC_OBSERVER_TYPE", "NO_OBSERVER")
51
+ )
52
+ )
53
+
54
+ # Endpoint API key
55
+ endpoint_api_key: str = field(
56
+ default_factory=lambda: os.getenv("VECTARA_AGENTIC_API_KEY", "dev-api-key")
57
+ )
58
+
59
+ def to_dict(self) -> dict:
60
+ """
61
+ Convert the AgentConfig to a dictionary.
62
+ """
63
+ return {
64
+ "agent_type": self.agent_type.value,
65
+ "main_llm_provider": self.main_llm_provider.value,
66
+ "main_llm_model_name": self.main_llm_model_name,
67
+ "tool_llm_provider": self.tool_llm_provider.value,
68
+ "tool_llm_model_name": self.tool_llm_model_name,
69
+ "observer": self.observer.value,
70
+ "endpoint_api_key": self.endpoint_api_key
71
+ }
72
+
73
+ @classmethod
74
+ def from_dict(cls, config_dict: dict) -> "AgentConfig":
75
+ """
76
+ Create an AgentConfig from a dictionary.
77
+ """
78
+ return cls(
79
+ agent_type=AgentType(config_dict["agent_type"]),
80
+ main_llm_provider=ModelProvider(config_dict["main_llm_provider"]),
81
+ main_llm_model_name=config_dict["main_llm_model_name"],
82
+ tool_llm_provider=ModelProvider(config_dict["tool_llm_provider"]),
83
+ tool_llm_model_name=config_dict["tool_llm_model_name"],
84
+ observer=ObserverType(config_dict["observer"]),
85
+ endpoint_api_key=config_dict["endpoint_api_key"]
86
+ )
@@ -1,7 +1,6 @@
1
1
  """
2
2
  This module contains functions to start the agent behind an API endpoint.
3
3
  """
4
- import os
5
4
  import logging
6
5
  from fastapi import FastAPI, HTTPException, Depends
7
6
  from fastapi.security.api_key import APIKeyHeader
@@ -9,10 +8,9 @@ from pydantic import BaseModel
9
8
  import uvicorn
10
9
 
11
10
  from .agent import Agent
11
+ from .agent_config import AgentConfig
12
12
 
13
- API_KEY_NAME = "X-API-Key"
14
- API_KEY = os.getenv("VECTARA_AGENTIC_API_KEY", "dev-api-key")
15
- api_key_header = APIKeyHeader(name=API_KEY_NAME)
13
+ api_key_header = APIKeyHeader(name="X-API-Key")
16
14
 
17
15
  class ChatRequest(BaseModel):
18
16
  """
@@ -21,18 +19,19 @@ class ChatRequest(BaseModel):
21
19
  message: str
22
20
 
23
21
 
24
- def create_app(agent: Agent) -> FastAPI:
22
+ def create_app(agent: Agent, config: AgentConfig) -> FastAPI:
25
23
  """
26
24
  Create a FastAPI application with a chat endpoint.
27
25
  """
28
26
  app = FastAPI()
29
27
  logger = logging.getLogger("uvicorn.error")
30
28
  logging.basicConfig(level=logging.INFO)
29
+ endpoint_api_key = config.endpoint_api_key
31
30
 
32
31
  @app.get("/chat", summary="Chat with the agent")
33
32
  async def chat(message: str, api_key: str = Depends(api_key_header)):
34
33
  logger.info(f"Received message: {message}")
35
- if api_key != API_KEY:
34
+ if api_key != endpoint_api_key:
36
35
  logger.warning("Unauthorized access attempt")
37
36
  raise HTTPException(status_code=403, detail="Unauthorized")
38
37
 
@@ -59,5 +58,5 @@ def start_app(agent: Agent, host='0.0.0.0', port=8000):
59
58
  host (str, optional): The host address for the API. Defaults to '127.0.0.1'.
60
59
  port (int, optional): The port for the API. Defaults to 8000.
61
60
  """
62
- app = create_app(agent)
61
+ app = create_app(agent, config=AgentConfig())
63
62
  uvicorn.run(app, host=host, port=port)
vectara_agentic/tools.py CHANGED
@@ -9,6 +9,7 @@ import os
9
9
 
10
10
  from typing import Callable, List, Dict, Any, Optional, Type
11
11
  from pydantic import BaseModel, Field
12
+ from pydantic_core import PydanticUndefined
12
13
 
13
14
  from llama_index.core.tools import FunctionTool
14
15
  from llama_index.core.tools.function_tool import AsyncCallable
@@ -227,8 +228,60 @@ class VectaraToolFactory:
227
228
  f'Unrecognized prefix {prefix}. Please make sure to use either "doc" or "part" for the prefix.'
228
229
  )
229
230
 
230
- # Check if value contains a known comparison operator at the start
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
231
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
232
285
  matched_operator = None
233
286
  for op in comparison_operators:
234
287
  if val_str.startswith(op):
@@ -251,12 +304,19 @@ class VectaraToolFactory:
251
304
  # = and != operators can be numeric or string
252
305
  if rhs.isdigit() or is_float(rhs):
253
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()}")
254
309
  else:
255
310
  # For string operands, wrap them in quotes
256
311
  filter_parts.append(f"{prefix}.{key}{matched_operator}'{rhs}'")
257
312
  else:
258
313
  if val_str.isdigit() or is_float(val_str):
259
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()}")
260
320
  else:
261
321
  filter_parts.append(f"{prefix}.{key}='{val_str}'")
262
322
 
@@ -274,7 +334,15 @@ class VectaraToolFactory:
274
334
  kwargs = bound_args.arguments
275
335
 
276
336
  query = kwargs.pop("query")
277
- filter_string = _build_filter_string(kwargs, tool_args_type)
337
+ try:
338
+ filter_string = _build_filter_string(kwargs, tool_args_type)
339
+ except ValueError as e:
340
+ return ToolOutput(
341
+ tool_name=rag_function.__name__,
342
+ content=str(e),
343
+ raw_input={"args": args, "kwargs": kwargs},
344
+ raw_output={"response": str(e)},
345
+ )
278
346
 
279
347
  vectara_query_engine = vectara.as_query_engine(
280
348
  summary_enabled=True,
vectara_agentic/utils.py CHANGED
@@ -2,7 +2,6 @@
2
2
  Utilities for the Vectara agentic.
3
3
  """
4
4
 
5
- import os
6
5
  from typing import Tuple, Callable, Optional
7
6
  from functools import lru_cache
8
7
 
@@ -13,6 +12,7 @@ from llama_index.llms.openai import OpenAI
13
12
  from llama_index.llms.anthropic import Anthropic
14
13
 
15
14
  from .types import LLMRole, AgentType, ModelProvider
15
+ from .agent_config import AgentConfig
16
16
 
17
17
  provider_to_default_model_name = {
18
18
  ModelProvider.OPENAI: "gpt-4o",
@@ -27,44 +27,52 @@ provider_to_default_model_name = {
27
27
  DEFAULT_MODEL_PROVIDER = ModelProvider.OPENAI
28
28
 
29
29
  @lru_cache(maxsize=None)
30
- def _get_llm_params_for_role(role: LLMRole) -> Tuple[ModelProvider, str]:
31
- """Get the model provider and model name for the specified role."""
30
+ def _get_llm_params_for_role(
31
+ role: LLMRole,
32
+ config: Optional[AgentConfig] = None
33
+ ) -> Tuple[ModelProvider, str]:
34
+ """
35
+ Get the model provider and model name for the specified role.
36
+
37
+ If config is None, a new AgentConfig() is instantiated using environment defaults.
38
+ """
39
+ config = config or AgentConfig() # fallback to default config
40
+
32
41
  if role == LLMRole.TOOL:
33
- model_provider = ModelProvider(
34
- os.getenv("VECTARA_AGENTIC_TOOL_LLM_PROVIDER", DEFAULT_MODEL_PROVIDER.value)
35
- )
36
- model_name = os.getenv(
37
- "VECTARA_AGENTIC_TOOL_MODEL_NAME",
38
- provider_to_default_model_name.get(model_provider),
42
+ model_provider = config.tool_llm_provider
43
+ # If the user hasn’t explicitly set a tool_llm_model_name,
44
+ # fallback to provider default from provider_to_default_model_name
45
+ model_name = (
46
+ config.tool_llm_model_name
47
+ or provider_to_default_model_name.get(model_provider)
39
48
  )
40
49
  else:
41
- model_provider = ModelProvider(
42
- os.getenv("VECTARA_AGENTIC_MAIN_LLM_PROVIDER", DEFAULT_MODEL_PROVIDER.value)
43
- )
44
- model_name = os.getenv(
45
- "VECTARA_AGENTIC_MAIN_MODEL_NAME",
46
- provider_to_default_model_name.get(model_provider),
50
+ model_provider = config.main_llm_provider
51
+ model_name = (
52
+ config.main_llm_model_name
53
+ or provider_to_default_model_name.get(model_provider)
47
54
  )
48
55
 
49
- agent_type = AgentType(
50
- os.getenv("VECTARA_AGENTIC_AGENT_TYPE", AgentType.OPENAI.value)
51
- )
52
- if (
53
- role == LLMRole.MAIN
54
- and agent_type == AgentType.OPENAI
55
- and model_provider != ModelProvider.OPENAI
56
- ):
57
- raise ValueError(
58
- "OpenAI agent requested but main model provider is not OpenAI."
59
- )
56
+ # If the agent type is OpenAI, check that the main LLM provider is also OpenAI.
57
+ if role == LLMRole.MAIN and config.agent_type == AgentType.OPENAI:
58
+ if model_provider != ModelProvider.OPENAI:
59
+ raise ValueError(
60
+ "OpenAI agent requested but main model provider is not OpenAI."
61
+ )
60
62
 
61
63
  return model_provider, model_name
62
64
 
63
65
  @lru_cache(maxsize=None)
64
- def get_tokenizer_for_model(role: LLMRole) -> Optional[Callable]:
65
- """Get the tokenizer for the specified model."""
66
- model_provider, model_name = _get_llm_params_for_role(role)
66
+ def get_tokenizer_for_model(
67
+ role: LLMRole,
68
+ config: Optional[AgentConfig] = None
69
+ ) -> Optional[Callable]:
70
+ """
71
+ Get the tokenizer for the specified model, as determined by the role & config.
72
+ """
73
+ model_provider, model_name = _get_llm_params_for_role(role, config)
67
74
  if model_provider == ModelProvider.OPENAI:
75
+ # This might raise an exception if the model_name is unknown to tiktoken
68
76
  return tiktoken.encoding_for_model(model_name).encode
69
77
  if model_provider == ModelProvider.ANTHROPIC:
70
78
  return Anthropic().tokenizer
@@ -72,10 +80,15 @@ def get_tokenizer_for_model(role: LLMRole) -> Optional[Callable]:
72
80
 
73
81
 
74
82
  @lru_cache(maxsize=None)
75
- def get_llm(role: LLMRole) -> LLM:
76
- """Get the LLM for the specified role."""
77
- model_provider, model_name = _get_llm_params_for_role(role)
78
-
83
+ def get_llm(
84
+ role: LLMRole,
85
+ config: Optional[AgentConfig] = None
86
+ ) -> LLM:
87
+ """
88
+ Get the LLM for the specified role, using the provided config
89
+ or a default if none is provided.
90
+ """
91
+ model_provider, model_name = _get_llm_params_for_role(role, config)
79
92
  if model_provider == ModelProvider.OPENAI:
80
93
  llm = OpenAI(model=model_name, temperature=0, is_function_calling_model=True)
81
94
  elif model_provider == ModelProvider.ANTHROPIC:
@@ -97,7 +110,6 @@ def get_llm(role: LLMRole) -> LLM:
97
110
  llm = Cohere(model=model_name, temperature=0)
98
111
  else:
99
112
  raise ValueError(f"Unknown LLM provider: {model_provider}")
100
-
101
113
  return llm
102
114
 
103
115
  def is_float(value: str) -> bool:
@@ -1,12 +1,12 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: vectara_agentic
3
- Version: 0.1.22
3
+ Version: 0.1.24
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
7
7
  Author-email: ofer@vectara.com
8
8
  Project-URL: Documentation, https://vectara.github.io/vectara-agentic-docs/
9
- Keywords: LLM,NLP,RAG,Agentic-RAG
9
+ Keywords: LLM,NLP,RAG,Agentic-RAG,AI assistant,AI Agent,Vectara
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: License :: OSI Approved :: Apache Software License
12
12
  Classifier: Operating System :: OS Independent
@@ -53,6 +53,17 @@ Requires-Dist: python-dotenv==1.0.1
53
53
  Requires-Dist: tiktoken==0.8.0
54
54
  Requires-Dist: dill>=0.3.7
55
55
  Requires-Dist: httpx==0.27.2
56
+ Dynamic: author
57
+ Dynamic: author-email
58
+ Dynamic: classifier
59
+ Dynamic: description
60
+ Dynamic: description-content-type
61
+ Dynamic: home-page
62
+ Dynamic: keywords
63
+ Dynamic: project-url
64
+ Dynamic: requires-dist
65
+ Dynamic: requires-python
66
+ Dynamic: summary
56
67
 
57
68
  # <img src="https://raw.githubusercontent.com/vectara/py-vectara-agentic/main/.github/assets/Vectara-logo.png" alt="Vectara Logo" width="30" height="30" style="vertical-align: middle;"> vectara-agentic
58
69
 
@@ -128,10 +139,18 @@ vec_factory = VectaraToolFactory(
128
139
  vectara_corpus_id=os.environ['VECTARA_CORPUS_ID']
129
140
  )
130
141
 
142
+ years = list(range(2020, 2024))
143
+ tickers = {
144
+ "AAPL": "Apple Computer",
145
+ "GOOG": "Google",
146
+ "AMZN": "Amazon",
147
+ "SNOW": "Snowflake",
148
+ }
149
+
131
150
  class QueryFinancialReportsArgs(BaseModel):
132
151
  query: str = Field(..., description="The user query.")
133
- year: int = Field(..., description="The year. An integer between {min(years)} and {max(years)}.")
134
- ticker: str = Field(..., description="The company ticker. Must be a valid ticket symbol from the list {tickers.keys()}.")
152
+ 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').")
153
+ ticker: str = Field(..., description=f"The company ticker. Must be a valid ticket symbol from the list {tickers.keys()}.")
135
154
 
136
155
  query_financial_reports_tool = vec_factory.create_rag_tool(
137
156
  tool_name="query_financial_reports",
@@ -218,16 +237,19 @@ mult_tool = ToolsFactory().create_tool(mult_func)
218
237
 
219
238
  ## 🛠️ Configuration
220
239
 
221
- Configure `vectara-agentic` using environment variables:
222
-
240
+ The main way to control the behavior of `vectara-agentic` is by passing an `AgentConfig` object to your `Agent` when creating it.
241
+ This object will include the following items:
223
242
  - `VECTARA_AGENTIC_AGENT_TYPE`: valid values are `REACT`, `LLMCOMPILER`, `LATS` or `OPENAI` (default: `OPENAI`)
224
243
  - `VECTARA_AGENTIC_MAIN_LLM_PROVIDER`: valid values are `OPENAI`, `ANTHROPIC`, `TOGETHER`, `GROQ`, `COHERE`, `GEMINI` or `FIREWORKS` (default: `OPENAI`)
225
244
  - `VECTARA_AGENTIC_MAIN_MODEL_NAME`: agent model name (default depends on provider)
226
245
  - `VECTARA_AGENTIC_TOOL_LLM_PROVIDER`: tool LLM provider (default: `OPENAI`)
227
246
  - `VECTARA_AGENTIC_TOOL_MODEL_NAME`: tool model name (default depends on provider)
228
247
  - `VECTARA_AGENTIC_OBSERVER_TYPE`: valid values are `ARIZE_PHOENIX` or `NONE` (default: `NONE`)
248
+ - `VECTARA_AGENTIC_API_KEY`: a secret key if using the API endpoint option (defaults to `dev-api-key`)
229
249
 
230
- When creating a VectaraToolFactory, you can pass in a `vectara_api_key`, `vectara_customer_id`, and `vectara_corpus_id` to the factory. If not passed in, it will be taken from the environment variables. Note that `VECTARA_CORPUS_ID` can be a single ID or a comma-separated list of IDs (if you want to query multiple corpora).
250
+ If any of these are not provided, `AgentConfig` first tries to read the values from the OS environment.
251
+
252
+ When creating a `VectaraToolFactory`, you can pass in a `vectara_api_key`, `vectara_customer_id`, and `vectara_corpus_id` to the factory. If not passed in, it will be taken from the environment variables (`VECTARA_API_KEY`, `VECTARA_CUSTOMER_ID` and `VECTARA_CORPUS_ID`). Note that `VECTARA_CORPUS_ID` can be a single ID or a comma-separated list of IDs (if you want to query multiple corpora).
231
253
 
232
254
  ## ℹ️ Additional Information
233
255
 
@@ -252,7 +274,8 @@ The `Agent` class supports serialization. Use the `dumps()` to serialize and `lo
252
274
  ### Observability
253
275
 
254
276
  vectara-agentic supports observability via the existing integration of LlamaIndex and Arize Phoenix.
255
- First, set `os["VECTARA_AGENTIC_OBSERVER_TYPE"] = "ARIZE_PHOENIX"`.
277
+ First, set `VECTARA_AGENTIC_OBSERVER_TYPE` to `ARIZE_PHOENIX` in `AgentConfig` (or env variable).
278
+
256
279
  Then you can use Arize Phoenix in three ways:
257
280
  1. **Locally**.
258
281
  1. If you have a local phoenix server that you've run using e.g. `python -m phoenix.server.main serve`, vectara-agentic will send all traces to it.
@@ -260,7 +283,7 @@ Then you can use Arize Phoenix in three ways:
260
283
  3. In both cases, traces will be sent to the local instance, and you can see the dashboard at `http://localhost:6006`
261
284
  2. **Hosted Instance**. In this case the traces are sent to the Phoenix instances hosted on Arize.
262
285
  1. Go to `https://app.phoenix.arize.com`, setup an account if you don't have one.
263
- 2. create an API key and put it in the `PHOENIX_API_KEY` variable. This variable indicates you want to use the hosted version.
286
+ 2. create an API key and put it in the `PHOENIX_API_KEY` environment variable - this indicates you want to use the hosted version.
264
287
  3. To view the traces go to `https://app.phoenix.arize.com`.
265
288
 
266
289
  Now when you run your agent, all call traces are sent to Phoenix and recorded.
@@ -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=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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,19 +0,0 @@
1
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- tests/test_agent.py,sha256=aQYYr_8hKlFiDgyI5Dd39TG5vkmDJe7F_nIzMTCLsTQ,2517
3
- tests/test_tools.py,sha256=hDAlXkWKuXHnAjeQwMuTLTwNdRsM-xR7muBzFkZRefw,2942
4
- vectara_agentic/__init__.py,sha256=X9K53dm3OLnIwIXZCiX2eQVCF5ou0jypGtFpTfrZYIs,508
5
- vectara_agentic/_callback.py,sha256=OBiHk_OJZTS3iKz_LigfEjnl_p5V90XYsQC0vEYVSPo,8782
6
- vectara_agentic/_observability.py,sha256=v0xxTk8KI8nVK2rpyGqOVhyva9ymqOmZK5brKqFOwMM,3828
7
- vectara_agentic/_prompts.py,sha256=N656x-bfy9I5j2Lw51gpaGq_8R6zpPelPQ2-KWHHpTk,6160
8
- vectara_agentic/agent.py,sha256=Vr8R4sR_ZxJfGI94Jjze4r_rCCWYNvJ9UqsfjlAReWM,22045
9
- vectara_agentic/agent_endpoint.py,sha256=I3zTEezbAiNeW5I41r0NjIaR8Ucn4oe1XVcALekakaA,1959
10
- vectara_agentic/db_tools.py,sha256=kCEENzmnorm8i-k4Kpd4KLJt1QWh_ZlAyX1aG-tzET0,3619
11
- vectara_agentic/tools.py,sha256=OZ0qLtwS60J8XdVvuRDm21QBpLpEudVl659P37k8fCI,24798
12
- vectara_agentic/tools_catalog.py,sha256=5NlJypdu0IKa7mODxVOwo05lw3PqQJtSl_ZOsUDH_TA,3986
13
- vectara_agentic/types.py,sha256=siRh9VmFt3jhTu4uJzYpvNlLi60lyIH5_xqYHKpB24Q,1149
14
- vectara_agentic/utils.py,sha256=XzSo5tKSsQpgd6Kx3q-XREohbyIe7-UqlpUW-9y1KeQ,3998
15
- vectara_agentic-0.1.22.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
16
- vectara_agentic-0.1.22.dist-info/METADATA,sha256=Jrw5HeZ2nyTeAX6cf69qmmpPUo2bArUNpA2WB_gqndo,14441
17
- vectara_agentic-0.1.22.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
18
- vectara_agentic-0.1.22.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
19
- vectara_agentic-0.1.22.dist-info/RECORD,,