veris-ai 1.12.1__py3-none-any.whl → 1.12.3__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 veris-ai might be problematic. Click here for more details.

@@ -1,5 +1,6 @@
1
1
  """OpenAI Agents wrapper for automatic tool mocking via Veris SDK."""
2
2
 
3
+ import inspect
3
4
  import json
4
5
  import logging
5
6
  from collections.abc import Callable
@@ -9,7 +10,7 @@ from agents import RunContextWrapper, RunResult, Runner as OpenAIRunner
9
10
  from pydantic import BaseModel
10
11
 
11
12
  from veris_ai import veris
12
- from veris_ai.tool_mock import mock_tool_call
13
+ from veris_ai.tool_mock import mock_tool_call, mock_tool_call_async
13
14
  from veris_ai.models import ToolCallOptions
14
15
 
15
16
  logger = logging.getLogger(__name__)
@@ -83,14 +84,26 @@ def _wrap(
83
84
  ) -> Any: # noqa: ANN401
84
85
  """Wrapped on_invoke_tool that intercepts the tool function."""
85
86
  session_id = veris.session_id
87
+ thread_id = veris.thread_id
86
88
  the_func = tool_functions.get(tool_id)
87
89
  if the_func and session_id:
88
- # mock_tool_call is synchronous, don't await it
90
+ # Check if async or sync, call appropriate version
91
+ if inspect.iscoroutinefunction(the_func):
92
+ # Use async version (non-blocking)
93
+ return await mock_tool_call_async(
94
+ the_func,
95
+ session_id,
96
+ json.loads(parameters),
97
+ tool_options.get(tool_name_inner),
98
+ thread_id=thread_id,
99
+ )
100
+ # Use sync version for sync functions
89
101
  return mock_tool_call(
90
102
  the_func,
91
103
  session_id,
92
104
  json.loads(parameters),
93
105
  tool_options.get(tool_name_inner),
106
+ thread_id=thread_id,
94
107
  )
95
108
  # Fall back to original if we couldn't extract the function
96
109
  return await orig_invoke(ctx, parameters)
@@ -189,8 +202,6 @@ def _find_user_function_in_closure(closure: tuple) -> Callable | None:
189
202
  Returns:
190
203
  The user function if found, None otherwise
191
204
  """
192
- import inspect
193
-
194
205
  # List of module prefixes that indicate library/framework code
195
206
  library_modules = ("json", "inspect", "agents", "pydantic", "openai", "typing")
196
207
 
veris_ai/api_client.py CHANGED
@@ -60,6 +60,24 @@ class SimulatorAPIClient:
60
60
  response.raise_for_status()
61
61
  return response.json() if response.content else None
62
62
 
63
+ async def post_async(self, endpoint: str, payload: dict[str, Any]) -> Any: # noqa: ANN401
64
+ """Make an asynchronous POST request to the specified endpoint.
65
+
66
+ This method uses httpx.AsyncClient and is safe to call from async functions
67
+ without blocking the event loop.
68
+ """
69
+ headers = self._build_headers()
70
+ # Validate endpoint URL; raise ConnectError for non-absolute URLs to
71
+ # mirror connection failures in tests when base URL is intentionally invalid.
72
+ if not endpoint.startswith(("http://", "https://")):
73
+ error_msg = f"Invalid endpoint URL (not absolute): {endpoint}"
74
+ raise httpx.ConnectError(error_msg)
75
+
76
+ async with httpx.AsyncClient(timeout=self.timeout) as client:
77
+ response = await client.post(endpoint, json=payload, headers=headers)
78
+ response.raise_for_status()
79
+ return response.json() if response.content else None
80
+
63
81
  @property
64
82
  def tool_mock_endpoint(self) -> str:
65
83
  """Get the tool mock endpoint URL."""
veris_ai/tool_mock.py CHANGED
@@ -206,14 +206,14 @@ class VerisSDK:
206
206
  return await func(*args, **kwargs)
207
207
  parameters = get_function_parameters(func, args, kwargs)
208
208
  logger.info(f"Spying on function: {func.__name__}")
209
- log_tool_call(
209
+ await log_tool_call_async(
210
210
  session_id=session_id,
211
211
  function_name=func.__name__,
212
212
  parameters=parameters,
213
213
  docstring=inspect.getdoc(func) or "",
214
214
  )
215
215
  result = await func(*args, **kwargs)
216
- log_tool_response(session_id=session_id, response=result)
216
+ await log_tool_response_async(session_id=session_id, response=result)
217
217
  return result
218
218
 
219
219
  @wraps(func)
@@ -275,7 +275,7 @@ class VerisSDK:
275
275
  return await func(*args, **kwargs)
276
276
  parameters = get_function_parameters(func, args, kwargs)
277
277
  thread_id = _thread_id_context.get()
278
- return mock_tool_call(
278
+ return await mock_tool_call_async(
279
279
  func,
280
280
  session_id,
281
281
  parameters,
@@ -358,7 +358,7 @@ def mock_tool_call(
358
358
  options: ToolCallOptions | None = None,
359
359
  thread_id: str | None = None,
360
360
  ) -> object:
361
- """Mock tool call.
361
+ """Mock tool call (synchronous).
362
362
 
363
363
  Args:
364
364
  func: Function being mocked
@@ -424,6 +424,84 @@ def mock_tool_call(
424
424
  return convert_to_type(mock_result, return_type_obj)
425
425
 
426
426
 
427
+ @tenacity.retry(
428
+ stop=tenacity.stop_after_attempt(3),
429
+ wait=tenacity.wait_exponential(multiplier=1, min=4, max=10),
430
+ reraise=True,
431
+ )
432
+ async def mock_tool_call_async(
433
+ func: Callable,
434
+ session_id: str, # noqa: ARG001
435
+ parameters: dict[str, dict[str, str]],
436
+ options: ToolCallOptions | None = None,
437
+ thread_id: str | None = None,
438
+ ) -> object:
439
+ """Mock tool call (asynchronous).
440
+
441
+ Args:
442
+ func: Function being mocked
443
+ session_id: Session ID (kept for backwards compatibility, not used)
444
+ parameters: Function parameters
445
+ options: Tool call options
446
+ thread_id: Thread ID to use as session_id in API request (required)
447
+
448
+ Raises:
449
+ ValueError: If thread_id is not provided
450
+ """
451
+ if thread_id is None:
452
+ raise ValueError(
453
+ "thread_id is required for mocking. "
454
+ "Use parse_token() to set both session_id and thread_id."
455
+ )
456
+
457
+ options = options or ToolCallOptions()
458
+ api_client = get_api_client()
459
+ endpoint = api_client.tool_mock_endpoint
460
+
461
+ logger.info(f"Simulating function: {func.__name__}")
462
+
463
+ type_hints = get_type_hints(func)
464
+
465
+ # Extract return type object (not just the name)
466
+ return_type_obj = type_hints.pop("return", Any)
467
+ # Get function docstring
468
+ docstring = inspect.getdoc(func) or ""
469
+
470
+ # Use thread_id as session_id in the payload
471
+ payload_session_id = thread_id
472
+ # Clean up parameters for V3 - just send values, not the nested dict
473
+ clean_params: dict[str, Any] = {}
474
+ for key, value in parameters.items():
475
+ if isinstance(value, dict) and "value" in value:
476
+ # Extract just the value from the nested structure
477
+ clean_params[key] = value["value"]
478
+ else:
479
+ # Already clean or unexpected format
480
+ clean_params[key] = value
481
+
482
+ # Determine response expectation
483
+ payload = {
484
+ "session_id": payload_session_id,
485
+ "response_expectation": options.response_expectation.value,
486
+ "cache_response": bool(options.cache_response),
487
+ "tool_call": {
488
+ "function_name": func.__name__,
489
+ "parameters": clean_params,
490
+ "return_type": json.dumps(extract_json_schema(return_type_obj)),
491
+ "docstring": docstring,
492
+ },
493
+ }
494
+
495
+ mock_result = await api_client.post_async(endpoint, payload)
496
+ logger.info(f"Mock response: {mock_result}")
497
+
498
+ if isinstance(mock_result, str):
499
+ with suppress(json.JSONDecodeError):
500
+ mock_result = json.loads(mock_result)
501
+ return convert_to_type(mock_result, return_type_obj)
502
+ return convert_to_type(mock_result, return_type_obj)
503
+
504
+
427
505
  def log_tool_call(
428
506
  session_id: str,
429
507
  function_name: str,
@@ -456,6 +534,38 @@ def log_tool_call(
456
534
  logger.warning(f"Failed to log tool call for {function_name}: {e}")
457
535
 
458
536
 
537
+ async def log_tool_call_async(
538
+ session_id: str,
539
+ function_name: str,
540
+ parameters: dict[str, dict[str, str]],
541
+ docstring: str,
542
+ ) -> None:
543
+ """Log tool call asynchronously to the VERIS logging endpoint."""
544
+ api_client = get_api_client()
545
+ endpoint = api_client.get_log_tool_call_endpoint(session_id)
546
+
547
+ # Clean up parameters for V3 - just send values, not the nested dict
548
+ clean_params: dict[str, Any] = {}
549
+ for key, value in parameters.items():
550
+ if isinstance(value, dict) and "value" in value:
551
+ # Extract just the value from the nested structure
552
+ clean_params[key] = value["value"]
553
+ else:
554
+ # Already clean or unexpected format
555
+ clean_params[key] = value
556
+
557
+ payload = {
558
+ "function_name": function_name,
559
+ "parameters": clean_params,
560
+ "docstring": docstring,
561
+ }
562
+ try:
563
+ await api_client.post_async(endpoint, payload)
564
+ logger.debug(f"Tool call logged for {function_name}")
565
+ except Exception as e:
566
+ logger.warning(f"Failed to log tool call for {function_name}: {e}")
567
+
568
+
459
569
  def log_tool_response(session_id: str, response: Any) -> None: # noqa: ANN401
460
570
  """Log tool response synchronously to the VERIS logging endpoint."""
461
571
  api_client = get_api_client()
@@ -472,4 +582,20 @@ def log_tool_response(session_id: str, response: Any) -> None: # noqa: ANN401
472
582
  logger.warning(f"Failed to log tool response: {e}")
473
583
 
474
584
 
585
+ async def log_tool_response_async(session_id: str, response: Any) -> None: # noqa: ANN401
586
+ """Log tool response asynchronously to the VERIS logging endpoint."""
587
+ api_client = get_api_client()
588
+ endpoint = api_client.get_log_tool_response_endpoint(session_id)
589
+
590
+ payload = {
591
+ "response": json.dumps(response, default=str),
592
+ }
593
+
594
+ try:
595
+ await api_client.post_async(endpoint, payload)
596
+ logger.debug("Tool response logged")
597
+ except Exception as e:
598
+ logger.warning(f"Failed to log tool response: {e}")
599
+
600
+
475
601
  veris = VerisSDK()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: veris-ai
3
- Version: 1.12.1
3
+ Version: 1.12.3
4
4
  Summary: A Python package for Veris AI tools
5
5
  Project-URL: Homepage, https://github.com/veris-ai/veris-python-sdk
6
6
  Project-URL: Bug Tracker, https://github.com/veris-ai/veris-python-sdk/issues
@@ -41,6 +41,13 @@ Description-Content-Type: text/markdown
41
41
 
42
42
  # Veris AI Python SDK
43
43
 
44
+ [![PyPI version](https://badge.fury.io/py/veris-ai.svg)](https://badge.fury.io/py/veris-ai)
45
+ [![Python Versions](https://img.shields.io/pypi/pyversions/veris-ai.svg)](https://pypi.org/project/veris-ai/)
46
+ [![Downloads](https://static.pepy.tech/badge/veris-ai)](https://pepy.tech/project/veris-ai)
47
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
48
+ [![CI](https://github.com/veris-ai/veris-python-sdk/actions/workflows/test.yml/badge.svg)](https://github.com/veris-ai/veris-python-sdk/actions/workflows/test.yml)
49
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
50
+
44
51
  For more information visit us at https://veris.ai
45
52
 
46
53
  A Python package for Veris AI tools with simulation capabilities and FastAPI MCP (Model Context Protocol) integration.
@@ -1,16 +1,16 @@
1
1
  veris_ai/README.md,sha256=Mxg9fyNt6hFkQaFBYZq85Kw9akw4cN2uu6j_mXZtNCs,3871
2
2
  veris_ai/__init__.py,sha256=enl_gEa6BQAjWvzCtsn_hFVJVVlJZ_dMsLL--E5W3nU,1907
3
- veris_ai/agents_wrapper.py,sha256=N-YZ0qvIEJ8rlFZAzVKg6eWKa7I_qIt-khlg_zL-qY8,11931
4
- veris_ai/api_client.py,sha256=BQ2Fn5pO7uaWmWNcLOoZO8o-D39OPRmclPROwJvXBUc,3199
3
+ veris_ai/agents_wrapper.py,sha256=gLUd_0TyCVsqqilQLvsSJIpsU5uu2CdjjWOQ4QJjoJk,12786
4
+ veris_ai/api_client.py,sha256=I1XyQ7J0ZU_JK9sZjF3XqFv5gGsrdKF38euOZmW8BG0,4150
5
5
  veris_ai/models.py,sha256=xKeheSJQle2tBeJG1DsGJzMDwv24p5jECjX6RAa39n4,495
6
6
  veris_ai/observability.py,sha256=eSIXmk6fpOAoWM-sDbsvzyUASh1ZwU6tRIPduy09RxY,4206
7
- veris_ai/tool_mock.py,sha256=h_nS0tTOl8eI_Hb-HNuurjbBPp0ThdMnJwsMXfRyT7I,17489
7
+ veris_ai/tool_mock.py,sha256=wqklgub07C9zon25P9XMAXTXUsRVKAnZo6nv4lJROCo,21850
8
8
  veris_ai/utils.py,sha256=hJetCiN8Bubhy0nqSoS1C2awN9cdkKuHM1v7YhtwtTs,10066
9
9
  veris_ai/jaeger_interface/README.md,sha256=kd9rKcE5xf3EyNaiHu0tjn-0oES9sfaK6Ih-OhhTyCM,2821
10
10
  veris_ai/jaeger_interface/__init__.py,sha256=KD7NSiMYRG_2uF6dOLKkGG5lNQe4K9ptEwucwMT4_aw,1128
11
11
  veris_ai/jaeger_interface/client.py,sha256=yJrh86wRR0Dk3Gq12DId99WogcMIVbL0QQFqVSevvlE,8772
12
12
  veris_ai/jaeger_interface/models.py,sha256=e64VV6IvOEFuzRUgvDAMQFyOZMRb56I-PUPZLBZ3rX0,1864
13
- veris_ai-1.12.1.dist-info/METADATA,sha256=NdXH_sXX2K9mcV2qRQLulBcJlCsG2wnE_fkrtT7qHyg,15960
14
- veris_ai-1.12.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
- veris_ai-1.12.1.dist-info/licenses/LICENSE,sha256=2g4i20atAgtD5einaKzhQrIB-JrPhyQgD3bC0wkHcCI,1065
16
- veris_ai-1.12.1.dist-info/RECORD,,
13
+ veris_ai-1.12.3.dist-info/METADATA,sha256=P6ZtsocGvpf7cifFzmvQLUZnFaYb5bVGiaYggn9KkC8,16684
14
+ veris_ai-1.12.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
+ veris_ai-1.12.3.dist-info/licenses/LICENSE,sha256=2g4i20atAgtD5einaKzhQrIB-JrPhyQgD3bC0wkHcCI,1065
16
+ veris_ai-1.12.3.dist-info/RECORD,,