vectara-agentic 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of vectara-agentic might be problematic. Click here for more details.
- tests/test_agent.py +73 -51
- tests/test_agent_planning.py +47 -20
- tests/test_agent_type.py +104 -10
- tests/test_fallback.py +83 -0
- tests/test_private_llm.py +11 -10
- tests/test_tools.py +1 -1
- tests/test_workflow.py +31 -6
- vectara_agentic/_observability.py +1 -1
- vectara_agentic/_prompts.py +5 -5
- vectara_agentic/_version.py +1 -1
- vectara_agentic/agent.py +226 -111
- vectara_agentic/sub_query_workflow.py +142 -3
- vectara_agentic/tools.py +97 -74
- vectara_agentic/types.py +6 -0
- vectara_agentic/utils.py +50 -11
- {vectara_agentic-0.2.5.dist-info → vectara_agentic-0.2.7.dist-info}/METADATA +10 -9
- vectara_agentic-0.2.7.dist-info/RECORD +28 -0
- {vectara_agentic-0.2.5.dist-info → vectara_agentic-0.2.7.dist-info}/WHEEL +1 -1
- vectara_agentic-0.2.5.dist-info/RECORD +0 -27
- {vectara_agentic-0.2.5.dist-info → vectara_agentic-0.2.7.dist-info/licenses}/LICENSE +0 -0
- {vectara_agentic-0.2.5.dist-info → vectara_agentic-0.2.7.dist-info}/top_level.txt +0 -0
|
@@ -60,16 +60,24 @@ class SubQuestionQueryWorkflow(Workflow):
|
|
|
60
60
|
|
|
61
61
|
if hasattr(ev, "agent"):
|
|
62
62
|
await ctx.set("agent", ev.agent)
|
|
63
|
+
else:
|
|
64
|
+
raise ValueError("Agent not provided to workflow Start Event.")
|
|
63
65
|
chat_history = [str(msg) for msg in ev.agent.memory.get()]
|
|
64
66
|
|
|
65
67
|
if hasattr(ev, "llm"):
|
|
66
68
|
await ctx.set("llm", ev.llm)
|
|
69
|
+
else:
|
|
70
|
+
raise ValueError("LLM not provided to workflow Start Event.")
|
|
67
71
|
|
|
68
72
|
if hasattr(ev, "tools"):
|
|
69
73
|
await ctx.set("tools", ev.tools)
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError("Tools not provided to workflow Start Event.")
|
|
70
76
|
|
|
71
77
|
if hasattr(ev, "verbose"):
|
|
72
78
|
await ctx.set("verbose", ev.verbose)
|
|
79
|
+
else:
|
|
80
|
+
await ctx.set("verbose", False)
|
|
73
81
|
|
|
74
82
|
llm = await ctx.get("llm")
|
|
75
83
|
response = llm.complete(
|
|
@@ -77,6 +85,7 @@ class SubQuestionQueryWorkflow(Workflow):
|
|
|
77
85
|
Given a user question, and a list of tools, output a list of
|
|
78
86
|
relevant sub-questions, such that the answers to all the
|
|
79
87
|
sub-questions put together will answer the question.
|
|
88
|
+
Order the sub-questions in the right order if there are dependencies.
|
|
80
89
|
Make sure sub-questions do not result in duplicate tool calling.
|
|
81
90
|
Respond in pure JSON without any markdown, like this:
|
|
82
91
|
{{
|
|
@@ -106,11 +115,11 @@ class SubQuestionQueryWorkflow(Workflow):
|
|
|
106
115
|
await ctx.set("sub_question_count", len(sub_questions))
|
|
107
116
|
|
|
108
117
|
for question in sub_questions:
|
|
109
|
-
|
|
118
|
+
ctx.send_event(self.QueryEvent(question=question))
|
|
110
119
|
|
|
111
120
|
return None
|
|
112
121
|
|
|
113
|
-
@step
|
|
122
|
+
@step(num_workers=3)
|
|
114
123
|
async def sub_question(self, ctx: Context, ev: QueryEvent) -> AnswerEvent:
|
|
115
124
|
"""
|
|
116
125
|
Given a sub-question, return the answer to the sub-question, using the agent.
|
|
@@ -151,7 +160,6 @@ class SubQuestionQueryWorkflow(Workflow):
|
|
|
151
160
|
Sub-questions and answers:
|
|
152
161
|
{answers}
|
|
153
162
|
"""
|
|
154
|
-
|
|
155
163
|
if await ctx.get("verbose"):
|
|
156
164
|
print(f"Final prompt is {prompt}")
|
|
157
165
|
|
|
@@ -163,3 +171,134 @@ class SubQuestionQueryWorkflow(Workflow):
|
|
|
163
171
|
|
|
164
172
|
output = self.OutputsModel(response=str(response))
|
|
165
173
|
return StopEvent(result=output)
|
|
174
|
+
|
|
175
|
+
class SequentialSubQuestionsWorkflow(Workflow):
|
|
176
|
+
"""
|
|
177
|
+
Workflow for breaking a query into sequential sub-questions
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
# Workflow inputs/outputs
|
|
181
|
+
class InputsModel(BaseModel):
|
|
182
|
+
"""
|
|
183
|
+
Inputs for the workflow.
|
|
184
|
+
"""
|
|
185
|
+
query: str
|
|
186
|
+
|
|
187
|
+
class OutputsModel(BaseModel):
|
|
188
|
+
"""
|
|
189
|
+
Outputs for the workflow.
|
|
190
|
+
"""
|
|
191
|
+
response: str
|
|
192
|
+
|
|
193
|
+
# Workflow Event types
|
|
194
|
+
class QueryEvent(Event):
|
|
195
|
+
"""Event for a query."""
|
|
196
|
+
question: str
|
|
197
|
+
prev_answer: str
|
|
198
|
+
num: int
|
|
199
|
+
|
|
200
|
+
@step
|
|
201
|
+
async def query(self, ctx: Context, ev: StartEvent) -> QueryEvent:
|
|
202
|
+
"""
|
|
203
|
+
Given a user question, and a list of tools, output a list of relevant
|
|
204
|
+
sub-questions, such that each question depends on the response of the
|
|
205
|
+
previous question, to answer the original user question.
|
|
206
|
+
"""
|
|
207
|
+
if not hasattr(ev, "inputs"):
|
|
208
|
+
raise ValueError("No inputs provided to workflow Start Event.")
|
|
209
|
+
if hasattr(ev, "inputs") and not isinstance(ev.inputs, self.InputsModel):
|
|
210
|
+
raise ValueError(f"Expected inputs to be of type {self.InputsModel}")
|
|
211
|
+
if hasattr(ev, "inputs"):
|
|
212
|
+
query = ev.inputs.query
|
|
213
|
+
await ctx.set("original_query", query)
|
|
214
|
+
|
|
215
|
+
if hasattr(ev, "agent"):
|
|
216
|
+
await ctx.set("agent", ev.agent)
|
|
217
|
+
else:
|
|
218
|
+
raise ValueError("Agent not provided to workflow Start Event.")
|
|
219
|
+
chat_history = [str(msg) for msg in ev.agent.memory.get()]
|
|
220
|
+
|
|
221
|
+
if hasattr(ev, "llm"):
|
|
222
|
+
await ctx.set("llm", ev.llm)
|
|
223
|
+
else:
|
|
224
|
+
raise ValueError("LLM not provided to workflow Start Event.")
|
|
225
|
+
|
|
226
|
+
if hasattr(ev, "tools"):
|
|
227
|
+
await ctx.set("tools", ev.tools)
|
|
228
|
+
else:
|
|
229
|
+
raise ValueError("Tools not provided to workflow Start Event.")
|
|
230
|
+
|
|
231
|
+
if hasattr(ev, "verbose"):
|
|
232
|
+
await ctx.set("verbose", ev.verbose)
|
|
233
|
+
else:
|
|
234
|
+
await ctx.set("verbose", False)
|
|
235
|
+
if ev.verbose:
|
|
236
|
+
print(f"Query is {await ctx.get('original_query')}")
|
|
237
|
+
|
|
238
|
+
llm = await ctx.get("llm")
|
|
239
|
+
response = llm.complete(
|
|
240
|
+
f"""
|
|
241
|
+
Given a user question, and a list of tools, output a list of
|
|
242
|
+
relevant sequential sub-questions, such that the answers to all the
|
|
243
|
+
sub-questions in sequence will answer the question, and the output
|
|
244
|
+
of each question can be used as input to the subsequent question.
|
|
245
|
+
Respond in pure JSON without any markdown, like this:
|
|
246
|
+
{{
|
|
247
|
+
"sub_questions": [
|
|
248
|
+
"What is the population of San Francisco?",
|
|
249
|
+
"Is that population larger than the population of San Jose?",
|
|
250
|
+
]
|
|
251
|
+
}}
|
|
252
|
+
As an example, for the question
|
|
253
|
+
"what is the name of the mayor of the largest city within 50 miles of San Francisco?",
|
|
254
|
+
the sub-questions could be:
|
|
255
|
+
- What is the largest city within 50 miles of San Francisco?
|
|
256
|
+
- Who is the mayor of this city?
|
|
257
|
+
The answer to the first question is San Jose, which is given as context to the second question.
|
|
258
|
+
The answer to the second question is Matt Mahan.
|
|
259
|
+
Here is the user question: {await ctx.get('original_query')}.
|
|
260
|
+
Here are previous chat messages: {chat_history}.
|
|
261
|
+
And here is the list of tools: {await ctx.get('tools')}
|
|
262
|
+
""",
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
response_obj = json.loads(str(response))
|
|
266
|
+
sub_questions = response_obj["sub_questions"]
|
|
267
|
+
|
|
268
|
+
await ctx.set("sub_questions", sub_questions)
|
|
269
|
+
if await ctx.get("verbose"):
|
|
270
|
+
print(f"Sub-questions are {sub_questions}")
|
|
271
|
+
|
|
272
|
+
return self.QueryEvent(question=sub_questions[0], prev_answer="", num=0)
|
|
273
|
+
|
|
274
|
+
@step
|
|
275
|
+
async def sub_question(self, ctx: Context, ev: QueryEvent) -> StopEvent | QueryEvent:
|
|
276
|
+
"""
|
|
277
|
+
Given a sub-question, return the answer to the sub-question, using the agent.
|
|
278
|
+
"""
|
|
279
|
+
if await ctx.get("verbose"):
|
|
280
|
+
print(f"Sub-question is {ev.question}")
|
|
281
|
+
agent = await ctx.get("agent")
|
|
282
|
+
sub_questions = await ctx.get("sub_questions")
|
|
283
|
+
if ev.prev_answer:
|
|
284
|
+
prev_question = sub_questions[ev.num - 1]
|
|
285
|
+
prompt = f"""
|
|
286
|
+
The answer to the question '{prev_question}' is: '{ev.prev_answer}'
|
|
287
|
+
Now answer the following question: '{ev.question}'
|
|
288
|
+
"""
|
|
289
|
+
response = await agent.achat(prompt)
|
|
290
|
+
else:
|
|
291
|
+
response = await agent.achat(ev.question)
|
|
292
|
+
if await ctx.get("verbose"):
|
|
293
|
+
print(f"Answer is {response}")
|
|
294
|
+
|
|
295
|
+
sub_questions = await ctx.get("sub_questions")
|
|
296
|
+
if ev.num + 1 < len(sub_questions):
|
|
297
|
+
return self.QueryEvent(
|
|
298
|
+
question=sub_questions[ev.num + 1],
|
|
299
|
+
prev_answer = response.response,
|
|
300
|
+
num=ev.num + 1
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
output = self.OutputsModel(response=response.response)
|
|
304
|
+
return StopEvent(result=output)
|
vectara_agentic/tools.py
CHANGED
|
@@ -8,7 +8,7 @@ import importlib
|
|
|
8
8
|
import os
|
|
9
9
|
|
|
10
10
|
from typing import Callable, List, Dict, Any, Optional, Union, Type
|
|
11
|
-
from pydantic import BaseModel, Field
|
|
11
|
+
from pydantic import BaseModel, Field, create_model
|
|
12
12
|
from pydantic_core import PydanticUndefined
|
|
13
13
|
|
|
14
14
|
from llama_index.core.tools import FunctionTool
|
|
@@ -21,7 +21,7 @@ from llama_index.core.workflow.context import Context
|
|
|
21
21
|
from .types import ToolType
|
|
22
22
|
from .tools_catalog import ToolsCatalog, get_bad_topics
|
|
23
23
|
from .db_tools import DBLoadSampleData, DBLoadUniqueValues, DBLoadData
|
|
24
|
-
from .utils import is_float
|
|
24
|
+
from .utils import is_float, summarize_vectara_document
|
|
25
25
|
from .agent_config import AgentConfig
|
|
26
26
|
|
|
27
27
|
LI_packages = {
|
|
@@ -74,6 +74,7 @@ class VectaraToolMetadata(ToolMetadata):
|
|
|
74
74
|
base_repr = super().__repr__()
|
|
75
75
|
return f"{base_repr}, tool_type={self.tool_type}"
|
|
76
76
|
|
|
77
|
+
|
|
77
78
|
class VectaraTool(FunctionTool):
|
|
78
79
|
"""
|
|
79
80
|
A subclass of FunctionTool adding the tool_type attribute.
|
|
@@ -161,7 +162,7 @@ class VectaraTool(FunctionTool):
|
|
|
161
162
|
self, *args: Any, ctx: Optional[Context] = None, **kwargs: Any
|
|
162
163
|
) -> ToolOutput:
|
|
163
164
|
try:
|
|
164
|
-
return super().
|
|
165
|
+
return await super().acall(*args, ctx=ctx, **kwargs)
|
|
165
166
|
except Exception as e:
|
|
166
167
|
err_output = ToolOutput(
|
|
167
168
|
tool_name=self.metadata.name,
|
|
@@ -171,6 +172,65 @@ class VectaraTool(FunctionTool):
|
|
|
171
172
|
)
|
|
172
173
|
return err_output
|
|
173
174
|
|
|
175
|
+
def _create_tool_from_dynamic_function(
|
|
176
|
+
function: Callable[..., ToolOutput],
|
|
177
|
+
tool_name: str,
|
|
178
|
+
tool_description: str,
|
|
179
|
+
base_params: list[inspect.Parameter],
|
|
180
|
+
tool_args_schema: type[BaseModel],
|
|
181
|
+
) -> VectaraTool:
|
|
182
|
+
"""
|
|
183
|
+
Create a VectaraTool from a dynamic function, including
|
|
184
|
+
setting the function signature and creating the tool schema.
|
|
185
|
+
"""
|
|
186
|
+
fields = {}
|
|
187
|
+
for param in base_params:
|
|
188
|
+
default_value = param.default if param.default != inspect.Parameter.empty else ...
|
|
189
|
+
fields[param.name] = (param.annotation, default_value)
|
|
190
|
+
for field_name, field_info in tool_args_schema.model_fields.items():
|
|
191
|
+
if field_name not in fields:
|
|
192
|
+
default_value = field_info.default if field_info.default is not PydanticUndefined else ...
|
|
193
|
+
fields[field_name] = (field_info.annotation, default_value)
|
|
194
|
+
fn_schema = create_model(f"{tool_name}", **fields)
|
|
195
|
+
|
|
196
|
+
schema_params = [
|
|
197
|
+
inspect.Parameter(
|
|
198
|
+
name=field_name,
|
|
199
|
+
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
200
|
+
default=field_info.default if field_info.default is not PydanticUndefined else inspect.Parameter.empty,
|
|
201
|
+
annotation=field_info.annotation if hasattr(field_info, 'annotation') else field_info,
|
|
202
|
+
)
|
|
203
|
+
for field_name, field_info in tool_args_schema.model_fields.items()
|
|
204
|
+
if field_name not in [p.name for p in base_params]
|
|
205
|
+
]
|
|
206
|
+
all_params = base_params + schema_params
|
|
207
|
+
|
|
208
|
+
required_params = [p for p in all_params if p.default is inspect.Parameter.empty]
|
|
209
|
+
optional_params = [p for p in all_params if p.default is not inspect.Parameter.empty]
|
|
210
|
+
sig = inspect.Signature(required_params + optional_params)
|
|
211
|
+
function.__signature__ = sig
|
|
212
|
+
function.__annotations__["return"] = dict[str, Any]
|
|
213
|
+
function.__name__ = "_" + re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
|
|
214
|
+
|
|
215
|
+
# Create the tool function signature string
|
|
216
|
+
param_strs = []
|
|
217
|
+
for param in all_params:
|
|
218
|
+
annotation = param.annotation
|
|
219
|
+
type_name = annotation.__name__ if hasattr(annotation, '__name__') else str(annotation)
|
|
220
|
+
param_strs.append(f"{param.name}: {type_name}")
|
|
221
|
+
args_str = ", ".join(param_strs)
|
|
222
|
+
function_str = f"{tool_name}({args_str}) -> str"
|
|
223
|
+
|
|
224
|
+
# Create the tool
|
|
225
|
+
tool = VectaraTool.from_defaults(
|
|
226
|
+
fn=function,
|
|
227
|
+
name=tool_name,
|
|
228
|
+
description=function_str + "\n" + tool_description,
|
|
229
|
+
fn_schema=fn_schema,
|
|
230
|
+
tool_type=ToolType.QUERY,
|
|
231
|
+
)
|
|
232
|
+
return tool
|
|
233
|
+
|
|
174
234
|
def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict], fixed_filter: str) -> str:
|
|
175
235
|
"""
|
|
176
236
|
Build filter string for Vectara from kwargs
|
|
@@ -196,7 +256,7 @@ def _build_filter_string(kwargs: Dict[str, Any], tool_args_type: Dict[str, dict]
|
|
|
196
256
|
|
|
197
257
|
if value is PydanticUndefined:
|
|
198
258
|
raise ValueError(
|
|
199
|
-
f"Value of argument {key} is undefined, and this is invalid.
|
|
259
|
+
f"Value of argument {key} is undefined, and this is invalid."
|
|
200
260
|
"Please form proper arguments and try again."
|
|
201
261
|
)
|
|
202
262
|
|
|
@@ -320,6 +380,8 @@ class VectaraToolFactory:
|
|
|
320
380
|
self.vectara_corpus_key = vectara_corpus_key
|
|
321
381
|
self.vectara_api_key = vectara_api_key
|
|
322
382
|
self.num_corpora = len(vectara_corpus_key.split(","))
|
|
383
|
+
self.cache_expiry = 60 * 60 # 1 hour
|
|
384
|
+
self.max_cache_size = 128
|
|
323
385
|
|
|
324
386
|
def create_search_tool(
|
|
325
387
|
self,
|
|
@@ -392,7 +454,7 @@ class VectaraToolFactory:
|
|
|
392
454
|
)
|
|
393
455
|
|
|
394
456
|
# Dynamically generate the search function
|
|
395
|
-
def search_function(*args, **kwargs) -> ToolOutput:
|
|
457
|
+
def search_function(*args: Any, **kwargs: Any) -> ToolOutput:
|
|
396
458
|
"""
|
|
397
459
|
Dynamically generated function for semantic search Vectara.
|
|
398
460
|
"""
|
|
@@ -404,6 +466,7 @@ class VectaraToolFactory:
|
|
|
404
466
|
|
|
405
467
|
query = kwargs.pop("query")
|
|
406
468
|
top_k = kwargs.pop("top_k", 10)
|
|
469
|
+
summarize = kwargs.pop("summarize", True)
|
|
407
470
|
try:
|
|
408
471
|
filter_string = _build_filter_string(kwargs, tool_args_type, fixed_filter)
|
|
409
472
|
except ValueError as e:
|
|
@@ -451,7 +514,11 @@ class VectaraToolFactory:
|
|
|
451
514
|
if doc.id_ in unique_ids:
|
|
452
515
|
continue
|
|
453
516
|
unique_ids.add(doc.id_)
|
|
454
|
-
|
|
517
|
+
if summarize:
|
|
518
|
+
summary = summarize_vectara_document(self.vectara_corpus_key, self.vectara_api_key, doc.id_)
|
|
519
|
+
tool_output += f"document_id: '{doc.id_}'\nmetadata: '{doc.metadata}'\nsummary: '{summary}'\n\n"
|
|
520
|
+
else:
|
|
521
|
+
tool_output += f"document_id: '{doc.id_}'\nmetadata: '{doc.metadata}'\n\n"
|
|
455
522
|
out = ToolOutput(
|
|
456
523
|
tool_name=search_function.__name__,
|
|
457
524
|
content=tool_output,
|
|
@@ -460,42 +527,22 @@ class VectaraToolFactory:
|
|
|
460
527
|
)
|
|
461
528
|
return out
|
|
462
529
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
inspect.Parameter(
|
|
466
|
-
|
|
467
|
-
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
468
|
-
default=field_info.default,
|
|
469
|
-
annotation=field_info,
|
|
470
|
-
)
|
|
471
|
-
for field_name, field_info in fields.items()
|
|
530
|
+
base_params = [
|
|
531
|
+
inspect.Parameter("query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str),
|
|
532
|
+
inspect.Parameter("top_k", inspect.Parameter.POSITIONAL_OR_KEYWORD, default=10, annotation=int),
|
|
533
|
+
inspect.Parameter("summarize", inspect.Parameter.POSITIONAL_OR_KEYWORD, default=True, annotation=bool),
|
|
472
534
|
]
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
search_function.__signature__ = sig
|
|
477
|
-
search_function.__annotations__["return"] = dict[str, Any]
|
|
478
|
-
search_function.__name__ = "_" + re.sub(r"[^A-Za-z0-9_]", "_", tool_name)
|
|
479
|
-
|
|
480
|
-
# Create the tool function signature string
|
|
481
|
-
fields = []
|
|
482
|
-
for name, field in tool_args_schema.model_fields.items():
|
|
483
|
-
annotation = field.annotation
|
|
484
|
-
type_name = annotation.__name__ if hasattr(annotation, '__name__') else str(annotation)
|
|
485
|
-
fields.append(f"{name}: {type_name}")
|
|
486
|
-
args_str = ", ".join(fields)
|
|
487
|
-
function_str = f"{tool_name}({args_str}) -> str"
|
|
488
|
-
|
|
489
|
-
# Create the tool
|
|
490
|
-
search_tool_extra_desc = """
|
|
491
|
-
The response includes metadata about each relevant document, but NOT the text itself.
|
|
535
|
+
search_tool_extra_desc = tool_description + "\n" + """
|
|
536
|
+
The response includes metadata about each relevant document.
|
|
537
|
+
If summarize=True, it also includes a summary of each document.
|
|
492
538
|
"""
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
539
|
+
|
|
540
|
+
tool = _create_tool_from_dynamic_function(
|
|
541
|
+
search_function,
|
|
542
|
+
tool_name,
|
|
543
|
+
search_tool_extra_desc,
|
|
544
|
+
base_params,
|
|
545
|
+
tool_args_schema,
|
|
499
546
|
)
|
|
500
547
|
return tool
|
|
501
548
|
|
|
@@ -595,11 +642,11 @@ class VectaraToolFactory:
|
|
|
595
642
|
vectara_corpus_key=self.vectara_corpus_key,
|
|
596
643
|
x_source_str="vectara-agentic",
|
|
597
644
|
vectara_base_url=vectara_base_url,
|
|
598
|
-
|
|
645
|
+
vectara_verify_ssl=vectara_verify_ssl,
|
|
599
646
|
)
|
|
600
647
|
|
|
601
648
|
# Dynamically generate the RAG function
|
|
602
|
-
def rag_function(*args, **kwargs) -> ToolOutput:
|
|
649
|
+
def rag_function(*args: Any, **kwargs: Any) -> ToolOutput:
|
|
603
650
|
"""
|
|
604
651
|
Dynamically generated function for RAG query with Vectara.
|
|
605
652
|
"""
|
|
@@ -719,39 +766,15 @@ class VectaraToolFactory:
|
|
|
719
766
|
)
|
|
720
767
|
return out
|
|
721
768
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
inspect.Parameter(
|
|
725
|
-
name=field_name,
|
|
726
|
-
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
727
|
-
default=field_info.default,
|
|
728
|
-
annotation=field_info,
|
|
729
|
-
)
|
|
730
|
-
for field_name, field_info in fields.items()
|
|
769
|
+
base_params = [
|
|
770
|
+
inspect.Parameter("query", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=str),
|
|
731
771
|
]
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
# Create the tool function signature string
|
|
740
|
-
fields = []
|
|
741
|
-
for name, field in tool_args_schema.model_fields.items():
|
|
742
|
-
annotation = field.annotation
|
|
743
|
-
type_name = annotation.__name__ if hasattr(annotation, '__name__') else str(annotation)
|
|
744
|
-
fields.append(f"{name}: {type_name}")
|
|
745
|
-
args_str = ", ".join(fields)
|
|
746
|
-
function_str = f"{tool_name}({args_str}) -> str"
|
|
747
|
-
|
|
748
|
-
# Create the tool
|
|
749
|
-
tool = VectaraTool.from_defaults(
|
|
750
|
-
fn=rag_function,
|
|
751
|
-
name=tool_name,
|
|
752
|
-
description=function_str + ". " + tool_description,
|
|
753
|
-
fn_schema=tool_args_schema,
|
|
754
|
-
tool_type=ToolType.QUERY,
|
|
772
|
+
tool = _create_tool_from_dynamic_function(
|
|
773
|
+
rag_function,
|
|
774
|
+
tool_name,
|
|
775
|
+
tool_description,
|
|
776
|
+
base_params,
|
|
777
|
+
tool_args_schema,
|
|
755
778
|
)
|
|
756
779
|
return tool
|
|
757
780
|
|
vectara_agentic/types.py
CHANGED
|
@@ -12,6 +12,7 @@ class AgentType(Enum):
|
|
|
12
12
|
|
|
13
13
|
REACT = "REACT"
|
|
14
14
|
OPENAI = "OPENAI"
|
|
15
|
+
FUNCTION_CALLING = "FUNCTION_CALLING"
|
|
15
16
|
LLMCOMPILER = "LLMCOMPILER"
|
|
16
17
|
LATS = "LATS"
|
|
17
18
|
|
|
@@ -57,6 +58,11 @@ class ToolType(Enum):
|
|
|
57
58
|
QUERY = "query"
|
|
58
59
|
ACTION = "action"
|
|
59
60
|
|
|
61
|
+
class AgentConfigType(Enum):
|
|
62
|
+
"""Enumeration for different types of agent configurations."""
|
|
63
|
+
DEFAULT = "default"
|
|
64
|
+
FALLBACK = "fallback"
|
|
65
|
+
|
|
60
66
|
|
|
61
67
|
# classes for Agent responses
|
|
62
68
|
ToolOutput = LI_ToolOutput
|
vectara_agentic/utils.py
CHANGED
|
@@ -5,6 +5,8 @@ Utilities for the Vectara agentic.
|
|
|
5
5
|
from typing import Tuple, Callable, Optional
|
|
6
6
|
from functools import lru_cache
|
|
7
7
|
from inspect import signature
|
|
8
|
+
import json
|
|
9
|
+
import requests
|
|
8
10
|
|
|
9
11
|
import tiktoken
|
|
10
12
|
|
|
@@ -17,13 +19,13 @@ from .agent_config import AgentConfig
|
|
|
17
19
|
|
|
18
20
|
provider_to_default_model_name = {
|
|
19
21
|
ModelProvider.OPENAI: "gpt-4o",
|
|
20
|
-
ModelProvider.ANTHROPIC: "claude-3-
|
|
22
|
+
ModelProvider.ANTHROPIC: "claude-3-7-sonnet-20250219",
|
|
21
23
|
ModelProvider.TOGETHER: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
22
24
|
ModelProvider.GROQ: "llama-3.3-70b-versatile",
|
|
23
25
|
ModelProvider.FIREWORKS: "accounts/fireworks/models/firefunction-v2",
|
|
24
26
|
ModelProvider.BEDROCK: "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
|
25
27
|
ModelProvider.COHERE: "command-r-plus",
|
|
26
|
-
ModelProvider.GEMINI: "models/gemini-
|
|
28
|
+
ModelProvider.GEMINI: "models/gemini-2.0-flash",
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
DEFAULT_MODEL_PROVIDER = ModelProvider.OPENAI
|
|
@@ -90,35 +92,50 @@ def get_llm(
|
|
|
90
92
|
Get the LLM for the specified role, using the provided config
|
|
91
93
|
or a default if none is provided.
|
|
92
94
|
"""
|
|
95
|
+
max_tokens = 8192
|
|
93
96
|
model_provider, model_name = _get_llm_params_for_role(role, config)
|
|
94
97
|
if model_provider == ModelProvider.OPENAI:
|
|
95
98
|
llm = OpenAI(model=model_name, temperature=0,
|
|
96
99
|
is_function_calling_model=True,
|
|
97
|
-
strict=True
|
|
100
|
+
strict=True,
|
|
101
|
+
max_tokens=max_tokens
|
|
102
|
+
)
|
|
98
103
|
elif model_provider == ModelProvider.ANTHROPIC:
|
|
99
|
-
llm = Anthropic(model=model_name, temperature=0)
|
|
104
|
+
llm = Anthropic(model=model_name, temperature=0, max_tokens=max_tokens)
|
|
100
105
|
elif model_provider == ModelProvider.GEMINI:
|
|
101
106
|
from llama_index.llms.gemini import Gemini
|
|
102
|
-
llm = Gemini(
|
|
107
|
+
llm = Gemini(
|
|
108
|
+
model=model_name, temperature=0,
|
|
109
|
+
is_function_calling_model=True,
|
|
110
|
+
max_tokens=max_tokens
|
|
111
|
+
)
|
|
103
112
|
elif model_provider == ModelProvider.TOGETHER:
|
|
104
113
|
from llama_index.llms.together import TogetherLLM
|
|
105
|
-
llm = TogetherLLM(
|
|
114
|
+
llm = TogetherLLM(
|
|
115
|
+
model=model_name, temperature=0,
|
|
116
|
+
is_function_calling_model=True,
|
|
117
|
+
max_tokens=max_tokens
|
|
118
|
+
)
|
|
106
119
|
elif model_provider == ModelProvider.GROQ:
|
|
107
120
|
from llama_index.llms.groq import Groq
|
|
108
|
-
llm = Groq(
|
|
121
|
+
llm = Groq(
|
|
122
|
+
model=model_name, temperature=0,
|
|
123
|
+
is_function_calling_model=True, max_tokens=max_tokens
|
|
124
|
+
)
|
|
109
125
|
elif model_provider == ModelProvider.FIREWORKS:
|
|
110
126
|
from llama_index.llms.fireworks import Fireworks
|
|
111
|
-
llm = Fireworks(model=model_name, temperature=0)
|
|
127
|
+
llm = Fireworks(model=model_name, temperature=0, max_tokens=max_tokens)
|
|
112
128
|
elif model_provider == ModelProvider.BEDROCK:
|
|
113
129
|
from llama_index.llms.bedrock import Bedrock
|
|
114
|
-
llm = Bedrock(model=model_name, temperature=0)
|
|
130
|
+
llm = Bedrock(model=model_name, temperature=0, max_tokens=max_tokens)
|
|
115
131
|
elif model_provider == ModelProvider.COHERE:
|
|
116
132
|
from llama_index.llms.cohere import Cohere
|
|
117
|
-
llm = Cohere(model=model_name, temperature=0)
|
|
133
|
+
llm = Cohere(model=model_name, temperature=0, max_tokens=max_tokens)
|
|
118
134
|
elif model_provider == ModelProvider.PRIVATE:
|
|
119
135
|
from llama_index.llms.openai_like import OpenAILike
|
|
120
136
|
llm = OpenAILike(model=model_name, temperature=0, is_function_calling_model=True,is_chat_model=True,
|
|
121
|
-
api_base=config.private_llm_api_base, api_key=config.private_llm_api_key
|
|
137
|
+
api_base=config.private_llm_api_base, api_key=config.private_llm_api_key,
|
|
138
|
+
max_tokens=max_tokens)
|
|
122
139
|
else:
|
|
123
140
|
raise ValueError(f"Unknown LLM provider: {model_provider}")
|
|
124
141
|
return llm
|
|
@@ -141,3 +158,25 @@ def remove_self_from_signature(func):
|
|
|
141
158
|
new_sig = sig.replace(parameters=params)
|
|
142
159
|
func.__signature__ = new_sig
|
|
143
160
|
return func
|
|
161
|
+
|
|
162
|
+
def summarize_vectara_document(corpus_key: str, vectara_api_key, doc_id: str) -> str:
|
|
163
|
+
"""
|
|
164
|
+
Summarize a document in a Vectara corpus using the Vectara API.
|
|
165
|
+
"""
|
|
166
|
+
url = f"https://api.vectara.io/v2/corpora/{corpus_key}/documents/{doc_id}/summarize"
|
|
167
|
+
|
|
168
|
+
payload = json.dumps({
|
|
169
|
+
"llm_name": "gpt-4o",
|
|
170
|
+
"model_parameters": {},
|
|
171
|
+
"stream_response": False
|
|
172
|
+
})
|
|
173
|
+
headers = {
|
|
174
|
+
'Content-Type': 'application/json',
|
|
175
|
+
'Accept': 'application/json',
|
|
176
|
+
'x-api-key': vectara_api_key
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
response = requests.request("POST", url, headers=headers, data=payload, timeout=60)
|
|
180
|
+
if response.status_code != 200:
|
|
181
|
+
return f"Vectara Summarization failed with error code {response.status_code}, error={response.text}"
|
|
182
|
+
return json.loads(response.text)["summary"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: vectara_agentic
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
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
|
|
@@ -41,18 +41,18 @@ Requires-Dist: llama-index-tools-exa==0.3.0
|
|
|
41
41
|
Requires-Dist: tavily-python==0.5.1
|
|
42
42
|
Requires-Dist: exa-py==1.8.9
|
|
43
43
|
Requires-Dist: yahoo-finance==1.4.0
|
|
44
|
-
Requires-Dist: openinference-instrumentation-llama-index==3.
|
|
45
|
-
Requires-Dist: opentelemetry-proto==1.
|
|
46
|
-
Requires-Dist: arize-phoenix==
|
|
47
|
-
Requires-Dist: arize-phoenix-otel==0.
|
|
48
|
-
Requires-Dist: protobuf==
|
|
44
|
+
Requires-Dist: openinference-instrumentation-llama-index==3.3.3
|
|
45
|
+
Requires-Dist: opentelemetry-proto==1.31.0
|
|
46
|
+
Requires-Dist: arize-phoenix==8.14.1
|
|
47
|
+
Requires-Dist: arize-phoenix-otel==0.8.0
|
|
48
|
+
Requires-Dist: protobuf==5.29.3
|
|
49
49
|
Requires-Dist: tokenizers>=0.20
|
|
50
|
-
Requires-Dist: pydantic==2.10.
|
|
50
|
+
Requires-Dist: pydantic==2.10.6
|
|
51
51
|
Requires-Dist: retrying==1.3.4
|
|
52
52
|
Requires-Dist: python-dotenv==1.0.1
|
|
53
53
|
Requires-Dist: tiktoken==0.9.0
|
|
54
54
|
Requires-Dist: cloudpickle>=3.1.1
|
|
55
|
-
Requires-Dist: httpx==0.
|
|
55
|
+
Requires-Dist: httpx==0.28.1
|
|
56
56
|
Dynamic: author
|
|
57
57
|
Dynamic: author-email
|
|
58
58
|
Dynamic: classifier
|
|
@@ -60,6 +60,7 @@ Dynamic: description
|
|
|
60
60
|
Dynamic: description-content-type
|
|
61
61
|
Dynamic: home-page
|
|
62
62
|
Dynamic: keywords
|
|
63
|
+
Dynamic: license-file
|
|
63
64
|
Dynamic: project-url
|
|
64
65
|
Dynamic: requires-dist
|
|
65
66
|
Dynamic: requires-python
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
tests/endpoint.py,sha256=frnpdZQpnuQNNKNYgAn2rFTarNG8MCJaNA77Bw_W22A,1420
|
|
3
|
+
tests/test_agent.py,sha256=CU7Zdb1J5kFjrkkIeDr70W1wsikYuOvoYKGYpg4EPxM,6678
|
|
4
|
+
tests/test_agent_planning.py,sha256=_mj73TNP9yUjkUJ-X31r-cQYreJ4qatXOtMrRvVpF4Y,2411
|
|
5
|
+
tests/test_agent_type.py,sha256=JM0Q2GBGHSADoBacz_DW551zWSfbpf7qa8xXqtyWsc4,5671
|
|
6
|
+
tests/test_fallback.py,sha256=M5YD7NHZ0joVU1frYIr9_OiRAIje5mrXrYVcekzlyGs,2829
|
|
7
|
+
tests/test_private_llm.py,sha256=CY-_rCpxGUuxnZ3ypkodw5Jj-sJCNdh6rLbCvULwuJI,2247
|
|
8
|
+
tests/test_tools.py,sha256=IVKn0HoS2erTCr1mOEGzTkktiY0PCfKNvqnD_pizjOg,3977
|
|
9
|
+
tests/test_workflow.py,sha256=lVyrVHdRO5leYNbYtHTmKqMX0c8_xehCpUA7cXQKVsc,2175
|
|
10
|
+
vectara_agentic/__init__.py,sha256=2GLDS3U6KckK-dBRl9v_x1kSV507gEhjOfuMmmu0Qxg,850
|
|
11
|
+
vectara_agentic/_callback.py,sha256=5PfqjLmuaZIR6dnqmhniTD_zwCgfi7kOu-nexb6Kss4,9688
|
|
12
|
+
vectara_agentic/_observability.py,sha256=fTL3KW0jQU-_JSpFgjO6-XzgDut_oiq9kt4QR-FkSqU,3804
|
|
13
|
+
vectara_agentic/_prompts.py,sha256=LYyiOAiC8imz3U7MSJiuCYAP39afsp7ycXY7-9biyJI,9314
|
|
14
|
+
vectara_agentic/_version.py,sha256=FGUM5lA5uZpmWWB52dt2AMCqWcU0M9b-2BB-raX-EN4,65
|
|
15
|
+
vectara_agentic/agent.py,sha256=nbBl66n56kjEZX4Zconb9IZjESzpjBZIEQdL4uLfurI,43333
|
|
16
|
+
vectara_agentic/agent_config.py,sha256=y1hSvU5ns0cE2R7BqF65LFstixF1ytJcoVgicGXo7w0,3691
|
|
17
|
+
vectara_agentic/agent_endpoint.py,sha256=QIMejCLlpW2qzXxeDAxv3anF46XMDdVMdKGWhJh3azY,1996
|
|
18
|
+
vectara_agentic/db_tools.py,sha256=VUdcjDFPwauFd2A92mXNYZnCjeMiTzcTka7S5At_3oQ,3595
|
|
19
|
+
vectara_agentic/sub_query_workflow.py,sha256=eS1S7l5PdyLPLZqxUJSR0oM2kvHb4raPGHk8t8td9sc,10939
|
|
20
|
+
vectara_agentic/tools.py,sha256=RpPGWiPHe-9ZiOxNz389W-gNWxegg7m4RlEx_pH9_W0,42881
|
|
21
|
+
vectara_agentic/tools_catalog.py,sha256=oiw3wAfbpFhh0_6rMvZsyPqWV6QIzHqhZCNzqRxuyV8,4818
|
|
22
|
+
vectara_agentic/types.py,sha256=HcS7vR8P2v2xQTlOc6ZFV2vvlr3OpzSNWhtcLMxqUZc,1792
|
|
23
|
+
vectara_agentic/utils.py,sha256=U4VWCyrvpXfPb9SJpd4Xj7rJCN-cZCNReNm9_uQjnlk,6759
|
|
24
|
+
vectara_agentic-0.2.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
25
|
+
vectara_agentic-0.2.7.dist-info/METADATA,sha256=z-IFDKlGmNh9QSIUV4xOOCYKF-PrTUnDukp8M5BNMe4,25046
|
|
26
|
+
vectara_agentic-0.2.7.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
27
|
+
vectara_agentic-0.2.7.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
|
|
28
|
+
vectara_agentic-0.2.7.dist-info/RECORD,,
|