vectara-agentic 0.2.10__tar.gz → 0.2.11__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.11}/PKG-INFO +1 -1
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/_callback.py +2 -2
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/_prompts.py +5 -4
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/_version.py +1 -1
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/db_tools.py +34 -14
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/sub_query_workflow.py +35 -10
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/tools.py +145 -58
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11/vectara_agentic.egg-info}/PKG-INFO +1 -1
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/LICENSE +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/MANIFEST.in +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/README.md +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/requirements.txt +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/setup.cfg +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/setup.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/__init__.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/endpoint.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/test_agent.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/test_agent_planning.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/test_agent_type.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/test_fallback.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/test_private_llm.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/test_serialization.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/test_tools.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/tests/test_workflow.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/__init__.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/_observability.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/agent.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/agent_config.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/agent_endpoint.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/tools_catalog.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/types.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic/utils.py +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic.egg-info/SOURCES.txt +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic.egg-info/dependency_links.txt +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic.egg-info/requires.txt +0 -0
- {vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic.egg-info/top_level.txt +0 -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
|
|
|
@@ -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.
|
|
@@ -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,35 +87,34 @@ class DatabaseTools(BaseReader):
|
|
|
86
87
|
func = getattr(self, fn_name)
|
|
87
88
|
except AttributeError:
|
|
88
89
|
return None
|
|
89
|
-
name = fn_name
|
|
90
|
+
name = self.tool_name_prefix + "_" + fn_name if self.tool_name_prefix else fn_name
|
|
90
91
|
docstring = func.__doc__ or ""
|
|
91
92
|
description = f"{name}{signature(func)}\n{docstring}"
|
|
92
93
|
fn_schema = create_schema_from_function(fn_name, getattr(self, fn_name))
|
|
93
94
|
return ToolMetadata(name=name, description=description, fn_schema=fn_schema)
|
|
94
95
|
|
|
95
|
-
def _load_data(self,
|
|
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:
|
|
@@ -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 {
|
|
131
|
+
return [f"Error ({str(e)}) occurred while executing the query {sql_query}"]
|
|
132
132
|
return [d.text for d in res]
|
|
133
133
|
|
|
134
134
|
def load_sample_data(self, table_name: str, num_rows: int = 25) -> Any:
|
|
@@ -141,6 +141,11 @@ class DatabaseTools(BaseReader):
|
|
|
141
141
|
Returns:
|
|
142
142
|
Any: The result of the database query.
|
|
143
143
|
"""
|
|
144
|
+
if table_name not in self.list_tables():
|
|
145
|
+
return (
|
|
146
|
+
f"Table {table_name} does not exist in the database."
|
|
147
|
+
f"Valid table names are: {self.list_tables()}"
|
|
148
|
+
)
|
|
144
149
|
try:
|
|
145
150
|
res = self._load_data(f"SELECT * FROM {table_name} LIMIT {num_rows}")
|
|
146
151
|
except Exception as e:
|
|
@@ -162,6 +167,15 @@ class DatabaseTools(BaseReader):
|
|
|
162
167
|
str: A string representation of the table schemas.
|
|
163
168
|
"""
|
|
164
169
|
table_names = tables or [table.name for table in self._metadata.sorted_tables]
|
|
170
|
+
if len(table_names) == 0:
|
|
171
|
+
return "You must specify at least one table name to describe."
|
|
172
|
+
for table_name in table_names:
|
|
173
|
+
if table_name not in self.list_tables():
|
|
174
|
+
return (
|
|
175
|
+
f"Table {table_name} does not exist in the database."
|
|
176
|
+
f"Valid table names are: {self.list_tables()}"
|
|
177
|
+
)
|
|
178
|
+
|
|
165
179
|
table_schemas = []
|
|
166
180
|
for table_name in table_names:
|
|
167
181
|
table = next(
|
|
@@ -186,6 +200,12 @@ class DatabaseTools(BaseReader):
|
|
|
186
200
|
Returns:
|
|
187
201
|
Any: the result of the database query
|
|
188
202
|
"""
|
|
203
|
+
if table_name not in self.list_tables():
|
|
204
|
+
return (
|
|
205
|
+
f"Table {table_name} does not exist in the database."
|
|
206
|
+
f"Valid table names are: {self.list_tables()}"
|
|
207
|
+
)
|
|
208
|
+
|
|
189
209
|
res = {}
|
|
190
210
|
try:
|
|
191
211
|
for column in columns:
|
|
@@ -3,6 +3,7 @@ This module contains the SubQuestionQueryEngine workflow, which is a workflow
|
|
|
3
3
|
that takes a user question and a list of tools, and outputs a list of sub-questions.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import re
|
|
6
7
|
import json
|
|
7
8
|
from pydantic import BaseModel
|
|
8
9
|
|
|
@@ -109,17 +110,27 @@ class SubQuestionQueryWorkflow(Workflow):
|
|
|
109
110
|
if await ctx.get("verbose"):
|
|
110
111
|
print(f"Sub-questions are {response}")
|
|
111
112
|
|
|
112
|
-
|
|
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
|
|
@@ -184,6 +196,7 @@ class VectaraTool(FunctionTool):
|
|
|
184
196
|
)
|
|
185
197
|
return err_output
|
|
186
198
|
|
|
199
|
+
|
|
187
200
|
def _create_tool_from_dynamic_function(
|
|
188
201
|
function: Callable[..., ToolOutput],
|
|
189
202
|
tool_name: str,
|
|
@@ -197,11 +210,17 @@ def _create_tool_from_dynamic_function(
|
|
|
197
210
|
"""
|
|
198
211
|
fields = {}
|
|
199
212
|
for param in base_params:
|
|
200
|
-
default_value =
|
|
213
|
+
default_value = (
|
|
214
|
+
param.default if param.default != inspect.Parameter.empty else ...
|
|
215
|
+
)
|
|
201
216
|
fields[param.name] = (param.annotation, default_value)
|
|
202
217
|
for field_name, field_info in tool_args_schema.model_fields.items():
|
|
203
218
|
if field_name not in fields:
|
|
204
|
-
default_value =
|
|
219
|
+
default_value = (
|
|
220
|
+
field_info.default
|
|
221
|
+
if field_info.default is not PydanticUndefined
|
|
222
|
+
else ...
|
|
223
|
+
)
|
|
205
224
|
fields[field_name] = (field_info.annotation, default_value)
|
|
206
225
|
fn_schema = create_model(f"{tool_name}", **fields)
|
|
207
226
|
|
|
@@ -209,8 +228,16 @@ def _create_tool_from_dynamic_function(
|
|
|
209
228
|
inspect.Parameter(
|
|
210
229
|
name=field_name,
|
|
211
230
|
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
212
|
-
default=
|
|
213
|
-
|
|
231
|
+
default=(
|
|
232
|
+
field_info.default
|
|
233
|
+
if field_info.default is not PydanticUndefined
|
|
234
|
+
else inspect.Parameter.empty
|
|
235
|
+
),
|
|
236
|
+
annotation=(
|
|
237
|
+
field_info.annotation
|
|
238
|
+
if hasattr(field_info, "annotation")
|
|
239
|
+
else field_info
|
|
240
|
+
),
|
|
214
241
|
)
|
|
215
242
|
for field_name, field_info in tool_args_schema.model_fields.items()
|
|
216
243
|
if field_name not in [p.name for p in base_params]
|
|
@@ -218,17 +245,21 @@ def _create_tool_from_dynamic_function(
|
|
|
218
245
|
all_params = base_params + schema_params
|
|
219
246
|
|
|
220
247
|
required_params = [p for p in all_params if p.default is inspect.Parameter.empty]
|
|
221
|
-
optional_params = [
|
|
248
|
+
optional_params = [
|
|
249
|
+
p for p in all_params if p.default is not inspect.Parameter.empty
|
|
250
|
+
]
|
|
222
251
|
sig = inspect.Signature(required_params + optional_params)
|
|
223
252
|
function.__signature__ = sig
|
|
224
253
|
function.__annotations__["return"] = dict[str, Any]
|
|
225
|
-
function.__name__ =
|
|
254
|
+
function.__name__ = re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
|
|
226
255
|
|
|
227
256
|
# Create the tool function signature string
|
|
228
257
|
param_strs = []
|
|
229
258
|
for param in all_params:
|
|
230
259
|
annotation = param.annotation
|
|
231
|
-
type_name =
|
|
260
|
+
type_name = (
|
|
261
|
+
annotation.__name__ if hasattr(annotation, "__name__") else str(annotation)
|
|
262
|
+
)
|
|
232
263
|
param_strs.append(f"{param.name}: {type_name}")
|
|
233
264
|
args_str = ", ".join(param_strs)
|
|
234
265
|
function_str = f"{tool_name}({args_str}) -> str"
|
|
@@ -243,7 +274,10 @@ def _create_tool_from_dynamic_function(
|
|
|
243
274
|
)
|
|
244
275
|
return tool
|
|
245
276
|
|
|
246
|
-
|
|
277
|
+
|
|
278
|
+
def _build_filter_string(
|
|
279
|
+
kwargs: Dict[str, Any], tool_args_type: Dict[str, dict], fixed_filter: str
|
|
280
|
+
) -> str:
|
|
247
281
|
"""
|
|
248
282
|
Build filter string for Vectara from kwargs
|
|
249
283
|
"""
|
|
@@ -257,9 +291,9 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
257
291
|
|
|
258
292
|
# Determine the prefix for the key. Valid values are "doc" or "part"
|
|
259
293
|
# default to 'doc' if not specified
|
|
260
|
-
tool_args_dict = tool_args_type.get(key, {
|
|
294
|
+
tool_args_dict = tool_args_type.get(key, {"type": "doc", "is_list": False})
|
|
261
295
|
prefix = tool_args_dict.get(key, "doc")
|
|
262
|
-
is_list = tool_args_dict.get(
|
|
296
|
+
is_list = tool_args_dict.get("is_list", False)
|
|
263
297
|
|
|
264
298
|
if prefix not in ["doc", "part"]:
|
|
265
299
|
raise ValueError(
|
|
@@ -312,12 +346,10 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
312
346
|
range_conditions.append(f"{prefix}.{key} {operator} {end_val}")
|
|
313
347
|
|
|
314
348
|
# Join the range conditions with AND
|
|
315
|
-
filter_parts.append(
|
|
349
|
+
filter_parts.append("( " + " AND ".join(range_conditions) + " )")
|
|
316
350
|
continue
|
|
317
351
|
|
|
318
|
-
raise ValueError(
|
|
319
|
-
f"Range operator requires two values for {key}: {value}"
|
|
320
|
-
)
|
|
352
|
+
raise ValueError(f"Range operator requires two values for {key}: {value}")
|
|
321
353
|
|
|
322
354
|
# Check if value contains a known comparison operator at the start
|
|
323
355
|
matched_operator = None
|
|
@@ -329,7 +361,7 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
329
361
|
# Break down operator from value
|
|
330
362
|
# e.g. val_str = ">2022" --> operator = ">", rhs = "2022"
|
|
331
363
|
if matched_operator:
|
|
332
|
-
rhs = val_str[len(matched_operator):].strip()
|
|
364
|
+
rhs = val_str[len(matched_operator) :].strip()
|
|
333
365
|
|
|
334
366
|
if matched_operator in numeric_only_ops:
|
|
335
367
|
# Must be numeric
|
|
@@ -343,7 +375,9 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
343
375
|
if rhs.isdigit() or is_float(rhs):
|
|
344
376
|
filter_parts.append(f"{prefix}.{key}{matched_operator}{rhs}")
|
|
345
377
|
elif rhs.lower() in ["true", "false"]:
|
|
346
|
-
filter_parts.append(
|
|
378
|
+
filter_parts.append(
|
|
379
|
+
f"{prefix}.{key}{matched_operator}{rhs.lower()}"
|
|
380
|
+
)
|
|
347
381
|
else:
|
|
348
382
|
# For string operands, wrap them in quotes
|
|
349
383
|
filter_parts.append(f"{prefix}.{key}{matched_operator}'{rhs}'")
|
|
@@ -373,6 +407,7 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
373
407
|
else:
|
|
374
408
|
return fixed_filter or filter_str
|
|
375
409
|
|
|
410
|
+
|
|
376
411
|
class VectaraToolFactory:
|
|
377
412
|
"""
|
|
378
413
|
A factory class for creating Vectara RAG tools.
|
|
@@ -480,7 +515,9 @@ class VectaraToolFactory:
|
|
|
480
515
|
top_k = kwargs.pop("top_k", 10)
|
|
481
516
|
summarize = kwargs.pop("summarize", True)
|
|
482
517
|
try:
|
|
483
|
-
filter_string = _build_filter_string(
|
|
518
|
+
filter_string = _build_filter_string(
|
|
519
|
+
kwargs, tool_args_type, fixed_filter
|
|
520
|
+
)
|
|
484
521
|
except ValueError as e:
|
|
485
522
|
return ToolOutput(
|
|
486
523
|
tool_name=search_function.__name__,
|
|
@@ -493,7 +530,11 @@ class VectaraToolFactory:
|
|
|
493
530
|
summary_enabled=False,
|
|
494
531
|
similarity_top_k=top_k,
|
|
495
532
|
reranker=reranker,
|
|
496
|
-
rerank_k=
|
|
533
|
+
rerank_k=(
|
|
534
|
+
rerank_k
|
|
535
|
+
if rerank_k * self.num_corpora <= 100
|
|
536
|
+
else int(100 / self.num_corpora)
|
|
537
|
+
),
|
|
497
538
|
rerank_limit=rerank_limit,
|
|
498
539
|
rerank_cutoff=rerank_cutoff,
|
|
499
540
|
mmr_diversity_bias=mmr_diversity_bias,
|
|
@@ -531,9 +572,7 @@ class VectaraToolFactory:
|
|
|
531
572
|
if summarize:
|
|
532
573
|
summaries_dict = asyncio.run(
|
|
533
574
|
summarize_documents(
|
|
534
|
-
self.vectara_corpus_key,
|
|
535
|
-
self.vectara_api_key,
|
|
536
|
-
list(unique_ids)
|
|
575
|
+
self.vectara_corpus_key, self.vectara_api_key, list(unique_ids)
|
|
537
576
|
)
|
|
538
577
|
)
|
|
539
578
|
for doc_id, metadata in docs:
|
|
@@ -541,7 +580,9 @@ class VectaraToolFactory:
|
|
|
541
580
|
tool_output += f"document_id: '{doc_id}'\nmetadata: '{metadata}'\nsummary: '{summary}'\n\n"
|
|
542
581
|
else:
|
|
543
582
|
for doc_id, metadata in docs:
|
|
544
|
-
tool_output +=
|
|
583
|
+
tool_output += (
|
|
584
|
+
f"document_id: '{doc_id}'\nmetadata: '{metadata}'\n\n"
|
|
585
|
+
)
|
|
545
586
|
|
|
546
587
|
out = ToolOutput(
|
|
547
588
|
tool_name=search_function.__name__,
|
|
@@ -552,16 +593,29 @@ class VectaraToolFactory:
|
|
|
552
593
|
return out
|
|
553
594
|
|
|
554
595
|
base_params = [
|
|
555
|
-
inspect.Parameter(
|
|
556
|
-
|
|
557
|
-
|
|
596
|
+
inspect.Parameter(
|
|
597
|
+
"query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str
|
|
598
|
+
),
|
|
599
|
+
inspect.Parameter(
|
|
600
|
+
"top_k", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=int
|
|
601
|
+
),
|
|
602
|
+
inspect.Parameter(
|
|
603
|
+
"summarize",
|
|
604
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
605
|
+
default=True,
|
|
606
|
+
annotation=bool,
|
|
607
|
+
),
|
|
558
608
|
]
|
|
559
|
-
search_tool_extra_desc =
|
|
609
|
+
search_tool_extra_desc = (
|
|
610
|
+
tool_description
|
|
611
|
+
+ "\n"
|
|
612
|
+
+ """
|
|
560
613
|
This tool is meant to perform a search for relevant documents, it is not meant for asking questions.
|
|
561
614
|
The response includes metadata about each relevant document.
|
|
562
615
|
If summarize=True, it also includes a summary of each document, but takes a lot longer to respond,
|
|
563
616
|
so avoid using it unless necessary.
|
|
564
617
|
"""
|
|
618
|
+
)
|
|
565
619
|
|
|
566
620
|
tool = _create_tool_from_dynamic_function(
|
|
567
621
|
search_function,
|
|
@@ -684,7 +738,9 @@ class VectaraToolFactory:
|
|
|
684
738
|
|
|
685
739
|
query = kwargs.pop("query")
|
|
686
740
|
try:
|
|
687
|
-
filter_string = _build_filter_string(
|
|
741
|
+
filter_string = _build_filter_string(
|
|
742
|
+
kwargs, tool_args_type, fixed_filter
|
|
743
|
+
)
|
|
688
744
|
except ValueError as e:
|
|
689
745
|
return ToolOutput(
|
|
690
746
|
tool_name=rag_function.__name__,
|
|
@@ -701,7 +757,11 @@ class VectaraToolFactory:
|
|
|
701
757
|
summary_prompt_name=vectara_summarizer,
|
|
702
758
|
prompt_text=vectara_prompt_text,
|
|
703
759
|
reranker=reranker,
|
|
704
|
-
rerank_k=
|
|
760
|
+
rerank_k=(
|
|
761
|
+
rerank_k
|
|
762
|
+
if rerank_k * self.num_corpora <= 100
|
|
763
|
+
else int(100 / self.num_corpora)
|
|
764
|
+
),
|
|
705
765
|
rerank_limit=rerank_limit,
|
|
706
766
|
rerank_cutoff=rerank_cutoff,
|
|
707
767
|
mmr_diversity_bias=mmr_diversity_bias,
|
|
@@ -793,7 +853,9 @@ class VectaraToolFactory:
|
|
|
793
853
|
return out
|
|
794
854
|
|
|
795
855
|
base_params = [
|
|
796
|
-
inspect.Parameter(
|
|
856
|
+
inspect.Parameter(
|
|
857
|
+
"query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str
|
|
858
|
+
),
|
|
797
859
|
]
|
|
798
860
|
tool = _create_tool_from_dynamic_function(
|
|
799
861
|
rag_function,
|
|
@@ -804,6 +866,7 @@ class VectaraToolFactory:
|
|
|
804
866
|
)
|
|
805
867
|
return tool
|
|
806
868
|
|
|
869
|
+
|
|
807
870
|
class ToolsFactory:
|
|
808
871
|
"""
|
|
809
872
|
A factory class for creating agent tools.
|
|
@@ -812,7 +875,9 @@ class ToolsFactory:
|
|
|
812
875
|
def __init__(self, agent_config: AgentConfig = None) -> None:
|
|
813
876
|
self.agent_config = agent_config
|
|
814
877
|
|
|
815
|
-
def create_tool(
|
|
878
|
+
def create_tool(
|
|
879
|
+
self, function: Callable, tool_type: ToolType = ToolType.QUERY
|
|
880
|
+
) -> VectaraTool:
|
|
816
881
|
"""
|
|
817
882
|
Create a tool from a function.
|
|
818
883
|
|
|
@@ -846,7 +911,9 @@ class ToolsFactory:
|
|
|
846
911
|
"""
|
|
847
912
|
# Dynamically install and import the module
|
|
848
913
|
if tool_package_name not in LI_packages:
|
|
849
|
-
raise ValueError(
|
|
914
|
+
raise ValueError(
|
|
915
|
+
f"Tool package {tool_package_name} from LlamaIndex not supported by Vectara-agentic."
|
|
916
|
+
)
|
|
850
917
|
|
|
851
918
|
module_name = f"llama_index.tools.{tool_package_name}"
|
|
852
919
|
module = importlib.import_module(module_name)
|
|
@@ -861,11 +928,18 @@ class ToolsFactory:
|
|
|
861
928
|
tool.metadata.name = tool_name_prefix + "_" + tool.metadata.name
|
|
862
929
|
if isinstance(func_type, dict):
|
|
863
930
|
if tool_spec_name not in func_type.keys():
|
|
864
|
-
raise ValueError(
|
|
931
|
+
raise ValueError(
|
|
932
|
+
f"Tool spec {tool_spec_name} not found in package {tool_package_name}."
|
|
933
|
+
)
|
|
865
934
|
tool_type = func_type[tool_spec_name]
|
|
866
935
|
else:
|
|
867
936
|
tool_type = func_type
|
|
868
|
-
vtool = VectaraTool(
|
|
937
|
+
vtool = VectaraTool(
|
|
938
|
+
tool_type=tool_type,
|
|
939
|
+
fn=tool.fn,
|
|
940
|
+
metadata=tool.metadata,
|
|
941
|
+
async_fn=tool.async_fn,
|
|
942
|
+
)
|
|
869
943
|
vtools.append(vtool)
|
|
870
944
|
return vtools
|
|
871
945
|
|
|
@@ -874,7 +948,10 @@ class ToolsFactory:
|
|
|
874
948
|
Create a list of standard tools.
|
|
875
949
|
"""
|
|
876
950
|
tc = ToolsCatalog(self.agent_config)
|
|
877
|
-
return [
|
|
951
|
+
return [
|
|
952
|
+
self.create_tool(tool)
|
|
953
|
+
for tool in [tc.summarize_text, tc.rephrase_text, tc.critique_text]
|
|
954
|
+
]
|
|
878
955
|
|
|
879
956
|
def guardrail_tools(self) -> List[FunctionTool]:
|
|
880
957
|
"""
|
|
@@ -886,7 +963,9 @@ class ToolsFactory:
|
|
|
886
963
|
"""
|
|
887
964
|
Create a list of financial tools.
|
|
888
965
|
"""
|
|
889
|
-
return self.get_llama_index_tools(
|
|
966
|
+
return self.get_llama_index_tools(
|
|
967
|
+
tool_package_name="yahoo_finance", tool_spec_name="YahooFinanceToolSpec"
|
|
968
|
+
)
|
|
890
969
|
|
|
891
970
|
def legal_tools(self) -> List[FunctionTool]:
|
|
892
971
|
"""
|
|
@@ -918,7 +997,9 @@ class ToolsFactory:
|
|
|
918
997
|
""",
|
|
919
998
|
)
|
|
920
999
|
|
|
921
|
-
return [
|
|
1000
|
+
return [
|
|
1001
|
+
self.create_tool(tool) for tool in [summarize_legal_text, critique_as_judge]
|
|
1002
|
+
]
|
|
922
1003
|
|
|
923
1004
|
def database_tools(
|
|
924
1005
|
self,
|
|
@@ -955,16 +1036,22 @@ class ToolsFactory:
|
|
|
955
1036
|
List[VectaraTool]: A list of VectaraTool objects.
|
|
956
1037
|
"""
|
|
957
1038
|
if sql_database:
|
|
958
|
-
dbt = DatabaseTools(
|
|
1039
|
+
dbt = DatabaseTools(
|
|
1040
|
+
tool_name_prefix=tool_name_prefix,
|
|
1041
|
+
sql_database=sql_database,
|
|
1042
|
+
max_rows=max_rows,
|
|
1043
|
+
)
|
|
959
1044
|
else:
|
|
960
1045
|
if scheme in ["postgresql", "mysql", "sqlite", "mssql", "oracle"]:
|
|
961
1046
|
dbt = DatabaseTools(
|
|
1047
|
+
tool_name_prefix=tool_name_prefix,
|
|
962
1048
|
scheme=scheme,
|
|
963
1049
|
host=host,
|
|
964
1050
|
port=port,
|
|
965
1051
|
user=user,
|
|
966
1052
|
password=password,
|
|
967
1053
|
dbname=dbname,
|
|
1054
|
+
max_rows=max_rows,
|
|
968
1055
|
)
|
|
969
1056
|
else:
|
|
970
1057
|
raise ValueError(
|
|
@@ -978,14 +1065,14 @@ class ToolsFactory:
|
|
|
978
1065
|
for tool in tools:
|
|
979
1066
|
if content_description:
|
|
980
1067
|
tool.metadata.description = (
|
|
981
|
-
tool.metadata.description
|
|
1068
|
+
tool.metadata.description
|
|
1069
|
+
+ f"The database tables include data about {content_description}."
|
|
982
1070
|
)
|
|
983
|
-
if len(tool_name_prefix) > 0:
|
|
984
|
-
tool.metadata.name = tool_name_prefix + "_" + tool.metadata.name
|
|
985
1071
|
vtool = VectaraTool(
|
|
986
1072
|
tool_type=ToolType.QUERY,
|
|
987
|
-
fn=tool.fn,
|
|
988
|
-
|
|
1073
|
+
fn=tool.fn,
|
|
1074
|
+
async_fn=tool.async_fn,
|
|
1075
|
+
metadata=tool.metadata,
|
|
989
1076
|
)
|
|
990
1077
|
vtools.append(vtool)
|
|
991
1078
|
return vtools
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vectara_agentic-0.2.10 → vectara_agentic-0.2.11}/vectara_agentic.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|