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.
- {vectara_agentic-0.2.13/vectara_agentic.egg-info → vectara_agentic-0.2.14}/PKG-INFO +25 -11
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/README.md +19 -5
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/requirements.txt +5 -5
- vectara_agentic-0.2.14/tests/test_groq.py +120 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_tools.py +41 -5
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_vectara_llms.py +0 -11
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/_version.py +1 -1
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/agent.py +65 -1
- vectara_agentic-0.2.13/vectara_agentic/utils.py → vectara_agentic-0.2.14/vectara_agentic/llm_utils.py +20 -85
- vectara_agentic-0.2.14/vectara_agentic/tool_utils.py +513 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/tools.py +23 -471
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/tools_catalog.py +2 -1
- vectara_agentic-0.2.14/vectara_agentic/utils.py +86 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14/vectara_agentic.egg-info}/PKG-INFO +25 -11
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic.egg-info/SOURCES.txt +3 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic.egg-info/requires.txt +5 -5
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/LICENSE +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/MANIFEST.in +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/setup.cfg +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/setup.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/__init__.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/endpoint.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_agent.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_agent_planning.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_agent_type.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_fallback.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_private_llm.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_serialization.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/tests/test_workflow.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/__init__.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/_callback.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/_observability.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/_prompts.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/agent_config.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/agent_endpoint.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/db_tools.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/sub_query_workflow.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic/types.py +0 -0
- {vectara_agentic-0.2.13 → vectara_agentic-0.2.14}/vectara_agentic.egg-info/dependency_links.txt +0 -0
- {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.
|
|
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.
|
|
20
|
-
Requires-Dist: llama-index-indices-managed-vectara==0.4.
|
|
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.
|
|
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-
|
|
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.
|
|
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
|
-
|
|
301
|
-
|
|
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
|
|
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
|
-
|
|
231
|
-
|
|
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
|
|
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.
|
|
2
|
-
llama-index-indices-managed-vectara==0.4.
|
|
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.
|
|
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-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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-
|
|
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="""
|
|
@@ -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 .
|
|
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.
|
|
105
|
+
from llama_index.llms.google_genai import GoogleGenAI
|
|
109
106
|
|
|
110
|
-
llm =
|
|
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))
|