vectara-agentic 0.4.5__py3-none-any.whl → 0.4.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of vectara-agentic might be problematic. Click here for more details.
- tests/test_tools.py +161 -0
- vectara_agentic/_version.py +1 -1
- vectara_agentic/agent_core/prompts.py +11 -11
- vectara_agentic/agent_core/streaming.py +6 -5
- vectara_agentic/agent_core/utils/tools.py +19 -11
- vectara_agentic/tools.py +112 -5
- {vectara_agentic-0.4.5.dist-info → vectara_agentic-0.4.7.dist-info}/METADATA +5 -5
- {vectara_agentic-0.4.5.dist-info → vectara_agentic-0.4.7.dist-info}/RECORD +11 -11
- {vectara_agentic-0.4.5.dist-info → vectara_agentic-0.4.7.dist-info}/WHEEL +0 -0
- {vectara_agentic-0.4.5.dist-info → vectara_agentic-0.4.7.dist-info}/licenses/LICENSE +0 -0
- {vectara_agentic-0.4.5.dist-info → vectara_agentic-0.4.7.dist-info}/top_level.txt +0 -0
tests/test_tools.py
CHANGED
|
@@ -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()
|
vectara_agentic/_version.py
CHANGED
|
@@ -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
|
|
35
|
-
|
|
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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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"
|
|
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"
|
|
322
|
+
logging.error(f"[STREAM_ERROR] Error processing stream events: {e}")
|
|
322
323
|
logging.error(
|
|
323
|
-
f"
|
|
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"
|
|
594
|
+
f"[REACT_STREAM_ERROR] Error processing ReAct stream events: {e}"
|
|
594
595
|
)
|
|
595
596
|
logging.error(
|
|
596
|
-
f"
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
func
|
|
49
|
-
|
|
50
|
-
|
|
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)
|
vectara_agentic/tools.py
CHANGED
|
@@ -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
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
|
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.
|
|
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.
|
|
20
|
-
Requires-Dist: llama-index-core==0.13.
|
|
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.
|
|
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.
|
|
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
|
|
@@ -23,14 +23,14 @@ tests/test_serialization.py,sha256=DJZ2E_K54t8INwZR0Q8gS1wi-MGbLIheOBcbRmZNcro,5
|
|
|
23
23
|
tests/test_session_memory.py,sha256=hnADl59agjpXySY-CBjw6sDPn3s6JketIK6XbLZsLzU,9691
|
|
24
24
|
tests/test_streaming.py,sha256=r-kj6DOB7sn2mkEv_8msGgIYeKXEtWgrDF2qTtCqnZY,1828
|
|
25
25
|
tests/test_together.py,sha256=zR06GoFU0549VYKZRZ5z8bbpvQ6l_vLA0bYLp5SokuU,4770
|
|
26
|
-
tests/test_tools.py,sha256=
|
|
26
|
+
tests/test_tools.py,sha256=W_V5NHjRfEX_Lrh5jNhhZbeKsRdFVUJNTOKfX1hkJGg,19638
|
|
27
27
|
tests/test_vectara_llms.py,sha256=WoswpfPGhQlBXyOijn5EBX0F2NL1Oq3FDB4wxu7mwXs,2485
|
|
28
28
|
tests/test_vhc.py,sha256=jVojp8ZUDF60yJaYp5pBRdAdNYK1hhhPz_RTmlTEm4g,1980
|
|
29
29
|
tests/test_workflow.py,sha256=43YUF-0YDbiiJrTSYjnyqrC4gvHYuHQp7uuzV2jMdTE,3553
|
|
30
30
|
vectara_agentic/__init__.py,sha256=CfS3QR4drKygcTcyH5zUUDuXXQ3WZtTCytz8W4-loeE,1077
|
|
31
31
|
vectara_agentic/_callback.py,sha256=hYbHU_3sMF4-h0YMierZ9EEWspakNixk7wXAAWztlmU,15364
|
|
32
32
|
vectara_agentic/_observability.py,sha256=rApfdndB2R021iM0xG4MumTSDX1Ba6qbNM0N_AOTbR0,4884
|
|
33
|
-
vectara_agentic/_version.py,sha256=
|
|
33
|
+
vectara_agentic/_version.py,sha256=LVDemBeUlAXxk_tSkMeZxasQy5XKZ9fTZCIyTuUQTGg,65
|
|
34
34
|
vectara_agentic/agent.py,sha256=5eC4BkMPWep8c_LIHSB2N1CvsFLdX6qPAhIpgLR08Gc,49125
|
|
35
35
|
vectara_agentic/agent_config.py,sha256=njqEX2qHJjAp2KpNuJglgZhyWXPK74wjIjBPACD6w7w,4074
|
|
36
36
|
vectara_agentic/agent_endpoint.py,sha256=E_AF-YwxaKqd1-p43X62e1e4ugwOWKIyNq4RWOfsO7A,7402
|
|
@@ -38,22 +38,22 @@ vectara_agentic/db_tools.py,sha256=nVZkpGdG63ooGngjX9g7YWyBZRtYMDpvzNasbO696nM,1
|
|
|
38
38
|
vectara_agentic/llm_utils.py,sha256=CqrBOMf9sL-CD0xD-yiEYNyduZV4GBho4e518z_pt5s,9422
|
|
39
39
|
vectara_agentic/sub_query_workflow.py,sha256=1y0fBoUem4i-R34QYlSzcMwM8YhmYgj6S_bWynUtL6w,13001
|
|
40
40
|
vectara_agentic/tool_utils.py,sha256=whnQlk9coeIt01sqUnKnzUorefgn96yWqhtRfHxNL84,25921
|
|
41
|
-
vectara_agentic/tools.py,sha256=
|
|
41
|
+
vectara_agentic/tools.py,sha256=nqfSkiLbuzjAud2pWgVMSvRj_N6M25Lii5quJWYKbeU,41614
|
|
42
42
|
vectara_agentic/tools_catalog.py,sha256=p6eRram-diJyMz5dZI703auSAm97FfW5wLAMyz_2sB0,4634
|
|
43
43
|
vectara_agentic/types.py,sha256=qKkK8vRNiLvEcMInMyOClK2bD7iFlrWGTkl3fGC6Xic,6117
|
|
44
44
|
vectara_agentic/utils.py,sha256=R9HitEG5K3Q_p2M_teosT181OUxkhs1-hnj98qDYGbE,2545
|
|
45
45
|
vectara_agentic/agent_core/__init__.py,sha256=R3KGbSOiY21FOjbeQ_GyIi6uR9Rz7PTfudO9RjSuEZQ,722
|
|
46
46
|
vectara_agentic/agent_core/factory.py,sha256=Nmmhl98r2Op4qJwq9cgfy7DfrWI62JUfxFXHoBxKHBo,14158
|
|
47
|
-
vectara_agentic/agent_core/prompts.py,sha256=
|
|
47
|
+
vectara_agentic/agent_core/prompts.py,sha256=bpUJ2JvQ9IYZZkNyTBzoFKcOVasixsZc0nLu9vNDLBY,10000
|
|
48
48
|
vectara_agentic/agent_core/serialization.py,sha256=Npfcgm9j8B0ck74uIUgqTGljt8HTpcMCdnWV6CKYBZE,11878
|
|
49
|
-
vectara_agentic/agent_core/streaming.py,sha256=
|
|
49
|
+
vectara_agentic/agent_core/streaming.py,sha256=mTHjCWBMMCeEDre_FqhXM27251M8fxSmDwcrOt-wfuw,26473
|
|
50
50
|
vectara_agentic/agent_core/utils/__init__.py,sha256=y5Xf0IH-5TRxMBRA9IyhmWnGZOVIyqV45P6lX4c2Qsc,762
|
|
51
51
|
vectara_agentic/agent_core/utils/hallucination.py,sha256=XmV7tW-MBN9BrzM79zu0T7zaWil7fIkNQjLfDZE43v4,5312
|
|
52
52
|
vectara_agentic/agent_core/utils/logging.py,sha256=-Ll8iUelml92WuhNWScuY6H-RheyZOTBHNxXQ1UGy0M,1701
|
|
53
53
|
vectara_agentic/agent_core/utils/schemas.py,sha256=4sEyQ-_z-eZJzgxCJf62AuBgV7RN1Azc9mLPPlj6IWg,2769
|
|
54
|
-
vectara_agentic/agent_core/utils/tools.py,sha256=
|
|
55
|
-
vectara_agentic-0.4.
|
|
56
|
-
vectara_agentic-0.4.
|
|
57
|
-
vectara_agentic-0.4.
|
|
58
|
-
vectara_agentic-0.4.
|
|
59
|
-
vectara_agentic-0.4.
|
|
54
|
+
vectara_agentic/agent_core/utils/tools.py,sha256=JQmiWldJd2_9SXE9cCEF9u4ESLJr15-JemORAAZbgnk,5068
|
|
55
|
+
vectara_agentic-0.4.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
56
|
+
vectara_agentic-0.4.7.dist-info/METADATA,sha256=nsbgh52lR8Dn01zeDX5mgWrFvEC04DMROyYw_QZYZuA,38906
|
|
57
|
+
vectara_agentic-0.4.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
58
|
+
vectara_agentic-0.4.7.dist-info/top_level.txt,sha256=Y7TQTFdOYGYodQRltUGRieZKIYuzeZj2kHqAUpfCUfg,22
|
|
59
|
+
vectara_agentic-0.4.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|