vectara-agentic 0.2.10__tar.gz → 0.2.12__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.
- {vectara_agentic-0.2.10/vectara_agentic.egg-info → vectara_agentic-0.2.12}/PKG-INFO +6 -6
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/README.md +2 -2
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/requirements.txt +3 -3
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/_callback.py +2 -2
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/_prompts.py +7 -6
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/_version.py +1 -1
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/agent.py +1 -1
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/db_tools.py +35 -15
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/sub_query_workflow.py +35 -10
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/tools.py +254 -99
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/utils.py +3 -2
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12/vectara_agentic.egg-info}/PKG-INFO +6 -6
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic.egg-info/requires.txt +3 -3
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/LICENSE +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/MANIFEST.in +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/setup.cfg +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/setup.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/__init__.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/endpoint.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/test_agent.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/test_agent_planning.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/test_agent_type.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/test_fallback.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/test_private_llm.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/test_serialization.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/test_tools.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/tests/test_workflow.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/__init__.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/_observability.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/agent_config.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/agent_endpoint.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/tools_catalog.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic/types.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic.egg-info/SOURCES.txt +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic.egg-info/dependency_links.txt +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vectara_agentic
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
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,17 +16,17 @@ 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.
|
|
19
|
+
Requires-Dist: llama-index==0.12.30
|
|
20
20
|
Requires-Dist: llama-index-indices-managed-vectara==0.4.2
|
|
21
21
|
Requires-Dist: llama-index-agent-llm-compiler==0.3.0
|
|
22
22
|
Requires-Dist: llama-index-agent-lats==0.3.0
|
|
23
23
|
Requires-Dist: llama-index-agent-openai==0.4.6
|
|
24
|
-
Requires-Dist: llama-index-llms-openai==0.3.
|
|
24
|
+
Requires-Dist: llama-index-llms-openai==0.3.35
|
|
25
25
|
Requires-Dist: llama-index-llms-anthropic==0.6.10
|
|
26
26
|
Requires-Dist: llama-index-llms-together==0.3.1
|
|
27
27
|
Requires-Dist: llama-index-llms-groq==0.3.1
|
|
28
28
|
Requires-Dist: llama-index-llms-fireworks==0.3.2
|
|
29
|
-
Requires-Dist: llama-index-llms-cohere==0.4.
|
|
29
|
+
Requires-Dist: llama-index-llms-cohere==0.4.1
|
|
30
30
|
Requires-Dist: llama-index-llms-gemini==0.4.14
|
|
31
31
|
Requires-Dist: llama-index-llms-bedrock==0.3.8
|
|
32
32
|
Requires-Dist: llama-index-tools-yahoo-finance==0.3.0
|
|
@@ -141,7 +141,6 @@ from vectara_agentic.tools import VectaraToolFactory
|
|
|
141
141
|
|
|
142
142
|
vec_factory = VectaraToolFactory(
|
|
143
143
|
vectara_api_key=os.environ['VECTARA_API_KEY'],
|
|
144
|
-
vectara_customer_id=os.environ['VECTARA_CUSTOMER_ID'],
|
|
145
144
|
vectara_corpus_key=os.environ['VECTARA_CORPUS_KEY']
|
|
146
145
|
)
|
|
147
146
|
```
|
|
@@ -162,7 +161,6 @@ tickers = {
|
|
|
162
161
|
}
|
|
163
162
|
|
|
164
163
|
class QueryFinancialReportsArgs(BaseModel):
|
|
165
|
-
query: str = Field(..., description="The user query.")
|
|
166
164
|
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').")
|
|
167
165
|
ticker: str = Field(..., description=f"The company ticker. Must be a valid ticket symbol from the list {tickers.keys()}.")
|
|
168
166
|
|
|
@@ -176,6 +174,8 @@ query_financial_reports_tool = vec_factory.create_rag_tool(
|
|
|
176
174
|
)
|
|
177
175
|
```
|
|
178
176
|
|
|
177
|
+
Note that we only defined the `year` and `ticker` arguments. The `query` argument is automatically added by `vectara-agentic`.
|
|
178
|
+
|
|
179
179
|
See the [docs](https://vectara.github.io/py-vectara-agentic/latest/) for additional arguments to customize your Vectara RAG tool.
|
|
180
180
|
|
|
181
181
|
### 3. Create other tools (optional)
|
|
@@ -73,7 +73,6 @@ from vectara_agentic.tools import VectaraToolFactory
|
|
|
73
73
|
|
|
74
74
|
vec_factory = VectaraToolFactory(
|
|
75
75
|
vectara_api_key=os.environ['VECTARA_API_KEY'],
|
|
76
|
-
vectara_customer_id=os.environ['VECTARA_CUSTOMER_ID'],
|
|
77
76
|
vectara_corpus_key=os.environ['VECTARA_CORPUS_KEY']
|
|
78
77
|
)
|
|
79
78
|
```
|
|
@@ -94,7 +93,6 @@ tickers = {
|
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
class QueryFinancialReportsArgs(BaseModel):
|
|
97
|
-
query: str = Field(..., description="The user query.")
|
|
98
96
|
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').")
|
|
99
97
|
ticker: str = Field(..., description=f"The company ticker. Must be a valid ticket symbol from the list {tickers.keys()}.")
|
|
100
98
|
|
|
@@ -108,6 +106,8 @@ query_financial_reports_tool = vec_factory.create_rag_tool(
|
|
|
108
106
|
)
|
|
109
107
|
```
|
|
110
108
|
|
|
109
|
+
Note that we only defined the `year` and `ticker` arguments. The `query` argument is automatically added by `vectara-agentic`.
|
|
110
|
+
|
|
111
111
|
See the [docs](https://vectara.github.io/py-vectara-agentic/latest/) for additional arguments to customize your Vectara RAG tool.
|
|
112
112
|
|
|
113
113
|
### 3. Create other tools (optional)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
llama-index==0.12.
|
|
1
|
+
llama-index==0.12.30
|
|
2
2
|
llama-index-indices-managed-vectara==0.4.2
|
|
3
3
|
llama-index-agent-llm-compiler==0.3.0
|
|
4
4
|
llama-index-agent-lats==0.3.0
|
|
5
5
|
llama-index-agent-openai==0.4.6
|
|
6
|
-
llama-index-llms-openai==0.3.
|
|
6
|
+
llama-index-llms-openai==0.3.35
|
|
7
7
|
llama-index-llms-anthropic==0.6.10
|
|
8
8
|
llama-index-llms-together==0.3.1
|
|
9
9
|
llama-index-llms-groq==0.3.1
|
|
10
10
|
llama-index-llms-fireworks==0.3.2
|
|
11
|
-
llama-index-llms-cohere==0.4.
|
|
11
|
+
llama-index-llms-cohere==0.4.1
|
|
12
12
|
llama-index-llms-gemini==0.4.14
|
|
13
13
|
llama-index-llms-bedrock==0.3.8
|
|
14
14
|
llama-index-tools-yahoo-finance==0.3.0
|
|
@@ -154,7 +154,7 @@ class AgentCallbackHandler(BaseCallbackHandler):
|
|
|
154
154
|
elif event_type == CBEventType.AGENT_STEP:
|
|
155
155
|
self._handle_agent_step(payload, event_id)
|
|
156
156
|
elif event_type == CBEventType.EXCEPTION:
|
|
157
|
-
print(f"Exception: {payload.get(EventPayload.EXCEPTION)}")
|
|
157
|
+
print(f"Exception in handle_event: {payload.get(EventPayload.EXCEPTION)}")
|
|
158
158
|
else:
|
|
159
159
|
print(f"Unknown event type: {event_type}, payload={payload}")
|
|
160
160
|
|
|
@@ -168,7 +168,7 @@ class AgentCallbackHandler(BaseCallbackHandler):
|
|
|
168
168
|
elif event_type == CBEventType.AGENT_STEP:
|
|
169
169
|
await self._ahandle_agent_step(payload, event_id)
|
|
170
170
|
elif event_type == CBEventType.EXCEPTION:
|
|
171
|
-
print(f"Exception: {payload.get(EventPayload.EXCEPTION)}")
|
|
171
|
+
print(f"Exception in ahandle_event: {payload.get(EventPayload.EXCEPTION)}")
|
|
172
172
|
else:
|
|
173
173
|
print(f"Unknown event type: {event_type}, payload={payload}")
|
|
174
174
|
|
|
@@ -22,8 +22,8 @@ GENERAL_INSTRUCTIONS = """
|
|
|
22
22
|
3) If a tool fails, try other tools that might be appropriate to gain the information you need.
|
|
23
23
|
- If after retrying you can't get the information or answer the question, respond with "I don't know".
|
|
24
24
|
- If a tool provides citations or references in markdown as part of its response, include the references in your response.
|
|
25
|
-
- Ensure that every URL in your
|
|
26
|
-
Avoid using generic terms like “source” or “reference
|
|
25
|
+
- Ensure that every URL in your response includes descriptive anchor text that clearly explains what the user can expect from the linked content.
|
|
26
|
+
Avoid using generic terms like “source” or “reference”, or the full URL, as the anchor text.
|
|
27
27
|
- If a tool returns in the metadata a valid URL pointing to a PDF file, along with page number - then combine the URL and page number in the response.
|
|
28
28
|
For example, if the URL returned from the tool is "https://example.com/doc.pdf" and "page=5", then the combined URL would be "https://example.com/doc.pdf#page=5".
|
|
29
29
|
If a tool returns in the metadata invalid URLs or an URL empty (e.g. "[[1]()]"), ignore it and do not include that citation or reference in your response.
|
|
@@ -37,10 +37,11 @@ GENERAL_INSTRUCTIONS = """
|
|
|
37
37
|
- If including latex equations in the markdown response, make sure the equations are on a separate line and enclosed in double dollar signs.
|
|
38
38
|
- Always respond in the language of the question, and in text (no images, videos or code).
|
|
39
39
|
- If you are provided with database tools use them for analytical queries (such as counting, calculating max, min, average, sum, or other statistics).
|
|
40
|
-
For each database, the database tools include: x_list_tables, x_load_data, x_describe_tables, and x_load_sample_data, where 'x' in the database name.
|
|
41
|
-
|
|
42
|
-
Before using the x_load_data with a SQL query, always follow these steps:
|
|
43
|
-
-
|
|
40
|
+
For each database, the database tools include: x_list_tables, x_load_data, x_describe_tables, x_load_unique_values, and x_load_sample_data, where 'x' in the database name.
|
|
41
|
+
for example, if the database name is "ev", the tools are: ev_list_tables, ev_load_data, ev_describe_tables, ev_load_unique_values, and ev_load_sample_data.
|
|
42
|
+
Before using the x_load_data with a SQL query, always follow these discovery steps:
|
|
43
|
+
- call the x_list_tables tool to list of available tables in the x database.
|
|
44
|
+
- Call the x_describe_tables tool to understand the schema of each table you want to query data from.
|
|
44
45
|
- Use the x_load_unique_values tool to understand the unique values in each column.
|
|
45
46
|
Sometimes the user may ask for a specific column value, but the actual value in the table may be different, and you will need to use the correct value.
|
|
46
47
|
- Use the x_load_sample_data tool to understand the column names, and typical values in each column.
|
|
@@ -661,7 +661,7 @@ class Agent:
|
|
|
661
661
|
for tool in self.tools:
|
|
662
662
|
if hasattr(tool, 'metadata'):
|
|
663
663
|
if detailed:
|
|
664
|
-
print(f"- {tool.metadata.
|
|
664
|
+
print(f"- {tool.metadata.description}")
|
|
665
665
|
else:
|
|
666
666
|
print(f"- {tool.metadata.name}")
|
|
667
667
|
else:
|
|
@@ -15,7 +15,6 @@ from sqlalchemy.engine import Engine
|
|
|
15
15
|
from sqlalchemy.exc import NoSuchTableError
|
|
16
16
|
from sqlalchemy.schema import CreateTable
|
|
17
17
|
|
|
18
|
-
from llama_index.core.readers.base import BaseReader
|
|
19
18
|
from llama_index.core.utilities.sql_wrapper import SQLDatabase
|
|
20
19
|
from llama_index.core.schema import Document
|
|
21
20
|
from llama_index.core.tools.function_tool import FunctionTool
|
|
@@ -25,7 +24,7 @@ from llama_index.core.tools.utils import create_schema_from_function
|
|
|
25
24
|
|
|
26
25
|
AsyncCallable = Callable[..., Awaitable[Any]]
|
|
27
26
|
|
|
28
|
-
class DatabaseTools
|
|
27
|
+
class DatabaseTools:
|
|
29
28
|
"""Database tools for vectara-agentic
|
|
30
29
|
This class provides a set of tools to interact with a database.
|
|
31
30
|
It allows you to load data, list tables, describe tables, and load unique values.
|
|
@@ -49,9 +48,11 @@ class DatabaseTools(BaseReader):
|
|
|
49
48
|
user: Optional[str] = None,
|
|
50
49
|
password: Optional[str] = None,
|
|
51
50
|
dbname: Optional[str] = None,
|
|
51
|
+
tool_name_prefix: str = "db",
|
|
52
52
|
**kwargs: Any,
|
|
53
53
|
) -> None:
|
|
54
54
|
self.max_rows = max_rows
|
|
55
|
+
self.tool_name_prefix = tool_name_prefix
|
|
55
56
|
|
|
56
57
|
if sql_database:
|
|
57
58
|
self.sql_database = sql_database
|
|
@@ -86,39 +87,38 @@ class DatabaseTools(BaseReader):
|
|
|
86
87
|
func = getattr(self, fn_name)
|
|
87
88
|
except AttributeError:
|
|
88
89
|
return None
|
|
89
|
-
name = fn_name
|
|
90
|
+
name = self.tool_name_prefix + "_" + fn_name if self.tool_name_prefix else fn_name
|
|
90
91
|
docstring = func.__doc__ or ""
|
|
91
92
|
description = f"{name}{signature(func)}\n{docstring}"
|
|
92
93
|
fn_schema = create_schema_from_function(fn_name, getattr(self, fn_name))
|
|
93
94
|
return ToolMetadata(name=name, description=description, fn_schema=fn_schema)
|
|
94
95
|
|
|
95
|
-
def _load_data(self,
|
|
96
|
+
def _load_data(self, sql_query: str) -> List[Document]:
|
|
96
97
|
documents = []
|
|
97
98
|
with self.sql_database.engine.connect() as connection:
|
|
98
|
-
if
|
|
99
|
+
if sql_query is None:
|
|
99
100
|
raise ValueError("A query parameter is necessary to filter the data")
|
|
100
|
-
result = connection.execute(text(
|
|
101
|
+
result = connection.execute(text(sql_query))
|
|
101
102
|
for item in result.fetchall():
|
|
102
103
|
doc_str = ", ".join([str(entry) for entry in item])
|
|
103
104
|
documents.append(Document(text=doc_str))
|
|
104
105
|
return documents
|
|
105
106
|
|
|
106
|
-
def load_data(self,
|
|
107
|
+
def load_data(self, sql_query: str) -> List[str]:
|
|
107
108
|
"""Query and load data from the Database, returning a list of Documents.
|
|
108
109
|
Args:
|
|
109
|
-
|
|
110
|
+
sql_query (str): an SQL query to filter tables and rows.
|
|
110
111
|
Returns:
|
|
111
|
-
List[
|
|
112
|
+
List[str]: a list of Document objects from the database.
|
|
112
113
|
"""
|
|
113
|
-
|
|
114
|
-
if query is None:
|
|
114
|
+
if sql_query is None:
|
|
115
115
|
raise ValueError("A query parameter is necessary to filter the data")
|
|
116
116
|
|
|
117
|
-
count_query = f"SELECT COUNT(*) FROM ({
|
|
117
|
+
count_query = f"SELECT COUNT(*) FROM ({sql_query})"
|
|
118
118
|
try:
|
|
119
119
|
count_rows = self._load_data(count_query)
|
|
120
120
|
except Exception as e:
|
|
121
|
-
return [f"Error ({str(e)}) occurred while counting number of rows"]
|
|
121
|
+
return [f"Error ({str(e)}) occurred while counting number of rows, check your query."]
|
|
122
122
|
num_rows = int(count_rows[0].text)
|
|
123
123
|
if num_rows > self.max_rows:
|
|
124
124
|
return [
|
|
@@ -126,9 +126,9 @@ class DatabaseTools(BaseReader):
|
|
|
126
126
|
"Please refactor your query to make it return less rows. "
|
|
127
127
|
]
|
|
128
128
|
try:
|
|
129
|
-
res = self._load_data(
|
|
129
|
+
res = self._load_data(sql_query)
|
|
130
130
|
except Exception as e:
|
|
131
|
-
return [f"Error ({str(e)}) occurred while executing the query {query
|
|
131
|
+
return [f"Error ({str(e)}) occurred while executing the query {sql_query}, check your query."]
|
|
132
132
|
return [d.text for d in res]
|
|
133
133
|
|
|
134
134
|
def load_sample_data(self, table_name: str, num_rows: int = 25) -> Any:
|
|
@@ -141,6 +141,11 @@ class DatabaseTools(BaseReader):
|
|
|
141
141
|
Returns:
|
|
142
142
|
Any: The result of the database query.
|
|
143
143
|
"""
|
|
144
|
+
if table_name not in self.list_tables():
|
|
145
|
+
return (
|
|
146
|
+
f"Table {table_name} does not exist in the database."
|
|
147
|
+
f"Valid table names are: {self.list_tables()}"
|
|
148
|
+
)
|
|
144
149
|
try:
|
|
145
150
|
res = self._load_data(f"SELECT * FROM {table_name} LIMIT {num_rows}")
|
|
146
151
|
except Exception as e:
|
|
@@ -162,6 +167,15 @@ class DatabaseTools(BaseReader):
|
|
|
162
167
|
str: A string representation of the table schemas.
|
|
163
168
|
"""
|
|
164
169
|
table_names = tables or [table.name for table in self._metadata.sorted_tables]
|
|
170
|
+
if len(table_names) == 0:
|
|
171
|
+
return "You must specify at least one table name to describe."
|
|
172
|
+
for table_name in table_names:
|
|
173
|
+
if table_name not in self.list_tables():
|
|
174
|
+
return (
|
|
175
|
+
f"Table {table_name} does not exist in the database."
|
|
176
|
+
f"Valid table names are: {self.list_tables()}"
|
|
177
|
+
)
|
|
178
|
+
|
|
165
179
|
table_schemas = []
|
|
166
180
|
for table_name in table_names:
|
|
167
181
|
table = next(
|
|
@@ -186,6 +200,12 @@ class DatabaseTools(BaseReader):
|
|
|
186
200
|
Returns:
|
|
187
201
|
Any: the result of the database query
|
|
188
202
|
"""
|
|
203
|
+
if table_name not in self.list_tables():
|
|
204
|
+
return (
|
|
205
|
+
f"Table {table_name} does not exist in the database."
|
|
206
|
+
f"Valid table names are: {self.list_tables()}"
|
|
207
|
+
)
|
|
208
|
+
|
|
189
209
|
res = {}
|
|
190
210
|
try:
|
|
191
211
|
for column in columns:
|
|
@@ -3,6 +3,7 @@ This module contains the SubQuestionQueryEngine workflow, which is a workflow
|
|
|
3
3
|
that takes a user question and a list of tools, and outputs a list of sub-questions.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import re
|
|
6
7
|
import json
|
|
7
8
|
from pydantic import BaseModel
|
|
8
9
|
|
|
@@ -109,17 +110,27 @@ class SubQuestionQueryWorkflow(Workflow):
|
|
|
109
110
|
if await ctx.get("verbose"):
|
|
110
111
|
print(f"Sub-questions are {response}")
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
response_str = str(response)
|
|
114
|
+
if not response_str:
|
|
113
115
|
raise ValueError(
|
|
114
116
|
f"No response from LLM when generating sub-questions for query {original_query}"
|
|
115
117
|
)
|
|
116
|
-
|
|
117
118
|
try:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
data = json.loads(response_str)
|
|
120
|
+
except json.JSONDecodeError as e1:
|
|
121
|
+
match = re.search(r"\{.*\}", response_str, re.DOTALL)
|
|
122
|
+
if not match:
|
|
123
|
+
raise ValueError(f"Invalid LLM response format: {response_str}") from e1
|
|
124
|
+
try:
|
|
125
|
+
data = json.loads(match.group(0))
|
|
126
|
+
except json.JSONDecodeError as e2:
|
|
127
|
+
raise ValueError(f"Invalid LLM response format: {response_str}") from e2
|
|
128
|
+
|
|
129
|
+
sub_questions = data.get("sub_questions")
|
|
130
|
+
if sub_questions is None:
|
|
131
|
+
raise ValueError(f"Invalid LLM response format: {response_str}")
|
|
132
|
+
if not sub_questions:
|
|
133
|
+
raise ValueError("LLM returned empty sub-questions list")
|
|
123
134
|
|
|
124
135
|
await ctx.set("sub_question_count", len(sub_questions))
|
|
125
136
|
for question in sub_questions:
|
|
@@ -273,8 +284,23 @@ class SequentialSubQuestionsWorkflow(Workflow):
|
|
|
273
284
|
if not str(response):
|
|
274
285
|
raise ValueError(f"No response from LLM for query {original_query}")
|
|
275
286
|
|
|
276
|
-
|
|
277
|
-
|
|
287
|
+
response_str = str(response)
|
|
288
|
+
try:
|
|
289
|
+
response_obj = json.loads(response_str)
|
|
290
|
+
except json.JSONDecodeError as e1:
|
|
291
|
+
match = re.search(r"\{.*\}", response_str, re.DOTALL)
|
|
292
|
+
if not match:
|
|
293
|
+
raise ValueError(
|
|
294
|
+
f"Failed to extract JSON object with subquestions from LLM response: {response_str}"
|
|
295
|
+
) from e1
|
|
296
|
+
try:
|
|
297
|
+
response_obj = json.loads(match.group(0))
|
|
298
|
+
except json.JSONDecodeError as e2:
|
|
299
|
+
raise ValueError(
|
|
300
|
+
f"Failed to extract JSON object with subquestions from LLM response: {response_str}"
|
|
301
|
+
) from e2
|
|
302
|
+
|
|
303
|
+
sub_questions = response_obj.get("sub_questions")
|
|
278
304
|
|
|
279
305
|
await ctx.set("sub_questions", sub_questions)
|
|
280
306
|
if await ctx.get("verbose"):
|
|
@@ -305,7 +331,6 @@ class SequentialSubQuestionsWorkflow(Workflow):
|
|
|
305
331
|
if await ctx.get("verbose"):
|
|
306
332
|
print(f"Answer is {response}")
|
|
307
333
|
|
|
308
|
-
sub_questions = await ctx.get("sub_questions")
|
|
309
334
|
if ev.num + 1 < len(sub_questions):
|
|
310
335
|
return self.QueryEvent(
|
|
311
336
|
question=sub_questions[ev.num + 1],
|
|
@@ -55,13 +55,15 @@ LI_packages = {
|
|
|
55
55
|
"send_message": ToolType.ACTION,
|
|
56
56
|
"fetch_channel": ToolType.QUERY,
|
|
57
57
|
}
|
|
58
|
-
}
|
|
58
|
+
},
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
|
|
61
62
|
class VectaraToolMetadata(ToolMetadata):
|
|
62
63
|
"""
|
|
63
64
|
A subclass of ToolMetadata adding the tool_type attribute.
|
|
64
65
|
"""
|
|
66
|
+
|
|
65
67
|
tool_type: ToolType
|
|
66
68
|
|
|
67
69
|
def __init__(self, tool_type: ToolType, **kwargs):
|
|
@@ -88,7 +90,9 @@ class VectaraTool(FunctionTool):
|
|
|
88
90
|
fn: Optional[Callable[..., Any]] = None,
|
|
89
91
|
async_fn: Optional[AsyncCallable] = None,
|
|
90
92
|
) -> None:
|
|
91
|
-
metadata_dict =
|
|
93
|
+
metadata_dict = (
|
|
94
|
+
metadata.dict() if hasattr(metadata, "dict") else metadata.__dict__
|
|
95
|
+
)
|
|
92
96
|
vm = VectaraToolMetadata(tool_type=tool_type, **metadata_dict)
|
|
93
97
|
super().__init__(fn, vm, async_fn)
|
|
94
98
|
|
|
@@ -107,19 +111,26 @@ class VectaraTool(FunctionTool):
|
|
|
107
111
|
tool_type: ToolType = ToolType.QUERY,
|
|
108
112
|
) -> "VectaraTool":
|
|
109
113
|
tool = FunctionTool.from_defaults(
|
|
110
|
-
fn,
|
|
111
|
-
|
|
114
|
+
fn,
|
|
115
|
+
name,
|
|
116
|
+
description,
|
|
117
|
+
return_direct,
|
|
118
|
+
fn_schema,
|
|
119
|
+
async_fn,
|
|
120
|
+
tool_metadata,
|
|
121
|
+
callback,
|
|
122
|
+
async_callback,
|
|
112
123
|
)
|
|
113
124
|
vectara_tool = cls(
|
|
114
|
-
tool_type=tool_type,
|
|
125
|
+
tool_type=tool_type,
|
|
126
|
+
fn=tool.fn,
|
|
127
|
+
metadata=tool.metadata,
|
|
128
|
+
async_fn=tool.async_fn,
|
|
115
129
|
)
|
|
116
130
|
return vectara_tool
|
|
117
131
|
|
|
118
132
|
def __str__(self) -> str:
|
|
119
|
-
return (
|
|
120
|
-
f"Tool(name={self.metadata.name}, "
|
|
121
|
-
f"Tool metadata={self.metadata})"
|
|
122
|
-
)
|
|
133
|
+
return f"Tool(name={self.metadata.name}, " f"Tool metadata={self.metadata})"
|
|
123
134
|
|
|
124
135
|
def __repr__(self) -> str:
|
|
125
136
|
return str(self)
|
|
@@ -137,19 +148,20 @@ class VectaraTool(FunctionTool):
|
|
|
137
148
|
# If schema is a dict-like object, compare the dict representation
|
|
138
149
|
try:
|
|
139
150
|
# Try to get schema as dict if possible
|
|
140
|
-
if hasattr(self.metadata.fn_schema,
|
|
151
|
+
if hasattr(self.metadata.fn_schema, "schema"):
|
|
141
152
|
self_schema = self.metadata.fn_schema.schema
|
|
142
153
|
other_schema = other.metadata.fn_schema.schema
|
|
143
154
|
|
|
144
155
|
# Compare only properties and required fields
|
|
145
|
-
self_props = self_schema.get(
|
|
146
|
-
other_props = other_schema.get(
|
|
156
|
+
self_props = self_schema.get("properties", {})
|
|
157
|
+
other_props = other_schema.get("properties", {})
|
|
147
158
|
|
|
148
|
-
self_required = self_schema.get(
|
|
149
|
-
other_required = other_schema.get(
|
|
159
|
+
self_required = self_schema.get("required", [])
|
|
160
|
+
other_required = other_schema.get("required", [])
|
|
150
161
|
|
|
151
|
-
return
|
|
152
|
-
|
|
162
|
+
return self_props.keys() == other_props.keys() and set(
|
|
163
|
+
self_required
|
|
164
|
+
) == set(other_required)
|
|
153
165
|
except Exception:
|
|
154
166
|
# If any exception occurs during schema comparison, fall back to name comparison
|
|
155
167
|
pass
|
|
@@ -161,6 +173,21 @@ class VectaraTool(FunctionTool):
|
|
|
161
173
|
) -> ToolOutput:
|
|
162
174
|
try:
|
|
163
175
|
return super().call(*args, ctx=ctx, **kwargs)
|
|
176
|
+
except TypeError as e:
|
|
177
|
+
sig = inspect.signature(self.metadata.fn_schema)
|
|
178
|
+
valid_parameters = list(sig.parameters.keys())
|
|
179
|
+
params_str = ", ".join(valid_parameters)
|
|
180
|
+
|
|
181
|
+
err_output = ToolOutput(
|
|
182
|
+
tool_name=self.metadata.name,
|
|
183
|
+
content=(
|
|
184
|
+
f"Wrong argument used when calling {self.metadata.name}: {str(e)}. "
|
|
185
|
+
f"Valid arguments: {params_str}. please call the tool again with the correct arguments."
|
|
186
|
+
),
|
|
187
|
+
raw_input={"args": args, "kwargs": kwargs},
|
|
188
|
+
raw_output={"response": str(e)},
|
|
189
|
+
)
|
|
190
|
+
return err_output
|
|
164
191
|
except Exception as e:
|
|
165
192
|
err_output = ToolOutput(
|
|
166
193
|
tool_name=self.metadata.name,
|
|
@@ -175,6 +202,21 @@ class VectaraTool(FunctionTool):
|
|
|
175
202
|
) -> ToolOutput:
|
|
176
203
|
try:
|
|
177
204
|
return await super().acall(*args, ctx=ctx, **kwargs)
|
|
205
|
+
except TypeError as e:
|
|
206
|
+
sig = inspect.signature(self.metadata.fn_schema)
|
|
207
|
+
valid_parameters = list(sig.parameters.keys())
|
|
208
|
+
params_str = ", ".join(valid_parameters)
|
|
209
|
+
|
|
210
|
+
err_output = ToolOutput(
|
|
211
|
+
tool_name=self.metadata.name,
|
|
212
|
+
content=(
|
|
213
|
+
f"Wrong argument used when calling {self.metadata.name}: {str(e)}. "
|
|
214
|
+
f"Valid arguments: {params_str}. please call the tool again with the correct arguments."
|
|
215
|
+
),
|
|
216
|
+
raw_input={"args": args, "kwargs": kwargs},
|
|
217
|
+
raw_output={"response": str(e)},
|
|
218
|
+
)
|
|
219
|
+
return err_output
|
|
178
220
|
except Exception as e:
|
|
179
221
|
err_output = ToolOutput(
|
|
180
222
|
tool_name=self.metadata.name,
|
|
@@ -184,66 +226,129 @@ class VectaraTool(FunctionTool):
|
|
|
184
226
|
)
|
|
185
227
|
return err_output
|
|
186
228
|
|
|
229
|
+
|
|
187
230
|
def _create_tool_from_dynamic_function(
|
|
188
231
|
function: Callable[..., ToolOutput],
|
|
189
232
|
tool_name: str,
|
|
190
233
|
tool_description: str,
|
|
191
|
-
|
|
192
|
-
tool_args_schema:
|
|
234
|
+
base_params_model: Type[BaseModel], # Now a Pydantic BaseModel
|
|
235
|
+
tool_args_schema: Type[BaseModel],
|
|
193
236
|
) -> VectaraTool:
|
|
194
|
-
"""
|
|
195
|
-
Create a VectaraTool from a dynamic function, including
|
|
196
|
-
setting the function signature and creating the tool schema.
|
|
197
|
-
"""
|
|
198
237
|
fields = {}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
238
|
+
base_params = []
|
|
239
|
+
|
|
240
|
+
# Create inspect.Parameter objects for base_params_model fields.
|
|
241
|
+
for param_name, model_field in base_params_model.model_fields.items():
|
|
242
|
+
field_type = base_params_model.__annotations__.get(
|
|
243
|
+
param_name, str
|
|
244
|
+
) # default to str if not found
|
|
245
|
+
default_value = (
|
|
246
|
+
model_field.default
|
|
247
|
+
if model_field.default is not None
|
|
248
|
+
else inspect.Parameter.empty
|
|
249
|
+
)
|
|
250
|
+
base_params.append(
|
|
251
|
+
inspect.Parameter(
|
|
252
|
+
param_name,
|
|
253
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
254
|
+
default=default_value,
|
|
255
|
+
annotation=field_type,
|
|
256
|
+
)
|
|
257
|
+
)
|
|
258
|
+
fields[param_name] = (
|
|
259
|
+
field_type,
|
|
260
|
+
model_field.default if model_field.default is not None else ...,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
# Add tool_args_schema fields to the fields dict if not already included.
|
|
264
|
+
# Also add them to the function signature by creating new inspect.Parameter objects.
|
|
202
265
|
for field_name, field_info in tool_args_schema.model_fields.items():
|
|
203
266
|
if field_name not in fields:
|
|
204
|
-
default_value =
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
267
|
+
default_value = (
|
|
268
|
+
field_info.default if field_info.default is not None else ...
|
|
269
|
+
)
|
|
270
|
+
field_type = tool_args_schema.__annotations__.get(field_name, None)
|
|
271
|
+
fields[field_name] = (field_type, default_value)
|
|
272
|
+
# Append these fields to the signature.
|
|
273
|
+
base_params.append(
|
|
274
|
+
inspect.Parameter(
|
|
275
|
+
field_name,
|
|
276
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
277
|
+
default=(
|
|
278
|
+
default_value
|
|
279
|
+
if default_value is not ...
|
|
280
|
+
else inspect.Parameter.empty
|
|
281
|
+
),
|
|
282
|
+
annotation=field_type,
|
|
283
|
+
)
|
|
284
|
+
)
|
|
219
285
|
|
|
286
|
+
# Create the dynamic schema with both base_params_model and tool_args_schema fields.
|
|
287
|
+
fn_schema = create_model(f"{tool_name}_schema", **fields)
|
|
288
|
+
|
|
289
|
+
# Combine parameters into a function signature.
|
|
290
|
+
all_params = base_params[:] # Now all_params contains parameters from both models.
|
|
220
291
|
required_params = [p for p in all_params if p.default is inspect.Parameter.empty]
|
|
221
|
-
optional_params = [
|
|
222
|
-
|
|
223
|
-
|
|
292
|
+
optional_params = [
|
|
293
|
+
p for p in all_params if p.default is not inspect.Parameter.empty
|
|
294
|
+
]
|
|
295
|
+
function.__signature__ = inspect.Signature(required_params + optional_params)
|
|
224
296
|
function.__annotations__["return"] = dict[str, Any]
|
|
225
|
-
function.__name__ =
|
|
297
|
+
function.__name__ = re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
|
|
226
298
|
|
|
227
|
-
#
|
|
228
|
-
|
|
299
|
+
# Build a docstring using parameter descriptions from the BaseModels.
|
|
300
|
+
params_str = ",\n ".join(
|
|
301
|
+
f"{p.name}: {p.annotation.__name__ if hasattr(p.annotation, '__name__') else p.annotation}"
|
|
302
|
+
for p in all_params
|
|
303
|
+
)
|
|
304
|
+
signature_line = f"{tool_name}(\n {params_str}\n) -> dict[str, Any]"
|
|
305
|
+
doc_lines = [
|
|
306
|
+
signature_line,
|
|
307
|
+
"",
|
|
308
|
+
tool_description.strip(),
|
|
309
|
+
"",
|
|
310
|
+
"Args:",
|
|
311
|
+
]
|
|
229
312
|
for param in all_params:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
313
|
+
description = ""
|
|
314
|
+
if param.name in base_params_model.model_fields:
|
|
315
|
+
description = base_params_model.model_fields[param.name].description
|
|
316
|
+
elif param.name in tool_args_schema.model_fields:
|
|
317
|
+
description = tool_args_schema.model_fields[param.name].description
|
|
318
|
+
if not description:
|
|
319
|
+
description = "No description provided."
|
|
320
|
+
type_name = (
|
|
321
|
+
param.annotation.__name__
|
|
322
|
+
if hasattr(param.annotation, "__name__")
|
|
323
|
+
else str(param.annotation)
|
|
324
|
+
)
|
|
325
|
+
default_text = (
|
|
326
|
+
f", default={param.default!r}"
|
|
327
|
+
if param.default is not inspect.Parameter.empty
|
|
328
|
+
else ""
|
|
329
|
+
)
|
|
330
|
+
doc_lines.append(f" {param.name} ({type_name}){default_text}: {description}")
|
|
331
|
+
doc_lines.append("")
|
|
332
|
+
doc_lines.append("Returns:")
|
|
333
|
+
return_desc = getattr(
|
|
334
|
+
function, "__return_description__", "A dictionary containing the result data."
|
|
335
|
+
)
|
|
336
|
+
doc_lines.append(f" dict[str, Any]: {return_desc}")
|
|
337
|
+
function.__doc__ = "\n".join(doc_lines)
|
|
235
338
|
|
|
236
|
-
# Create the tool
|
|
237
339
|
tool = VectaraTool.from_defaults(
|
|
238
340
|
fn=function,
|
|
239
341
|
name=tool_name,
|
|
240
|
-
description=
|
|
342
|
+
description=function.__doc__,
|
|
241
343
|
fn_schema=fn_schema,
|
|
242
344
|
tool_type=ToolType.QUERY,
|
|
243
345
|
)
|
|
244
346
|
return tool
|
|
245
347
|
|
|
246
|
-
|
|
348
|
+
|
|
349
|
+
def _build_filter_string(
|
|
350
|
+
kwargs: Dict[str, Any], tool_args_type: Dict[str, dict], fixed_filter: str
|
|
351
|
+
) -> str:
|
|
247
352
|
"""
|
|
248
353
|
Build filter string for Vectara from kwargs
|
|
249
354
|
"""
|
|
@@ -257,9 +362,9 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
257
362
|
|
|
258
363
|
# Determine the prefix for the key. Valid values are "doc" or "part"
|
|
259
364
|
# default to 'doc' if not specified
|
|
260
|
-
tool_args_dict = tool_args_type.get(key, {
|
|
365
|
+
tool_args_dict = tool_args_type.get(key, {"type": "doc", "is_list": False})
|
|
261
366
|
prefix = tool_args_dict.get(key, "doc")
|
|
262
|
-
is_list = tool_args_dict.get(
|
|
367
|
+
is_list = tool_args_dict.get("is_list", False)
|
|
263
368
|
|
|
264
369
|
if prefix not in ["doc", "part"]:
|
|
265
370
|
raise ValueError(
|
|
@@ -312,12 +417,10 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
312
417
|
range_conditions.append(f"{prefix}.{key} {operator} {end_val}")
|
|
313
418
|
|
|
314
419
|
# Join the range conditions with AND
|
|
315
|
-
filter_parts.append(
|
|
420
|
+
filter_parts.append("( " + " AND ".join(range_conditions) + " )")
|
|
316
421
|
continue
|
|
317
422
|
|
|
318
|
-
raise ValueError(
|
|
319
|
-
f"Range operator requires two values for {key}: {value}"
|
|
320
|
-
)
|
|
423
|
+
raise ValueError(f"Range operator requires two values for {key}: {value}")
|
|
321
424
|
|
|
322
425
|
# Check if value contains a known comparison operator at the start
|
|
323
426
|
matched_operator = None
|
|
@@ -329,7 +432,7 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
329
432
|
# Break down operator from value
|
|
330
433
|
# e.g. val_str = ">2022" --> operator = ">", rhs = "2022"
|
|
331
434
|
if matched_operator:
|
|
332
|
-
rhs = val_str[len(matched_operator):].strip()
|
|
435
|
+
rhs = val_str[len(matched_operator) :].strip()
|
|
333
436
|
|
|
334
437
|
if matched_operator in numeric_only_ops:
|
|
335
438
|
# Must be numeric
|
|
@@ -343,7 +446,9 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
343
446
|
if rhs.isdigit() or is_float(rhs):
|
|
344
447
|
filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs}")
|
|
345
448
|
elif rhs.lower() in ["true", "false"]:
|
|
346
|
-
filter_parts.append(
|
|
449
|
+
filter_parts.append(
|
|
450
|
+
f"{prefix}.{key}{matched_operator}{rhs.lower()}"
|
|
451
|
+
)
|
|
347
452
|
else:
|
|
348
453
|
# For string operands, wrap them in quotes
|
|
349
454
|
filter_parts.append(f"{prefix}.{key}{matched_operator}'{rhs}'")
|
|
@@ -373,6 +478,7 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
373
478
|
else:
|
|
374
479
|
return fixed_filter or filter_str
|
|
375
480
|
|
|
481
|
+
|
|
376
482
|
class VectaraToolFactory:
|
|
377
483
|
"""
|
|
378
484
|
A factory class for creating Vectara RAG tools.
|
|
@@ -480,7 +586,9 @@ class VectaraToolFactory:
|
|
|
480
586
|
top_k = kwargs.pop("top_k", 10)
|
|
481
587
|
summarize = kwargs.pop("summarize", True)
|
|
482
588
|
try:
|
|
483
|
-
filter_string = _build_filter_string(
|
|
589
|
+
filter_string = _build_filter_string(
|
|
590
|
+
kwargs, tool_args_type, fixed_filter
|
|
591
|
+
)
|
|
484
592
|
except ValueError as e:
|
|
485
593
|
return ToolOutput(
|
|
486
594
|
tool_name=search_function.__name__,
|
|
@@ -493,7 +601,11 @@ class VectaraToolFactory:
|
|
|
493
601
|
summary_enabled=False,
|
|
494
602
|
similarity_top_k=top_k,
|
|
495
603
|
reranker=reranker,
|
|
496
|
-
rerank_k=
|
|
604
|
+
rerank_k=(
|
|
605
|
+
rerank_k
|
|
606
|
+
if rerank_k * self.num_corpora <= 100
|
|
607
|
+
else int(100 / self.num_corpora)
|
|
608
|
+
),
|
|
497
609
|
rerank_limit=rerank_limit,
|
|
498
610
|
rerank_cutoff=rerank_cutoff,
|
|
499
611
|
mmr_diversity_bias=mmr_diversity_bias,
|
|
@@ -531,9 +643,7 @@ class VectaraToolFactory:
|
|
|
531
643
|
if summarize:
|
|
532
644
|
summaries_dict = asyncio.run(
|
|
533
645
|
summarize_documents(
|
|
534
|
-
self.vectara_corpus_key,
|
|
535
|
-
self.vectara_api_key,
|
|
536
|
-
list(unique_ids)
|
|
646
|
+
self.vectara_corpus_key, self.vectara_api_key, list(unique_ids)
|
|
537
647
|
)
|
|
538
648
|
)
|
|
539
649
|
for doc_id, metadata in docs:
|
|
@@ -541,7 +651,9 @@ class VectaraToolFactory:
|
|
|
541
651
|
tool_output += f"document_id: '{doc_id}'\nmetadata: '{metadata}'\nsummary: '{summary}'\n\n"
|
|
542
652
|
else:
|
|
543
653
|
for doc_id, metadata in docs:
|
|
544
|
-
tool_output +=
|
|
654
|
+
tool_output += (
|
|
655
|
+
f"document_id: '{doc_id}'\nmetadata: '{metadata}'\n\n"
|
|
656
|
+
)
|
|
545
657
|
|
|
546
658
|
out = ToolOutput(
|
|
547
659
|
tool_name=search_function.__name__,
|
|
@@ -551,23 +663,31 @@ class VectaraToolFactory:
|
|
|
551
663
|
)
|
|
552
664
|
return out
|
|
553
665
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
666
|
+
class SearchToolBaseParams(BaseModel):
|
|
667
|
+
"""Model for the base parameters of the search tool."""
|
|
668
|
+
query: str = Field(
|
|
669
|
+
...,
|
|
670
|
+
description="The search query to perform, always in the form of a question.",
|
|
671
|
+
)
|
|
672
|
+
top_k: int = Field(
|
|
673
|
+
10, description="The number of top documents to retrieve."
|
|
674
|
+
)
|
|
675
|
+
summarize: bool = Field(
|
|
676
|
+
True,
|
|
677
|
+
description="Flag that indicates whether to summarize the retrieved documents.",
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
search_tool_extra_desc = (
|
|
681
|
+
tool_description
|
|
682
|
+
+ "\n"
|
|
683
|
+
+ "This tool is meant to perform a search for relevant documents, it is not meant for asking questions."
|
|
684
|
+
)
|
|
565
685
|
|
|
566
686
|
tool = _create_tool_from_dynamic_function(
|
|
567
687
|
search_function,
|
|
568
688
|
tool_name,
|
|
569
689
|
search_tool_extra_desc,
|
|
570
|
-
|
|
690
|
+
SearchToolBaseParams,
|
|
571
691
|
tool_args_schema,
|
|
572
692
|
)
|
|
573
693
|
return tool
|
|
@@ -684,7 +804,9 @@ class VectaraToolFactory:
|
|
|
684
804
|
|
|
685
805
|
query = kwargs.pop("query")
|
|
686
806
|
try:
|
|
687
|
-
filter_string = _build_filter_string(
|
|
807
|
+
filter_string = _build_filter_string(
|
|
808
|
+
kwargs, tool_args_type, fixed_filter
|
|
809
|
+
)
|
|
688
810
|
except ValueError as e:
|
|
689
811
|
return ToolOutput(
|
|
690
812
|
tool_name=rag_function.__name__,
|
|
@@ -701,7 +823,11 @@ class VectaraToolFactory:
|
|
|
701
823
|
summary_prompt_name=vectara_summarizer,
|
|
702
824
|
prompt_text=vectara_prompt_text,
|
|
703
825
|
reranker=reranker,
|
|
704
|
-
rerank_k=
|
|
826
|
+
rerank_k=(
|
|
827
|
+
rerank_k
|
|
828
|
+
if rerank_k * self.num_corpora <= 100
|
|
829
|
+
else int(100 / self.num_corpora)
|
|
830
|
+
),
|
|
705
831
|
rerank_limit=rerank_limit,
|
|
706
832
|
rerank_cutoff=rerank_cutoff,
|
|
707
833
|
mmr_diversity_bias=mmr_diversity_bias,
|
|
@@ -792,18 +918,23 @@ class VectaraToolFactory:
|
|
|
792
918
|
)
|
|
793
919
|
return out
|
|
794
920
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
921
|
+
class RagToolBaseParams(BaseModel):
|
|
922
|
+
"""Model for the base parameters of the RAG tool."""
|
|
923
|
+
query: str = Field(
|
|
924
|
+
...,
|
|
925
|
+
description="The search query to perform, always in the form of a question",
|
|
926
|
+
)
|
|
927
|
+
|
|
798
928
|
tool = _create_tool_from_dynamic_function(
|
|
799
929
|
rag_function,
|
|
800
930
|
tool_name,
|
|
801
931
|
tool_description,
|
|
802
|
-
|
|
932
|
+
RagToolBaseParams,
|
|
803
933
|
tool_args_schema,
|
|
804
934
|
)
|
|
805
935
|
return tool
|
|
806
936
|
|
|
937
|
+
|
|
807
938
|
class ToolsFactory:
|
|
808
939
|
"""
|
|
809
940
|
A factory class for creating agent tools.
|
|
@@ -812,7 +943,9 @@ class ToolsFactory:
|
|
|
812
943
|
def __init__(self, agent_config: AgentConfig = None) -> None:
|
|
813
944
|
self.agent_config = agent_config
|
|
814
945
|
|
|
815
|
-
def create_tool(
|
|
946
|
+
def create_tool(
|
|
947
|
+
self, function: Callable, tool_type: ToolType = ToolType.QUERY
|
|
948
|
+
) -> VectaraTool:
|
|
816
949
|
"""
|
|
817
950
|
Create a tool from a function.
|
|
818
951
|
|
|
@@ -846,7 +979,9 @@ class ToolsFactory:
|
|
|
846
979
|
"""
|
|
847
980
|
# Dynamically install and import the module
|
|
848
981
|
if tool_package_name not in LI_packages:
|
|
849
|
-
raise ValueError(
|
|
982
|
+
raise ValueError(
|
|
983
|
+
f"Tool package {tool_package_name} from LlamaIndex not supported by Vectara-agentic."
|
|
984
|
+
)
|
|
850
985
|
|
|
851
986
|
module_name = f"llama_index.tools.{tool_package_name}"
|
|
852
987
|
module = importlib.import_module(module_name)
|
|
@@ -861,11 +996,18 @@ class ToolsFactory:
|
|
|
861
996
|
tool.metadata.name = tool_name_prefix + "_" + tool.metadata.name
|
|
862
997
|
if isinstance(func_type, dict):
|
|
863
998
|
if tool_spec_name not in func_type.keys():
|
|
864
|
-
raise ValueError(
|
|
999
|
+
raise ValueError(
|
|
1000
|
+
f"Tool spec {tool_spec_name} not found in package {tool_package_name}."
|
|
1001
|
+
)
|
|
865
1002
|
tool_type = func_type[tool_spec_name]
|
|
866
1003
|
else:
|
|
867
1004
|
tool_type = func_type
|
|
868
|
-
vtool = VectaraTool(
|
|
1005
|
+
vtool = VectaraTool(
|
|
1006
|
+
tool_type=tool_type,
|
|
1007
|
+
fn=tool.fn,
|
|
1008
|
+
metadata=tool.metadata,
|
|
1009
|
+
async_fn=tool.async_fn,
|
|
1010
|
+
)
|
|
869
1011
|
vtools.append(vtool)
|
|
870
1012
|
return vtools
|
|
871
1013
|
|
|
@@ -874,7 +1016,10 @@ class ToolsFactory:
|
|
|
874
1016
|
Create a list of standard tools.
|
|
875
1017
|
"""
|
|
876
1018
|
tc = ToolsCatalog(self.agent_config)
|
|
877
|
-
return [
|
|
1019
|
+
return [
|
|
1020
|
+
self.create_tool(tool)
|
|
1021
|
+
for tool in [tc.summarize_text, tc.rephrase_text, tc.critique_text]
|
|
1022
|
+
]
|
|
878
1023
|
|
|
879
1024
|
def guardrail_tools(self) -> List[FunctionTool]:
|
|
880
1025
|
"""
|
|
@@ -886,7 +1031,9 @@ class ToolsFactory:
|
|
|
886
1031
|
"""
|
|
887
1032
|
Create a list of financial tools.
|
|
888
1033
|
"""
|
|
889
|
-
return self.get_llama_index_tools(
|
|
1034
|
+
return self.get_llama_index_tools(
|
|
1035
|
+
tool_package_name="yahoo_finance", tool_spec_name="YahooFinanceToolSpec"
|
|
1036
|
+
)
|
|
890
1037
|
|
|
891
1038
|
def legal_tools(self) -> List[FunctionTool]:
|
|
892
1039
|
"""
|
|
@@ -918,7 +1065,9 @@ class ToolsFactory:
|
|
|
918
1065
|
""",
|
|
919
1066
|
)
|
|
920
1067
|
|
|
921
|
-
return [
|
|
1068
|
+
return [
|
|
1069
|
+
self.create_tool(tool) for tool in [summarize_legal_text, critique_as_judge]
|
|
1070
|
+
]
|
|
922
1071
|
|
|
923
1072
|
def database_tools(
|
|
924
1073
|
self,
|
|
@@ -955,16 +1104,22 @@ class ToolsFactory:
|
|
|
955
1104
|
List[VectaraTool]: A list of VectaraTool objects.
|
|
956
1105
|
"""
|
|
957
1106
|
if sql_database:
|
|
958
|
-
dbt = DatabaseTools(
|
|
1107
|
+
dbt = DatabaseTools(
|
|
1108
|
+
tool_name_prefix=tool_name_prefix,
|
|
1109
|
+
sql_database=sql_database,
|
|
1110
|
+
max_rows=max_rows,
|
|
1111
|
+
)
|
|
959
1112
|
else:
|
|
960
1113
|
if scheme in ["postgresql", "mysql", "sqlite", "mssql", "oracle"]:
|
|
961
1114
|
dbt = DatabaseTools(
|
|
1115
|
+
tool_name_prefix=tool_name_prefix,
|
|
962
1116
|
scheme=scheme,
|
|
963
1117
|
host=host,
|
|
964
1118
|
port=port,
|
|
965
1119
|
user=user,
|
|
966
1120
|
password=password,
|
|
967
1121
|
dbname=dbname,
|
|
1122
|
+
max_rows=max_rows,
|
|
968
1123
|
)
|
|
969
1124
|
else:
|
|
970
1125
|
raise ValueError(
|
|
@@ -978,14 +1133,14 @@ class ToolsFactory:
|
|
|
978
1133
|
for tool in tools:
|
|
979
1134
|
if content_description:
|
|
980
1135
|
tool.metadata.description = (
|
|
981
|
-
tool.metadata.description
|
|
1136
|
+
tool.metadata.description
|
|
1137
|
+
+ f"The database tables include data about {content_description}."
|
|
982
1138
|
)
|
|
983
|
-
if len(tool_name_prefix) > 0:
|
|
984
|
-
tool.metadata.name = tool_name_prefix + "_" + tool.metadata.name
|
|
985
1139
|
vtool = VectaraTool(
|
|
986
1140
|
tool_type=ToolType.QUERY,
|
|
987
|
-
fn=tool.fn,
|
|
988
|
-
|
|
1141
|
+
fn=tool.fn,
|
|
1142
|
+
async_fn=tool.async_fn,
|
|
1143
|
+
metadata=tool.metadata,
|
|
989
1144
|
)
|
|
990
1145
|
vtools.append(vtool)
|
|
991
1146
|
return vtools
|
|
@@ -103,7 +103,7 @@ def get_llm(
|
|
|
103
103
|
elif model_provider == ModelProvider.ANTHROPIC:
|
|
104
104
|
llm = Anthropic(
|
|
105
105
|
model=model_name, temperature=0,
|
|
106
|
-
max_tokens=max_tokens,
|
|
106
|
+
max_tokens=max_tokens,
|
|
107
107
|
)
|
|
108
108
|
elif model_provider == ModelProvider.GEMINI:
|
|
109
109
|
from llama_index.llms.gemini import Gemini
|
|
@@ -124,7 +124,8 @@ def get_llm(
|
|
|
124
124
|
from llama_index.llms.groq import Groq
|
|
125
125
|
llm = Groq(
|
|
126
126
|
model=model_name, temperature=0,
|
|
127
|
-
is_function_calling_model=True,
|
|
127
|
+
is_function_calling_model=True,
|
|
128
|
+
max_tokens=max_tokens
|
|
128
129
|
)
|
|
129
130
|
elif model_provider == ModelProvider.FIREWORKS:
|
|
130
131
|
from llama_index.llms.fireworks import Fireworks
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vectara_agentic
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
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,17 +16,17 @@ 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.
|
|
19
|
+
Requires-Dist: llama-index==0.12.30
|
|
20
20
|
Requires-Dist: llama-index-indices-managed-vectara==0.4.2
|
|
21
21
|
Requires-Dist: llama-index-agent-llm-compiler==0.3.0
|
|
22
22
|
Requires-Dist: llama-index-agent-lats==0.3.0
|
|
23
23
|
Requires-Dist: llama-index-agent-openai==0.4.6
|
|
24
|
-
Requires-Dist: llama-index-llms-openai==0.3.
|
|
24
|
+
Requires-Dist: llama-index-llms-openai==0.3.35
|
|
25
25
|
Requires-Dist: llama-index-llms-anthropic==0.6.10
|
|
26
26
|
Requires-Dist: llama-index-llms-together==0.3.1
|
|
27
27
|
Requires-Dist: llama-index-llms-groq==0.3.1
|
|
28
28
|
Requires-Dist: llama-index-llms-fireworks==0.3.2
|
|
29
|
-
Requires-Dist: llama-index-llms-cohere==0.4.
|
|
29
|
+
Requires-Dist: llama-index-llms-cohere==0.4.1
|
|
30
30
|
Requires-Dist: llama-index-llms-gemini==0.4.14
|
|
31
31
|
Requires-Dist: llama-index-llms-bedrock==0.3.8
|
|
32
32
|
Requires-Dist: llama-index-tools-yahoo-finance==0.3.0
|
|
@@ -141,7 +141,6 @@ from vectara_agentic.tools import VectaraToolFactory
|
|
|
141
141
|
|
|
142
142
|
vec_factory = VectaraToolFactory(
|
|
143
143
|
vectara_api_key=os.environ['VECTARA_API_KEY'],
|
|
144
|
-
vectara_customer_id=os.environ['VECTARA_CUSTOMER_ID'],
|
|
145
144
|
vectara_corpus_key=os.environ['VECTARA_CORPUS_KEY']
|
|
146
145
|
)
|
|
147
146
|
```
|
|
@@ -162,7 +161,6 @@ tickers = {
|
|
|
162
161
|
}
|
|
163
162
|
|
|
164
163
|
class QueryFinancialReportsArgs(BaseModel):
|
|
165
|
-
query: str = Field(..., description="The user query.")
|
|
166
164
|
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').")
|
|
167
165
|
ticker: str = Field(..., description=f"The company ticker. Must be a valid ticket symbol from the list {tickers.keys()}.")
|
|
168
166
|
|
|
@@ -176,6 +174,8 @@ query_financial_reports_tool = vec_factory.create_rag_tool(
|
|
|
176
174
|
)
|
|
177
175
|
```
|
|
178
176
|
|
|
177
|
+
Note that we only defined the `year` and `ticker` arguments. The `query` argument is automatically added by `vectara-agentic`.
|
|
178
|
+
|
|
179
179
|
See the [docs](https://vectara.github.io/py-vectara-agentic/latest/) for additional arguments to customize your Vectara RAG tool.
|
|
180
180
|
|
|
181
181
|
### 3. Create other tools (optional)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
llama-index==0.12.
|
|
1
|
+
llama-index==0.12.30
|
|
2
2
|
llama-index-indices-managed-vectara==0.4.2
|
|
3
3
|
llama-index-agent-llm-compiler==0.3.0
|
|
4
4
|
llama-index-agent-lats==0.3.0
|
|
5
5
|
llama-index-agent-openai==0.4.6
|
|
6
|
-
llama-index-llms-openai==0.3.
|
|
6
|
+
llama-index-llms-openai==0.3.35
|
|
7
7
|
llama-index-llms-anthropic==0.6.10
|
|
8
8
|
llama-index-llms-together==0.3.1
|
|
9
9
|
llama-index-llms-groq==0.3.1
|
|
10
10
|
llama-index-llms-fireworks==0.3.2
|
|
11
|
-
llama-index-llms-cohere==0.4.
|
|
11
|
+
llama-index-llms-cohere==0.4.1
|
|
12
12
|
llama-index-llms-gemini==0.4.14
|
|
13
13
|
llama-index-llms-bedrock==0.3.8
|
|
14
14
|
llama-index-tools-yahoo-finance==0.3.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vectara_agentic-0.2.10 → vectara_agentic-0.2.12}/vectara_agentic.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|