vectara-agentic 0.4.0__tar.gz → 0.4.1__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 (65) hide show
  1. {vectara_agentic-0.4.0/vectara_agentic.egg-info → vectara_agentic-0.4.1}/PKG-INFO +49 -8
  2. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/README.md +45 -2
  3. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/requirements.txt +3 -5
  4. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/conftest.py +5 -1
  5. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/run_tests.py +1 -0
  6. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_agent.py +26 -29
  7. vectara_agentic-0.4.1/tests/test_agent_fallback_memory.py +270 -0
  8. vectara_agentic-0.4.1/tests/test_agent_memory_consistency.py +229 -0
  9. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_agent_type.py +4 -0
  10. vectara_agentic-0.4.1/tests/test_bedrock.py +61 -0
  11. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_gemini.py +7 -22
  12. vectara_agentic-0.4.1/tests/test_groq.py +61 -0
  13. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_serialization.py +3 -6
  14. vectara_agentic-0.4.1/tests/test_session_memory.py +252 -0
  15. vectara_agentic-0.4.1/tests/test_streaming.py +109 -0
  16. vectara_agentic-0.4.1/tests/test_together.py +62 -0
  17. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_vhc.py +3 -2
  18. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_workflow.py +9 -28
  19. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/_version.py +1 -1
  20. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent.py +212 -33
  21. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_core/factory.py +30 -148
  22. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_core/prompts.py +20 -13
  23. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_core/serialization.py +3 -0
  24. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_core/streaming.py +22 -34
  25. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_core/utils/__init__.py +0 -5
  26. vectara_agentic-0.4.1/vectara_agentic/agent_core/utils/hallucination.py +157 -0
  27. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/llm_utils.py +1 -1
  28. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/types.py +9 -3
  29. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1/vectara_agentic.egg-info}/PKG-INFO +49 -8
  30. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic.egg-info/SOURCES.txt +4 -1
  31. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic.egg-info/requires.txt +3 -5
  32. vectara_agentic-0.4.0/tests/test_bedrock.py +0 -46
  33. vectara_agentic-0.4.0/tests/test_groq.py +0 -46
  34. vectara_agentic-0.4.0/tests/test_streaming.py +0 -88
  35. vectara_agentic-0.4.0/vectara_agentic/agent_core/utils/hallucination.py +0 -202
  36. vectara_agentic-0.4.0/vectara_agentic/agent_core/utils/prompt_formatting.py +0 -56
  37. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/LICENSE +0 -0
  38. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/MANIFEST.in +0 -0
  39. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/setup.cfg +0 -0
  40. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/setup.py +0 -0
  41. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/__init__.py +0 -0
  42. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/endpoint.py +0 -0
  43. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_api_endpoint.py +0 -0
  44. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_fallback.py +0 -0
  45. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_private_llm.py +0 -0
  46. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_return_direct.py +0 -0
  47. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_tools.py +0 -0
  48. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/tests/test_vectara_llms.py +0 -0
  49. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/__init__.py +0 -0
  50. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/_callback.py +0 -0
  51. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/_observability.py +0 -0
  52. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_config.py +0 -0
  53. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_core/__init__.py +0 -0
  54. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_core/utils/logging.py +0 -0
  55. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_core/utils/schemas.py +0 -0
  56. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_core/utils/tools.py +0 -0
  57. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/agent_endpoint.py +0 -0
  58. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/db_tools.py +0 -0
  59. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/sub_query_workflow.py +0 -0
  60. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/tool_utils.py +0 -0
  61. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/tools.py +0 -0
  62. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/tools_catalog.py +0 -0
  63. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic/utils.py +0 -0
  64. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/vectara_agentic.egg-info/dependency_links.txt +0 -0
  65. {vectara_agentic-0.4.0 → vectara_agentic-0.4.1}/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.4.0
3
+ Version: 0.4.1
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,11 @@ 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.49
20
- Requires-Dist: llama-index-core==0.12.49
19
+ Requires-Dist: llama-index==0.12.52
20
+ Requires-Dist: llama-index-core==0.12.52.post1
21
21
  Requires-Dist: llama-index-workflow==1.0.1
22
22
  Requires-Dist: llama-index-cli==0.4.4
23
23
  Requires-Dist: llama-index-indices-managed-vectara==0.4.5
24
- Requires-Dist: llama-index-agent-llm-compiler==0.3.2
25
- Requires-Dist: llama-index-agent-lats==0.3.2
26
24
  Requires-Dist: llama-index-agent-openai==0.4.12
27
25
  Requires-Dist: llama-index-llms-openai==0.4.7
28
26
  Requires-Dist: llama-index-llms-openai-like==0.4.0
@@ -53,7 +51,7 @@ Requires-Dist: openinference-instrumentation-llama-index==4.3.1
53
51
  Requires-Dist: opentelemetry-proto>=1.31.0
54
52
  Requires-Dist: arize-phoenix==10.9.1
55
53
  Requires-Dist: arize-phoenix-otel==0.10.3
56
- Requires-Dist: protobuf==5.29.3
54
+ Requires-Dist: protobuf==5.29.5
57
55
  Requires-Dist: tokenizers>=0.20
58
56
  Requires-Dist: pydantic==2.11.5
59
57
  Requires-Dist: pandas==2.2.3
@@ -125,7 +123,7 @@ Dynamic: summary
125
123
  - **Rapid Tool Creation:**
126
124
  Build Vectara RAG tools or search tools with a single line of code.
127
125
  - **Agent Flexibility:**
128
- Supports multiple agent types including `ReAct`, `Function Calling`, `LATS`, and `LLMCompiler`.
126
+ Supports multiple agent types including `ReAct` and `Function Calling`.
129
127
  - **Pre-Built Domain Tools:**
130
128
  Tools tailored for finance, legal, and other verticals.
131
129
  - **Multi-LLM Integration:**
@@ -532,6 +530,49 @@ Built-in formatters include `format_as_table`, `format_as_json`, and `format_as_
532
530
 
533
531
  The human-readable format, if available, is used when using Vectara Hallucination Correction.
534
532
 
533
+ ## 🔍 Vectara Hallucination Correction (VHC)
534
+
535
+ `vectara-agentic` provides built-in support for Vectara Hallucination Correction (VHC), which analyzes agent responses and corrects any detected hallucinations based on the factual content retrieved by VHC-eligible tools.
536
+
537
+ ### Computing VHC
538
+
539
+ After a chat interaction, you can compute VHC to analyze and correct the agent's response:
540
+
541
+ ```python
542
+ # Chat with the agent
543
+ response = agent.chat("What was Apple's revenue in 2022?")
544
+ print(response.response)
545
+
546
+ # Compute VHC analysis
547
+ vhc_result = agent.compute_vhc()
548
+
549
+ # Access corrected text and corrections
550
+ if vhc_result["corrected_text"]:
551
+ print("Original:", response.response)
552
+ print("Corrected:", vhc_result["corrected_text"])
553
+ print("Corrections:", vhc_result["corrections"])
554
+ else:
555
+ print("No corrections needed or VHC not available")
556
+ ```
557
+
558
+ ### Async VHC Computation
559
+
560
+ For async applications, use `acompute_vhc()`:
561
+
562
+ ```python
563
+ # Async chat
564
+ response = await agent.achat("What was Apple's revenue in 2022?")
565
+
566
+ # Async VHC computation
567
+ vhc_result = await agent.acompute_vhc()
568
+ ```
569
+
570
+ ### VHC Requirements
571
+
572
+ - VHC requires a valid `VECTARA_API_KEY` environment variable
573
+ - Only VHC-eligible tools (those marked with `vhc_eligible=True`) contribute to the analysis
574
+ - VHC results are cached for each query/response pair to avoid redundant computation
575
+
535
576
  ### Tool Validation
536
577
 
537
578
  When creating an agent, you can enable tool validation by setting `validate_tools=True`. This will check that any tools mentioned in your custom instructions actually exist in the agent's tool set:
@@ -745,7 +786,7 @@ agent = Agent(
745
786
  ```
746
787
 
747
788
  The `AgentConfig` object may include the following items:
748
- - `agent_type`: the agent type. Valid values are `REACT`, `LLMCOMPILER`, `LATS` or `FUNCTION_CALLING` (default: `FUNCTION_CALLING`).
789
+ - `agent_type`: the agent type. Valid values are `REACT` or `FUNCTION_CALLING` (default: `FUNCTION_CALLING`).
749
790
  - `main_llm_provider` and `tool_llm_provider`: the LLM provider for main agent and for the tools. Valid values are `OPENAI`, `ANTHROPIC`, `TOGETHER`, `GROQ`, `COHERE`, `BEDROCK`, `GEMINI` (default: `OPENAI`).
750
791
 
751
792
  > **Note:** Fireworks AI support has been removed. If you were using Fireworks, please migrate to one of the supported providers listed above.
@@ -48,7 +48,7 @@
48
48
  - **Rapid Tool Creation:**
49
49
  Build Vectara RAG tools or search tools with a single line of code.
50
50
  - **Agent Flexibility:**
51
- Supports multiple agent types including `ReAct`, `Function Calling`, `LATS`, and `LLMCompiler`.
51
+ Supports multiple agent types including `ReAct` and `Function Calling`.
52
52
  - **Pre-Built Domain Tools:**
53
53
  Tools tailored for finance, legal, and other verticals.
54
54
  - **Multi-LLM Integration:**
@@ -455,6 +455,49 @@ Built-in formatters include `format_as_table`, `format_as_json`, and `format_as_
455
455
 
456
456
  The human-readable format, if available, is used when using Vectara Hallucination Correction.
457
457
 
458
+ ## 🔍 Vectara Hallucination Correction (VHC)
459
+
460
+ `vectara-agentic` provides built-in support for Vectara Hallucination Correction (VHC), which analyzes agent responses and corrects any detected hallucinations based on the factual content retrieved by VHC-eligible tools.
461
+
462
+ ### Computing VHC
463
+
464
+ After a chat interaction, you can compute VHC to analyze and correct the agent's response:
465
+
466
+ ```python
467
+ # Chat with the agent
468
+ response = agent.chat("What was Apple's revenue in 2022?")
469
+ print(response.response)
470
+
471
+ # Compute VHC analysis
472
+ vhc_result = agent.compute_vhc()
473
+
474
+ # Access corrected text and corrections
475
+ if vhc_result["corrected_text"]:
476
+ print("Original:", response.response)
477
+ print("Corrected:", vhc_result["corrected_text"])
478
+ print("Corrections:", vhc_result["corrections"])
479
+ else:
480
+ print("No corrections needed or VHC not available")
481
+ ```
482
+
483
+ ### Async VHC Computation
484
+
485
+ For async applications, use `acompute_vhc()`:
486
+
487
+ ```python
488
+ # Async chat
489
+ response = await agent.achat("What was Apple's revenue in 2022?")
490
+
491
+ # Async VHC computation
492
+ vhc_result = await agent.acompute_vhc()
493
+ ```
494
+
495
+ ### VHC Requirements
496
+
497
+ - VHC requires a valid `VECTARA_API_KEY` environment variable
498
+ - Only VHC-eligible tools (those marked with `vhc_eligible=True`) contribute to the analysis
499
+ - VHC results are cached for each query/response pair to avoid redundant computation
500
+
458
501
  ### Tool Validation
459
502
 
460
503
  When creating an agent, you can enable tool validation by setting `validate_tools=True`. This will check that any tools mentioned in your custom instructions actually exist in the agent's tool set:
@@ -668,7 +711,7 @@ agent = Agent(
668
711
  ```
669
712
 
670
713
  The `AgentConfig` object may include the following items:
671
- - `agent_type`: the agent type. Valid values are `REACT`, `LLMCOMPILER`, `LATS` or `FUNCTION_CALLING` (default: `FUNCTION_CALLING`).
714
+ - `agent_type`: the agent type. Valid values are `REACT` or `FUNCTION_CALLING` (default: `FUNCTION_CALLING`).
672
715
  - `main_llm_provider` and `tool_llm_provider`: the LLM provider for main agent and for the tools. Valid values are `OPENAI`, `ANTHROPIC`, `TOGETHER`, `GROQ`, `COHERE`, `BEDROCK`, `GEMINI` (default: `OPENAI`).
673
716
 
674
717
  > **Note:** Fireworks AI support has been removed. If you were using Fireworks, please migrate to one of the supported providers listed above.
@@ -1,10 +1,8 @@
1
- llama-index==0.12.49
2
- llama-index-core==0.12.49
1
+ llama-index==0.12.52
2
+ llama-index-core==0.12.52.post1
3
3
  llama-index-workflow==1.0.1
4
4
  llama-index-cli==0.4.4
5
5
  llama-index-indices-managed-vectara==0.4.5
6
- llama-index-agent-llm-compiler==0.3.2
7
- llama-index-agent-lats==0.3.2
8
6
  llama-index-agent-openai==0.4.12
9
7
  llama-index-llms-openai==0.4.7
10
8
  llama-index-llms-openai-like==0.4.0
@@ -35,7 +33,7 @@ openinference-instrumentation-llama-index==4.3.1
35
33
  opentelemetry-proto>=1.31.0
36
34
  arize-phoenix==10.9.1
37
35
  arize-phoenix-otel==0.10.3
38
- protobuf==5.29.3
36
+ protobuf==5.29.5
39
37
  tokenizers>=0.20
40
38
  pydantic==2.11.5
41
39
  pandas==2.2.3
@@ -19,6 +19,7 @@ from vectara_agentic.types import AgentType, ModelProvider
19
19
  # Common Test Functions
20
20
  # ========================================
21
21
 
22
+
22
23
  def mult(x: float, y: float) -> float:
23
24
  """Multiply two numbers - common test function used across multiple test files."""
24
25
  return x * y
@@ -37,7 +38,9 @@ def add(x: float, y: float) -> float:
37
38
  STANDARD_TEST_TOPIC = "AI topic"
38
39
 
39
40
  # Standard test instructions used across most tests
40
- STANDARD_TEST_INSTRUCTIONS = "Always do as your father tells you, if your mother agrees!"
41
+ STANDARD_TEST_INSTRUCTIONS = (
42
+ "Always do as your father tells you, if your mother agrees!"
43
+ )
41
44
 
42
45
  # Alternative instructions for specific tests
43
46
  WORKFLOW_TEST_INSTRUCTIONS = "You are a helpful AI assistant."
@@ -139,6 +142,7 @@ private_llm_fc_config = AgentConfig(
139
142
  # Error Detection and Testing Utilities
140
143
  # ========================================
141
144
 
145
+
142
146
  def is_rate_limited(response_text: str) -> bool:
143
147
  """
144
148
  Check if a response indicates a rate limit error from any LLM provider.
@@ -34,6 +34,7 @@ def suppress_pydantic_warnings():
34
34
  resource_patterns = [
35
35
  ".*unclosed transport.*",
36
36
  ".*unclosed <socket\\.socket.*",
37
+ ".*unclosed event loop.*",
37
38
  ]
38
39
 
39
40
  for pattern in pydantic_patterns:
@@ -7,17 +7,13 @@ import threading
7
7
  from datetime import date
8
8
 
9
9
  from vectara_agentic.agent import Agent, AgentType
10
- from vectara_agentic.agent_core.utils.prompt_formatting import format_prompt
10
+ from vectara_agentic.agent_core.factory import format_prompt
11
11
  from vectara_agentic.agent_config import AgentConfig
12
12
  from vectara_agentic.types import ModelProvider, ObserverType
13
13
  from vectara_agentic.tools import ToolsFactory
14
14
 
15
15
  from vectara_agentic.agent_core.prompts import GENERAL_INSTRUCTIONS
16
-
17
-
18
- def mult(x: float, y: float) -> float:
19
- "Multiply two numbers"
20
- return x * y
16
+ from conftest import mult, STANDARD_TEST_TOPIC, STANDARD_TEST_INSTRUCTIONS
21
17
 
22
18
 
23
19
  ARIZE_LOCK = threading.Lock()
@@ -38,12 +34,10 @@ class TestAgentPackage(unittest.TestCase):
38
34
 
39
35
  def test_agent_init(self):
40
36
  tools = [ToolsFactory().create_tool(mult)]
41
- topic = "AI"
42
- custom_instructions = "Always do as your mother tells you!"
43
- agent = Agent(tools, topic, custom_instructions)
37
+ agent = Agent(tools, STANDARD_TEST_TOPIC, STANDARD_TEST_INSTRUCTIONS)
44
38
  self.assertEqual(agent.agent_type, AgentType.FUNCTION_CALLING)
45
- self.assertEqual(agent._topic, topic)
46
- self.assertEqual(agent._custom_instructions, custom_instructions)
39
+ self.assertEqual(agent._topic, STANDARD_TEST_TOPIC)
40
+ self.assertEqual(agent._custom_instructions, STANDARD_TEST_INSTRUCTIONS)
47
41
 
48
42
  # To run this test, you must have appropriate API key in your environment
49
43
  self.assertEqual(
@@ -56,8 +50,6 @@ class TestAgentPackage(unittest.TestCase):
56
50
  def test_agent_config(self):
57
51
  with ARIZE_LOCK:
58
52
  tools = [ToolsFactory().create_tool(mult)]
59
- topic = "AI topic"
60
- instructions = "Always do as your father tells you, if your mother agrees!"
61
53
  config = AgentConfig(
62
54
  agent_type=AgentType.REACT,
63
55
  main_llm_provider=ModelProvider.ANTHROPIC,
@@ -69,12 +61,12 @@ class TestAgentPackage(unittest.TestCase):
69
61
 
70
62
  agent = Agent(
71
63
  tools=tools,
72
- topic=topic,
73
- custom_instructions=instructions,
64
+ topic=STANDARD_TEST_TOPIC,
65
+ custom_instructions=STANDARD_TEST_INSTRUCTIONS,
74
66
  agent_config=config
75
67
  )
76
- self.assertEqual(agent._topic, topic)
77
- self.assertEqual(agent._custom_instructions, instructions)
68
+ self.assertEqual(agent._topic, STANDARD_TEST_TOPIC)
69
+ self.assertEqual(agent._custom_instructions, STANDARD_TEST_INSTRUCTIONS)
78
70
  self.assertEqual(agent.agent_type, AgentType.REACT)
79
71
  self.assertEqual(agent.agent_config.observer, ObserverType.ARIZE_PHOENIX)
80
72
  self.assertEqual(agent.agent_config.main_llm_provider, ModelProvider.ANTHROPIC)
@@ -89,19 +81,20 @@ class TestAgentPackage(unittest.TestCase):
89
81
  )
90
82
 
91
83
  def test_multiturn(self):
92
- tools = [ToolsFactory().create_tool(mult)]
93
- topic = "AI topic"
94
- instructions = "Always do as your father tells you, if your mother agrees!"
95
- agent = Agent(
96
- tools=tools,
97
- topic=topic,
98
- custom_instructions=instructions,
99
- )
84
+ with ARIZE_LOCK:
85
+ tools = [ToolsFactory().create_tool(mult)]
86
+ topic = "AI topic"
87
+ instructions = "Always do as your father tells you, if your mother agrees!"
88
+ agent = Agent(
89
+ tools=tools,
90
+ topic=topic,
91
+ custom_instructions=instructions,
92
+ )
100
93
 
101
- agent.chat("What is 5 times 10. Only give the answer, nothing else")
102
- agent.chat("what is 3 times 7. Only give the answer, nothing else")
103
- res = agent.chat("multiply the results of the last two questions. Output only the answer.")
104
- self.assertEqual(res.response, "1050")
94
+ agent.chat("What is 5 times 10. Only give the answer, nothing else")
95
+ agent.chat("what is 3 times 7. Only give the answer, nothing else")
96
+ res = agent.chat("multiply the results of the last two questions. Output only the answer.")
97
+ self.assertEqual(res.response, "1050")
105
98
 
106
99
  def test_from_corpus(self):
107
100
  agent = Agent.from_corpus(
@@ -126,6 +119,10 @@ class TestAgentPackage(unittest.TestCase):
126
119
  chat_history=[("What is 5 times 10", "50"), ("What is 3 times 7", "21")]
127
120
  )
128
121
 
122
+ data = agent.dumps()
123
+ clone = Agent.loads(data)
124
+ assert clone.memory.get() == agent.memory.get()
125
+
129
126
  res = agent.chat("multiply the results of the last two questions. Output only the answer.")
130
127
  self.assertEqual(res.response, "1050")
131
128
 
@@ -0,0 +1,270 @@
1
+ # Suppress external dependency warnings before any other imports
2
+ import warnings
3
+
4
+ warnings.simplefilter("ignore", DeprecationWarning)
5
+
6
+ import unittest
7
+ import threading
8
+
9
+ from vectara_agentic.agent import Agent, AgentType
10
+ from vectara_agentic.agent_config import AgentConfig
11
+ from vectara_agentic.types import ModelProvider, AgentConfigType
12
+ from vectara_agentic.tools import ToolsFactory
13
+
14
+ from llama_index.core.llms import ChatMessage, MessageRole
15
+ from conftest import mult, add
16
+
17
+
18
+ ARIZE_LOCK = threading.Lock()
19
+
20
+
21
+ class TestAgentFallbackMemoryConsistency(unittest.TestCase):
22
+ """Test memory consistency between main and fallback agents"""
23
+
24
+ def setUp(self):
25
+ """Set up test fixtures"""
26
+ self.tools = [ToolsFactory().create_tool(mult), ToolsFactory().create_tool(add)]
27
+ self.topic = "Mathematics"
28
+ self.custom_instructions = "You are a helpful math assistant."
29
+
30
+ # Main agent config
31
+ self.main_config = AgentConfig(
32
+ agent_type=AgentType.FUNCTION_CALLING,
33
+ main_llm_provider=ModelProvider.ANTHROPIC,
34
+ )
35
+
36
+ # Fallback agent config
37
+ self.fallback_config = AgentConfig(
38
+ agent_type=AgentType.REACT, main_llm_provider=ModelProvider.ANTHROPIC
39
+ )
40
+
41
+ self.session_id = "test-fallback-session-123"
42
+
43
+ def test_memory_consistency_on_agent_creation(self):
44
+ """Test that main and fallback agents are created with the same memory content"""
45
+ agent = Agent(
46
+ tools=self.tools,
47
+ topic=self.topic,
48
+ custom_instructions=self.custom_instructions,
49
+ agent_config=self.main_config,
50
+ fallback_agent_config=self.fallback_config,
51
+ session_id=self.session_id,
52
+ )
53
+
54
+ # Add some memory before creating the agents
55
+ test_messages = [
56
+ ChatMessage(role=MessageRole.USER, content="What is 2*3?"),
57
+ ChatMessage(role=MessageRole.ASSISTANT, content="2*3 = 6"),
58
+ ]
59
+ agent.memory.put_messages(test_messages)
60
+
61
+ # Verify both agents have memory with the same content
62
+ # Memory is managed by the main Agent class, not individual agent instances
63
+ main_memory = agent.memory.get()
64
+ fallback_memory = agent.memory.get() # Both access the same memory
65
+
66
+ self.assertEqual(len(main_memory), 2)
67
+ self.assertEqual(len(fallback_memory), 2)
68
+ self.assertEqual(main_memory[0].content, "What is 2*3?")
69
+ self.assertEqual(fallback_memory[0].content, "What is 2*3?")
70
+
71
+ # Verify session_id consistency
72
+ # Memory is managed by the main Agent class
73
+ self.assertEqual(agent.memory.session_id, self.session_id)
74
+
75
+ def test_memory_sync_during_agent_switching(self):
76
+ """Test that memory remains consistent when switching between main and fallback agents"""
77
+ agent = Agent(
78
+ tools=self.tools,
79
+ topic=self.topic,
80
+ custom_instructions=self.custom_instructions,
81
+ agent_config=self.main_config,
82
+ fallback_agent_config=self.fallback_config,
83
+ session_id=self.session_id,
84
+ )
85
+
86
+ # Start with main agent
87
+ self.assertEqual(agent.agent_config_type, AgentConfigType.DEFAULT)
88
+
89
+ # Add initial memory
90
+ initial_messages = [
91
+ ChatMessage(role=MessageRole.USER, content="Initial question"),
92
+ ChatMessage(role=MessageRole.ASSISTANT, content="Initial response"),
93
+ ]
94
+ agent.memory.put_messages(initial_messages)
95
+
96
+ # Access main agent to ensure it's loaded
97
+ main_memory_before = agent.memory.get() # Memory managed by main Agent class
98
+ self.assertEqual(len(main_memory_before), 2)
99
+
100
+ # Switch to fallback agent (this should clear the fallback agent instance)
101
+ agent._switch_agent_config()
102
+ self.assertEqual(agent.agent_config_type, AgentConfigType.FALLBACK)
103
+
104
+ # Access fallback agent (should be recreated with current memory)
105
+ fallback_memory = agent.memory.get() # Memory managed by main Agent class
106
+
107
+ # Verify fallback agent has the same memory content
108
+ self.assertEqual(len(fallback_memory), 2)
109
+ self.assertEqual(fallback_memory[0].content, "Initial question")
110
+ self.assertEqual(fallback_memory[1].content, "Initial response")
111
+
112
+ # Add more memory while using fallback agent
113
+ additional_messages = [
114
+ ChatMessage(role=MessageRole.USER, content="Fallback question"),
115
+ ChatMessage(role=MessageRole.ASSISTANT, content="Fallback response"),
116
+ ]
117
+ agent.memory.put_messages(additional_messages)
118
+
119
+ # Switch back to main agent (this should clear the main agent instance)
120
+ agent._switch_agent_config()
121
+ self.assertEqual(agent.agent_config_type, AgentConfigType.DEFAULT)
122
+
123
+ # Verify recreated main agent now has all the memory including what was added during fallback
124
+ main_memory_after = agent.memory.get() # Memory managed by main Agent class
125
+ self.assertEqual(len(main_memory_after), 4)
126
+ self.assertEqual(main_memory_after[2].content, "Fallback question")
127
+ self.assertEqual(main_memory_after[3].content, "Fallback response")
128
+
129
+ def test_memory_sync_on_clear_memory(self):
130
+ """Test that memory clearing resets agent instances for consistency"""
131
+ agent = Agent(
132
+ tools=self.tools,
133
+ topic=self.topic,
134
+ custom_instructions=self.custom_instructions,
135
+ agent_config=self.main_config,
136
+ fallback_agent_config=self.fallback_config,
137
+ session_id=self.session_id,
138
+ )
139
+
140
+ # Add memory
141
+ test_messages = [
142
+ ChatMessage(role=MessageRole.USER, content="Test question"),
143
+ ChatMessage(role=MessageRole.ASSISTANT, content="Test response"),
144
+ ]
145
+ agent.memory.put_messages(test_messages)
146
+
147
+ # Verify memory exists
148
+ # Memory is managed by the main Agent class
149
+ self.assertEqual(len(agent.memory.get()), 2)
150
+ self.assertEqual(len(agent.memory.get()), 2) # Both access same memory
151
+
152
+ # Clear memory (should reset agent instances)
153
+ agent.clear_memory()
154
+
155
+ # Verify core memory is cleared
156
+ self.assertEqual(len(agent.memory.get()), 0)
157
+
158
+ # Verify agent instances were reset (None)
159
+ self.assertIsNone(agent._agent)
160
+ self.assertIsNone(agent._fallback_agent)
161
+
162
+ # Verify new agents have cleared memory
163
+ # Memory is managed by the main Agent class
164
+ self.assertEqual(len(agent.memory.get()), 0)
165
+ self.assertEqual(len(agent.memory.get()), 0) # Both access same memory
166
+
167
+ def test_memory_consistency_after_serialization(self):
168
+ """Test that memory consistency is maintained after serialization/deserialization"""
169
+ agent = Agent(
170
+ tools=self.tools,
171
+ topic=self.topic,
172
+ custom_instructions=self.custom_instructions,
173
+ agent_config=self.main_config,
174
+ fallback_agent_config=self.fallback_config,
175
+ session_id=self.session_id,
176
+ )
177
+
178
+ # Add memory and load both agents
179
+ test_messages = [
180
+ ChatMessage(role=MessageRole.USER, content="Serialization test"),
181
+ ChatMessage(role=MessageRole.ASSISTANT, content="Serialization response"),
182
+ ]
183
+ agent.memory.put_messages(test_messages)
184
+
185
+ # Access both agents
186
+ _ = agent.agent
187
+ _ = agent.fallback_agent
188
+
189
+ # Serialize and deserialize
190
+ serialized_data = agent.dumps()
191
+ restored_agent = Agent.loads(serialized_data)
192
+
193
+ # Verify memory is preserved and consistent
194
+ self.assertEqual(restored_agent.session_id, self.session_id)
195
+ self.assertEqual(len(restored_agent.memory.get()), 2)
196
+
197
+ # Verify memory consistency
198
+ # Individual agent instances don't have .memory attribute - memory is managed by main Agent class
199
+ # Both agent instances should use the same memory from the main Agent
200
+
201
+ main_memory = restored_agent.memory.get()
202
+ fallback_memory = restored_agent.memory.get() # Both access same memory
203
+
204
+ self.assertEqual(len(main_memory), 2)
205
+ self.assertEqual(len(fallback_memory), 2)
206
+ self.assertEqual(main_memory[0].content, "Serialization test")
207
+ self.assertEqual(fallback_memory[0].content, "Serialization test")
208
+
209
+ def test_session_id_consistency_across_agents(self):
210
+ """Test that session_id is consistent between main and fallback agents"""
211
+ agent = Agent(
212
+ tools=self.tools,
213
+ topic=self.topic,
214
+ custom_instructions=self.custom_instructions,
215
+ agent_config=self.main_config,
216
+ fallback_agent_config=self.fallback_config,
217
+ session_id=self.session_id,
218
+ )
219
+
220
+ # Verify main agent session_id consistency
221
+ self.assertEqual(agent.session_id, self.session_id)
222
+ self.assertEqual(agent.memory.session_id, self.session_id)
223
+
224
+ # Verify session_id consistency across all agents
225
+ # Memory is managed by the main Agent class
226
+ self.assertEqual(agent.memory.session_id, self.session_id)
227
+ self.assertEqual(
228
+ agent.memory.session_id, self.session_id
229
+ ) # Both access same memory
230
+
231
+ def test_agent_recreation_on_switch(self):
232
+ """Test that agents are properly recreated when switching configurations"""
233
+ agent = Agent(
234
+ tools=self.tools,
235
+ topic=self.topic,
236
+ custom_instructions=self.custom_instructions,
237
+ agent_config=self.main_config,
238
+ fallback_agent_config=self.fallback_config,
239
+ session_id=self.session_id,
240
+ )
241
+
242
+ # Load main agent
243
+ original_main_agent = agent.agent
244
+ self.assertIsNotNone(original_main_agent)
245
+
246
+ # Load fallback agent
247
+ original_fallback_agent = agent.fallback_agent
248
+ self.assertIsNotNone(original_fallback_agent)
249
+
250
+ # Switch to fallback - should clear the fallback agent instance
251
+ agent._switch_agent_config()
252
+ self.assertEqual(agent.agent_config_type, AgentConfigType.FALLBACK)
253
+ self.assertIsNone(agent._fallback_agent) # Should be cleared
254
+
255
+ # Access fallback agent again - should be a new instance
256
+ new_fallback_agent = agent.fallback_agent
257
+ self.assertIsNot(new_fallback_agent, original_fallback_agent)
258
+
259
+ # Switch back to main - should clear the main agent instance
260
+ agent._switch_agent_config()
261
+ self.assertEqual(agent.agent_config_type, AgentConfigType.DEFAULT)
262
+ self.assertIsNone(agent._agent) # Should be cleared
263
+
264
+ # Access main agent again - should be a new instance
265
+ new_main_agent = agent.agent
266
+ self.assertIsNot(new_main_agent, original_main_agent)
267
+
268
+
269
+ if __name__ == "__main__":
270
+ unittest.main()