vectara-agentic 0.4.5__tar.gz → 0.4.7__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 (66) hide show
  1. {vectara_agentic-0.4.5/vectara_agentic.egg-info → vectara_agentic-0.4.7}/PKG-INFO +5 -5
  2. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/requirements.txt +4 -4
  3. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_tools.py +161 -0
  4. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/_version.py +1 -1
  5. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/prompts.py +11 -11
  6. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/streaming.py +6 -5
  7. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/utils/tools.py +19 -11
  8. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/tools.py +112 -5
  9. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7/vectara_agentic.egg-info}/PKG-INFO +5 -5
  10. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic.egg-info/requires.txt +4 -4
  11. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/LICENSE +0 -0
  12. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/MANIFEST.in +0 -0
  13. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/README.md +0 -0
  14. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/setup.cfg +0 -0
  15. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/setup.py +0 -0
  16. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/__init__.py +0 -0
  17. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/benchmark_models.py +0 -0
  18. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/conftest.py +0 -0
  19. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/endpoint.py +0 -0
  20. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/run_tests.py +0 -0
  21. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_agent.py +0 -0
  22. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_agent_fallback_memory.py +0 -0
  23. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_agent_memory_consistency.py +0 -0
  24. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_agent_type.py +0 -0
  25. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_api_endpoint.py +0 -0
  26. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_bedrock.py +0 -0
  27. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_fallback.py +0 -0
  28. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_gemini.py +0 -0
  29. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_groq.py +0 -0
  30. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_openai.py +0 -0
  31. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_private_llm.py +0 -0
  32. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_react_error_handling.py +0 -0
  33. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_react_memory.py +0 -0
  34. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_react_streaming.py +0 -0
  35. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_react_workflow_events.py +0 -0
  36. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_return_direct.py +0 -0
  37. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_serialization.py +0 -0
  38. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_session_memory.py +0 -0
  39. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_streaming.py +0 -0
  40. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_together.py +0 -0
  41. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_vectara_llms.py +0 -0
  42. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_vhc.py +0 -0
  43. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/tests/test_workflow.py +0 -0
  44. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/__init__.py +0 -0
  45. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/_callback.py +0 -0
  46. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/_observability.py +0 -0
  47. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent.py +0 -0
  48. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_config.py +0 -0
  49. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/__init__.py +0 -0
  50. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/factory.py +0 -0
  51. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/serialization.py +0 -0
  52. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/utils/__init__.py +0 -0
  53. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/utils/hallucination.py +0 -0
  54. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/utils/logging.py +0 -0
  55. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_core/utils/schemas.py +0 -0
  56. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/agent_endpoint.py +0 -0
  57. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/db_tools.py +0 -0
  58. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/llm_utils.py +0 -0
  59. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/sub_query_workflow.py +0 -0
  60. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/tool_utils.py +0 -0
  61. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/tools_catalog.py +0 -0
  62. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/types.py +0 -0
  63. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic/utils.py +0 -0
  64. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic.egg-info/SOURCES.txt +0 -0
  65. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/vectara_agentic.egg-info/dependency_links.txt +0 -0
  66. {vectara_agentic-0.4.5 → vectara_agentic-0.4.7}/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.5
3
+ Version: 0.4.7
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,20 +16,20 @@ 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.13.3
20
- Requires-Dist: llama-index-core==0.13.3
19
+ Requires-Dist: llama-index==0.13.6
20
+ Requires-Dist: llama-index-core==0.13.6
21
21
  Requires-Dist: llama-index-workflows==1.3.0
22
22
  Requires-Dist: llama-index-cli==0.5.0
23
23
  Requires-Dist: llama-index-indices-managed-vectara==0.5.0
24
24
  Requires-Dist: llama-index-llms-openai==0.5.4
25
25
  Requires-Dist: llama-index-llms-openai-like==0.5.0
26
- Requires-Dist: llama-index-llms-anthropic==0.8.5
26
+ Requires-Dist: llama-index-llms-anthropic==0.8.6
27
27
  Requires-Dist: llama-index-llms-together==0.4.0
28
28
  Requires-Dist: llama-index-llms-groq==0.4.0
29
29
  Requires-Dist: llama-index-llms-cohere==0.6.0
30
30
  Requires-Dist: llama-index-llms-google-genai==0.3.0
31
31
  Requires-Dist: google_genai>=1.31.0
32
- Requires-Dist: llama-index-llms-bedrock-converse==0.8.2
32
+ Requires-Dist: llama-index-llms-bedrock-converse==0.9.0
33
33
  Requires-Dist: llama-index-tools-yahoo-finance==0.4.0
34
34
  Requires-Dist: llama-index-tools-arxiv==0.4.0
35
35
  Requires-Dist: llama-index-tools-database==0.4.0
@@ -1,17 +1,17 @@
1
- llama-index==0.13.3
2
- llama-index-core==0.13.3
1
+ llama-index==0.13.6
2
+ llama-index-core==0.13.6
3
3
  llama-index-workflows==1.3.0
4
4
  llama-index-cli==0.5.0
5
5
  llama-index-indices-managed-vectara==0.5.0
6
6
  llama-index-llms-openai==0.5.4
7
7
  llama-index-llms-openai-like==0.5.0
8
- llama-index-llms-anthropic==0.8.5
8
+ llama-index-llms-anthropic==0.8.6
9
9
  llama-index-llms-together==0.4.0
10
10
  llama-index-llms-groq==0.4.0
11
11
  llama-index-llms-cohere==0.6.0
12
12
  llama-index-llms-google-genai==0.3.0
13
13
  google_genai>=1.31.0
14
- llama-index-llms-bedrock-converse==0.8.2
14
+ llama-index-llms-bedrock-converse==0.9.0
15
15
  llama-index-tools-yahoo-finance==0.4.0
16
16
  llama-index-tools-arxiv==0.4.0
17
17
  llama-index-tools-database==0.4.0
@@ -15,6 +15,8 @@ from vectara_agentic.tools import (
15
15
  VectaraToolFactory,
16
16
  ToolsFactory,
17
17
  ToolType,
18
+ normalize_url,
19
+ citation_appears_in_text,
18
20
  )
19
21
  from vectara_agentic.agent import Agent
20
22
  from vectara_agentic.agent_config import AgentConfig
@@ -367,6 +369,165 @@ class TestToolsPackage(unittest.TestCase):
367
369
  self.assertIn("Returns:", doc)
368
370
  self.assertIn("dict[str, Any]: A dictionary containing the result data.", doc)
369
371
 
372
+ def test_normalize_url(self):
373
+ """Test URL normalization function"""
374
+ # Test space encoding normalization
375
+ self.assertEqual(
376
+ normalize_url("http://example.com/file with spaces.pdf"),
377
+ "http://example.com/file%20with%20spaces.pdf",
378
+ )
379
+
380
+ # Test that already encoded URLs remain normalized
381
+ self.assertEqual(
382
+ normalize_url("http://example.com/file%20with%20spaces.pdf"),
383
+ "http://example.com/file%20with%20spaces.pdf",
384
+ )
385
+
386
+ # Test special characters
387
+ self.assertEqual(
388
+ normalize_url("http://example.com/path?query=hello world&foo=bar"),
389
+ "http://example.com/path?query=hello%20world&foo=bar",
390
+ )
391
+
392
+ # Test empty/None input
393
+ self.assertEqual(normalize_url(""), "")
394
+ self.assertEqual(normalize_url(None), None)
395
+
396
+ # Test complex URL with multiple encodable characters
397
+ result = normalize_url("http://example.com/docs/My Document [v2].pdf#section 1")
398
+ expected = "http://example.com/docs/My%20Document%20[v2].pdf#section%201"
399
+ self.assertEqual(result, expected)
400
+
401
+ def test_citation_appears_in_text_exact_match(self):
402
+ """Test citation matching with exact format"""
403
+ response_text = "Here's the info [Document Title](http://example.com/doc.pdf) for reference."
404
+
405
+ # Should match exact citation
406
+ self.assertTrue(
407
+ citation_appears_in_text(
408
+ "Document Title", "http://example.com/doc.pdf", response_text
409
+ )
410
+ )
411
+
412
+ # Should not match different text with different URL
413
+ self.assertFalse(
414
+ citation_appears_in_text(
415
+ "Wrong Title", "http://different.com/other.pdf", response_text
416
+ )
417
+ )
418
+
419
+ def test_citation_appears_in_text_url_encoding(self):
420
+ """Test citation matching with URL encoding differences"""
421
+ # Response text with percent-encoded URL
422
+ response_text_encoded = (
423
+ "See [My Doc](http://example.com/my%20document.pdf) for details."
424
+ )
425
+
426
+ # Should match when citation URL has spaces
427
+ self.assertTrue(
428
+ citation_appears_in_text(
429
+ "My Doc", "http://example.com/my document.pdf", response_text_encoded
430
+ )
431
+ )
432
+
433
+ # Response text with spaces in URL
434
+ response_text_spaces = (
435
+ "See [My Doc](http://example.com/my document.pdf) for details."
436
+ )
437
+
438
+ # Should match when citation URL is encoded
439
+ self.assertTrue(
440
+ citation_appears_in_text(
441
+ "My Doc", "http://example.com/my%20document.pdf", response_text_spaces
442
+ )
443
+ )
444
+
445
+ def test_citation_appears_in_text_url_presence(self):
446
+ """Test fallback URL presence matching"""
447
+ # Response text that contains URL but not in exact citation format
448
+ response_text = (
449
+ "The document at http://example.com/report.pdf contains the analysis."
450
+ )
451
+
452
+ # Should match based on URL presence
453
+ self.assertTrue(
454
+ citation_appears_in_text(
455
+ "Report", "http://example.com/report.pdf", response_text
456
+ )
457
+ )
458
+
459
+ # Should work with encoded URL in response
460
+ response_encoded = (
461
+ "The document at http://example.com/my%20report.pdf contains data."
462
+ )
463
+ self.assertTrue(
464
+ citation_appears_in_text(
465
+ "Report", "http://example.com/my report.pdf", response_encoded
466
+ )
467
+ )
468
+
469
+ def test_citation_appears_in_text_edge_cases(self):
470
+ """Test edge cases and error conditions"""
471
+ response_text = "Some text with [citations](http://example.com/doc.pdf) here."
472
+
473
+ # Empty inputs should return False
474
+ self.assertFalse(
475
+ citation_appears_in_text("", "http://example.com/doc.pdf", response_text)
476
+ )
477
+ self.assertFalse(citation_appears_in_text("Title", "", response_text))
478
+ self.assertFalse(
479
+ citation_appears_in_text("Title", "http://example.com/doc.pdf", "")
480
+ )
481
+ self.assertFalse(
482
+ citation_appears_in_text(None, "http://example.com/doc.pdf", response_text)
483
+ )
484
+
485
+ # Both None should return False
486
+ self.assertFalse(citation_appears_in_text(None, None, response_text))
487
+
488
+ # Very short filename should not trigger filename matching
489
+ self.assertFalse(
490
+ citation_appears_in_text(
491
+ "Title", "http://example.com/x.y", "Different content"
492
+ )
493
+ )
494
+
495
+ def test_citation_appears_in_text_complex_encoding(self):
496
+ """Test complex URL encoding scenarios"""
497
+ # Test case with multiple special characters
498
+ response_text = "Document: [Legal Doc](http://example.com/docs/Contract%20%5B2024%5D%20%26%20Agreement.pdf)"
499
+
500
+ # Should match with unencoded URL
501
+ self.assertTrue(
502
+ citation_appears_in_text(
503
+ "Legal Doc",
504
+ "http://example.com/docs/Contract [2024] & Agreement.pdf",
505
+ response_text,
506
+ )
507
+ )
508
+
509
+ def test_citation_appears_in_text_url_only(self):
510
+ """Test citation matching when only URL is available (no text)"""
511
+ # Test the [(url)] format when only URL is available
512
+ response_text = "Reference: [(http://example.com/report.pdf)] shows data."
513
+
514
+ # Should match with URL-only citation format
515
+ self.assertTrue(
516
+ citation_appears_in_text(
517
+ None, "http://example.com/report.pdf", response_text
518
+ )
519
+ )
520
+
521
+ # Should also work with URL encoding differences
522
+ response_encoded = (
523
+ "Reference: [(http://example.com/my%20report.pdf)] shows data."
524
+ )
525
+ self.assertTrue(
526
+ citation_appears_in_text(
527
+ None, "http://example.com/my report.pdf", response_encoded
528
+ )
529
+ )
530
+
370
531
 
371
532
  if __name__ == "__main__":
372
533
  unittest.main()
@@ -1,4 +1,4 @@
1
1
  """
2
2
  Define the version of the package.
3
3
  """
4
- __version__ = "0.4.5"
4
+ __version__ = "0.4.7"
@@ -23,7 +23,7 @@ GENERAL_INSTRUCTIONS = """
23
23
  In rephrasing, aim for alternative queries that may work better for searching for the information.
24
24
  For example, you can rephrase "CEO" with "Chief Executive Officer".
25
25
  2) Break the question into sub-questions and call this tool or another tool for each sub-question, then combine the answers to provide a complete response.
26
- For example if asked "what is the population of France and Germany", you can call the tool twice, once for France and once for Germany.
26
+ For example if asked "what is the population of France and Germany", you can call the tool twice, once for France and once for Germany,
27
27
  and then combine the responses to provide the full answer.
28
28
  3) If a tool fails, try other tools that might be appropriate to gain the information you need.
29
29
  - If after retrying you can't get the information or answer the question, respond with "I don't know".
@@ -31,22 +31,22 @@ GENERAL_INSTRUCTIONS = """
31
31
  Be consistent with the format of numbers and dates across multi turn conversations.
32
32
  - Handling citations - IMPORTANT:
33
33
  1) Always embed citations inline with the text of your response, using valid URLs provided by tools.
34
- Never omit a legitimate citations.
35
- Avoid creating a bibliography or a list of sources at the end of your response, and referring the reader to that list.
34
+ Never omit a legitimate citation.
35
+ Never repeat the same citation multiple times in a response.
36
+ 2) Avoid creating a bibliography or a list of sources at the end of your response, and referring the reader to that list.
36
37
  Instead, embed citations directly in the text where the information is presented.
37
38
  For example, "According to the [Nvidia 10-K report](https://www.nvidia.com/doc.pdf#page=8), revenue in 2021 was $10B."
38
- 2) When including URLs in the citation, only use well-formed, non-empty URLs (beginning with “http://” or “https://”) and ignore any malformed or placeholder links.
39
- 3) Use descriptive link text for citations whenever possible, falling back to numeric labels only when necessary.
39
+ 3) When including URLs in the citation, only use well-formed, non-empty URLs (beginning with “http://” or “https://”) and ignore any malformed or placeholder links.
40
+ 4) Use descriptive link text for citations whenever possible, falling back to numeric labels only when necessary.
40
41
  Preferred: "According to the [Nvidia 10-K report](https://www.nvidia.com/doc.pdf#page=8), revenue in 2021 was $10B."
41
42
  Fallback: "According to the Nvidia 10-K report, revenue in 2021 was $10B [1](https://www.nvidia.com/doc.pdf#page=8)."
42
- 4) If a URL is for a PDF file, and the tool also provided a page number, append "#page=X" to the URL.
43
+ 5) If a URL is for a PDF file, and the tool also provided a page number, append "#page=X" to the URL.
43
44
  For example, if the URL is "https://www.xxx.com/doc.pdf" and "page='5'", then the URL used in the citation would be "https://www.xxx.com/doc.pdf#page=5".
44
45
  Always include the page number in the URL, whether you use anchor text or a numeric label.
45
- 5) When citing images, figures, or tables, link directly to the file (or PDF page) just as you would for text.
46
- 6) Give each discrete fact its own citation (or citations), even if multiple facts come from the same document.
47
- Avoid lumping multiple pages into one citation.
48
- 7) Ensure a space or punctuation precedes and follows every citation.
49
- Here's an example where there is no proper spacing, and the citation is shown right after "10-K": "As shown in the [Nvidia 10-K](https://www.nvidia.com), the revenue in 2021 was $10B".
46
+ 6) When citing images, figures, or tables, link directly to the file (or PDF page) just as you would for text.
47
+ 7) Give each discrete fact its own citation (or citations), even if multiple facts come from the same document.
48
+ 8) Ensure a space or punctuation precedes and follows every citation.
49
+ Here's an example where there is no proper spacing, and the citation is shown right after "10-K": "As shown in the[Nvidia 10-K](https://www.nvidia.com), the revenue in 2021 was $10B".
50
50
  Instead use spacing properly: "As shown in the [Nvidia 10-K](https://www.nvidia.com), the revenue in 2021 was $10B".
51
51
  - If a tool returns a "Malfunction" error - notify the user that you cannot respond due a tool not operating properly (and the tool name).
52
52
  - Your response should never be the input to a tool, only the output.
@@ -302,6 +302,7 @@ class FunctionCallingStreamHandler:
302
302
  hasattr(ev, "tool_calls")
303
303
  and not ev.tool_calls
304
304
  and hasattr(ev, "delta")
305
+ and transitioned_to_prose
305
306
  ):
306
307
  yield ev.delta
307
308
 
@@ -311,16 +312,16 @@ class FunctionCallingStreamHandler:
311
312
  except Exception as e:
312
313
  error_str = str(e).lower()
313
314
  if "rate limit" in error_str or "429" in error_str:
314
- logging.error(f"🔍 [RATE_LIMIT_ERROR] Rate limit exceeded: {e}")
315
+ logging.error(f"[RATE_LIMIT_ERROR] Rate limit exceeded: {e}")
315
316
  self.final_response_container["resp"] = AgentResponse(
316
317
  response="Rate limit exceeded. Please try again later.",
317
318
  source_nodes=[],
318
319
  metadata={"error_type": "rate_limit", "original_error": str(e)},
319
320
  )
320
321
  else:
321
- logging.error(f"🔍 [STREAM_ERROR] Error processing stream events: {e}")
322
+ logging.error(f"[STREAM_ERROR] Error processing stream events: {e}")
322
323
  logging.error(
323
- f"🔍 [STREAM_ERROR] Full traceback: {traceback.format_exc()}"
324
+ f"[STREAM_ERROR] Full traceback: {traceback.format_exc()}"
324
325
  )
325
326
  self.final_response_container["resp"] = AgentResponse(
326
327
  response="Response completion Error",
@@ -590,10 +591,10 @@ class ReActStreamHandler:
590
591
  self.final_response_container["resp"] = await self.handler
591
592
  except Exception as e:
592
593
  logging.error(
593
- f"🔍 [REACT_STREAM_ERROR] Error processing ReAct stream events: {e}"
594
+ f"[REACT_STREAM_ERROR] Error processing ReAct stream events: {e}"
594
595
  )
595
596
  logging.error(
596
- f"🔍 [REACT_STREAM_ERROR] Full traceback: {traceback.format_exc()}"
597
+ f"[REACT_STREAM_ERROR] Full traceback: {traceback.format_exc()}"
597
598
  )
598
599
  self.final_response_container["resp"] = AgentResponse(
599
600
  response="ReAct Response completion Error", source_nodes=[], metadata={}
@@ -9,9 +9,11 @@ import inspect
9
9
  from typing import Any, List
10
10
  from inspect import Signature, Parameter, ismethod
11
11
  from collections import Counter
12
+ import logging
12
13
 
13
14
  from pydantic import Field, create_model
14
15
  from llama_index.core.tools import FunctionTool
16
+
15
17
  from ...llm_utils import get_llm
16
18
  from ...types import LLMRole
17
19
 
@@ -37,17 +39,23 @@ def sanitize_tools_for_gemini(tools: List[FunctionTool]) -> List[FunctionTool]:
37
39
  for func in (tool.fn, tool.async_fn):
38
40
  if not func:
39
41
  continue
40
- orig_sig = inspect.signature(func)
41
- new_params = [
42
- p.replace(default=Parameter.empty) for p in orig_sig.parameters.values()
43
- ]
44
- new_sig = Signature(
45
- new_params, return_annotation=orig_sig.return_annotation
46
- )
47
- if ismethod(func):
48
- func.__func__.__signature__ = new_sig
49
- else:
50
- func.__signature__ = new_sig
42
+ try:
43
+ orig_sig = inspect.signature(func)
44
+ new_params = [
45
+ p.replace(default=Parameter.empty) for p in orig_sig.parameters.values()
46
+ ]
47
+ new_sig = Signature(
48
+ new_params, return_annotation=orig_sig.return_annotation
49
+ )
50
+ if ismethod(func):
51
+ func.__func__.__signature__ = new_sig
52
+ else:
53
+ func.__signature__ = new_sig
54
+ except Exception as e:
55
+ logging.warning(
56
+ f"Could not modify signature for tool '{tool.metadata.name}': {e}. "
57
+ "Proceeding with Pydantic schema modification."
58
+ )
51
59
 
52
60
  # 2) Rebuild the Pydantic schema so that *every* field is required
53
61
  schema_cls = getattr(tool.metadata, "fn_schema", None)
@@ -6,7 +6,9 @@ import inspect
6
6
  import importlib
7
7
  import os
8
8
  import asyncio
9
+ import urllib.parse
9
10
  from typing import Callable, List, Dict, Any, Optional, Union
11
+ import logging
10
12
 
11
13
  from retrying import retry
12
14
  from pydantic import BaseModel, Field
@@ -65,6 +67,105 @@ LI_packages = {
65
67
  }
66
68
 
67
69
 
70
+ def normalize_url(url):
71
+ """
72
+ Normalize URL for consistent comparison by handling percent-encoding.
73
+
74
+ Args:
75
+ url (str): The URL to normalize
76
+
77
+ Returns:
78
+ str: Normalized URL with consistent percent-encoding
79
+ """
80
+ if not url:
81
+ return url
82
+
83
+ try:
84
+ # Decode percent-encoded characters
85
+ decoded = urllib.parse.unquote(url)
86
+ # Re-encode consistently using standard safe characters
87
+ normalized = urllib.parse.quote(decoded, safe=':/?#[]@!$&\'()*+,;=')
88
+ return normalized
89
+ except Exception as e:
90
+ logging.warning(f"Error normalizing URL '{url}': {e}")
91
+ return url
92
+
93
+
94
+ def citation_appears_in_text(citation_text, citation_url, response_text):
95
+ """
96
+ Check if citation appears in response text using multiple matching strategies.
97
+ Handles citation formatting internally based on available text and URL.
98
+
99
+ Args:
100
+ citation_text (str): The text part of the citation (can be None)
101
+ citation_url (str): The URL part of the citation (can be None)
102
+ response_text (str): The response text to search in
103
+
104
+ Returns:
105
+ bool: True if citation appears in response text
106
+ """
107
+ if not response_text:
108
+ return False
109
+
110
+ # If no citation info available, return False
111
+ # Empty strings should be treated as None
112
+ if not citation_text and not citation_url:
113
+ return False
114
+
115
+ # Generate possible citation formats based on available data
116
+ citation_formats = []
117
+
118
+ # Normalize empty strings to None for cleaner logic
119
+ if citation_text == "":
120
+ citation_text = None
121
+ if citation_url == "":
122
+ citation_url = None
123
+
124
+ if citation_text and citation_url:
125
+ citation_formats.append(f"[{citation_text}]({citation_url})")
126
+ # Also try with normalized URL
127
+ normalized_url = normalize_url(citation_url)
128
+ if normalized_url != citation_url:
129
+ citation_formats.append(f"[{citation_text}]({normalized_url})")
130
+ # Also try with decoded URL (for cases where input is encoded but response has spaces)
131
+ decoded_url = urllib.parse.unquote(citation_url)
132
+ if decoded_url != citation_url:
133
+ citation_formats.append(f"[{citation_text}]({decoded_url})")
134
+ # Also try with aggressive encoding (encode more characters)
135
+ aggressive_encoded = urllib.parse.quote(decoded_url, safe=':/?#')
136
+ if aggressive_encoded not in (citation_url, normalized_url):
137
+ citation_formats.append(f"[{citation_text}]({aggressive_encoded})")
138
+ elif citation_url:
139
+ # Handle case where only URL is available (original logic: "[({url})]")
140
+ citation_formats.append(f"[({citation_url})]")
141
+ normalized_url = normalize_url(citation_url)
142
+ if normalized_url != citation_url:
143
+ citation_formats.append(f"[({normalized_url})]")
144
+ decoded_url = urllib.parse.unquote(citation_url)
145
+ if decoded_url != citation_url:
146
+ citation_formats.append(f"[({decoded_url})]")
147
+
148
+ # Strategy 1: Exact citation format matches
149
+ for citation_format in citation_formats:
150
+ if citation_format in response_text:
151
+ return True
152
+
153
+ # Strategy 2: URL appears anywhere in response (more lenient)
154
+ # Only apply this strategy if we have both citation_text and citation_url
155
+ # URL-only matching happens in Strategy 1 through citation formats
156
+ if citation_text and citation_url:
157
+ normalized_url = normalize_url(citation_url)
158
+ # Try both encoded and decoded versions
159
+ decoded_url = urllib.parse.unquote(citation_url)
160
+
161
+ if (citation_url in response_text or
162
+ normalized_url in response_text or
163
+ decoded_url in response_text):
164
+ return True
165
+
166
+ return False
167
+
168
+
68
169
  @retry(stop_max_attempt_number=3, wait_exponential_multiplier=1000, wait_exponential_max=10000)
69
170
  def _query_with_retry(vectara_query_engine, query):
70
171
  """Execute Vectara query with automatic retry on timeout/failure."""
@@ -595,11 +696,16 @@ class VectaraToolFactory:
595
696
  part_data = {k: v for k, v in node_metadata.items() if k != 'document'}
596
697
  template_data['part'] = to_obj(part_data)
597
698
 
598
- formatted_citation_text = computed_citations_text_pattern.format(**template_data)
599
- formatted_citation_url = computed_citations_url_pattern.format(**template_data)
600
- expected_citation = f"[{formatted_citation_text}]({formatted_citation_url})"
699
+ try:
700
+ formatted_citation_text = computed_citations_text_pattern.format(**template_data)
701
+ except Exception:
702
+ formatted_citation_text = None
703
+ try:
704
+ formatted_citation_url = computed_citations_url_pattern.format(**template_data)
705
+ except Exception:
706
+ formatted_citation_url = None
601
707
 
602
- if expected_citation in response_text:
708
+ if citation_appears_in_text(formatted_citation_text, formatted_citation_url, response_text):
603
709
  citation_metadata.append({
604
710
  'doc_id': node_id,
605
711
  'text': node_text,
@@ -673,7 +779,8 @@ class ToolsFactory:
673
779
  VectaraTool: A VectaraTool object.
674
780
  """
675
781
  return VectaraTool.from_defaults(
676
- tool_type=tool_type, fn=function, vhc_eligible=vhc_eligible
782
+ tool_type=tool_type, fn=function, vhc_eligible=vhc_eligible,
783
+ description=function.__doc__,
677
784
  )
678
785
 
679
786
  def get_llama_index_tools(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vectara_agentic
3
- Version: 0.4.5
3
+ Version: 0.4.7
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,20 +16,20 @@ 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.13.3
20
- Requires-Dist: llama-index-core==0.13.3
19
+ Requires-Dist: llama-index==0.13.6
20
+ Requires-Dist: llama-index-core==0.13.6
21
21
  Requires-Dist: llama-index-workflows==1.3.0
22
22
  Requires-Dist: llama-index-cli==0.5.0
23
23
  Requires-Dist: llama-index-indices-managed-vectara==0.5.0
24
24
  Requires-Dist: llama-index-llms-openai==0.5.4
25
25
  Requires-Dist: llama-index-llms-openai-like==0.5.0
26
- Requires-Dist: llama-index-llms-anthropic==0.8.5
26
+ Requires-Dist: llama-index-llms-anthropic==0.8.6
27
27
  Requires-Dist: llama-index-llms-together==0.4.0
28
28
  Requires-Dist: llama-index-llms-groq==0.4.0
29
29
  Requires-Dist: llama-index-llms-cohere==0.6.0
30
30
  Requires-Dist: llama-index-llms-google-genai==0.3.0
31
31
  Requires-Dist: google_genai>=1.31.0
32
- Requires-Dist: llama-index-llms-bedrock-converse==0.8.2
32
+ Requires-Dist: llama-index-llms-bedrock-converse==0.9.0
33
33
  Requires-Dist: llama-index-tools-yahoo-finance==0.4.0
34
34
  Requires-Dist: llama-index-tools-arxiv==0.4.0
35
35
  Requires-Dist: llama-index-tools-database==0.4.0
@@ -1,17 +1,17 @@
1
- llama-index==0.13.3
2
- llama-index-core==0.13.3
1
+ llama-index==0.13.6
2
+ llama-index-core==0.13.6
3
3
  llama-index-workflows==1.3.0
4
4
  llama-index-cli==0.5.0
5
5
  llama-index-indices-managed-vectara==0.5.0
6
6
  llama-index-llms-openai==0.5.4
7
7
  llama-index-llms-openai-like==0.5.0
8
- llama-index-llms-anthropic==0.8.5
8
+ llama-index-llms-anthropic==0.8.6
9
9
  llama-index-llms-together==0.4.0
10
10
  llama-index-llms-groq==0.4.0
11
11
  llama-index-llms-cohere==0.6.0
12
12
  llama-index-llms-google-genai==0.3.0
13
13
  google_genai>=1.31.0
14
- llama-index-llms-bedrock-converse==0.8.2
14
+ llama-index-llms-bedrock-converse==0.9.0
15
15
  llama-index-tools-yahoo-finance==0.4.0
16
16
  llama-index-tools-arxiv==0.4.0
17
17
  llama-index-tools-database==0.4.0
File without changes