vectara-agentic 0.3.3__py3-none-any.whl → 0.4.0__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.

Files changed (53) hide show
  1. tests/__init__.py +7 -0
  2. tests/conftest.py +312 -0
  3. tests/endpoint.py +54 -17
  4. tests/run_tests.py +111 -0
  5. tests/test_agent.py +10 -5
  6. tests/test_agent_type.py +82 -143
  7. tests/test_api_endpoint.py +4 -0
  8. tests/test_bedrock.py +4 -0
  9. tests/test_fallback.py +4 -0
  10. tests/test_gemini.py +28 -45
  11. tests/test_groq.py +4 -0
  12. tests/test_private_llm.py +11 -2
  13. tests/test_return_direct.py +6 -2
  14. tests/test_serialization.py +4 -0
  15. tests/test_streaming.py +88 -0
  16. tests/test_tools.py +10 -82
  17. tests/test_vectara_llms.py +4 -0
  18. tests/test_vhc.py +66 -0
  19. tests/test_workflow.py +4 -0
  20. vectara_agentic/__init__.py +27 -4
  21. vectara_agentic/_callback.py +65 -67
  22. vectara_agentic/_observability.py +30 -30
  23. vectara_agentic/_version.py +1 -1
  24. vectara_agentic/agent.py +375 -848
  25. vectara_agentic/agent_config.py +15 -14
  26. vectara_agentic/agent_core/__init__.py +22 -0
  27. vectara_agentic/agent_core/factory.py +501 -0
  28. vectara_agentic/{_prompts.py → agent_core/prompts.py} +3 -35
  29. vectara_agentic/agent_core/serialization.py +345 -0
  30. vectara_agentic/agent_core/streaming.py +495 -0
  31. vectara_agentic/agent_core/utils/__init__.py +34 -0
  32. vectara_agentic/agent_core/utils/hallucination.py +202 -0
  33. vectara_agentic/agent_core/utils/logging.py +52 -0
  34. vectara_agentic/agent_core/utils/prompt_formatting.py +56 -0
  35. vectara_agentic/agent_core/utils/schemas.py +87 -0
  36. vectara_agentic/agent_core/utils/tools.py +125 -0
  37. vectara_agentic/agent_endpoint.py +4 -6
  38. vectara_agentic/db_tools.py +37 -12
  39. vectara_agentic/llm_utils.py +41 -42
  40. vectara_agentic/sub_query_workflow.py +9 -14
  41. vectara_agentic/tool_utils.py +138 -83
  42. vectara_agentic/tools.py +36 -21
  43. vectara_agentic/tools_catalog.py +16 -16
  44. vectara_agentic/types.py +98 -6
  45. {vectara_agentic-0.3.3.dist-info → vectara_agentic-0.4.0.dist-info}/METADATA +69 -30
  46. vectara_agentic-0.4.0.dist-info/RECORD +50 -0
  47. tests/test_agent_planning.py +0 -64
  48. tests/test_hhem.py +0 -100
  49. vectara_agentic/hhem.py +0 -82
  50. vectara_agentic-0.3.3.dist-info/RECORD +0 -39
  51. {vectara_agentic-0.3.3.dist-info → vectara_agentic-0.4.0.dist-info}/WHEEL +0 -0
  52. {vectara_agentic-0.3.3.dist-info → vectara_agentic-0.4.0.dist-info}/licenses/LICENSE +0 -0
  53. {vectara_agentic-0.3.3.dist-info → vectara_agentic-0.4.0.dist-info}/top_level.txt +0 -0
tests/test_tools.py CHANGED
@@ -1,3 +1,8 @@
1
+ # Suppress external dependency warnings before any other imports
2
+ import warnings
3
+
4
+ warnings.simplefilter("ignore", DeprecationWarning)
5
+
1
6
  import unittest
2
7
  from pydantic import Field, BaseModel
3
8
  from unittest.mock import patch, MagicMock
@@ -13,7 +18,6 @@ from vectara_agentic.tools import (
13
18
  )
14
19
  from vectara_agentic.agent import Agent
15
20
  from vectara_agentic.agent_config import AgentConfig
16
- from vectara_agentic.types import AgentType, ModelProvider
17
21
 
18
22
  from llama_index.core.tools import FunctionTool
19
23
 
@@ -96,7 +100,6 @@ class TestToolsPackage(unittest.TestCase):
96
100
  examples=["AAPL", "GOOG"],
97
101
  )
98
102
  year: Optional[int | str] = Field(
99
- default=None,
100
103
  description="The year this query relates to. An integer between 2015 and 2024 or a string specifying a condition on the year",
101
104
  examples=[
102
105
  2020,
@@ -154,8 +157,7 @@ class TestToolsPackage(unittest.TestCase):
154
157
  description="The ticker symbol for the company",
155
158
  examples=["AAPL", "GOOG"],
156
159
  )
157
- year: int | str = Field(
158
- default=None,
160
+ year: Optional[int | str] = Field(
159
161
  description="The year this query relates to. An integer between 2015 and 2024 or a string specifying a condition on the year",
160
162
  examples=[
161
163
  2020,
@@ -235,80 +237,6 @@ class TestToolsPackage(unittest.TestCase):
235
237
  self.assertIsInstance(tool, FunctionTool)
236
238
  self.assertEqual(tool.metadata.tool_type, ToolType.QUERY)
237
239
 
238
- def test_tool_with_many_arguments(self):
239
- vec_factory = VectaraToolFactory(vectara_corpus_key, vectara_api_key)
240
-
241
- class QueryToolArgs(BaseModel):
242
- arg1: str = Field(description="the first argument", examples=["val1"])
243
- arg2: str = Field(description="the second argument", examples=["val2"])
244
- arg3: str = Field(description="the third argument", examples=["val3"])
245
- arg4: str = Field(description="the fourth argument", examples=["val4"])
246
- arg5: str = Field(description="the fifth argument", examples=["val5"])
247
- arg6: str = Field(description="the sixth argument", examples=["val6"])
248
- arg7: str = Field(description="the seventh argument", examples=["val7"])
249
- arg8: str = Field(description="the eighth argument", examples=["val8"])
250
- arg9: str = Field(description="the ninth argument", examples=["val9"])
251
- arg10: str = Field(description="the tenth argument", examples=["val10"])
252
- arg11: str = Field(description="the eleventh argument", examples=["val11"])
253
- arg12: str = Field(description="the twelfth argument", examples=["val12"])
254
- arg13: str = Field(
255
- description="the thirteenth argument", examples=["val13"]
256
- )
257
- arg14: str = Field(
258
- description="the fourteenth argument", examples=["val14"]
259
- )
260
- arg15: str = Field(description="the fifteenth argument", examples=["val15"])
261
-
262
- query_tool_1 = vec_factory.create_rag_tool(
263
- tool_name="rag_tool",
264
- tool_description="""
265
- A dummy tool that takes 15 arguments and returns a response (str) to the user query based on the data in this corpus.
266
- We are using this tool to test the tool factory works and does not crash with OpenAI.
267
- """,
268
- tool_args_schema=QueryToolArgs,
269
- )
270
-
271
- # Test with 15 arguments to make sure no issues occur
272
- config = AgentConfig(agent_type=AgentType.OPENAI)
273
- agent = Agent(
274
- tools=[query_tool_1],
275
- topic="Sample topic",
276
- custom_instructions="Call the tool with 15 arguments for OPENAI",
277
- agent_config=config,
278
- )
279
- res = agent.chat("What is the stock price for Yahoo on 12/31/22?")
280
- self.assertNotIn("maximum length of 1024 characters", str(res))
281
-
282
- # Same test but with GROQ, should not have this limit
283
- config = AgentConfig(
284
- agent_type=AgentType.FUNCTION_CALLING,
285
- main_llm_provider=ModelProvider.GROQ,
286
- tool_llm_provider=ModelProvider.GROQ,
287
- )
288
- agent = Agent(
289
- tools=[query_tool_1],
290
- topic="Sample topic",
291
- custom_instructions="Call the tool with 15 arguments for GROQ",
292
- agent_config=config,
293
- )
294
- res = agent.chat("What is the stock price?")
295
- self.assertNotIn("maximum length of 1024 characters", str(res))
296
-
297
- # Same test but with ANTHROPIC, should not have this limit
298
- config = AgentConfig(
299
- agent_type=AgentType.FUNCTION_CALLING,
300
- main_llm_provider=ModelProvider.ANTHROPIC,
301
- tool_llm_provider=ModelProvider.ANTHROPIC,
302
- )
303
- agent = Agent(
304
- tools=[query_tool_1],
305
- topic="Sample topic",
306
- custom_instructions="Call the tool with 15 arguments for ANTHROPIC",
307
- agent_config=config,
308
- )
309
- res = agent.chat("What is the stock price?")
310
- self.assertIn("stock price", str(res))
311
-
312
240
  @patch.object(VectaraIndex, "as_query_engine")
313
241
  def test_vectara_tool_args_type(
314
242
  self,
@@ -384,7 +312,7 @@ class TestToolsPackage(unittest.TestCase):
384
312
  def __init__(self):
385
313
  pass
386
314
 
387
- def mult(self, x, y):
315
+ def mult(self, x: float, y: float) -> float:
388
316
  return x * y
389
317
 
390
318
  test_class = TestClass()
@@ -410,7 +338,7 @@ class TestToolsPackage(unittest.TestCase):
410
338
  class DummyArgs(BaseModel):
411
339
  foo: int = Field(..., description="how many foos", examples=[1, 2, 3])
412
340
  bar: str = Field(
413
- "baz",
341
+ default="baz",
414
342
  description="what bar to use",
415
343
  examples=["x", "y"],
416
344
  )
@@ -425,7 +353,7 @@ class TestToolsPackage(unittest.TestCase):
425
353
  doc = dummy_tool.metadata.description
426
354
  self.assertTrue(
427
355
  doc.startswith(
428
- "dummy_tool(query: str, foo: int, bar: str) -> dict[str, Any]"
356
+ "dummy_tool(query: str, foo: int, bar: str | None) -> dict[str, Any]"
429
357
  )
430
358
  )
431
359
  self.assertIn("Args:", doc)
@@ -433,7 +361,7 @@ class TestToolsPackage(unittest.TestCase):
433
361
  "query (str): The search query to perform, in the form of a question", doc
434
362
  )
435
363
  self.assertIn("foo (int): how many foos (e.g., 1, 2, 3)", doc)
436
- self.assertIn("bar (str, default='baz'): what bar to use (e.g., 'x', 'y')", doc)
364
+ self.assertIn("bar (str | None, default='baz'): what bar to use (e.g., 'x', 'y')", doc)
437
365
  self.assertIn("Returns:", doc)
438
366
  self.assertIn("dict[str, Any]: A dictionary containing the result data.", doc)
439
367
 
@@ -1,3 +1,7 @@
1
+ # Suppress external dependency warnings before any other imports
2
+ import warnings
3
+ warnings.simplefilter("ignore", DeprecationWarning)
4
+
1
5
  import unittest
2
6
 
3
7
  from vectara_agentic.tools import (
tests/test_vhc.py ADDED
@@ -0,0 +1,66 @@
1
+ # Suppress external dependency warnings before any other imports
2
+ import warnings
3
+ warnings.simplefilter("ignore", DeprecationWarning)
4
+
5
+ import unittest
6
+
7
+ from vectara_agentic.agent import Agent, AgentType
8
+ from vectara_agentic.agent_config import AgentConfig
9
+ from vectara_agentic.tools import ToolsFactory
10
+ from vectara_agentic.types import ModelProvider
11
+
12
+ import nest_asyncio
13
+ nest_asyncio.apply()
14
+
15
+ statements = [
16
+ "The sky is blue.",
17
+ "Cats are better than dogs.",
18
+ "Python is a great programming language.",
19
+ "The Earth revolves around the Sun.",
20
+ "Chocolate is the best ice cream flavor.",
21
+ ]
22
+ st_inx = 0
23
+ def get_statement() -> str:
24
+ "Generate next statement"
25
+ global st_inx
26
+ st = statements[st_inx]
27
+ st_inx += 1
28
+ return st
29
+
30
+
31
+ fc_config = AgentConfig(
32
+ agent_type=AgentType.FUNCTION_CALLING,
33
+ main_llm_provider=ModelProvider.OPENAI,
34
+ tool_llm_provider=ModelProvider.OPENAI,
35
+ )
36
+
37
+ vectara_api_key = 'zqt_UXrBcnI2UXINZkrv4g1tQPhzj02vfdtqYJIDiA'
38
+
39
+ class TestVHC(unittest.TestCase):
40
+
41
+ def test_vhc(self):
42
+ tools = [ToolsFactory().create_tool(get_statement)]
43
+ topic = "statements"
44
+ instructions = (
45
+ f"Call the get_statement tool multiple times to get all {len(statements)} statements."
46
+ f"Respond to the user question based exclusively on the statements you receive - do not use any other knowledge or information."
47
+ )
48
+
49
+ agent = Agent(
50
+ tools=tools,
51
+ topic=topic,
52
+ agent_config=fc_config,
53
+ custom_instructions=instructions,
54
+ vectara_api_key=vectara_api_key,
55
+ )
56
+
57
+ res = agent.chat("Are large cats better than small dogs?")
58
+ vhc_corrections = res.metadata.get("corrections", None)
59
+ self.assertTrue(
60
+ len(vhc_corrections) >= 0 and len(vhc_corrections) <= 2,
61
+ "Corrections should be between 0 and 2"
62
+ )
63
+
64
+
65
+ if __name__ == "__main__":
66
+ unittest.main()
tests/test_workflow.py CHANGED
@@ -1,3 +1,7 @@
1
+ # Suppress external dependency warnings before any other imports
2
+ import warnings
3
+ warnings.simplefilter("ignore", DeprecationWarning)
4
+
1
5
  import unittest
2
6
 
3
7
  from vectara_agentic.agent import Agent
@@ -2,25 +2,48 @@
2
2
  vectara_agentic package.
3
3
  """
4
4
 
5
+ # Simple global warning suppression for end users
6
+ import warnings
7
+
8
+ warnings.simplefilter("ignore", DeprecationWarning)
9
+
10
+ # pylint: disable=wrong-import-position
5
11
  from .agent import Agent
6
12
  from .tools import VectaraToolFactory, VectaraTool, ToolsFactory
7
13
  from .tools_catalog import ToolsCatalog
8
14
  from .agent_config import AgentConfig
9
15
  from .agent_endpoint import create_app, start_app
10
16
  from .types import (
11
- AgentType, ObserverType, ModelProvider, AgentStatusType, LLMRole, ToolType
17
+ AgentType,
18
+ ObserverType,
19
+ ModelProvider,
20
+ AgentStatusType,
21
+ LLMRole,
22
+ ToolType,
12
23
  )
13
24
 
14
25
  # Define the __all__ variable for wildcard imports
15
26
  __all__ = [
16
- 'Agent', 'VectaraToolFactory', 'VectaraTool', 'ToolsFactory', 'AgentConfig',
17
- 'create_app', 'start_app', 'ToolsCatalog',
18
- 'AgentType', 'ObserverType', 'ModelProvider', 'AgentStatusType', 'LLMRole', 'ToolType'
27
+ "Agent",
28
+ "VectaraToolFactory",
29
+ "VectaraTool",
30
+ "ToolsFactory",
31
+ "AgentConfig",
32
+ "create_app",
33
+ "start_app",
34
+ "ToolsCatalog",
35
+ "AgentType",
36
+ "ObserverType",
37
+ "ModelProvider",
38
+ "AgentStatusType",
39
+ "LLMRole",
40
+ "ToolType",
19
41
  ]
20
42
 
21
43
  # Ensure package version is available
22
44
  try:
23
45
  import importlib.metadata
46
+
24
47
  __version__ = importlib.metadata.version("vectara_agentic")
25
48
  except Exception:
26
49
  __version__ = "0.0.0" # fallback if not installed
@@ -3,14 +3,17 @@ Module to handle agent callbacks
3
3
  """
4
4
 
5
5
  import inspect
6
+ import logging
6
7
  from typing import Any, Dict, Optional, List, Callable
7
8
  from functools import wraps
9
+ import traceback
8
10
 
9
11
  from llama_index.core.callbacks.base_handler import BaseCallbackHandler
10
12
  from llama_index.core.callbacks.schema import CBEventType, EventPayload
11
13
 
12
14
  from .types import AgentStatusType
13
15
 
16
+
14
17
  def wrap_callback_fn(callback):
15
18
  """
16
19
  Wrap a callback function to ensure it only receives the parameters it can accept.
@@ -34,6 +37,7 @@ def wrap_callback_fn(callback):
34
37
 
35
38
  return new_callback
36
39
 
40
+
37
41
  class AgentCallbackHandler(BaseCallbackHandler):
38
42
  """
39
43
  Callback handler to track agent status
@@ -153,10 +157,8 @@ class AgentCallbackHandler(BaseCallbackHandler):
153
157
  self._handle_function_call(payload, event_id)
154
158
  elif event_type == CBEventType.AGENT_STEP:
155
159
  self._handle_agent_step(payload, event_id)
156
- elif event_type == CBEventType.EXCEPTION:
157
- print(f"Exception event in handle_event: {payload.get(EventPayload.EXCEPTION)}")
158
160
  else:
159
- print(f"Unknown event type: {event_type}, payload={payload}")
161
+ pass
160
162
 
161
163
  async def _ahandle_event(
162
164
  self, event_type: CBEventType, payload: Dict[str, Any], event_id: str
@@ -167,10 +169,8 @@ class AgentCallbackHandler(BaseCallbackHandler):
167
169
  await self._ahandle_function_call(payload, event_id)
168
170
  elif event_type == CBEventType.AGENT_STEP:
169
171
  await self._ahandle_agent_step(payload, event_id)
170
- elif event_type == CBEventType.EXCEPTION:
171
- print(f"Exception event in ahandle_event: {payload.get(EventPayload.EXCEPTION)}")
172
172
  else:
173
- print(f"Unknown event type: {event_type}, payload={payload}")
173
+ pass
174
174
 
175
175
  # Synchronous handlers
176
176
  def _handle_llm(
@@ -196,37 +196,38 @@ class AgentCallbackHandler(BaseCallbackHandler):
196
196
  event_id=event_id,
197
197
  )
198
198
  else:
199
- print(
200
- f"vectara-agentic llm callback: no messages or prompt found in payload {payload}"
201
- )
199
+ pass
202
200
 
203
201
  def _handle_function_call(self, payload: dict, event_id: str) -> None:
204
- if EventPayload.FUNCTION_CALL in payload:
205
- fcall = payload.get(EventPayload.FUNCTION_CALL)
206
- tool = payload.get(EventPayload.TOOL)
207
- if tool:
208
- tool_name = tool.name
209
- if self.fn:
202
+ try:
203
+ if EventPayload.FUNCTION_CALL in payload:
204
+ fcall = payload.get(EventPayload.FUNCTION_CALL)
205
+ tool = payload.get(EventPayload.TOOL)
206
+
207
+ if tool:
208
+ tool_name = tool.name
209
+ if self.fn:
210
+ self.fn(
211
+ status_type=AgentStatusType.TOOL_CALL,
212
+ msg={"tool_name": tool_name, "arguments": fcall},
213
+ event_id=event_id,
214
+ )
215
+
216
+ elif EventPayload.FUNCTION_OUTPUT in payload:
217
+ response = payload.get(EventPayload.FUNCTION_OUTPUT)
218
+ tool = payload.get(EventPayload.TOOL)
219
+
220
+ if tool and self.fn:
210
221
  self.fn(
211
- status_type=AgentStatusType.TOOL_CALL,
212
- msg={
213
- "tool_name": tool_name,
214
- "arguments": fcall
215
- },
222
+ status_type=AgentStatusType.TOOL_OUTPUT,
223
+ msg={"tool_name": tool.name, "content": response},
216
224
  event_id=event_id,
217
225
  )
218
- elif EventPayload.FUNCTION_OUTPUT in payload:
219
- response = payload.get(EventPayload.FUNCTION_OUTPUT)
220
- if self.fn:
221
- self.fn(
222
- status_type=AgentStatusType.TOOL_OUTPUT,
223
- msg=response,
224
- event_id=event_id,
225
- )
226
- else:
227
- print(
228
- f"Vectara-agentic callback handler: no function call or output found in payload {payload}"
229
- )
226
+
227
+ except Exception as e:
228
+ logging.error(f"Exception in _handle_function_call: {e}")
229
+ logging.error(f"Traceback: {traceback.format_exc()}")
230
+ # Continue execution to prevent callback failures from breaking the agent
230
231
 
231
232
  def _handle_agent_step(self, payload: dict, event_id: str) -> None:
232
233
  if EventPayload.MESSAGES in payload:
@@ -245,10 +246,6 @@ class AgentCallbackHandler(BaseCallbackHandler):
245
246
  msg=response,
246
247
  event_id=event_id,
247
248
  )
248
- else:
249
- print(
250
- f"Vectara-agentic agent_step: no messages or prompt found in payload {payload}"
251
- )
252
249
 
253
250
  # Asynchronous handlers
254
251
  async def _ahandle_llm(self, payload: dict, event_id: str) -> None:
@@ -276,52 +273,55 @@ class AgentCallbackHandler(BaseCallbackHandler):
276
273
  msg=prompt,
277
274
  event_id=event_id,
278
275
  )
279
- else:
280
- print(
281
- f"vectara-agentic llm callback: no messages or prompt found in payload {payload}"
282
- )
283
276
 
284
277
  async def _ahandle_function_call(self, payload: dict, event_id: str) -> None:
285
- if EventPayload.FUNCTION_CALL in payload:
286
- fcall = payload.get(EventPayload.FUNCTION_CALL)
287
- tool = payload.get(EventPayload.TOOL)
288
- if tool:
289
- if self.fn:
278
+ try:
279
+ if EventPayload.FUNCTION_CALL in payload:
280
+ fcall = payload.get(EventPayload.FUNCTION_CALL)
281
+ tool = payload.get(EventPayload.TOOL)
282
+
283
+ if tool and self.fn:
290
284
  if inspect.iscoroutinefunction(self.fn):
291
285
  await self.fn(
292
286
  status_type=AgentStatusType.TOOL_CALL,
287
+ msg={"tool_name": tool.name, "arguments": fcall},
288
+ event_id=event_id,
289
+ )
290
+ else:
291
+ self.fn(
292
+ status_type=AgentStatusType.TOOL_CALL,
293
+ msg={"tool_name": tool.name, "arguments": fcall},
294
+ event_id=event_id,
295
+ )
296
+
297
+ elif EventPayload.FUNCTION_OUTPUT in payload:
298
+ response = payload.get(EventPayload.FUNCTION_OUTPUT)
299
+ tool = payload.get(EventPayload.TOOL)
300
+
301
+ if tool and self.fn:
302
+ if inspect.iscoroutinefunction(self.fn):
303
+ await self.fn(
304
+ status_type=AgentStatusType.TOOL_OUTPUT,
293
305
  msg={
294
306
  "tool_name": tool.name,
295
- "arguments": fcall
307
+ "content": response,
296
308
  },
297
309
  event_id=event_id,
298
310
  )
299
311
  else:
300
312
  self.fn(
301
- status_type=AgentStatusType.TOOL_CALL,
313
+ status_type=AgentStatusType.TOOL_OUTPUT,
302
314
  msg={
303
315
  "tool_name": tool.name,
304
- "arguments": fcall
316
+ "content": response,
305
317
  },
306
318
  event_id=event_id,
307
319
  )
308
- elif EventPayload.FUNCTION_OUTPUT in payload:
309
- if self.fn:
310
- response = payload.get(EventPayload.FUNCTION_OUTPUT)
311
- if inspect.iscoroutinefunction(self.fn):
312
- await self.fn(
313
- status_type=AgentStatusType.TOOL_OUTPUT,
314
- msg=response,
315
- event_id=event_id,
316
- )
317
- else:
318
- self.fn(
319
- status_type=AgentStatusType.TOOL_OUTPUT,
320
- msg=response,
321
- event_id=event_id,
322
- )
323
- else:
324
- print(f"No function call or output found in payload {payload}")
320
+
321
+ except Exception as e:
322
+ logging.error(f"Exception in _ahandle_function_call: {e}")
323
+ logging.error(f"Traceback: {traceback.format_exc()}")
324
+ # Continue execution to prevent callback failures from breaking the agent
325
325
 
326
326
  async def _ahandle_agent_step(self, payload: dict, event_id: str) -> None:
327
327
  if EventPayload.MESSAGES in payload:
@@ -354,5 +354,3 @@ class AgentCallbackHandler(BaseCallbackHandler):
354
354
  msg=response,
355
355
  event_id=event_id,
356
356
  )
357
- else:
358
- print(f"No messages or prompt found in payload {payload}")
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Observability for Vectara Agentic.
3
3
  """
4
+
4
5
  import os
5
6
  import json
6
7
  from typing import Optional, Union
@@ -13,9 +14,9 @@ SPAN_NAME: str = "VectaraQueryEngine._query"
13
14
 
14
15
 
15
16
  def setup_observer(config: AgentConfig, verbose: bool) -> bool:
16
- '''
17
+ """
17
18
  Setup the observer.
18
- '''
19
+ """
19
20
  if config.observer != ObserverType.ARIZE_PHOENIX:
20
21
  if verbose:
21
22
  print("No Phoenix observer set.")
@@ -38,9 +39,11 @@ def setup_observer(config: AgentConfig, verbose: bool) -> bool:
38
39
  if not phoenix_endpoint:
39
40
  print("Phoenix endpoint not set. Attempting to launch local Phoenix UI...")
40
41
  px.launch_app()
41
- print("Local Phoenix UI launched. You can view traces at the UI address (usually http://localhost:6006).")
42
+ print(
43
+ "Local Phoenix UI launched. You can view traces at the UI address (usually http://localhost:6006)."
44
+ )
42
45
 
43
- if phoenix_endpoint and 'app.phoenix.arize.com' in phoenix_endpoint:
46
+ if phoenix_endpoint and "app.phoenix.arize.com" in phoenix_endpoint:
44
47
  phoenix_api_key = os.getenv("PHOENIX_API_KEY")
45
48
  if not phoenix_api_key:
46
49
  raise ValueError(
@@ -50,7 +53,7 @@ def setup_observer(config: AgentConfig, verbose: bool) -> bool:
50
53
  os.environ["PHOENIX_COLLECTOR_ENDPOINT"] = "https://app.phoenix.arize.com"
51
54
 
52
55
  reg_kwargs = {
53
- "endpoint": phoenix_endpoint or 'http://localhost:6006/v1/traces',
56
+ "endpoint": phoenix_endpoint or "http://localhost:6006/v1/traces",
54
57
  "project_name": PROJECT_NAME,
55
58
  "batch": False,
56
59
  "set_global_tracer_provider": False,
@@ -58,35 +61,35 @@ def setup_observer(config: AgentConfig, verbose: bool) -> bool:
58
61
  tracer_provider = register(**reg_kwargs)
59
62
  LlamaIndexInstrumentor().instrument(tracer_provider=tracer_provider)
60
63
  if verbose:
61
- print(f"Phoenix observer configured for project 'vectara-agentic' at endpoint: {reg_kwargs['endpoint']}")
64
+ print(
65
+ f"Phoenix observer configured for project 'vectara-agentic' at endpoint: {reg_kwargs['endpoint']}"
66
+ )
62
67
  return True
63
68
 
64
69
 
65
70
  def _extract_fcs_value(output: Union[str, dict]) -> Optional[float]:
66
- '''
71
+ """
67
72
  Extract the FCS value from the output.
68
- '''
73
+ """
69
74
  try:
70
75
  output_json = json.loads(output)
71
- if 'metadata' in output_json and 'fcs' in output_json['metadata']:
72
- return output_json['metadata']['fcs']
73
- except json.JSONDecodeError:
74
- print(f"Failed to parse JSON: {output}")
75
- except KeyError:
76
- print(f"'fcs' not found in: {output_json}")
76
+ if "metadata" in output_json and "fcs" in output_json["metadata"]:
77
+ return output_json["metadata"]["fcs"]
78
+ except Exception as e:
79
+ print(f"Error extracting FCS value: {e}")
77
80
  return None
78
81
 
79
82
 
80
83
  def _find_top_level_parent_id(row: pd.Series, all_spans: pd.DataFrame) -> Optional[str]:
81
- '''
84
+ """
82
85
  Find the top level parent id for the given span.
83
- '''
84
- current_id = row['parent_id']
86
+ """
87
+ current_id = row["parent_id"]
85
88
  while current_id is not None:
86
89
  parent_row = all_spans[all_spans.index == current_id]
87
90
  if parent_row.empty:
88
91
  break
89
- new_parent_id = parent_row['parent_id'].iloc[0]
92
+ new_parent_id = parent_row["parent_id"].iloc[0]
90
93
  if new_parent_id == current_id:
91
94
  break
92
95
  if new_parent_id is None:
@@ -96,17 +99,14 @@ def _find_top_level_parent_id(row: pd.Series, all_spans: pd.DataFrame) -> Option
96
99
 
97
100
 
98
101
  def eval_fcs() -> None:
99
- '''
102
+ """
100
103
  Evaluate the FCS score for the VectaraQueryEngine._query span.
101
- '''
104
+ """
102
105
  import phoenix as px
103
106
  from phoenix.trace.dsl import SpanQuery
104
107
  from phoenix.trace import SpanEvaluations
105
- query = SpanQuery().select(
106
- "output.value",
107
- "parent_id",
108
- "name"
109
- )
108
+
109
+ query = SpanQuery().select("output.value", "parent_id", "name")
110
110
  try:
111
111
  client = px.Client()
112
112
  all_spans = client.query_spans(query, project_name=PROJECT_NAME)
@@ -114,17 +114,17 @@ def eval_fcs() -> None:
114
114
  print(f"Failed to query spans: {e}")
115
115
  return
116
116
 
117
- vectara_spans = all_spans[all_spans['name'] == SPAN_NAME].copy()
118
- vectara_spans['top_level_parent_id'] = vectara_spans.apply(
117
+ vectara_spans = all_spans[all_spans["name"] == SPAN_NAME].copy()
118
+ vectara_spans["top_level_parent_id"] = vectara_spans.apply(
119
119
  lambda row: _find_top_level_parent_id(row, all_spans), axis=1
120
120
  )
121
- vectara_spans['score'] = vectara_spans['output.value'].apply(_extract_fcs_value)
121
+ vectara_spans["score"] = vectara_spans["output.value"].apply(_extract_fcs_value)
122
122
 
123
123
  vectara_spans.reset_index(inplace=True)
124
124
  top_level_spans = vectara_spans.copy()
125
- top_level_spans['context.span_id'] = top_level_spans['top_level_parent_id']
125
+ top_level_spans["context.span_id"] = top_level_spans["top_level_parent_id"]
126
126
  vectara_spans = pd.concat([vectara_spans, top_level_spans], ignore_index=True)
127
- vectara_spans.set_index('context.span_id', inplace=True)
127
+ vectara_spans.set_index("context.span_id", inplace=True)
128
128
 
129
129
  px.Client().log_evaluations(
130
130
  SpanEvaluations(
@@ -1,4 +1,4 @@
1
1
  """
2
2
  Define the version of the package.
3
3
  """
4
- __version__ = "0.3.3"
4
+ __version__ = "0.4.0"