vectara-agentic 0.2.4__py3-none-any.whl → 0.2.6__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.

@@ -13,7 +13,7 @@ class DBTool(ABC):
13
13
  """
14
14
  A base class for vectara-agentic database tools extensions
15
15
  """
16
- def __init__(self, load_data_fn: Callable, max_rows: int = 500):
16
+ def __init__(self, load_data_fn: Callable, max_rows: int = 1000):
17
17
  self.load_data_fn = load_data_fn
18
18
  self.max_rows = max_rows
19
19
 
@@ -39,7 +39,7 @@ class DBLoadData(DBTool):
39
39
  if num_rows > self.max_rows:
40
40
  return [
41
41
  f"The query is expected to return more than {self.max_rows} rows. "
42
- "Please refine your query to make it return less rows."
42
+ "Please refactor your query to make it return less rows. "
43
43
  ]
44
44
  try:
45
45
  res = self.load_data_fn(query)
@@ -0,0 +1,292 @@
1
+ """
2
+ This module contains the SubQuestionQueryEngine workflow, which is a workflow
3
+ that takes a user question and a list of tools, and outputs a list of sub-questions.
4
+ """
5
+ import json
6
+ from pydantic import BaseModel
7
+
8
+ from llama_index.core.workflow import (
9
+ step,
10
+ Context,
11
+ Workflow,
12
+ Event,
13
+ StartEvent,
14
+ StopEvent,
15
+ )
16
+
17
+ class SubQuestionQueryWorkflow(Workflow):
18
+ """
19
+ Workflow for sub-question query engine.
20
+ """
21
+
22
+ # Workflow inputs/outputs
23
+ class InputsModel(BaseModel):
24
+ """
25
+ Inputs for the workflow.
26
+ """
27
+ query: str
28
+
29
+ class OutputsModel(BaseModel):
30
+ """
31
+ Outputs for the workflow.
32
+ """
33
+ response: str
34
+
35
+ # Workflow Event types
36
+ class QueryEvent(Event):
37
+ """Event for a query."""
38
+ question: str
39
+
40
+ class AnswerEvent(Event):
41
+ """Event for an answer."""
42
+ question: str
43
+ answer: str
44
+
45
+ @step
46
+ async def query(self, ctx: Context, ev: StartEvent) -> QueryEvent:
47
+ """
48
+ Given a user question, and a list of tools, output a list of relevant
49
+ sub-questions, such that the answers to all the sub-questions put together
50
+ will answer the question.
51
+ """
52
+ if not hasattr(ev, "inputs"):
53
+ raise ValueError("No inputs provided to workflow Start Event.")
54
+ if hasattr(ev, "inputs") and not isinstance(ev.inputs, self.InputsModel):
55
+ raise ValueError(f"Expected inputs to be of type {self.InputsModel}")
56
+ if hasattr(ev, "inputs"):
57
+ query = ev.inputs.query
58
+ await ctx.set("original_query", query)
59
+ print(f"Query is {await ctx.get('original_query')}")
60
+
61
+ if hasattr(ev, "agent"):
62
+ await ctx.set("agent", ev.agent)
63
+ else:
64
+ raise ValueError("Agent not provided to workflow Start Event.")
65
+ chat_history = [str(msg) for msg in ev.agent.memory.get()]
66
+
67
+ if hasattr(ev, "llm"):
68
+ await ctx.set("llm", ev.llm)
69
+ else:
70
+ raise ValueError("LLM not provided to workflow Start Event.")
71
+
72
+ if hasattr(ev, "tools"):
73
+ await ctx.set("tools", ev.tools)
74
+ else:
75
+ raise ValueError("Tools not provided to workflow Start Event.")
76
+
77
+ if hasattr(ev, "verbose"):
78
+ await ctx.set("verbose", ev.verbose)
79
+ else:
80
+ await ctx.set("verbose", False)
81
+
82
+ llm = await ctx.get("llm")
83
+ response = llm.complete(
84
+ f"""
85
+ Given a user question, and a list of tools, output a list of
86
+ relevant sub-questions, such that the answers to all the
87
+ sub-questions put together will answer the question.
88
+ Order the sub-questions in the right order if there are dependencies.
89
+ Make sure sub-questions do not result in duplicate tool calling.
90
+ Respond in pure JSON without any markdown, like this:
91
+ {{
92
+ "sub_questions": [
93
+ "What is the population of San Francisco?",
94
+ "What is the budget of San Francisco?",
95
+ "What is the GDP of San Francisco?"
96
+ ]
97
+ }}
98
+ As an example, for the question
99
+ "what is the name of the mayor of the largest city within 50 miles of San Francisco?",
100
+ the sub-questions could be:
101
+ - What is the largest city within 50 miles of San Francisco? (answer is San Jose)
102
+ - What is the name of the mayor of San Jose?
103
+ Here is the user question: {await ctx.get('original_query')}.
104
+ Here are previous chat messages: {chat_history}.
105
+ And here is the list of tools: {await ctx.get('tools')}
106
+ """,
107
+ )
108
+
109
+ if await ctx.get("verbose"):
110
+ print(f"Sub-questions are {response}")
111
+
112
+ response_obj = json.loads(str(response))
113
+ sub_questions = response_obj["sub_questions"]
114
+
115
+ await ctx.set("sub_question_count", len(sub_questions))
116
+
117
+ for question in sub_questions:
118
+ ctx.send_event(self.QueryEvent(question=question))
119
+
120
+ return None
121
+
122
+ @step(num_workers=3)
123
+ async def sub_question(self, ctx: Context, ev: QueryEvent) -> AnswerEvent:
124
+ """
125
+ Given a sub-question, return the answer to the sub-question, using the agent.
126
+ """
127
+ if await ctx.get("verbose"):
128
+ print(f"Sub-question is {ev.question}")
129
+ agent = await ctx.get("agent")
130
+ response = await agent.achat(ev.question)
131
+ return self.AnswerEvent(question=ev.question, answer=str(response))
132
+
133
+ @step
134
+ async def combine_answers(
135
+ self, ctx: Context, ev: AnswerEvent
136
+ ) -> StopEvent | None:
137
+ """
138
+ Given a list of answers to sub-questions, combine them into a single answer.
139
+ """
140
+ ready = ctx.collect_events(
141
+ ev, [self.AnswerEvent] * await ctx.get("sub_question_count")
142
+ )
143
+ if ready is None:
144
+ return None
145
+
146
+ answers = "\n\n".join(
147
+ [
148
+ f"Question: {event.question}: \n Answer: {event.answer}"
149
+ for event in ready
150
+ ]
151
+ )
152
+
153
+ prompt = f"""
154
+ You are given an overall question that has been split into sub-questions,
155
+ each of which has been answered. Combine the answers to all the sub-questions
156
+ into a single answer to the original question.
157
+
158
+ Original question: {await ctx.get('original_query')}
159
+
160
+ Sub-questions and answers:
161
+ {answers}
162
+ """
163
+ if await ctx.get("verbose"):
164
+ print(f"Final prompt is {prompt}")
165
+
166
+ llm = await ctx.get("llm")
167
+ response = llm.complete(prompt)
168
+
169
+ if await ctx.get("verbose"):
170
+ print("Final response is", response)
171
+
172
+ output = self.OutputsModel(response=str(response))
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? (answer is San Jose)
256
+ - What is the name of the mayor of San Jose?
257
+ Here is the user question: {await ctx.get('original_query')}.
258
+ Here are previous chat messages: {chat_history}.
259
+ And here is the list of tools: {await ctx.get('tools')}
260
+ """,
261
+ )
262
+
263
+ response_obj = json.loads(str(response))
264
+ sub_questions = response_obj["sub_questions"]
265
+
266
+ await ctx.set("sub_questions", sub_questions)
267
+ if await ctx.get("verbose"):
268
+ print(f"Sub-questions are {sub_questions}")
269
+
270
+ return self.QueryEvent(question=sub_questions[0], prev_answer="", num=0)
271
+
272
+ @step
273
+ async def sub_question(self, ctx: Context, ev: QueryEvent) -> StopEvent | QueryEvent:
274
+ """
275
+ Given a sub-question, return the answer to the sub-question, using the agent.
276
+ """
277
+ if await ctx.get("verbose"):
278
+ print(f"Sub-question is {ev.question}")
279
+ agent = await ctx.get("agent")
280
+ response = await agent.achat(ev.question)
281
+ if await ctx.get("verbose"):
282
+ print(f"Answer is {response}")
283
+
284
+ sub_questions = await ctx.get("sub_questions")
285
+ if ev.num + 1 < len(sub_questions):
286
+ return self.QueryEvent(
287
+ question=sub_questions[ev.num + 1],
288
+ prev_answer = response.response,
289
+ num=ev.num + 1)
290
+
291
+ output = self.OutputsModel(response=response.response)
292
+ return StopEvent(result=output)
vectara_agentic/tools.py CHANGED
@@ -112,28 +112,36 @@ class VectaraTool(FunctionTool):
112
112
  return vectara_tool
113
113
 
114
114
  def __eq__(self, other):
115
+ if not isinstance(other, VectaraTool):
116
+ return False
117
+
115
118
  if self.metadata.tool_type != other.metadata.tool_type:
116
119
  return False
117
120
 
118
- if self.metadata.name != other.metadata.name or self.metadata.description != other.metadata.description:
121
+ if self.metadata.name != other.metadata.name:
119
122
  return False
120
123
 
121
- # Check if fn_schema is an instance of a BaseModel or a class itself (metaclass)
122
- self_schema_dict = self.metadata.fn_schema.model_fields
123
- other_schema_dict = other.metadata.fn_schema.model_fields
124
- is_equal = True
125
- for key in self_schema_dict.keys():
126
- if key not in other_schema_dict:
127
- is_equal = False
128
- break
129
- if (
130
- self_schema_dict[key].annotation != other_schema_dict[key].annotation
131
- or self_schema_dict[key].description != other_schema_dict[key].description
132
- or self_schema_dict[key].is_required() != other_schema_dict[key].is_required()
133
- ):
134
- is_equal = False
135
- break
136
- return is_equal
124
+ # If schema is a dict-like object, compare the dict representation
125
+ try:
126
+ # Try to get schema as dict if possible
127
+ if hasattr(self.metadata.fn_schema, 'schema'):
128
+ self_schema = self.metadata.fn_schema.schema
129
+ other_schema = other.metadata.fn_schema.schema
130
+
131
+ # Compare only properties and required fields
132
+ self_props = self_schema.get('properties', {})
133
+ other_props = other_schema.get('properties', {})
134
+
135
+ self_required = self_schema.get('required', [])
136
+ other_required = other_schema.get('required', [])
137
+
138
+ return (self_props.keys() == other_props.keys() and
139
+ set(self_required) == set(other_required))
140
+ except Exception:
141
+ # If any exception occurs during schema comparison, fall back to name comparison
142
+ pass
143
+
144
+ return True
137
145
 
138
146
  def call(
139
147
  self, *args: Any, ctx: Optional[Context] = None, **kwargs: Any
@@ -312,6 +320,8 @@ class VectaraToolFactory:
312
320
  self.vectara_corpus_key = vectara_corpus_key
313
321
  self.vectara_api_key = vectara_api_key
314
322
  self.num_corpora = len(vectara_corpus_key.split(","))
323
+ self.cache_expiry = 60 * 60 # 1 hour
324
+ self.max_cache_size = 128
315
325
 
316
326
  def create_search_tool(
317
327
  self,
@@ -443,7 +453,7 @@ class VectaraToolFactory:
443
453
  if doc.id_ in unique_ids:
444
454
  continue
445
455
  unique_ids.add(doc.id_)
446
- tool_output += f"document '{doc.id_}' metadata: {doc.metadata}\n"
456
+ tool_output += f"document_id: '{doc.id_}'\nmetadata: '{doc.metadata}'\n"
447
457
  out = ToolOutput(
448
458
  tool_name=search_function.__name__,
449
459
  content=tool_output,
@@ -479,10 +489,13 @@ class VectaraToolFactory:
479
489
  function_str = f"{tool_name}({args_str}) -> str"
480
490
 
481
491
  # Create the tool
492
+ search_tool_extra_desc = """
493
+ The response includes metadata about each relevant document, but NOT the text itself.
494
+ """
482
495
  tool = VectaraTool.from_defaults(
483
496
  fn=search_function,
484
497
  name=tool_name,
485
- description=function_str + ". " + tool_description,
498
+ description=function_str + "\n" + tool_description + '\n' + search_tool_extra_desc,
486
499
  fn_schema=tool_args_schema,
487
500
  tool_type=ToolType.QUERY,
488
501
  )
@@ -623,8 +636,8 @@ class VectaraToolFactory:
623
636
  mmr_diversity_bias=mmr_diversity_bias,
624
637
  udf_expression=udf_expression,
625
638
  rerank_chain=rerank_chain,
626
- n_sentence_before=n_sentences_before,
627
- n_sentence_after=n_sentences_after,
639
+ n_sentences_before=n_sentences_before,
640
+ n_sentences_after=n_sentences_after,
628
641
  offset=offset,
629
642
  lambda_val=lambda_val,
630
643
  semantics=semantics,
vectara_agentic/types.py CHANGED
@@ -57,6 +57,11 @@ class ToolType(Enum):
57
57
  QUERY = "query"
58
58
  ACTION = "action"
59
59
 
60
+ class AgentConfigType(Enum):
61
+ """Enumeration for different types of agent configurations."""
62
+ DEFAULT = "default"
63
+ FALLBACK = "fallback"
64
+
60
65
 
61
66
  # classes for Agent responses
62
67
  ToolOutput = LI_ToolOutput
vectara_agentic/utils.py CHANGED
@@ -17,13 +17,13 @@ from .agent_config import AgentConfig
17
17
 
18
18
  provider_to_default_model_name = {
19
19
  ModelProvider.OPENAI: "gpt-4o",
20
- ModelProvider.ANTHROPIC: "claude-3-5-sonnet-20241022",
20
+ ModelProvider.ANTHROPIC: "claude-3-7-sonnet-20250219",
21
21
  ModelProvider.TOGETHER: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
22
22
  ModelProvider.GROQ: "llama-3.3-70b-versatile",
23
23
  ModelProvider.FIREWORKS: "accounts/fireworks/models/firefunction-v2",
24
24
  ModelProvider.BEDROCK: "anthropic.claude-3-5-sonnet-20241022-v2:0",
25
25
  ModelProvider.COHERE: "command-r-plus",
26
- ModelProvider.GEMINI: "models/gemini-1.5-flash",
26
+ ModelProvider.GEMINI: "models/gemini-2.0-flash",
27
27
  }
28
28
 
29
29
  DEFAULT_MODEL_PROVIDER = ModelProvider.OPENAI
@@ -92,7 +92,9 @@ def get_llm(
92
92
  """
93
93
  model_provider, model_name = _get_llm_params_for_role(role, config)
94
94
  if model_provider == ModelProvider.OPENAI:
95
- llm = OpenAI(model=model_name, temperature=0, is_function_calling_model=True)
95
+ llm = OpenAI(model=model_name, temperature=0,
96
+ is_function_calling_model=True,
97
+ strict=True)
96
98
  elif model_provider == ModelProvider.ANTHROPIC:
97
99
  llm = Anthropic(model=model_name, temperature=0)
98
100
  elif model_provider == ModelProvider.GEMINI:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: vectara_agentic
3
- Version: 0.2.4
3
+ Version: 0.2.6
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,13 +16,13 @@ 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.22
20
- Requires-Dist: llama-index-indices-managed-vectara==0.4.1
19
+ Requires-Dist: llama-index==0.12.25
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
24
  Requires-Dist: llama-index-llms-openai==0.3.25
25
- Requires-Dist: llama-index-llms-anthropic==0.6.7
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
@@ -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.1.4
45
- Requires-Dist: opentelemetry-proto==1.26.0
46
- Requires-Dist: arize-phoenix==7.11.0
47
- Requires-Dist: arize-phoenix-otel==0.6.1
48
- Requires-Dist: protobuf==4.25.5
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.3
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.27.2
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
@@ -93,14 +94,20 @@ Dynamic: summary
93
94
  <img src="https://raw.githubusercontent.com/vectara/py-vectara-agentic/main/.github/assets/diagram1.png" alt="Agentic RAG diagram" width="100%" style="vertical-align: middle;">
94
95
  </p>
95
96
 
96
- ### Features
97
-
98
- - Enables easy creation of custom AI assistants and agents.
99
- - Create a Vectara RAG tool or search tool with a single line of code.
100
- - Supports `ReAct`, `OpenAIAgent`, `LATS` and `LLMCompiler` agent types.
101
- - Includes pre-built tools for various domains (e.g., finance, legal).
102
- - Integrates with various LLM inference services like OpenAI, Anthropic, Gemini, GROQ, Together.AI, Cohere, Bedrock and Fireworks
103
- - Built-in support for observability with Arize Phoenix
97
+ ### Key Features
98
+
99
+ - **Rapid Tool Creation:**
100
+ Build Vectara RAG tools or search tools with a single line of code.
101
+ - **Agent Flexibility:**
102
+ Supports multiple agent types including `ReAct`, `OpenAIAgent`, `LATS`, and `LLMCompiler`.
103
+ - **Pre-Built Domain Tools:**
104
+ Tools tailored for finance, legal, and other verticals.
105
+ - **Multi-LLM Integration:**
106
+ Seamless integration with OpenAI, Anthropic, Gemini, GROQ, Together.AI, Cohere, Bedrock, and Fireworks.
107
+ - **Observability:**
108
+ Built-in support with Arize Phoenix for monitoring and feedback.
109
+ - **Workflow Support:**
110
+ Extend your agent’s capabilities by defining custom workflows using the `run()` method.
104
111
 
105
112
  ### 📚 Example AI Assistants
106
113
 
@@ -200,7 +207,7 @@ agent = Agent(
200
207
 
201
208
  See the [docs](https://vectara.github.io/vectara-agentic-docs/) for additional arguments, including `agent_progress_callback` and `query_logging_callback`.
202
209
 
203
- ### 5. Run your agent
210
+ ### 5. Run a chat interaction
204
211
 
205
212
  ```python
206
213
  res = agent.chat("What was the revenue for Apple in 2021?")
@@ -213,6 +220,77 @@ Note that:
213
220
  response it's available as the `response` variable, or just use `str()`. For advanced use-cases you can look
214
221
  at other `AgentResponse` variables [such as `sources`](https://github.com/run-llama/llama_index/blob/659f9faaafbecebb6e6c65f42143c0bf19274a37/llama-index-core/llama_index/core/chat_engine/types.py#L53).
215
222
 
223
+ ## Advanced Usage: Workflows
224
+
225
+ In addition to standard chat interactions, `vectara-agentic` supports custom workflows via the `run()` method.
226
+ Workflows allow you to structure multi-step interactions where inputs and outputs are validated using Pydantic models.
227
+ To learn more about workflows read [the documentation](https://docs.llamaindex.ai/en/stable/understanding/workflows/basic_flow/)
228
+
229
+ ### Defining a Custom Workflow
230
+
231
+ Create a workflow by subclassing `llama_index.core.workflow.Workflow` and defining the input/output models:
232
+
233
+ ```python
234
+ from pydantic import BaseModel
235
+ from llama_index.core.workflow import (
236
+ StartEvent,StopEvent, Workflow, step,
237
+ )
238
+
239
+ class MyWorkflow(Workflow):
240
+ class InputsModel(BaseModel):
241
+ query: str
242
+
243
+ class OutputsModel(BaseModel):
244
+ answer: str
245
+
246
+ @step
247
+ async def my_step(self, ev: StartEvent) -> StopEvent:
248
+ # do something here
249
+ return StopEvent(result="Hello, world!")
250
+ ```
251
+
252
+ When the `run()` method in vectara-agentic is invoked, it calls the workflow with the following variables in the StartEvent:
253
+ * `agent`: the agent object used to call `run()` (self)
254
+ * `tools`: the tools provided to the agent. Those can be used as needed in the flow.
255
+ * `llm`: a pointer to a LlamaIndex llm, so it can be used in the workflow. For example, one of the steps may call `llm.acomplete(prompt)`
256
+ * `verbose`: controls whether extra debug information is displayed
257
+ * `inputs`: this is the actual inputs to the workflow provided by the call to `run()` and must be of type `InputsModel`
258
+
259
+ ### Using the Workflow with Your Agent
260
+
261
+ When initializing your agent, pass the workflow class using the `workflow_cls` parameter:
262
+
263
+ ```python
264
+ agent = Agent(
265
+ tools=[query_financial_reports_tool],
266
+ topic="10-K financial reports",
267
+ custom_instructions="You are a helpful financial assistant.",
268
+ workflow_cls=MyWorkflow, # Provide your custom workflow here
269
+ workflow_timeout=120 # Optional: Set a timeout (default is 120 seconds)
270
+ )
271
+ ```
272
+
273
+ ### Running the Workflow
274
+
275
+ Prepare the inputs using your workflow’s `InputsModel` and execute the workflow using `run()`:
276
+
277
+ ```python
278
+ # Create an instance of the workflow's input model
279
+ inputs = MyWorkflow.InputsModel(query="What is Vectara?", extra_param=42)
280
+
281
+ # Run the workflow (ensure you're in an async context or use asyncio.run)
282
+ workflow_result = asyncio.run(agent.run(inputs))
283
+
284
+ # Access the output from the workflow's OutputsModel
285
+ print(workflow_result.answer)
286
+ ```
287
+
288
+ ### Using SubQuestionQueryWorkflow
289
+
290
+ vectara-agentic already includes one useful workflow you can use right away (it is also useful as an advanced example)
291
+ This workflow is called `SubQuestionQueryWorkflow` and it works by breaking a complex query into sub-queries and then
292
+ executing each sub-query with the agent until it reaches a good response.
293
+
216
294
  ## 🧰 Vectara tools
217
295
 
218
296
  `vectara-agentic` provides two helper functions to connect with Vectara RAG
@@ -353,9 +431,9 @@ The `AgentConfig` object may include the following items:
353
431
 
354
432
  If any of these are not provided, `AgentConfig` first tries to read the values from the OS environment.
355
433
 
356
- ## Configuring Vectara RAG or search tools
434
+ ## Configuring Vectara tools: rag_tool, or search_tool
357
435
 
358
- When creating a `VectaraToolFactory`, you can pass in a `vectara_api_key`, `vectara_customer_id`, and `vectara_corpus_id` to the factory.
436
+ When creating a `VectaraToolFactory`, you can pass in a `vectara_api_key`, and `vectara_corpus_key` to the factory.
359
437
 
360
438
  If not passed in, it will be taken from the environment variables (`VECTARA_API_KEY` and `VECTARA_CORPUS_KEY`). Note that `VECTARA_CORPUS_KEY` can be a single KEY or a comma-separated list of KEYs (if you want to query multiple corpora).
361
439
 
@@ -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=5iYlROsGQV_fPF9IR1JZ_ByhQ3EoaLG_40ntrCgugWo,6461
4
+ tests/test_agent_planning.py,sha256=0GEI-b7g5tV8xP_FbTfIu-a8J9s_EhDXC_9T6HS6DsU,1457
5
+ tests/test_agent_type.py,sha256=-14Y6vwYTaRJuj8VZ-c6d1vIiWpV31k2zs_frdoxR5s,2920
6
+ tests/test_fallback.py,sha256=4ZqP_7XsabhzaVgXa599PDbwp38t_XY5fMzQwr8F6Z8,2793
7
+ tests/test_private_llm.py,sha256=rPXQ-NKL2MnrMcGNEG1Zz3U8uK9pjxUfjvIl2gH9gnw,2224
8
+ tests/test_tools.py,sha256=0-2oWX8DW0WIjViNFl0xj_6JOhIdyx6zV0IlTuMzxjk,3954
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=EFHZPv0y0xF__sBHhCA8j-o21yOSHXl15GJEp-lZLy4,65
15
+ vectara_agentic/agent.py,sha256=74_2XzBvl5jPyAqqYhoxsS7PXITWBdJpxs4L_XeyZio,42561
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=KcIfUaDcv25n8iLQmZ9ZhNlKyZAKAu-3otXADukBios,10394
20
+ vectara_agentic/tools.py,sha256=xWxl1ixSCsBPjZ-GNpkjN_nXRBxvH_vr8oDauAYrIW0,41763
21
+ vectara_agentic/tools_catalog.py,sha256=oiw3wAfbpFhh0_6rMvZsyPqWV6QIzHqhZCNzqRxuyV8,4818
22
+ vectara_agentic/types.py,sha256=tLpyDY-UbFN2Iqk_fgWoOxlGexh_AQ5BaXQ593sCkRc,1750
23
+ vectara_agentic/utils.py,sha256=AUyWrL8aY67AGx6j9m00k75JRHTI44EAKtal73aMczM,5504
24
+ vectara_agentic-0.2.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
25
+ vectara_agentic-0.2.6.dist-info/METADATA,sha256=u9gIGxK3XEPeSItrUevqwJVOWWzRJ3Mqdo55-l3o098,25046
26
+ vectara_agentic-0.2.6.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
27
+ vectara_agentic-0.2.6.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
28
+ vectara_agentic-0.2.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.2)
2
+ Generator: setuptools (77.0.3)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5