vectara-agentic 0.2.22__py3-none-any.whl → 0.2.23__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_workflow.py CHANGED
@@ -1,9 +1,5 @@
1
1
  import unittest
2
2
 
3
- from pydantic import BaseModel
4
-
5
- from llama_index.core.workflow import WorkflowTimeoutError
6
-
7
3
  from vectara_agentic.agent import Agent
8
4
  from vectara_agentic.agent_config import AgentConfig
9
5
  from vectara_agentic.tools import ToolsFactory
@@ -68,7 +64,7 @@ class TestWorkflowPackage(unittest.IsolatedAsyncioTestCase):
68
64
 
69
65
  class TestWorkflowFailure(unittest.IsolatedAsyncioTestCase):
70
66
 
71
- async def test_workflow_failure(self):
67
+ async def test_workflow_failure_sub_question(self):
72
68
  tools = [ToolsFactory().create_tool(mult)] + [ToolsFactory().create_tool(add)]
73
69
  topic = "AI topic"
74
70
  instructions = "You are a helpful AI assistant."
@@ -84,50 +80,27 @@ class TestWorkflowFailure(unittest.IsolatedAsyncioTestCase):
84
80
  inputs = SubQuestionQueryWorkflow.InputsModel(
85
81
  query="Compute 5 times 3, then add 7 to the result."
86
82
  )
83
+ res = await agent.run(inputs=inputs)
84
+ self.assertIsInstance(res, SubQuestionQueryWorkflow.OutputModelOnFail)
87
85
 
88
- res = None
89
-
90
- try:
91
- res = await agent.run(inputs=inputs)
92
- except Exception as e:
93
- self.assertIsInstance(e, WorkflowTimeoutError)
94
-
95
- self.assertIsNone(res)
96
-
97
- async def test_workflow_with_fail_class(self):
86
+ async def test_workflow_failure_seq_sub_question(self):
98
87
  tools = [ToolsFactory().create_tool(mult)] + [ToolsFactory().create_tool(add)]
99
88
  topic = "AI topic"
100
89
  instructions = "You are a helpful AI assistant."
101
-
102
- class SubQuestionQueryWorkflowWithFailClass(SubQuestionQueryWorkflow):
103
- class OutputModelOnFail(BaseModel):
104
- """
105
- In case of failure, returns the user's original query
106
- """
107
- original_query: str
108
-
109
90
  agent = Agent(
110
91
  tools=tools,
111
92
  topic=topic,
112
93
  custom_instructions=instructions,
113
94
  agent_config = AgentConfig(),
114
- workflow_cls = SubQuestionQueryWorkflowWithFailClass,
95
+ workflow_cls = SequentialSubQuestionsWorkflow,
115
96
  workflow_timeout = 1
116
97
  )
117
98
 
118
- inputs = SubQuestionQueryWorkflow.InputsModel(
99
+ inputs = SequentialSubQuestionsWorkflow.InputsModel(
119
100
  query="Compute 5 times 3, then add 7 to the result."
120
101
  )
121
-
122
- res = None
123
-
124
- try:
125
- res = await agent.run(inputs=inputs)
126
- except Exception as e:
127
- assert isinstance(e, WorkflowTimeoutError)
128
-
129
- self.assertIsInstance(res, SubQuestionQueryWorkflowWithFailClass.OutputModelOnFail)
130
- self.assertEqual(res.original_query, "Compute 5 times 3, then add 7 to the result.")
102
+ res = await agent.run(inputs=inputs)
103
+ self.assertIsInstance(res, SequentialSubQuestionsWorkflow.OutputModelOnFail)
131
104
 
132
105
 
133
106
  if __name__ == "__main__":
@@ -44,6 +44,7 @@ GENERAL_INSTRUCTIONS = """
44
44
  - If you are provided with database tools use them for analytical queries (such as counting, calculating max, min, average, sum, or other statistics).
45
45
  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.
46
46
  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.
47
+ Use ANSI SQL-92 syntax for the SQL queries, and do not use any other SQL dialect.
47
48
  Before using the x_load_data with a SQL query, always follow these discovery steps:
48
49
  - call the x_list_tables tool to list of available tables in the x database.
49
50
  - Call the x_describe_tables tool to understand the schema of each table you want to query data from.
@@ -1,4 +1,4 @@
1
1
  """
2
2
  Define the version of the package.
3
3
  """
4
- __version__ = "0.2.22"
4
+ __version__ = "0.2.23"
vectara_agentic/agent.py CHANGED
@@ -15,6 +15,8 @@ from collections import Counter
15
15
  import inspect
16
16
  from inspect import Signature, Parameter, ismethod
17
17
  from pydantic import Field, create_model, ValidationError, BaseModel
18
+ from pydantic_core import PydanticUndefined
19
+
18
20
  import cloudpickle as pickle
19
21
 
20
22
  from dotenv import load_dotenv
@@ -1083,6 +1085,15 @@ class Agent:
1083
1085
  if not isinstance(inputs, self.workflow_cls.InputsModel):
1084
1086
  raise ValueError(f"Inputs must be an instance of {workflow.InputsModel}.")
1085
1087
 
1088
+ outputs_model_on_fail_cls = getattr(workflow.__class__, "OutputModelOnFail", None)
1089
+ if outputs_model_on_fail_cls:
1090
+ fields_without_default = []
1091
+ for name, field_info in outputs_model_on_fail_cls.model_fields.items():
1092
+ if field_info.default_factory is PydanticUndefined:
1093
+ fields_without_default.append(name)
1094
+ if fields_without_default:
1095
+ raise ValueError(f"Fields without default values: {fields_without_default}")
1096
+
1086
1097
  workflow_context = Context(workflow=workflow)
1087
1098
  try:
1088
1099
  # run workflow
@@ -1102,15 +1113,14 @@ class Agent:
1102
1113
  raise ValueError(f"Failed to map workflow output to model: {e}") from e
1103
1114
 
1104
1115
  except Exception as e:
1105
- outputs_model_on_fail_cls = getattr(workflow.__class__, "OutputModelOnFail", None)
1116
+ _missing = object()
1106
1117
  if outputs_model_on_fail_cls:
1107
1118
  model_fields = outputs_model_on_fail_cls.model_fields
1108
- input_dict = {
1109
- key: await workflow_context.get(key, None)
1110
- for key in model_fields
1111
- }
1112
-
1113
- # return output in the form of workflow.OutputModelOnFail(BaseModel)
1119
+ input_dict = {}
1120
+ for key in model_fields:
1121
+ value = await workflow_context.get(key, default=_missing)
1122
+ if value is not _missing:
1123
+ input_dict[key] = value
1114
1124
  output = outputs_model_on_fail_cls.model_validate(input_dict)
1115
1125
  else:
1116
1126
  print(f"Vectara Agentic: Workflow failed with unexpected error: {e}")
@@ -5,7 +5,7 @@ that takes a user question and a list of tools, and outputs a list of sub-questi
5
5
 
6
6
  import re
7
7
  import json
8
- from pydantic import BaseModel
8
+ from pydantic import BaseModel, Field
9
9
 
10
10
  from llama_index.core.workflow import (
11
11
  step,
@@ -37,6 +37,13 @@ class SubQuestionQueryWorkflow(Workflow):
37
37
 
38
38
  response: str
39
39
 
40
+ class OutputModelOnFail(BaseModel):
41
+ """
42
+ Outputs for the workflow when it fails.
43
+ """
44
+
45
+ qna: list[tuple[str,str]] = Field(default_factory=list, description="List of question-answer pairs")
46
+
40
47
  # Workflow Event types
41
48
  class QueryEvent(Event):
42
49
  """Event for a query."""
@@ -141,7 +148,7 @@ class SubQuestionQueryWorkflow(Workflow):
141
148
 
142
149
  return None
143
150
 
144
- @step(num_workers=4)
151
+ @step(num_workers=8)
145
152
  async def sub_question(self, ctx: Context, ev: QueryEvent) -> AnswerEvent:
146
153
  """
147
154
  Given a sub-question, return the answer to the sub-question, using the agent.
@@ -149,8 +156,11 @@ class SubQuestionQueryWorkflow(Workflow):
149
156
  if await ctx.get("verbose"):
150
157
  print(f"Sub-question is {ev.question}")
151
158
  agent = await ctx.get("agent")
152
- response = await agent.achat(ev.question)
153
- return self.AnswerEvent(question=ev.question, answer=str(response))
159
+ question = ev.question
160
+ response = await agent.achat(question)
161
+ answer = str(response)
162
+ await ctx.set("qna", await ctx.get("qna", []) + [(question, answer)])
163
+ return self.AnswerEvent(question=question, answer=answer)
154
164
 
155
165
  @step
156
166
  async def combine_answers(self, ctx: Context, ev: AnswerEvent) -> StopEvent | None:
@@ -209,6 +219,15 @@ class SequentialSubQuestionsWorkflow(Workflow):
209
219
 
210
220
  response: str
211
221
 
222
+ class OutputModelOnFail(BaseModel):
223
+ """
224
+ Outputs for the workflow when it fails.
225
+ """
226
+
227
+ qna: list[tuple[str,str]] = Field(
228
+ default_factory=list, description="List of question-answer pairs"
229
+ )
230
+
212
231
  # Workflow Event types
213
232
  class QueryEvent(Event):
214
233
  """Event for a query."""
@@ -322,24 +341,27 @@ class SequentialSubQuestionsWorkflow(Workflow):
322
341
  print(f"Sub-question is {ev.question}")
323
342
  agent = await ctx.get("agent")
324
343
  sub_questions = await ctx.get("sub_questions")
344
+ question = ev.question
325
345
  if ev.prev_answer:
326
346
  prev_question = sub_questions[ev.num - 1]
327
347
  prompt = f"""
328
348
  The answer to the question '{prev_question}' is: '{ev.prev_answer}'
329
- Now answer the following question: '{ev.question}'
349
+ Now answer the following question: '{question}'
330
350
  """
331
351
  response = await agent.achat(prompt)
332
352
  else:
333
- response = await agent.achat(ev.question)
353
+ response = await agent.achat(question)
354
+ answer = response.response
334
355
  if await ctx.get("verbose"):
335
- print(f"Answer is {response}")
356
+ print(f"Answer is {answer}")
336
357
 
337
358
  if ev.num + 1 < len(sub_questions):
359
+ await ctx.set("qna", await ctx.get("qna", []) + [(question, answer)])
338
360
  return self.QueryEvent(
339
361
  question=sub_questions[ev.num + 1],
340
- prev_answer=response.response,
362
+ prev_answer=answer,
341
363
  num=ev.num + 1,
342
364
  )
343
365
 
344
- output = self.OutputsModel(response=response.response)
366
+ output = self.OutputsModel(response=answer)
345
367
  return StopEvent(result=output)
vectara_agentic/tools.py CHANGED
@@ -129,6 +129,8 @@ class VectaraToolFactory:
129
129
  - 'type': the type of each filter attribute in Vectara (doc or part).
130
130
  - 'is_list': whether the filterable attribute is a list.
131
131
  - 'filter_name': the name of the filterable attribute in Vectara.
132
+ summarize_docs (bool, optional): Whether to summarize the retrieved documents.
133
+ summarize_llm_name (str, optional): The name of the LLM to use for summarization.
132
134
  fixed_filter (str, optional): A fixed Vectara filter condition to apply to all queries.
133
135
  lambda_val (Union[List[float] | float], optional): Lambda value (or list of values for each corpora)
134
136
  for the Vectara query, when using hybrid search.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vectara_agentic
3
- Version: 0.2.22
3
+ Version: 0.2.23
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
@@ -530,6 +530,9 @@ class MyWorkflow(Workflow):
530
530
  class OutputsModel(BaseModel):
531
531
  answer: str
532
532
 
533
+ class OutputModelOnFail(BaseModel):
534
+ partial_response: str = ""
535
+
533
536
  @step
534
537
  async def my_step(self, ev: StartEvent) -> StopEvent:
535
538
  # do something here
@@ -587,6 +590,11 @@ workflow_result = asyncio.run(agent.run(inputs))
587
590
  print(workflow_result.answer)
588
591
  ```
589
592
 
593
+ When a workflow reaches its timeout, the timeout handler builds and returns an `OutputModelOnFail`
594
+ by reading each field named in that model from the workflow’s Context; for any field that isn’t set in the context,
595
+ it uses the default value you’ve defined on `OutputModelOnFail`. In other words, every property in `OutputModelOnFail`
596
+ must declare a default so that even if the corresponding context variable is missing, the model can be fully populated and returned without errors.
597
+
590
598
  ### Built-in Workflows
591
599
 
592
600
  `vectara-agentic` includes two workflow implementations that you can use right away:
@@ -13,25 +13,25 @@ tests/test_return_direct.py,sha256=Y_K_v88eS_kJfxE6A0Yghma0nUT8u6COitj0SNnZGNs,1
13
13
  tests/test_serialization.py,sha256=Ed23GN2zhSJNdPFrVK4aqLkOhJKviczR_o0t-r9TuRI,4762
14
14
  tests/test_tools.py,sha256=j6wY-jqeerE4Q7lxzuxbftRyzPLHE1scWKNFLgX7wSk,17138
15
15
  tests/test_vectara_llms.py,sha256=gw5KQ4XT3L-_A6yj2jKqj-QomCjOb9VESKBtBH2Xb8s,2362
16
- tests/test_workflow.py,sha256=06NvgUQMzPb2b2mrxtVo7xribZEDQM1LdcXNJdiOfPc,4391
16
+ tests/test_workflow.py,sha256=TmNBxBqSW5owk_Nz9LLtHvqryVNsFPkf-M1G_uFSsAM,3739
17
17
  vectara_agentic/__init__.py,sha256=2GLDS3U6KckK-dBRl9v_x1kSV507gEhjOfuMmmu0Qxg,850
18
18
  vectara_agentic/_callback.py,sha256=DMExGJPiZPowB0gL7Re_3406BHg75go1cgzetfGb-KQ,13074
19
19
  vectara_agentic/_observability.py,sha256=iZlByeQTyx6g3Y8aBYcdGcxdRkoYrfxHdcrTEKO26UE,4485
20
- vectara_agentic/_prompts.py,sha256=WryFU4c8qU8UC6py89IGv4cDLkmRlLuePgmw_urqV2s,9966
21
- vectara_agentic/_version.py,sha256=08iJeapvamCbfYG1wkjAUeW32ANfxUq8jOhrV27Akks,66
22
- vectara_agentic/agent.py,sha256=vymmh-c89q0BumE5nMPjHn7J1pTf7krdtK1nUwTRPNs,55417
20
+ vectara_agentic/_prompts.py,sha256=NbKloHQYc25IvtpxmPdRKHXIM-p-lAK-jyk41iafL60,10050
21
+ vectara_agentic/_version.py,sha256=_HryRNoBbJs5TpiuE6yWSRf90S1J6zt4RlW3hL6aslM,66
22
+ vectara_agentic/agent.py,sha256=M4hiHfrVBYIdknI5JMHezabF1QcM1jfHSCx2VXoVTQ4,55912
23
23
  vectara_agentic/agent_config.py,sha256=E-rtYMcpoGxnEAyy8231bizo2n0uGQ2qWxuSgTEfwdQ,4327
24
24
  vectara_agentic/agent_endpoint.py,sha256=PzIN7HhEHv8Mq_Zo5cZ2xYrgdv2AN6kx6dc_2AJq28I,7497
25
25
  vectara_agentic/db_tools.py,sha256=Kfz6n-rSj5TQEbAiJnWGmqWtcwB0A5GpxD7d1UwGzlc,11194
26
26
  vectara_agentic/llm_utils.py,sha256=oYm2S_xBCacN6GmFr_ASMIKEtRy2z2EQ_qDBfgSpiWE,7247
27
- vectara_agentic/sub_query_workflow.py,sha256=cPeossVPFajpSAwy45fSXhTXbQOfzv_l66pxSa4molM,12366
27
+ vectara_agentic/sub_query_workflow.py,sha256=JYwN0wK4QzHjTaFDsSCAQvMx9GD4g6CnqxZCnzi6xb4,13086
28
28
  vectara_agentic/tool_utils.py,sha256=FkwKY2IsMHXIb-uD-wef8vVB_eZKkReeb74CcXGo1cQ,19219
29
- vectara_agentic/tools.py,sha256=RahCvqF0LvcSnbyEKMr6_LvskrDDmyxtQpqDjWaSvxo,33334
29
+ vectara_agentic/tools.py,sha256=JIHPk08obKtQ4s6bhVYyyi82U7X5WaxEOxju6L_eej8,33519
30
30
  vectara_agentic/tools_catalog.py,sha256=cAN_kDOWZUoW4GNFwY5GdS6ImMUQNnF2sggx9OGK9Cg,4906
31
31
  vectara_agentic/types.py,sha256=HcS7vR8P2v2xQTlOc6ZFV2vvlr3OpzSNWhtcLMxqUZc,1792
32
32
  vectara_agentic/utils.py,sha256=R9HitEG5K3Q_p2M_teosT181OUxkhs1-hnj98qDYGbE,2545
33
- vectara_agentic-0.2.22.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
34
- vectara_agentic-0.2.22.dist-info/METADATA,sha256=f8AfwsDdl6zLWbCi0H_I_KOqlbit7tsuhSiHhE_m8nY,30559
35
- vectara_agentic-0.2.22.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
- vectara_agentic-0.2.22.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
37
- vectara_agentic-0.2.22.dist-info/RECORD,,
33
+ vectara_agentic-0.2.23.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
34
+ vectara_agentic-0.2.23.dist-info/METADATA,sha256=Hv56Z2BC5nxAT1YrMvr4iXQMUquXg8JYCsEQOXyHrzU,31127
35
+ vectara_agentic-0.2.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ vectara_agentic-0.2.23.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
37
+ vectara_agentic-0.2.23.dist-info/RECORD,,