vectara-agentic 0.1.4__tar.gz → 0.1.5__tar.gz

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

Potentially problematic release.


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

Files changed (23) hide show
  1. {vectara_agentic-0.1.4/vectara_agentic.egg-info → vectara_agentic-0.1.5}/PKG-INFO +3 -2
  2. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/README.md +1 -1
  3. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/requirements.txt +1 -0
  4. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/setup.py +1 -1
  5. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/tests/test_agent.py +2 -2
  6. vectara_agentic-0.1.5/vectara_agentic/agent.py +283 -0
  7. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic/tools.py +53 -56
  8. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic/utils.py +22 -6
  9. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5/vectara_agentic.egg-info}/PKG-INFO +3 -2
  10. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic.egg-info/requires.txt +1 -0
  11. vectara_agentic-0.1.4/vectara_agentic/agent.py +0 -172
  12. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/LICENSE +0 -0
  13. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/MANIFEST.in +0 -0
  14. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/setup.cfg +0 -0
  15. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/tests/test_tools.py +0 -0
  16. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic/__init__.py +0 -0
  17. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic/_callback.py +0 -0
  18. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic/_prompts.py +0 -0
  19. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic/tools_catalog.py +0 -0
  20. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic/types.py +0 -0
  21. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic.egg-info/SOURCES.txt +0 -0
  22. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic.egg-info/dependency_links.txt +0 -0
  23. {vectara_agentic-0.1.4 → vectara_agentic-0.1.5}/vectara_agentic.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vectara_agentic
3
- Version: 0.1.4
3
+ Version: 0.1.5
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
@@ -37,6 +37,7 @@ Requires-Dist: pylint==3.2.6
37
37
  Requires-Dist: flake8==7.1.0
38
38
  Requires-Dist: pymongo==4.6.1
39
39
  Requires-Dist: python-dotenv==1.0.1
40
+ Requires-Dist: tiktoken==0.7.0
40
41
 
41
42
  # vectara-agentic
42
43
 
@@ -202,7 +203,7 @@ We have created a few example AI assistants that you can look at for inspiration
202
203
  ## 🤝 Contributing
203
204
 
204
205
  Contributions, issues and feature requests are welcome and appreciated!<br />
205
- Feel free to check [issues page](https://github.com/vectara/py-vectara-agentic/issues). You can also take a look at the [contributing guide](https://github.com/vectara/py-vectara-agentic/blob/master/CONTRIBUTING.md).
206
+ Feel free to check [issues page](https://github.com/vectara/py-vectara-agentic/issues). You can also take a look at the [contributing guide](https://github.com/vectara/py-vectara-agentic/blob/main/CONTRIBUTING.md).
206
207
 
207
208
  ## Show your support
208
209
 
@@ -162,7 +162,7 @@ We have created a few example AI assistants that you can look at for inspiration
162
162
  ## 🤝 Contributing
163
163
 
164
164
  Contributions, issues and feature requests are welcome and appreciated!<br />
165
- Feel free to check [issues page](https://github.com/vectara/py-vectara-agentic/issues). You can also take a look at the [contributing guide](https://github.com/vectara/py-vectara-agentic/blob/master/CONTRIBUTING.md).
165
+ Feel free to check [issues page](https://github.com/vectara/py-vectara-agentic/issues). You can also take a look at the [contributing guide](https://github.com/vectara/py-vectara-agentic/blob/main/CONTRIBUTING.md).
166
166
 
167
167
  ## Show your support
168
168
 
@@ -19,3 +19,4 @@ pylint==3.2.6
19
19
  flake8==7.1.0
20
20
  pymongo==4.6.1
21
21
  python-dotenv==1.0.1
22
+ tiktoken==0.7.0
@@ -8,7 +8,7 @@ def read_requirements():
8
8
 
9
9
  setup(
10
10
  name="vectara_agentic",
11
- version="0.1.4",
11
+ version="0.1.5",
12
12
  author="Ofer Mendelevitch",
13
13
  author_email="ofer@vectara.com",
14
14
  description="A Python package for creating AI Assistants and AI Agents with Vectara",
@@ -1,7 +1,7 @@
1
1
  import unittest
2
2
  from datetime import date
3
3
 
4
- from vectara_agentic.agent import get_prompt, Agent, AgentType, FunctionTool
4
+ from vectara_agentic.agent import _get_prompt, Agent, AgentType, FunctionTool
5
5
 
6
6
 
7
7
  class TestAgentPackage(unittest.TestCase):
@@ -15,7 +15,7 @@ class TestAgentPackage(unittest.TestCase):
15
15
  + " with Always do as your mother tells you!"
16
16
  )
17
17
  self.assertEqual(
18
- get_prompt(prompt_template, topic, custom_instructions), expected_output
18
+ _get_prompt(prompt_template, topic, custom_instructions), expected_output
19
19
  )
20
20
 
21
21
  def test_agent_init(self):
@@ -0,0 +1,283 @@
1
+ """
2
+ This module contains the Agent class for handling different types of agents and their interactions.
3
+ """
4
+
5
+ from typing import List, Callable, Optional
6
+ import os
7
+ from datetime import date
8
+
9
+ from retrying import retry
10
+ from pydantic import Field, create_model
11
+
12
+
13
+ from llama_index.core.tools import FunctionTool
14
+ from llama_index.core.agent import ReActAgent
15
+ from llama_index.core.agent.react.formatter import ReActChatFormatter
16
+ from llama_index.core.callbacks import CallbackManager, TokenCountingHandler
17
+ from llama_index.agent.openai import OpenAIAgent
18
+ from llama_index.core.memory import ChatMemoryBuffer
19
+
20
+ from dotenv import load_dotenv
21
+
22
+ from .types import AgentType, AgentStatusType, LLMRole
23
+ from .utils import get_llm, get_tokenizer_for_model
24
+ from ._prompts import REACT_PROMPT_TEMPLATE, GENERAL_PROMPT_TEMPLATE
25
+ from ._callback import AgentCallbackHandler
26
+ from .tools import VectaraToolFactory
27
+
28
+ load_dotenv(override=True)
29
+
30
+
31
+ def _get_prompt(prompt_template: str, topic: str, custom_instructions: str):
32
+ """
33
+ Generate a prompt by replacing placeholders with topic and date.
34
+
35
+ Args:
36
+
37
+ prompt_template (str): The template for the prompt.
38
+ topic (str): The topic to be included in the prompt.
39
+
40
+ Returns:
41
+ str: The formatted prompt.
42
+ """
43
+ return (
44
+ prompt_template.replace("{chat_topic}", topic)
45
+ .replace("{today}", date.today().strftime("%A, %B %d, %Y"))
46
+ .replace("{custom_instructions}", custom_instructions)
47
+ )
48
+
49
+
50
+ def _retry_if_exception(exception):
51
+ # Define the condition to retry on certain exceptions
52
+ return isinstance(
53
+ exception, (TimeoutError)
54
+ ) # Replace SomeOtherException with other exceptions you want to catch
55
+
56
+
57
+ class Agent:
58
+ """
59
+ Agent class for handling different types of agents and their interactions.
60
+ """
61
+
62
+ def __init__(
63
+ self,
64
+ tools: list[FunctionTool],
65
+ topic: str = "general",
66
+ custom_instructions: str = "",
67
+ verbose: bool = True,
68
+ update_func: Optional[Callable[[AgentStatusType, str], None]] = None,
69
+ ):
70
+ """
71
+ Initialize the agent with the specified type, tools, topic, and system message.
72
+
73
+ Args:
74
+
75
+ tools (list[FunctionTool]): A list of tools to be used by the agent.
76
+ topic (str, optional): The topic for the agent. Defaults to 'general'.
77
+ custom_instructions (str, optional): custom instructions for the agent. Defaults to ''.
78
+ update_func (Callable): a callback function the code calls on any agent updates.
79
+ """
80
+ self.agent_type = AgentType(os.getenv("VECTARA_AGENTIC_AGENT_TYPE", "OPENAI"))
81
+ self.tools = tools
82
+ self.llm = get_llm(LLMRole.MAIN)
83
+ self._custom_instructions = custom_instructions
84
+ self._topic = topic
85
+
86
+ main_tok = get_tokenizer_for_model(role=LLMRole.MAIN)
87
+ self.main_token_counter = TokenCountingHandler(tokenizer = main_tok) if main_tok else None
88
+ tool_tok = get_tokenizer_for_model(role=LLMRole.TOOL)
89
+ self.tool_token_counter = TokenCountingHandler(tokenizer = tool_tok) if tool_tok else None
90
+
91
+ callbacks = [AgentCallbackHandler(update_func)]
92
+ if self.main_token_counter:
93
+ callbacks.append(self.main_token_counter)
94
+ if self.tool_token_counter:
95
+ callbacks.append(self.tool_token_counter)
96
+ callback_manager = CallbackManager(callbacks) # type: ignore
97
+ self.llm.callback_manager = callback_manager
98
+
99
+ memory = ChatMemoryBuffer.from_defaults(token_limit=128000)
100
+ if self.agent_type == AgentType.REACT:
101
+ prompt = _get_prompt(REACT_PROMPT_TEMPLATE, topic, custom_instructions)
102
+ self.agent = ReActAgent.from_tools(
103
+ tools=tools,
104
+ llm=self.llm,
105
+ memory=memory,
106
+ verbose=verbose,
107
+ react_chat_formatter=ReActChatFormatter(system_header=prompt),
108
+ max_iterations=20,
109
+ callable_manager=callback_manager,
110
+ )
111
+ elif self.agent_type == AgentType.OPENAI:
112
+ prompt = _get_prompt(GENERAL_PROMPT_TEMPLATE, topic, custom_instructions)
113
+ self.agent = OpenAIAgent.from_tools(
114
+ tools=tools,
115
+ llm=self.llm,
116
+ memory=memory,
117
+ verbose=verbose,
118
+ callable_manager=callback_manager,
119
+ max_function_calls=10,
120
+ system_prompt=prompt,
121
+ )
122
+ else:
123
+ raise ValueError(f"Unknown agent type: {self.agent_type}")
124
+
125
+ @classmethod
126
+ def from_tools(
127
+ cls,
128
+ tools: List[FunctionTool],
129
+ topic: str = "general",
130
+ custom_instructions: str = "",
131
+ verbose: bool = True,
132
+ update_func: Optional[Callable[[AgentStatusType, str], None]] = None,
133
+ ) -> "Agent":
134
+ """
135
+ Create an agent from tools, agent type, and language model.
136
+
137
+ Args:
138
+
139
+ tools (list[FunctionTool]): A list of tools to be used by the agent.
140
+ topic (str, optional): The topic for the agent. Defaults to 'general'.
141
+ custom_instructions (str, optional): custom instructions for the agent. Defaults to ''.
142
+ llm (LLM): The language model to be used by the agent.
143
+
144
+ Returns:
145
+ Agent: An instance of the Agent class.
146
+ """
147
+ return cls(tools, topic, custom_instructions, verbose, update_func)
148
+
149
+
150
+ @classmethod
151
+ def from_corpus(
152
+ cls,
153
+ vectara_customer_id: str,
154
+ vectara_corpus_id: str,
155
+ vectara_api_key: str,
156
+ data_description: str,
157
+ assistant_specialty: str,
158
+ verbose: bool = False,
159
+ vectara_filter_fields: list[dict] = [],
160
+ vectara_lambda_val: float = 0.005,
161
+ vectara_reranker: str = "mmr",
162
+ vectara_rerank_k: int = 50,
163
+ vectara_n_sentences_before: int = 2,
164
+ vectara_n_sentences_after: int = 2,
165
+ vectara_summary_num_results: int = 10,
166
+ vectara_summarizer: str = "vectara-summary-ext-24-05-sml",
167
+ ) -> "Agent":
168
+ """
169
+ Create an agent from a single Vectara corpus
170
+
171
+ Args:
172
+ name (str): The name .
173
+ vectara_customer_id (str): The Vectara customer ID.
174
+ vectara_corpus_id (str): The Vectara corpus ID.
175
+ vectara_api_key (str): The Vectara API key.
176
+ data_description (str): The description of the data.
177
+ assistant_specialty (str): The specialty of the assistant.
178
+ verbose (bool): Whether to print verbose output.
179
+ vectara_filter_fields (List[dict]): The filterable attributes (each dict includes name, type, and description).
180
+ vectara_lambda_val (float): The lambda value for Vectara hybrid search.
181
+ vectara_reranker (str): The Vectara reranker name (default "mmr")
182
+ vectara_rerank_k (int): The number of results to use with reranking.
183
+ vectara_n_sentences_before (int): The number of sentences before the matching text
184
+ vectara_n_sentences_after (int): The number of sentences after the matching text.
185
+ vectara_summary_num_results (int): The number of results to use in summarization.
186
+ vectara_summarizer (str): The Vectara summarizer name.
187
+
188
+ Returns:
189
+ Agent: An instance of the Agent class.
190
+ """
191
+ vec_factory = VectaraToolFactory(vectara_api_key=vectara_api_key,
192
+ vectara_customer_id=vectara_customer_id,
193
+ vectara_corpus_id=vectara_corpus_id)
194
+ QueryArgs = create_model(
195
+ "QueryArgs",
196
+ query=(str, Field(description="The user query")),
197
+ **{
198
+ field['name']: (field['type'], Field(description=field['description'], default=None))
199
+ for field in vectara_filter_fields
200
+ }
201
+ )
202
+
203
+ vectara_tool = vec_factory.create_rag_tool(
204
+ tool_name = f"vectara_{vectara_corpus_id}",
205
+ tool_description = f"""
206
+ Given a user query,
207
+ returns a response (str) to a user question about {data_description}.
208
+ """,
209
+ tool_args_schema = QueryArgs,
210
+ reranker = vectara_reranker, rerank_k = vectara_rerank_k,
211
+ n_sentences_before = vectara_n_sentences_before,
212
+ n_sentences_after = vectara_n_sentences_after,
213
+ lambda_val = vectara_lambda_val,
214
+ summary_num_results = vectara_summary_num_results,
215
+ vectara_summarizer = vectara_summarizer,
216
+ include_citations = False,
217
+ )
218
+
219
+ assistant_instructions = f"""
220
+ - You are a helpful {assistant_specialty} assistant.
221
+ - You can answer questions about {data_description}.
222
+ - Never discuss politics, and always respond politely.
223
+ """
224
+
225
+ return cls(
226
+ tools=[vectara_tool],
227
+ topic=assistant_specialty,
228
+ custom_instructions=assistant_instructions,
229
+ verbose=verbose,
230
+ update_func=None
231
+ )
232
+
233
+ def report(self) -> str:
234
+ """
235
+ Get a report from the agent.
236
+
237
+ Returns:
238
+ str: The report from the agent.
239
+ """
240
+ print("Vectara agentic Report:")
241
+ print(f"Agent Type = {self.agent_type}")
242
+ print(f"Topic = {self._topic}")
243
+ print("Tools:")
244
+ for tool in self.tools:
245
+ print(f"- {tool._metadata.name}")
246
+ print(f"Agent LLM = {get_llm(LLMRole.MAIN).model}")
247
+ print(f"Tool LLM = {get_llm(LLMRole.TOOL).model}")
248
+
249
+ def token_counts(self) -> dict:
250
+ """
251
+ Get the token counts for the agent and tools.
252
+
253
+ Returns:
254
+ dict: The token counts for the agent and tools.
255
+ """
256
+ return {
257
+ "main token count": self.main_token_counter.total_llm_token_count if self.main_token_counter else -1,
258
+ "tool token count": self.tool_token_counter.total_llm_token_count if self.tool_token_counter else -1,
259
+ }
260
+
261
+ @retry(
262
+ retry_on_exception=_retry_if_exception,
263
+ stop_max_attempt_number=3,
264
+ wait_fixed=2000,
265
+ )
266
+ def chat(self, prompt: str) -> str:
267
+ """
268
+ Interact with the agent using a chat prompt.
269
+
270
+ Args:
271
+ prompt (str): The chat prompt.
272
+
273
+ Returns:
274
+ str: The response from the agent.
275
+ """
276
+
277
+ try:
278
+ agent_response = self.agent.chat(prompt)
279
+ return agent_response.response
280
+ except Exception as e:
281
+ import traceback
282
+
283
+ return f"Vectara Agentic: encountered an exception ({e}) at ({traceback.format_exc()}), and can't respond."
@@ -13,6 +13,8 @@ from llama_index.core.tools import FunctionTool
13
13
  from llama_index.core.base.response.schema import Response
14
14
  from llama_index.indices.managed.vectara import VectaraIndex
15
15
  from llama_index.core.utilities.sql_wrapper import SQLDatabase
16
+ from llama_index.core.tools.types import AsyncBaseTool, ToolMetadata
17
+
16
18
 
17
19
  from .types import ToolType
18
20
  from .tools_catalog import (
@@ -49,7 +51,7 @@ LI_packages = {
49
51
  }
50
52
 
51
53
 
52
- class VectaraTool:
54
+ class VectaraTool(AsyncBaseTool):
53
55
  """
54
56
  A wrapper of FunctionTool class for Vectara tools, adding the tool_type attribute.
55
57
  """
@@ -63,6 +65,26 @@ class VectaraTool:
63
65
 
64
66
  def __call__(self, *args, **kwargs):
65
67
  return self.function_tool(*args, **kwargs)
68
+
69
+ def call(self, *args, **kwargs):
70
+ return self.function_tool.call(*args, **kwargs)
71
+
72
+ def acall(self, *args, **kwargs):
73
+ return self.function_tool.acall(*args, **kwargs)
74
+
75
+ @property
76
+ def metadata(self) -> ToolMetadata:
77
+ """Metadata."""
78
+ return self.function_tool.metadata
79
+
80
+ def __repr__(self):
81
+ repr_str = f"""
82
+ Name: {self.function_tool._metadata.name}
83
+ Tool Type: {self.tool_type}
84
+ Description: {self.function_tool._metadata.description}
85
+ Schema: {inspect.signature(self.function_tool._metadata.fn_schema)}
86
+ """
87
+ return repr_str
66
88
 
67
89
 
68
90
  class VectaraToolFactory:
@@ -76,6 +98,13 @@ class VectaraToolFactory:
76
98
  vectara_corpus_id: str,
77
99
  vectara_api_key: str,
78
100
  ) -> None:
101
+ """
102
+ Initialize the VectaraToolFactory
103
+ Args:
104
+ vectara_customer_id (str): The Vectara customer ID.
105
+ vectara_corpus_id (str): The Vectara corpus ID.
106
+ vectara_api_key (str): The Vectara API key.
107
+ """
79
108
  self.vectara_customer_id = vectara_customer_id
80
109
  self.vectara_corpus_id = vectara_corpus_id
81
110
  self.vectara_api_key = vectara_api_key
@@ -100,7 +129,6 @@ class VectaraToolFactory:
100
129
  Creates a RAG (Retrieve and Generate) tool.
101
130
 
102
131
  Args:
103
-
104
132
  tool_name (str): The name of the tool.
105
133
  tool_description (str): The description of the tool.
106
134
  tool_args_schema (BaseModel): The schema for the tool arguments.
@@ -125,7 +153,7 @@ class VectaraToolFactory:
125
153
  vectara_corpus_id=self.vectara_corpus_id,
126
154
  )
127
155
 
128
- def build_filter_string(kwargs):
156
+ def _build_filter_string(kwargs):
129
157
  filter_parts = []
130
158
  for key, value in kwargs.items():
131
159
  if value:
@@ -147,7 +175,7 @@ class VectaraToolFactory:
147
175
  kwargs = bound_args.arguments
148
176
 
149
177
  query = kwargs.pop("query")
150
- filter_string = build_filter_string(kwargs)
178
+ filter_string = _build_filter_string(kwargs)
151
179
 
152
180
  vectara_query_engine = vectara.as_query_engine(
153
181
  summary_enabled=True,
@@ -189,65 +217,28 @@ class VectaraToolFactory:
189
217
  }
190
218
  return res
191
219
 
192
- # Create signature for rag_function based on tool_args_schema
193
- parameters = []
194
- for name, param in tool_args_schema.__fields__.items():
195
- default = inspect.Parameter.empty
196
- if param.default is not None:
197
- default = param.default
198
- elif param.default_factory is not None:
199
- default = param.default_factory()
200
-
201
- parameters.append(
202
- inspect.Parameter(
203
- name,
204
- inspect.Parameter.POSITIONAL_OR_KEYWORD,
205
- default=default,
206
- annotation=param.type_ if param.required else Optional[param.type_],
207
- )
208
- )
209
- if (
210
- "query" not in tool_args_schema.__fields__
211
- ): # Add 'query' parameter if it's not already in the schema
212
- parameters.append(
213
- inspect.Parameter(
214
- "query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str
215
- )
220
+ fields = tool_args_schema.__fields__
221
+ params = [
222
+ inspect.Parameter(
223
+ name=field_name,
224
+ kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
225
+ default=field_info.default,
226
+ annotation=field_info.field_info,
216
227
  )
217
- new_signature = inspect.Signature(parameters, return_annotation=dict[str, Any])
218
- setattr(rag_function, "__signature__", new_signature)
219
-
220
- # Set the function name and docstring
221
- doc_string = f"{tool_description}\n\n"
222
- doc_string += "Parameters:\n"
223
- for field_name, field in tool_args_schema.__fields__.items():
224
- type_name = field.type_.__name__
225
- if field.allow_none:
226
- type_name = f"Optional[{type_name}]"
227
-
228
- default_info = ""
229
- if field.default is not None:
230
- default_info = f" (default: {field.default})"
231
- elif field.default_factory is not None:
232
- default_info = " (default: set by factory)"
233
-
234
- doc_string += f"- {field_name} ({type_name}): {field.field_info.description}{default_info}\n"
235
-
236
- doc_string += "\nReturns: a dict[str, Any] with the following:\n"
237
- doc_string += (
238
- "- response: The response string in markdown format with citations.\n"
239
- )
240
- doc_string += "- citation_metadata: a dictionary of metadata for each citation included in the response string.\n"
241
- doc_string += "- response_factual_consistency: a value representing confidence in the response being factually correct (1.0=high, 0.0=low).\n"
228
+ for field_name, field_info in fields.items()
229
+ ]
242
230
 
231
+ # Create a new signature using the extracted parameters
232
+ sig = inspect.Signature(params)
233
+ rag_function.__signature__ = sig
234
+ rag_function.__annotations__['return'] = dict[str, Any]
243
235
  rag_function.__name__ = "_" + re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
244
- rag_function.__doc__ = doc_string
245
236
 
246
237
  # Create the tool
247
238
  tool = FunctionTool.from_defaults(
248
239
  fn=rag_function,
249
240
  name=tool_name,
250
- description=doc_string,
241
+ description=tool_description,
251
242
  fn_schema=tool_args_schema,
252
243
  )
253
244
  return VectaraTool(tool, ToolType.QUERY)
@@ -283,7 +274,7 @@ class ToolsFactory:
283
274
  """
284
275
  Get a tool from the llama_index hub.
285
276
 
286
- Parameters:
277
+ Args:
287
278
  tool_package_name (str): The name of the tool package.
288
279
  tool_spec_name (str): The name of the tool spec.
289
280
  tool_name_prefix (str): The prefix to add to the tool names (added to every tool in the spec).
@@ -329,6 +320,9 @@ class ToolsFactory:
329
320
  return [self.create_tool(tool) for tool in [summarize_text, rephrase_text]]
330
321
 
331
322
  def guardrail_tools(self) -> List[FunctionTool]:
323
+ """
324
+ Create a list of guardrail tools to avoid controversial topics.
325
+ """
332
326
  return [
333
327
  self.create_tool(tool)
334
328
  for tool in [guardrails_no_politics, guardrails_be_polite]
@@ -341,6 +335,9 @@ class ToolsFactory:
341
335
  return self.get_llama_index_tools("yahoo_finance", "YahooFinanceToolSpec")
342
336
 
343
337
  def legal_tools(self) -> List[FunctionTool]:
338
+ """
339
+ Create a list of legal tools.
340
+ """
344
341
  def summarize_legal_text(
345
342
  text: str = Field(description="the original text."),
346
343
  ) -> str:
@@ -10,23 +10,22 @@ from llama_index.llms.anthropic import Anthropic
10
10
  from llama_index.llms.together import TogetherLLM
11
11
  from llama_index.llms.groq import Groq
12
12
  from llama_index.llms.fireworks import Fireworks
13
+ import tiktoken
13
14
 
14
15
  from .types import LLMRole, AgentType, ModelProvider
15
16
 
16
17
  provider_to_default_model_name = {
17
- ModelProvider.OPENAI: "gpt-4o",
18
+ ModelProvider.OPENAI: "gpt-4o-2024-08-06",
18
19
  ModelProvider.ANTHROPIC: "claude-3-5-sonnet-20240620",
19
20
  ModelProvider.TOGETHER: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
20
- ModelProvider.GROQ: "mixtral-8x7b-32768",
21
+ ModelProvider.GROQ: "llama3-groq-70b-8192-tool-use-preview",
21
22
  ModelProvider.FIREWORKS: "accounts/fireworks/models/firefunction-v2",
22
23
  }
23
24
 
24
25
  DEFAULT_MODEL_PROVIDER = ModelProvider.OPENAI
25
26
 
26
-
27
- def get_llm(role: LLMRole) -> LLM:
28
- """Get the LLM for the specified role."""
29
- agent_type = AgentType(os.getenv("VECTARA_AGENTIC_AGENT_TYPE", AgentType.OPENAI))
27
+ def _get_llm_params_for_role(role: LLMRole) -> tuple[str, str]:
28
+ """Get the model provider and model name for the specified role."""
30
29
  if role == LLMRole.TOOL:
31
30
  model_provider = ModelProvider(
32
31
  os.getenv("VECTARA_AGENTIC_TOOL_LLM_PROVIDER", DEFAULT_MODEL_PROVIDER)
@@ -44,6 +43,7 @@ def get_llm(role: LLMRole) -> LLM:
44
43
  provider_to_default_model_name.get(model_provider),
45
44
  )
46
45
 
46
+ agent_type = AgentType(os.getenv("VECTARA_AGENTIC_AGENT_TYPE", AgentType.OPENAI))
47
47
  if (
48
48
  role == LLMRole.MAIN
49
49
  and agent_type == AgentType.OPENAI
@@ -53,6 +53,22 @@ def get_llm(role: LLMRole) -> LLM:
53
53
  "OpenAI agent requested but main model provider is not OpenAI."
54
54
  )
55
55
 
56
+ return model_provider, model_name
57
+
58
+ def get_tokenizer_for_model(role: LLMRole) -> str:
59
+ """Get the tokenizer for the specified model."""
60
+ model_provider, model_name = _get_llm_params_for_role(role)
61
+ if model_provider == ModelProvider.OPENAI:
62
+ return tiktoken.encoding_for_model(model_name).encode
63
+ elif model_provider == ModelProvider.ANTHROPIC:
64
+ return Anthropic().tokenizer
65
+ else:
66
+ return None
67
+
68
+ def get_llm(role: LLMRole) -> LLM:
69
+ """Get the LLM for the specified role."""
70
+ model_provider, model_name = _get_llm_params_for_role(role)
71
+
56
72
  if model_provider == ModelProvider.OPENAI:
57
73
  llm = OpenAI(model=model_name, temperature=0)
58
74
  elif model_provider == ModelProvider.ANTHROPIC:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vectara_agentic
3
- Version: 0.1.4
3
+ Version: 0.1.5
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
@@ -37,6 +37,7 @@ Requires-Dist: pylint==3.2.6
37
37
  Requires-Dist: flake8==7.1.0
38
38
  Requires-Dist: pymongo==4.6.1
39
39
  Requires-Dist: python-dotenv==1.0.1
40
+ Requires-Dist: tiktoken==0.7.0
40
41
 
41
42
  # vectara-agentic
42
43
 
@@ -202,7 +203,7 @@ We have created a few example AI assistants that you can look at for inspiration
202
203
  ## 🤝 Contributing
203
204
 
204
205
  Contributions, issues and feature requests are welcome and appreciated!<br />
205
- Feel free to check [issues page](https://github.com/vectara/py-vectara-agentic/issues). You can also take a look at the [contributing guide](https://github.com/vectara/py-vectara-agentic/blob/master/CONTRIBUTING.md).
206
+ Feel free to check [issues page](https://github.com/vectara/py-vectara-agentic/issues). You can also take a look at the [contributing guide](https://github.com/vectara/py-vectara-agentic/blob/main/CONTRIBUTING.md).
206
207
 
207
208
  ## Show your support
208
209
 
@@ -19,3 +19,4 @@ pylint==3.2.6
19
19
  flake8==7.1.0
20
20
  pymongo==4.6.1
21
21
  python-dotenv==1.0.1
22
+ tiktoken==0.7.0
@@ -1,172 +0,0 @@
1
- """
2
- This module contains the Agent class for handling different types of agents and their interactions.
3
- """
4
-
5
- from typing import List, Callable, Optional
6
- import os
7
- from datetime import date
8
-
9
- from retrying import retry
10
-
11
- from llama_index.core.tools import FunctionTool
12
- from llama_index.core.agent import ReActAgent
13
- from llama_index.core.agent.react.formatter import ReActChatFormatter
14
- from llama_index.core.callbacks import CallbackManager
15
- from llama_index.agent.openai import OpenAIAgent
16
- from llama_index.core.memory import ChatMemoryBuffer
17
-
18
- from dotenv import load_dotenv
19
-
20
- from .types import AgentType, AgentStatusType, LLMRole
21
- from .utils import get_llm
22
- from ._prompts import REACT_PROMPT_TEMPLATE, GENERAL_PROMPT_TEMPLATE
23
- from ._callback import AgentCallbackHandler
24
-
25
- load_dotenv(override=True)
26
-
27
-
28
- def get_prompt(prompt_template: str, topic: str, custom_instructions: str):
29
- """
30
- Generate a prompt by replacing placeholders with topic and date.
31
-
32
- Args:
33
-
34
- prompt_template (str): The template for the prompt.
35
- topic (str): The topic to be included in the prompt.
36
-
37
- Returns:
38
- str: The formatted prompt.
39
- """
40
- return (
41
- prompt_template.replace("{chat_topic}", topic)
42
- .replace("{today}", date.today().strftime("%A, %B %d, %Y"))
43
- .replace("{custom_instructions}", custom_instructions)
44
- )
45
-
46
-
47
- def retry_if_exception(exception):
48
- # Define the condition to retry on certain exceptions
49
- return isinstance(
50
- exception, (TimeoutError)
51
- ) # Replace SomeOtherException with other exceptions you want to catch
52
-
53
-
54
- class Agent:
55
- """
56
- Agent class for handling different types of agents and their interactions.
57
- """
58
-
59
- def __init__(
60
- self,
61
- tools: list[FunctionTool],
62
- topic: str = "general",
63
- custom_instructions: str = "",
64
- update_func: Optional[Callable[[AgentStatusType, str], None]] = None,
65
- ):
66
- """
67
- Initialize the agent with the specified type, tools, topic, and system message.
68
-
69
- Args:
70
-
71
- tools (list[FunctionTool]): A list of tools to be used by the agent.
72
- topic (str, optional): The topic for the agent. Defaults to 'general'.
73
- custom_instructions (str, optional): custom instructions for the agent. Defaults to ''.
74
- update_func (Callable): a callback function the code calls on any agent updates.
75
- """
76
- self.agent_type = AgentType(os.getenv("VECTARA_AGENTIC_AGENT_TYPE", "OPENAI"))
77
- self.tools = tools
78
- self.llm = get_llm(LLMRole.MAIN)
79
- self._custom_instructions = custom_instructions
80
- self._topic = topic
81
-
82
- callback_manager = CallbackManager([AgentCallbackHandler(update_func)]) # type: ignore
83
- self.llm.callback_manager = callback_manager
84
-
85
- memory = ChatMemoryBuffer.from_defaults(token_limit=128000)
86
- if self.agent_type == AgentType.REACT:
87
- prompt = get_prompt(REACT_PROMPT_TEMPLATE, topic, custom_instructions)
88
- self.agent = ReActAgent.from_tools(
89
- tools=tools,
90
- llm=self.llm,
91
- memory=memory,
92
- verbose=True,
93
- react_chat_formatter=ReActChatFormatter(system_header=prompt),
94
- max_iterations=20,
95
- callable_manager=callback_manager,
96
- )
97
- elif self.agent_type == AgentType.OPENAI:
98
- prompt = get_prompt(GENERAL_PROMPT_TEMPLATE, topic, custom_instructions)
99
- self.agent = OpenAIAgent.from_tools(
100
- tools=tools,
101
- llm=self.llm,
102
- memory=memory,
103
- verbose=True,
104
- callable_manager=callback_manager,
105
- max_function_calls=10,
106
- system_prompt=prompt,
107
- )
108
- else:
109
- raise ValueError(f"Unknown agent type: {self.agent_type}")
110
-
111
- @classmethod
112
- def from_tools(
113
- cls,
114
- tools: List[FunctionTool],
115
- topic: str = "general",
116
- custom_instructions: str = "",
117
- update_func: Optional[Callable[[AgentStatusType, str], None]] = None,
118
- ) -> "Agent":
119
- """
120
- Create an agent from tools, agent type, and language model.
121
-
122
- Args:
123
-
124
- tools (list[FunctionTool]): A list of tools to be used by the agent.
125
- topic (str, optional): The topic for the agent. Defaults to 'general'.
126
- custom_instructions (str, optional): custom instructions for the agent. Defaults to ''.
127
- llm (LLM): The language model to be used by the agent.
128
-
129
- Returns:
130
- Agent: An instance of the Agent class.
131
- """
132
- return cls(tools, topic, custom_instructions, update_func)
133
-
134
- def report(self) -> str:
135
- """
136
- Get a report from the agent.
137
-
138
- Returns:
139
- str: The report from the agent.
140
- """
141
- print("Vectara agentic Report:")
142
- print(f"Agent Type = {self.agent_type}")
143
- print(f"Topic = {self._topic}")
144
- print("Tools:")
145
- for tool in self.tools:
146
- print(f"- {tool._metadata.name}")
147
- print(f"Agent LLM = {get_llm(LLMRole.MAIN).model}")
148
- print(f"Tool LLM = {get_llm(LLMRole.TOOL).model}")
149
-
150
- @retry(
151
- retry_on_exception=retry_if_exception,
152
- stop_max_attempt_number=3,
153
- wait_fixed=2000,
154
- )
155
- def chat(self, prompt: str) -> str:
156
- """
157
- Interact with the agent using a chat prompt.
158
-
159
- Args:
160
- prompt (str): The chat prompt.
161
-
162
- Returns:
163
- str: The response from the agent.
164
- """
165
-
166
- try:
167
- agent_response = self.agent.chat(prompt)
168
- return agent_response.response
169
- except Exception as e:
170
- import traceback
171
-
172
- return f"Vectara Agentic: encountered an exception ({e}) at ({traceback.format_exc()}), and can't respond."
File without changes