vectara-agentic 0.2.13__tar.gz → 0.2.14__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.

Files changed (40) hide show
  1. {vectara_agentic-0.2.13/vectara_agentic.egg-info → vectara_agentic-0.2.14}/PKG-INFO +25 -11
  2. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/README.md +19 -5
  3. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/requirements.txt +5 -5
  4. vectara_agentic-0.2.14/tests/test_groq.py +120 -0
  5. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_tools.py +41 -5
  6. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_vectara_llms.py +0 -11
  7. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/_version.py +1 -1
  8. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/agent.py +65 -1
  9. vectara_agentic-0.2.13/vectara_agentic/utils.py → vectara_agentic-0.2.14/vectara_agentic/llm_utils.py +20 -85
  10. vectara_agentic-0.2.14/vectara_agentic/tool_utils.py +513 -0
  11. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/tools.py +23 -471
  12. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/tools_catalog.py +2 -1
  13. vectara_agentic-0.2.14/vectara_agentic/utils.py +86 -0
  14. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14/vectara_agentic.egg-info}/PKG-INFO +25 -11
  15. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic.egg-info/SOURCES.txt +3 -0
  16. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic.egg-info/requires.txt +5 -5
  17. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/LICENSE +0 -0
  18. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/MANIFEST.in +0 -0
  19. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/setup.cfg +0 -0
  20. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/setup.py +0 -0
  21. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/__init__.py +0 -0
  22. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/endpoint.py +0 -0
  23. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_agent.py +0 -0
  24. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_agent_planning.py +0 -0
  25. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_agent_type.py +0 -0
  26. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_fallback.py +0 -0
  27. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_private_llm.py +0 -0
  28. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_serialization.py +0 -0
  29. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_workflow.py +0 -0
  30. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/__init__.py +0 -0
  31. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/_callback.py +0 -0
  32. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/_observability.py +0 -0
  33. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/_prompts.py +0 -0
  34. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/agent_config.py +0 -0
  35. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/agent_endpoint.py +0 -0
  36. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/db_tools.py +0 -0
  37. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/sub_query_workflow.py +0 -0
  38. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/types.py +0 -0
  39. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic.egg-info/dependency_links.txt +0 -0
  40. {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vectara_agentic
3
- Version: 0.2.13
3
+ Version: 0.2.14
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,18 +16,18 @@ 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.31
20
- Requires-Dist: llama-index-indices-managed-vectara==0.4.3
19
+ Requires-Dist: llama-index==0.12.33
20
+ Requires-Dist: llama-index-indices-managed-vectara==0.4.4
21
21
  Requires-Dist: llama-index-agent-llm-compiler==0.3.0
22
22
  Requires-Dist: llama-index-agent-lats==0.3.0
23
23
  Requires-Dist: llama-index-agent-openai==0.4.6
24
- Requires-Dist: llama-index-llms-openai==0.3.35
24
+ Requires-Dist: llama-index-llms-openai==0.3.38
25
25
  Requires-Dist: llama-index-llms-anthropic==0.6.10
26
26
  Requires-Dist: llama-index-llms-together==0.3.1
27
27
  Requires-Dist: llama-index-llms-groq==0.3.1
28
28
  Requires-Dist: llama-index-llms-fireworks==0.3.2
29
29
  Requires-Dist: llama-index-llms-cohere==0.4.1
30
- Requires-Dist: llama-index-llms-gemini==0.4.14
30
+ Requires-Dist: llama-index-llms-google-genai==0.1.8
31
31
  Requires-Dist: llama-index-llms-bedrock==0.3.8
32
32
  Requires-Dist: llama-index-tools-yahoo-finance==0.3.0
33
33
  Requires-Dist: llama-index-tools-arxiv==0.3.0
@@ -49,7 +49,7 @@ Requires-Dist: arize-phoenix==8.26.1
49
49
  Requires-Dist: arize-phoenix-otel==0.9.2
50
50
  Requires-Dist: protobuf==5.29.3
51
51
  Requires-Dist: tokenizers>=0.20
52
- Requires-Dist: pydantic==2.10.6
52
+ Requires-Dist: pydantic==2.11.3
53
53
  Requires-Dist: retrying==1.3.4
54
54
  Requires-Dist: python-dotenv==1.0.1
55
55
  Requires-Dist: tiktoken==0.9.0
@@ -292,13 +292,12 @@ For example, in the quickstart example the schema is:
292
292
 
293
293
  ```python
294
294
  class QueryFinancialReportsArgs(BaseModel):
295
- query: str = Field(..., description="The user query.")
296
295
  year: int | str = Field(..., description=f"The year this query relates to. An integer between {min(years)} and {max(years)} or a string specifying a condition on the year (example: '>2020').")
297
296
  ticker: str = Field(..., description=f"The company ticker. Must be a valid ticket symbol from the list {tickers.keys()}.")
298
297
  ```
299
298
 
300
- The `query` is required and is always the query string.
301
- The other arguments are optional and will be interpreted as Vectara metadata filters.
299
+ Remember, the `query` argument is part of the rag_tool that is generated, but `vectara-agentic` creates it and you do
300
+ not need to specify it explicitly.
302
301
 
303
302
  For example, in the example above, the agent may call the `query_financial_reports_tool` tool with
304
303
  query='what is the revenue?', year=2022 and ticker='AAPL'. Subsequently the RAG tool will issue
@@ -483,13 +482,28 @@ class MyWorkflow(Workflow):
483
482
  return StopEvent(result="Hello, world!")
484
483
  ```
485
484
 
486
- When the `run()` method in vectara-agentic is invoked, it calls the workflow with the following variables in the StartEvent:
485
+ When the `run()` method in vectara-agentic is invoked, it calls the workflow with the following variables in the `StartEvent`:
487
486
  * `agent`: the agent object used to call `run()` (self)
488
487
  * `tools`: the tools provided to the agent. Those can be used as needed in the flow.
489
488
  * `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)`
490
489
  * `verbose`: controls whether extra debug information is displayed
491
490
  * `inputs`: this is the actual inputs to the workflow provided by the call to `run()` and must be of type `InputsModel`
492
491
 
492
+ If you want to use `agent`, `tools`, `llm` or `verbose` in other events (that are not `StartEvent`), you can store them in
493
+ the `Context` of the Workflow as follows:
494
+
495
+ ```python
496
+ await ctx.set("agent", ev.agent)
497
+ ```
498
+
499
+ and then in any other event you can pull that agent object with
500
+
501
+ ```python
502
+ agent = await ctx.get("agent")
503
+ ```
504
+
505
+ Similarly you can reuse the `llm`, `tools` or `verbose` arguments within other nodes in the workflow.
506
+
493
507
  ### Using the Workflow with Your Agent
494
508
 
495
509
  When initializing your agent, pass the workflow class using the `workflow_cls` parameter:
@@ -521,7 +535,7 @@ print(workflow_result.answer)
521
535
 
522
536
  ### Built-in Workflows
523
537
 
524
- `vectara-agentic` includes two powerful workflow implementations that you can use right away:
538
+ `vectara-agentic` includes two workflow implementations that you can use right away:
525
539
 
526
540
  #### 1. `SubQuestionQueryWorkflow`
527
541
 
@@ -222,13 +222,12 @@ For example, in the quickstart example the schema is:
222
222
 
223
223
  ```python
224
224
  class QueryFinancialReportsArgs(BaseModel):
225
- query: str = Field(..., description="The user query.")
226
225
  year: int | str = Field(..., description=f"The year this query relates to. An integer between {min(years)} and {max(years)} or a string specifying a condition on the year (example: '>2020').")
227
226
  ticker: str = Field(..., description=f"The company ticker. Must be a valid ticket symbol from the list {tickers.keys()}.")
228
227
  ```
229
228
 
230
- The `query` is required and is always the query string.
231
- The other arguments are optional and will be interpreted as Vectara metadata filters.
229
+ Remember, the `query` argument is part of the rag_tool that is generated, but `vectara-agentic` creates it and you do
230
+ not need to specify it explicitly.
232
231
 
233
232
  For example, in the example above, the agent may call the `query_financial_reports_tool` tool with
234
233
  query='what is the revenue?', year=2022 and ticker='AAPL'. Subsequently the RAG tool will issue
@@ -413,13 +412,28 @@ class MyWorkflow(Workflow):
413
412
  return StopEvent(result="Hello, world!")
414
413
  ```
415
414
 
416
- When the `run()` method in vectara-agentic is invoked, it calls the workflow with the following variables in the StartEvent:
415
+ When the `run()` method in vectara-agentic is invoked, it calls the workflow with the following variables in the `StartEvent`:
417
416
  * `agent`: the agent object used to call `run()` (self)
418
417
  * `tools`: the tools provided to the agent. Those can be used as needed in the flow.
419
418
  * `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)`
420
419
  * `verbose`: controls whether extra debug information is displayed
421
420
  * `inputs`: this is the actual inputs to the workflow provided by the call to `run()` and must be of type `InputsModel`
422
421
 
422
+ If you want to use `agent`, `tools`, `llm` or `verbose` in other events (that are not `StartEvent`), you can store them in
423
+ the `Context` of the Workflow as follows:
424
+
425
+ ```python
426
+ await ctx.set("agent", ev.agent)
427
+ ```
428
+
429
+ and then in any other event you can pull that agent object with
430
+
431
+ ```python
432
+ agent = await ctx.get("agent")
433
+ ```
434
+
435
+ Similarly you can reuse the `llm`, `tools` or `verbose` arguments within other nodes in the workflow.
436
+
423
437
  ### Using the Workflow with Your Agent
424
438
 
425
439
  When initializing your agent, pass the workflow class using the `workflow_cls` parameter:
@@ -451,7 +465,7 @@ print(workflow_result.answer)
451
465
 
452
466
  ### Built-in Workflows
453
467
 
454
- `vectara-agentic` includes two powerful workflow implementations that you can use right away:
468
+ `vectara-agentic` includes two workflow implementations that you can use right away:
455
469
 
456
470
  #### 1. `SubQuestionQueryWorkflow`
457
471
 
@@ -1,15 +1,15 @@
1
- llama-index==0.12.31
2
- llama-index-indices-managed-vectara==0.4.3
1
+ llama-index==0.12.33
2
+ llama-index-indices-managed-vectara==0.4.4
3
3
  llama-index-agent-llm-compiler==0.3.0
4
4
  llama-index-agent-lats==0.3.0
5
5
  llama-index-agent-openai==0.4.6
6
- llama-index-llms-openai==0.3.35
6
+ llama-index-llms-openai==0.3.38
7
7
  llama-index-llms-anthropic==0.6.10
8
8
  llama-index-llms-together==0.3.1
9
9
  llama-index-llms-groq==0.3.1
10
10
  llama-index-llms-fireworks==0.3.2
11
11
  llama-index-llms-cohere==0.4.1
12
- llama-index-llms-gemini==0.4.14
12
+ llama-index-llms-google-genai ==0.1.8
13
13
  llama-index-llms-bedrock==0.3.8
14
14
  llama-index-tools-yahoo-finance==0.3.0
15
15
  llama-index-tools-arxiv==0.3.0
@@ -31,7 +31,7 @@ arize-phoenix==8.26.1
31
31
  arize-phoenix-otel==0.9.2
32
32
  protobuf==5.29.3
33
33
  tokenizers>=0.20
34
- pydantic==2.10.6
34
+ pydantic==2.11.3
35
35
  retrying==1.3.4
36
36
  python-dotenv==1.0.1
37
37
  tiktoken==0.9.0
@@ -0,0 +1,120 @@
1
+ import unittest
2
+
3
+ from pydantic import Field, BaseModel
4
+
5
+ from vectara_agentic.agent import Agent, AgentType
6
+ from vectara_agentic.agent_config import AgentConfig
7
+ from vectara_agentic.tools import VectaraToolFactory
8
+ from vectara_agentic.types import ModelProvider
9
+
10
+
11
+ import nest_asyncio
12
+ nest_asyncio.apply()
13
+
14
+ tickers = {
15
+ "C": "Citigroup",
16
+ "COF": "Capital One",
17
+ "JPM": "JPMorgan Chase",
18
+ "AAPL": "Apple Computer",
19
+ "GOOG": "Google",
20
+ "AMZN": "Amazon",
21
+ "SNOW": "Snowflake",
22
+ "TEAM": "Atlassian",
23
+ "TSLA": "Tesla",
24
+ "NVDA": "Nvidia",
25
+ "MSFT": "Microsoft",
26
+ "AMD": "Advanced Micro Devices",
27
+ "INTC": "Intel",
28
+ "NFLX": "Netflix",
29
+ "STT": "State Street",
30
+ "BK": "Bank of New York Mellon",
31
+ }
32
+ years = list(range(2015, 2025))
33
+
34
+
35
+ def mult(x: float, y: float) -> float:
36
+ "Multiply two numbers"
37
+ return x * y
38
+
39
+
40
+ def get_company_info() -> list[str]:
41
+ """
42
+ Returns a dictionary of companies you can query about. Always check this before using any other tool.
43
+ The output is a dictionary of valid ticker symbols mapped to company names.
44
+ You can use this to identify the companies you can query about, and their ticker information.
45
+ """
46
+ return tickers
47
+
48
+
49
+ def get_valid_years() -> list[str]:
50
+ """
51
+ Returns a list of the years for which financial reports are available.
52
+ Always check this before using any other tool.
53
+ """
54
+ return years
55
+
56
+
57
+ config_gemini = AgentConfig(
58
+ agent_type=AgentType.FUNCTION_CALLING,
59
+ main_llm_provider=ModelProvider.GEMINI,
60
+ tool_llm_provider=ModelProvider.GEMINI,
61
+ )
62
+
63
+
64
+ fc_config_groq = AgentConfig(
65
+ agent_type=AgentType.FUNCTION_CALLING,
66
+ main_llm_provider=ModelProvider.GROQ,
67
+ tool_llm_provider=ModelProvider.GROQ,
68
+ )
69
+
70
+
71
+ class TestGROQ(unittest.TestCase):
72
+
73
+ def test_tool_with_many_arguments(self):
74
+
75
+ vectara_corpus_key = "vectara-docs_1"
76
+ vectara_api_key = "zqt_UXrBcnI2UXINZkrv4g1tQPhzj02vfdtqYJIDiA"
77
+ vec_factory = VectaraToolFactory(vectara_corpus_key, vectara_api_key)
78
+
79
+ class QueryToolArgs(BaseModel):
80
+ arg1: str = Field(description="the first argument", examples=["val1"])
81
+ arg2: str = Field(description="the second argument", examples=["val2"])
82
+ arg3: str = Field(description="the third argument", examples=["val3"])
83
+ arg4: str = Field(description="the fourth argument", examples=["val4"])
84
+ arg5: str = Field(description="the fifth argument", examples=["val5"])
85
+ arg6: str = Field(description="the sixth argument", examples=["val6"])
86
+ arg7: str = Field(description="the seventh argument", examples=["val7"])
87
+ arg8: str = Field(description="the eighth argument", examples=["val8"])
88
+ arg9: str = Field(description="the ninth argument", examples=["val9"])
89
+ arg10: str = Field(description="the tenth argument", examples=["val10"])
90
+ arg11: str = Field(description="the eleventh argument", examples=["val11"])
91
+ arg12: str = Field(description="the twelfth argument", examples=["val12"])
92
+ arg13: str = Field(
93
+ description="the thirteenth argument", examples=["val13"]
94
+ )
95
+ arg14: str = Field(
96
+ description="the fourteenth argument", examples=["val14"]
97
+ )
98
+ arg15: str = Field(description="the fifteenth argument", examples=["val15"])
99
+
100
+ query_tool_1 = vec_factory.create_rag_tool(
101
+ tool_name="rag_tool",
102
+ tool_description="""
103
+ A dummy tool that takes 15 arguments and returns a response (str) to the user query based on the data in this corpus.
104
+ We are using this tool to test the tool factory works and does not crash with OpenAI.
105
+ """,
106
+ tool_args_schema=QueryToolArgs,
107
+ )
108
+
109
+ agent = Agent(
110
+ tools=[query_tool_1],
111
+ topic="Sample topic",
112
+ custom_instructions="Call the tool with 15 arguments",
113
+ agent_config=fc_config_groq,
114
+ )
115
+ res = agent.chat("What is the stock price?")
116
+ self.assertIn("I don't know", str(res))
117
+
118
+
119
+ if __name__ == "__main__":
120
+ unittest.main()
@@ -9,6 +9,7 @@ from vectara_agentic.tools import (
9
9
  )
10
10
  from vectara_agentic.agent import Agent
11
11
  from vectara_agentic.agent_config import AgentConfig
12
+ from vectara_agentic.types import AgentType, ModelProvider
12
13
 
13
14
  from llama_index.core.tools import FunctionTool
14
15
 
@@ -179,22 +180,57 @@ class TestToolsPackage(unittest.TestCase):
179
180
  query_tool_1 = vec_factory.create_rag_tool(
180
181
  tool_name="rag_tool",
181
182
  tool_description="""
182
- A dummy tool that takes 20 arguments and returns a response (str) to the user query based on the data in this corpus.
183
+ A dummy tool that takes 15 arguments and returns a response (str) to the user query based on the data in this corpus.
183
184
  We are using this tool to test the tool factory works and does not crash with OpenAI.
184
185
  """,
185
186
  tool_args_schema=QueryToolArgs,
186
187
  )
187
188
 
188
- config = AgentConfig()
189
+ # Test with 15 arguments which go over the 1024 limit.
190
+ config = AgentConfig(
191
+ agent_type=AgentType.OPENAI
192
+ )
189
193
  agent = Agent(
190
194
  tools=[query_tool_1],
191
195
  topic="Sample topic",
192
- custom_instructions="Call the tool with 20 arguments",
196
+ custom_instructions="Call the tool with 15 arguments for OPENAI",
193
197
  agent_config=config,
194
198
  )
195
199
  res = agent.chat("What is the stock price?")
196
200
  self.assertIn("maximum length of 1024 characters", str(res))
197
201
 
202
+ # Same test but with GROQ
203
+ config = AgentConfig(
204
+ agent_type=AgentType.FUNCTION_CALLING,
205
+ main_llm_provider=ModelProvider.GROQ,
206
+ tool_llm_provider=ModelProvider.GROQ,
207
+ )
208
+ agent = Agent(
209
+ tools=[query_tool_1],
210
+ topic="Sample topic",
211
+ custom_instructions="Call the tool with 15 arguments for GROQ",
212
+ agent_config=config,
213
+ )
214
+ res = agent.chat("What is the stock price?")
215
+ self.assertNotIn("maximum length of 1024 characters", str(res))
216
+
217
+ # Same test but with ANTHROPIC
218
+ config = AgentConfig(
219
+ agent_type=AgentType.FUNCTION_CALLING,
220
+ main_llm_provider=ModelProvider.ANTHROPIC,
221
+ tool_llm_provider=ModelProvider.ANTHROPIC,
222
+ )
223
+ agent = Agent(
224
+ tools=[query_tool_1],
225
+ topic="Sample topic",
226
+ custom_instructions="Call the tool with 15 arguments for ANTHROPIC",
227
+ agent_config=config,
228
+ )
229
+ res = agent.chat("What is the stock price?")
230
+ # ANTHROPIC does not have that 1024 limit
231
+ self.assertIn("stock price", str(res))
232
+
233
+ # But using Compact_docstring=True, we can pass 15 arguments successfully.
198
234
  vec_factory = VectaraToolFactory(
199
235
  vectara_corpus_key, vectara_api_key, compact_docstring=True
200
236
  )
@@ -211,7 +247,7 @@ class TestToolsPackage(unittest.TestCase):
211
247
  agent = Agent(
212
248
  tools=[query_tool_2],
213
249
  topic="Sample topic",
214
- custom_instructions="Call the tool with 20 arguments",
250
+ custom_instructions="Call the tool with 15 arguments",
215
251
  agent_config=config,
216
252
  )
217
253
  res = agent.chat("What is the stock price?")
@@ -227,7 +263,7 @@ class TestToolsPackage(unittest.TestCase):
227
263
  tool_name="ask_vectara",
228
264
  data_description="data from Vectara website",
229
265
  assistant_specialty="RAG as a service",
230
- vectara_summarizer="mockingbird-1.0-2024-07-16",
266
+ vectara_summarizer="mockingbird-2.0",
231
267
  )
232
268
 
233
269
  self.assertIn(
@@ -51,17 +51,6 @@ class TestLLMPackage(unittest.TestCase):
51
51
 
52
52
  def test_vectara_mockingbird(self):
53
53
  vec_factory = VectaraToolFactory(vectara_corpus_key, vectara_api_key)
54
-
55
- query_tool = vec_factory.create_rag_tool(
56
- tool_name="rag_tool",
57
- tool_description="""
58
- Returns a response (str) to the user query based on the data in this corpus.
59
- """,
60
- vectara_summarizer="mockingbird-1.0-2024-07-16",
61
- )
62
- res = query_tool(query="What is Vectara?")
63
- self.assertIn("Vectara is an end-to-end platform", str(res))
64
-
65
54
  query_tool = vec_factory.create_rag_tool(
66
55
  tool_name="rag_tool",
67
56
  tool_description="""
@@ -1,4 +1,4 @@
1
1
  """
2
2
  Define the version of the package.
3
3
  """
4
- __version__ = "0.2.13"
4
+ __version__ = "0.2.14"
@@ -12,6 +12,8 @@ import logging
12
12
  import asyncio
13
13
  import importlib
14
14
  from collections import Counter
15
+ import inspect
16
+ from inspect import Signature, Parameter, ismethod
15
17
 
16
18
  import cloudpickle as pickle
17
19
 
@@ -19,6 +21,7 @@ from dotenv import load_dotenv
19
21
 
20
22
  from pydantic import Field, create_model, ValidationError
21
23
 
24
+
22
25
  from llama_index.core.memory import ChatMemoryBuffer
23
26
  from llama_index.core.llms import ChatMessage, MessageRole
24
27
  from llama_index.core.tools import FunctionTool
@@ -47,7 +50,7 @@ from .types import (
47
50
  AgentStreamingResponse,
48
51
  AgentConfigType,
49
52
  )
50
- from .utils import get_llm, get_tokenizer_for_model
53
+ from .llm_utils import get_llm, get_tokenizer_for_model
51
54
  from ._prompts import (
52
55
  REACT_PROMPT_TEMPLATE,
53
56
  GENERAL_PROMPT_TEMPLATE,
@@ -230,6 +233,10 @@ class Agent:
230
233
  self.workflow_cls = workflow_cls
231
234
  self.workflow_timeout = workflow_timeout
232
235
 
236
+ # Sanitize tools for Gemini if needed
237
+ if self.agent_config.main_llm_provider == ModelProvider.GEMINI:
238
+ self.tools = self._sanitize_tools_for_gemini(self.tools)
239
+
233
240
  # Validate tools
234
241
  # Check for:
235
242
  # 1. multiple copies of the same tool
@@ -311,6 +318,63 @@ class Agent:
311
318
  print(f"Failed to set up observer ({e}), ignoring")
312
319
  self.observability_enabled = False
313
320
 
321
+ def _sanitize_tools_for_gemini(
322
+ self, tools: list[FunctionTool]
323
+ ) -> list[FunctionTool]:
324
+ """
325
+ Strip all default values from:
326
+ - tool.fn
327
+ - tool.async_fn
328
+ - tool.metadata.fn_schema
329
+ so Gemini sees *only* required parameters, no defaults.
330
+ """
331
+ for tool in tools:
332
+ # 1) strip defaults off the actual callables
333
+ for func in (tool.fn, tool.async_fn):
334
+ if not func:
335
+ continue
336
+ orig_sig = inspect.signature(func)
337
+ new_params = [
338
+ p.replace(default=Parameter.empty)
339
+ for p in orig_sig.parameters.values()
340
+ ]
341
+ new_sig = Signature(
342
+ new_params, return_annotation=orig_sig.return_annotation
343
+ )
344
+ if ismethod(func):
345
+ func.__func__.__signature__ = new_sig
346
+ else:
347
+ func.__signature__ = new_sig
348
+
349
+ # 2) rebuild the Pydantic schema so that *every* field is required
350
+ schema_cls = getattr(tool.metadata, "fn_schema", None)
351
+ if schema_cls and hasattr(schema_cls, "model_fields"):
352
+ # collect (name → (type, Field(...))) for all fields
353
+ new_fields: dict[str, tuple[type, Any]] = {}
354
+ for name, mf in schema_cls.model_fields.items():
355
+ typ = mf.annotation
356
+ desc = getattr(mf, "description", "")
357
+ # force required (no default) with Field(...)
358
+ new_fields[name] = (typ, Field(..., description=desc))
359
+
360
+ # make a brand-new schema class where every field is required
361
+ no_default_schema = create_model(
362
+ f"{schema_cls.__name__}", # new class name
363
+ **new_fields, # type: ignore
364
+ )
365
+
366
+ # give it a clean __signature__ so inspect.signature sees no defaults
367
+ params = [
368
+ Parameter(n, Parameter.POSITIONAL_OR_KEYWORD, annotation=typ)
369
+ for n, (typ, _) in new_fields.items()
370
+ ]
371
+ no_default_schema.__signature__ = Signature(params)
372
+
373
+ # swap it back onto the tool
374
+ tool.metadata.fn_schema = no_default_schema
375
+
376
+ return tools
377
+
314
378
  def _create_agent(
315
379
  self, config: AgentConfig, llm_callback_manager: CallbackManager
316
380
  ) -> Union[BaseAgent, AgentRunner]:
@@ -1,14 +1,10 @@
1
1
  """
2
2
  Utilities for the Vectara agentic.
3
3
  """
4
-
4
+ from types import MethodType
5
5
  from typing import Tuple, Callable, Optional
6
6
  from functools import lru_cache
7
- from inspect import signature
8
- import json
9
- import asyncio
10
7
  import tiktoken
11
- import aiohttp
12
8
 
13
9
  from llama_index.core.llms import LLM
14
10
  from llama_index.llms.openai import OpenAI
@@ -16,6 +12,7 @@ from llama_index.llms.anthropic import Anthropic
16
12
 
17
13
  from .types import LLMRole, AgentType, ModelProvider
18
14
  from .agent_config import AgentConfig
15
+ from .tool_utils import _updated_openai_prepare_chat_with_tools
19
16
 
20
17
  provider_to_default_model_name = {
21
18
  ModelProvider.OPENAI: "gpt-4o",
@@ -105,9 +102,9 @@ def get_llm(role: LLMRole, config: Optional[AgentConfig] = None) -> LLM:
105
102
  max_tokens=max_tokens,
106
103
  )
107
104
  elif model_provider == ModelProvider.GEMINI:
108
- from llama_index.llms.gemini import Gemini
105
+ from llama_index.llms.google_genai import GoogleGenAI
109
106
 
110
- llm = Gemini(
107
+ llm = GoogleGenAI(
111
108
  model=model_name,
112
109
  temperature=0,
113
110
  is_function_calling_model=True,
@@ -123,6 +120,11 @@ def get_llm(role: LLMRole, config: Optional[AgentConfig] = None) -> LLM:
123
120
  is_function_calling_model=True,
124
121
  max_tokens=max_tokens,
125
122
  )
123
+ # pylint: disable=protected-access
124
+ llm._prepare_chat_with_tools = MethodType(
125
+ _updated_openai_prepare_chat_with_tools,
126
+ llm,
127
+ )
126
128
  elif model_provider == ModelProvider.GROQ:
127
129
  from llama_index.llms.groq import Groq
128
130
 
@@ -132,6 +134,11 @@ def get_llm(role: LLMRole, config: Optional[AgentConfig] = None) -> LLM:
132
134
  is_function_calling_model=True,
133
135
  max_tokens=max_tokens,
134
136
  )
137
+ # pylint: disable=protected-access
138
+ llm._prepare_chat_with_tools = MethodType(
139
+ _updated_openai_prepare_chat_with_tools,
140
+ llm,
141
+ )
135
142
  elif model_provider == ModelProvider.FIREWORKS:
136
143
  from llama_index.llms.fireworks import Fireworks
137
144
 
@@ -156,84 +163,12 @@ def get_llm(role: LLMRole, config: Optional[AgentConfig] = None) -> LLM:
156
163
  api_key=config.private_llm_api_key,
157
164
  max_tokens=max_tokens,
158
165
  )
166
+ # pylint: disable=protected-access
167
+ llm._prepare_chat_with_tools = MethodType(
168
+ _updated_openai_prepare_chat_with_tools,
169
+ llm,
170
+ )
171
+
159
172
  else:
160
173
  raise ValueError(f"Unknown LLM provider: {model_provider}")
161
174
  return llm
162
-
163
-
164
- def is_float(value: str) -> bool:
165
- """Check if a string can be converted to a float."""
166
- try:
167
- float(value)
168
- return True
169
- except ValueError:
170
- return False
171
-
172
-
173
- def remove_self_from_signature(func):
174
- """Decorator to remove 'self' from a method's signature for introspection."""
175
- sig = signature(func)
176
- params = list(sig.parameters.values())
177
- # Remove the first parameter if it is named 'self'
178
- if params and params[0].name == "self":
179
- params = params[1:]
180
- new_sig = sig.replace(parameters=params)
181
- func.__signature__ = new_sig
182
- return func
183
-
184
-
185
- async def summarize_vectara_document(
186
- llm_name: str, corpus_key: str, api_key: str, doc_id: str
187
- ) -> str:
188
- """
189
- Summarize a document in a Vectara corpus using the Vectara API.
190
- """
191
- url = f"https://api.vectara.io/v2/corpora/{corpus_key}/documents/{doc_id}/summarize"
192
-
193
- payload = json.dumps(
194
- {
195
- "llm_name": llm_name,
196
- "model_parameters": {"temperature": 0.0},
197
- "stream_response": False,
198
- }
199
- )
200
- headers = {
201
- "Content-Type": "application/json",
202
- "Accept": "application/json",
203
- "x-api-key": api_key,
204
- }
205
- timeout = aiohttp.ClientTimeout(total=60)
206
- async with aiohttp.ClientSession(timeout=timeout) as session:
207
- async with session.post(url, headers=headers, data=payload) as response:
208
- if response.status != 200:
209
- error_json = await response.json()
210
- return (
211
- f"Vectara Summarization failed with error code {response.status}, "
212
- f"error={error_json['messages'][0]}"
213
- )
214
- data = await response.json()
215
- return data["summary"]
216
- return json.loads(response.text)["summary"]
217
-
218
-
219
- async def summarize_documents(
220
- corpus_key: str,
221
- api_key: str,
222
- doc_ids: list[str],
223
- llm_name: str = "gpt-4o",
224
- ) -> dict[str, str]:
225
- """
226
- Summarize multiple documents in a Vectara corpus using the Vectara API.
227
- """
228
- if not doc_ids:
229
- return {}
230
- if llm_name is None:
231
- llm_name = "gpt-4o"
232
- tasks = [
233
- summarize_vectara_document(
234
- corpus_key=corpus_key, api_key=api_key, llm_name=llm_name, doc_id=doc_id
235
- )
236
- for doc_id in doc_ids
237
- ]
238
- summaries = await asyncio.gather(*tasks, return_exceptions=True)
239
- return dict(zip(doc_ids, summaries))