veris-ai 1.13.0__tar.gz → 1.14.0__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 veris-ai might be problematic. Click here for more details.
- {veris_ai-1.13.0 → veris_ai-1.14.0}/PKG-INFO +1 -1
- {veris_ai-1.13.0 → veris_ai-1.14.0}/pyproject.toml +1 -1
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/tool_mock.py +11 -1
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/utils.py +72 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/test_side_effects.py +354 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/uv.lock +1 -1
- {veris_ai-1.13.0 → veris_ai-1.14.0}/.cursor/rules/documentation-management.mdc +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/.github/workflows/release.yml +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/.github/workflows/test.yml +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/.gitignore +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/.pre-commit-config.yaml +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/CHANGELOG.md +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/CLAUDE.md +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/LICENSE +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/README.md +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/examples/README.md +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/examples/__init__.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/examples/import_options.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/examples/openai_agents_example.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/README.md +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/__init__.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/agents_wrapper.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/api_client.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/jaeger_interface/README.md +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/jaeger_interface/__init__.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/jaeger_interface/client.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/jaeger_interface/models.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/models.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/src/veris_ai/observability.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/README.md +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/__init__.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/conftest.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/fixtures/__init__.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/fixtures/http_server.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/fixtures/simple_app.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/test_agents_wrapper_extract.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/test_agents_wrapper_simple.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/test_helpers.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/test_mcp_protocol_server_mocked.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/test_token_decoding.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/test_tool_mock.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/test_utils.py +0 -0
- {veris_ai-1.13.0 → veris_ai-1.14.0}/tests/test_veris_runner_tool_options.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: veris-ai
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.14.0
|
|
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
|
|
@@ -20,10 +20,12 @@ from veris_ai.api_client import get_api_client
|
|
|
20
20
|
from veris_ai.utils import (
|
|
21
21
|
convert_to_type,
|
|
22
22
|
execute_callback,
|
|
23
|
+
execute_combined_callback,
|
|
23
24
|
extract_json_schema,
|
|
24
25
|
get_function_parameters,
|
|
25
26
|
get_input_parameters,
|
|
26
27
|
launch_callback_task,
|
|
28
|
+
launch_combined_callback_task,
|
|
27
29
|
)
|
|
28
30
|
|
|
29
31
|
logger = logging.getLogger(__name__)
|
|
@@ -245,13 +247,14 @@ class VerisSDK:
|
|
|
245
247
|
|
|
246
248
|
return decorator
|
|
247
249
|
|
|
248
|
-
def mock( # noqa: C901, PLR0915
|
|
250
|
+
def mock( # noqa: C901, PLR0915, PLR0913
|
|
249
251
|
self,
|
|
250
252
|
mode: Literal["tool", "function"] = "tool",
|
|
251
253
|
expects_response: bool | None = None,
|
|
252
254
|
cache_response: bool | None = None,
|
|
253
255
|
input_callback: Callable[..., Any] | None = None,
|
|
254
256
|
output_callback: Callable[[Any], Any] | None = None,
|
|
257
|
+
combined_callback: Callable[..., Any] | None = None,
|
|
255
258
|
) -> Callable:
|
|
256
259
|
"""Decorator for mocking tool calls.
|
|
257
260
|
|
|
@@ -261,6 +264,7 @@ class VerisSDK:
|
|
|
261
264
|
cache_response: Whether to cache the response
|
|
262
265
|
input_callback: Callable that receives input parameters as individual arguments
|
|
263
266
|
output_callback: Callable that receives the output value
|
|
267
|
+
combined_callback: Callable that receives both input parameters and mock_output
|
|
264
268
|
"""
|
|
265
269
|
response_expectation = (
|
|
266
270
|
ResponseExpectation.NONE
|
|
@@ -306,6 +310,7 @@ class VerisSDK:
|
|
|
306
310
|
input_params = get_input_parameters(func, args, kwargs)
|
|
307
311
|
launch_callback_task(input_callback, input_params, unpack=True)
|
|
308
312
|
launch_callback_task(output_callback, result, unpack=False)
|
|
313
|
+
launch_combined_callback_task(combined_callback, input_params, result)
|
|
309
314
|
|
|
310
315
|
return result
|
|
311
316
|
|
|
@@ -337,6 +342,7 @@ class VerisSDK:
|
|
|
337
342
|
input_params = get_input_parameters(func, args, kwargs)
|
|
338
343
|
execute_callback(input_callback, input_params, unpack=True)
|
|
339
344
|
execute_callback(output_callback, result, unpack=False)
|
|
345
|
+
execute_combined_callback(combined_callback, input_params, result)
|
|
340
346
|
|
|
341
347
|
return result
|
|
342
348
|
|
|
@@ -350,6 +356,7 @@ class VerisSDK:
|
|
|
350
356
|
return_value: Any, # noqa: ANN401
|
|
351
357
|
input_callback: Callable[..., Any] | None = None,
|
|
352
358
|
output_callback: Callable[[Any], Any] | None = None,
|
|
359
|
+
combined_callback: Callable[..., Any] | None = None,
|
|
353
360
|
) -> Callable:
|
|
354
361
|
"""Decorator for stubbing tool calls.
|
|
355
362
|
|
|
@@ -357,6 +364,7 @@ class VerisSDK:
|
|
|
357
364
|
return_value: The value to return when the function is stubbed
|
|
358
365
|
input_callback: Callable that receives input parameters as individual arguments
|
|
359
366
|
output_callback: Callable that receives the output value
|
|
367
|
+
combined_callback: Callable that receives both input parameters and mock_output
|
|
360
368
|
"""
|
|
361
369
|
|
|
362
370
|
def decorator(func: Callable) -> Callable:
|
|
@@ -380,6 +388,7 @@ class VerisSDK:
|
|
|
380
388
|
input_params = get_input_parameters(func, args, kwargs)
|
|
381
389
|
launch_callback_task(input_callback, input_params, unpack=True)
|
|
382
390
|
launch_callback_task(output_callback, return_value, unpack=False)
|
|
391
|
+
launch_combined_callback_task(combined_callback, input_params, return_value)
|
|
383
392
|
|
|
384
393
|
return return_value
|
|
385
394
|
|
|
@@ -397,6 +406,7 @@ class VerisSDK:
|
|
|
397
406
|
input_params = get_input_parameters(func, args, kwargs)
|
|
398
407
|
execute_callback(input_callback, input_params, unpack=True)
|
|
399
408
|
execute_callback(output_callback, return_value, unpack=False)
|
|
409
|
+
execute_combined_callback(combined_callback, input_params, return_value)
|
|
400
410
|
|
|
401
411
|
return return_value
|
|
402
412
|
|
|
@@ -489,3 +489,75 @@ def launch_callback_task(
|
|
|
489
489
|
except RuntimeError:
|
|
490
490
|
# If no event loop is running, log a warning
|
|
491
491
|
logger.warning("Cannot launch callback task: no event loop running")
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def execute_combined_callback(
|
|
495
|
+
callback: Callable | None,
|
|
496
|
+
input_params: dict[str, Any],
|
|
497
|
+
mock_output: Any, # noqa: ANN401
|
|
498
|
+
) -> None:
|
|
499
|
+
"""Execute a combined callback synchronously with input parameters and mock output.
|
|
500
|
+
|
|
501
|
+
Args:
|
|
502
|
+
callback: The callback callable to execute
|
|
503
|
+
input_params: Dictionary of input parameters
|
|
504
|
+
mock_output: The output from the mock/stub call
|
|
505
|
+
|
|
506
|
+
Note:
|
|
507
|
+
Exceptions in callbacks are caught and logged to prevent breaking the main flow.
|
|
508
|
+
"""
|
|
509
|
+
if callback is None:
|
|
510
|
+
return
|
|
511
|
+
|
|
512
|
+
try:
|
|
513
|
+
# Combine input params with mock_output
|
|
514
|
+
combined_data = {**input_params, "mock_output": mock_output}
|
|
515
|
+
# Filter parameters to match callback signature
|
|
516
|
+
filtered_data = filter_callback_parameters(callback, combined_data)
|
|
517
|
+
callback(**filtered_data)
|
|
518
|
+
except Exception as e:
|
|
519
|
+
logger.warning(f"Combined callback execution failed: {e}", exc_info=True)
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def launch_combined_callback_task(
|
|
523
|
+
callback: Callable | None,
|
|
524
|
+
input_params: dict[str, Any],
|
|
525
|
+
mock_output: Any, # noqa: ANN401
|
|
526
|
+
) -> None:
|
|
527
|
+
"""Launch a combined callback as a background task (fire-and-forget).
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
callback: The callback callable to execute (can be sync or async)
|
|
531
|
+
input_params: Dictionary of input parameters
|
|
532
|
+
mock_output: The output from the mock/stub call
|
|
533
|
+
|
|
534
|
+
Note:
|
|
535
|
+
This launches the callback without blocking. Errors are logged but won't
|
|
536
|
+
affect the main execution flow.
|
|
537
|
+
"""
|
|
538
|
+
if callback is None:
|
|
539
|
+
return
|
|
540
|
+
|
|
541
|
+
async def _run_callback() -> None:
|
|
542
|
+
"""Wrapper to run combined callback with error handling."""
|
|
543
|
+
try:
|
|
544
|
+
# Combine input params with mock_output
|
|
545
|
+
combined_data = {**input_params, "mock_output": mock_output}
|
|
546
|
+
# Filter parameters to match callback signature
|
|
547
|
+
filtered_data = filter_callback_parameters(callback, combined_data)
|
|
548
|
+
|
|
549
|
+
if inspect.iscoroutinefunction(callback):
|
|
550
|
+
await callback(**filtered_data)
|
|
551
|
+
else:
|
|
552
|
+
result = callback(**filtered_data)
|
|
553
|
+
if inspect.iscoroutine(result):
|
|
554
|
+
await result
|
|
555
|
+
except Exception as e:
|
|
556
|
+
logger.warning(f"Combined callback execution failed: {e}", exc_info=True)
|
|
557
|
+
|
|
558
|
+
# Create task without awaiting (fire-and-forget)
|
|
559
|
+
try:
|
|
560
|
+
asyncio.create_task(_run_callback())
|
|
561
|
+
except RuntimeError:
|
|
562
|
+
# If no event loop is running, log a warning
|
|
563
|
+
logger.warning("Cannot launch combined callback task: no event loop running")
|
|
@@ -948,3 +948,357 @@ def test_callback_with_no_matching_parameters(simulation_env):
|
|
|
948
948
|
result = some_func(1, "test")
|
|
949
949
|
assert result == "result"
|
|
950
950
|
assert len(called) == 1
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
# Test combined_callback functionality
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
@pytest.mark.asyncio
|
|
957
|
+
async def test_mock_with_async_combined_callback(simulation_env):
|
|
958
|
+
"""Test mock decorator with async combined_callback."""
|
|
959
|
+
captured_results = []
|
|
960
|
+
|
|
961
|
+
async def combined_handler(a: int, b: int, d: int = None, mock_output: int = None):
|
|
962
|
+
"""Combined callback that uses both inputs and output."""
|
|
963
|
+
# Compute sum of inputs that are not None, then multiply by mock_output
|
|
964
|
+
total_input = a + b + (d if d is not None else 0)
|
|
965
|
+
result = total_input * (mock_output if mock_output is not None else 1)
|
|
966
|
+
captured_results.append(result)
|
|
967
|
+
|
|
968
|
+
@veris.mock(mode="function", combined_callback=combined_handler)
|
|
969
|
+
async def add(a: int, b: int, c: int = None, d: int = None) -> int:
|
|
970
|
+
"""Add numbers together."""
|
|
971
|
+
return a + b + (c if c is not None else 0) + (d if d is not None else 0)
|
|
972
|
+
|
|
973
|
+
mock_response = 6
|
|
974
|
+
|
|
975
|
+
with patch.object(get_api_client(), "post_async", return_value=mock_response):
|
|
976
|
+
result = await add(1, 2, d=4)
|
|
977
|
+
assert result == 6
|
|
978
|
+
# Wait for background tasks to complete
|
|
979
|
+
await asyncio.sleep(0.01)
|
|
980
|
+
assert len(captured_results) == 1
|
|
981
|
+
# (1 + 2 + 4) * 6 = 42
|
|
982
|
+
assert captured_results[0] == 42
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
@pytest.mark.asyncio
|
|
986
|
+
async def test_mock_with_sync_combined_callback_async_func(simulation_env):
|
|
987
|
+
"""Test mock decorator with sync combined_callback on async function."""
|
|
988
|
+
captured_results = []
|
|
989
|
+
|
|
990
|
+
def multiply_by_result(a: int, b: int, mock_output: int = None):
|
|
991
|
+
"""Multiply inputs by the mock output."""
|
|
992
|
+
result = (a + b) * (mock_output if mock_output is not None else 1)
|
|
993
|
+
captured_results.append(result)
|
|
994
|
+
|
|
995
|
+
@veris.mock(mode="function", combined_callback=multiply_by_result)
|
|
996
|
+
async def add(a: int, b: int) -> int:
|
|
997
|
+
return a + b
|
|
998
|
+
|
|
999
|
+
mock_response = 10
|
|
1000
|
+
|
|
1001
|
+
with patch.object(get_api_client(), "post_async", return_value=mock_response):
|
|
1002
|
+
result = await add(3, 7)
|
|
1003
|
+
assert result == 10
|
|
1004
|
+
# Wait for background tasks to complete
|
|
1005
|
+
await asyncio.sleep(0.01)
|
|
1006
|
+
assert len(captured_results) == 1
|
|
1007
|
+
# (3 + 7) * 10 = 100
|
|
1008
|
+
assert captured_results[0] == 100
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
def test_mock_with_sync_combined_callback_sync_func(simulation_env):
|
|
1012
|
+
"""Test mock decorator with sync combined_callback on sync function."""
|
|
1013
|
+
captured_results = []
|
|
1014
|
+
|
|
1015
|
+
def process_combined(x: int, y: str, mock_output: dict = None):
|
|
1016
|
+
"""Process both input and output."""
|
|
1017
|
+
result = {
|
|
1018
|
+
"input_x": x,
|
|
1019
|
+
"input_y": y,
|
|
1020
|
+
"output": mock_output,
|
|
1021
|
+
"combined": f"{y}:{x}:{mock_output.get('value') if mock_output else 'none'}",
|
|
1022
|
+
}
|
|
1023
|
+
captured_results.append(result)
|
|
1024
|
+
|
|
1025
|
+
@veris.mock(mode="function", combined_callback=process_combined)
|
|
1026
|
+
def test_func(x: int, y: str) -> dict:
|
|
1027
|
+
return {"value": x}
|
|
1028
|
+
|
|
1029
|
+
mock_response = {"value": 999}
|
|
1030
|
+
|
|
1031
|
+
with patch.object(get_api_client(), "post", return_value=mock_response):
|
|
1032
|
+
result = test_func(42, "test")
|
|
1033
|
+
assert result == {"value": 999}
|
|
1034
|
+
assert len(captured_results) == 1
|
|
1035
|
+
assert captured_results[0]["input_x"] == 42
|
|
1036
|
+
assert captured_results[0]["input_y"] == "test"
|
|
1037
|
+
assert captured_results[0]["output"] == {"value": 999}
|
|
1038
|
+
assert captured_results[0]["combined"] == "test:42:999"
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
@pytest.mark.asyncio
|
|
1042
|
+
async def test_stub_with_async_combined_callback(simulation_env):
|
|
1043
|
+
"""Test stub decorator with async combined_callback."""
|
|
1044
|
+
captured_results = []
|
|
1045
|
+
|
|
1046
|
+
async def combined_handler(name: str, count: int, mock_output: str = None):
|
|
1047
|
+
"""Combine input and output."""
|
|
1048
|
+
result = f"{name}:{count}:{mock_output}"
|
|
1049
|
+
captured_results.append(result)
|
|
1050
|
+
|
|
1051
|
+
@veris.stub(return_value="stubbed_value", combined_callback=combined_handler)
|
|
1052
|
+
async def process(name: str, count: int) -> str:
|
|
1053
|
+
return f"{name}_{count}"
|
|
1054
|
+
|
|
1055
|
+
result = await process("test", 5)
|
|
1056
|
+
assert result == "stubbed_value"
|
|
1057
|
+
# Wait for background tasks to complete
|
|
1058
|
+
await asyncio.sleep(0.01)
|
|
1059
|
+
assert len(captured_results) == 1
|
|
1060
|
+
assert captured_results[0] == "test:5:stubbed_value"
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
def test_stub_with_sync_combined_callback_sync_func(simulation_env):
|
|
1064
|
+
"""Test stub decorator with sync combined_callback on sync function."""
|
|
1065
|
+
captured_results = []
|
|
1066
|
+
|
|
1067
|
+
def multiply_inputs_by_output(a: int, b: int, mock_output: int = None):
|
|
1068
|
+
"""Multiply sum of inputs by output."""
|
|
1069
|
+
result = (a + b) * (mock_output if mock_output is not None else 1)
|
|
1070
|
+
captured_results.append(result)
|
|
1071
|
+
|
|
1072
|
+
@veris.stub(return_value=5, combined_callback=multiply_inputs_by_output)
|
|
1073
|
+
def add(a: int, b: int) -> int:
|
|
1074
|
+
return a + b
|
|
1075
|
+
|
|
1076
|
+
result = add(2, 3)
|
|
1077
|
+
assert result == 5
|
|
1078
|
+
assert len(captured_results) == 1
|
|
1079
|
+
# (2 + 3) * 5 = 25
|
|
1080
|
+
assert captured_results[0] == 25
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
@pytest.mark.asyncio
|
|
1084
|
+
async def test_combined_callback_with_parameter_filtering(simulation_env):
|
|
1085
|
+
"""Test that combined callback only receives parameters it accepts."""
|
|
1086
|
+
captured_results = []
|
|
1087
|
+
|
|
1088
|
+
async def selective_callback(a: int, mock_output: int = None):
|
|
1089
|
+
"""Callback that only accepts 'a' and 'mock_output', not 'b' or 'c'."""
|
|
1090
|
+
result = a * (mock_output if mock_output is not None else 1)
|
|
1091
|
+
captured_results.append(result)
|
|
1092
|
+
|
|
1093
|
+
@veris.mock(mode="function", combined_callback=selective_callback)
|
|
1094
|
+
async def complex_func(a: int, b: int, c: str = "default") -> int:
|
|
1095
|
+
return a + b
|
|
1096
|
+
|
|
1097
|
+
mock_response = 7
|
|
1098
|
+
|
|
1099
|
+
with patch.object(get_api_client(), "post_async", return_value=mock_response):
|
|
1100
|
+
result = await complex_func(3, 5, c="ignored")
|
|
1101
|
+
assert result == 7
|
|
1102
|
+
# Wait for background tasks to complete
|
|
1103
|
+
await asyncio.sleep(0.01)
|
|
1104
|
+
assert len(captured_results) == 1
|
|
1105
|
+
# 3 * 7 = 21 (b and c are filtered out)
|
|
1106
|
+
assert captured_results[0] == 21
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
@pytest.mark.asyncio
|
|
1110
|
+
async def test_combined_callback_in_production_mode(production_env):
|
|
1111
|
+
"""Test that combined_callback is not called in production mode."""
|
|
1112
|
+
callback_mock = Mock()
|
|
1113
|
+
|
|
1114
|
+
@veris.mock(mode="function", combined_callback=callback_mock)
|
|
1115
|
+
async def test_func(x: int) -> int:
|
|
1116
|
+
return x * 2
|
|
1117
|
+
|
|
1118
|
+
result = await test_func(21)
|
|
1119
|
+
assert result == 42
|
|
1120
|
+
|
|
1121
|
+
# Combined callback should NOT have been called
|
|
1122
|
+
callback_mock.assert_not_called()
|
|
1123
|
+
|
|
1124
|
+
|
|
1125
|
+
@pytest.mark.asyncio
|
|
1126
|
+
async def test_combined_callback_exception_is_logged(simulation_env):
|
|
1127
|
+
"""Test that exceptions in combined callback are caught and logged."""
|
|
1128
|
+
|
|
1129
|
+
async def failing_combined_callback(x: int, mock_output: int = None):
|
|
1130
|
+
raise ValueError("Combined callback error")
|
|
1131
|
+
|
|
1132
|
+
@veris.mock(mode="function", combined_callback=failing_combined_callback)
|
|
1133
|
+
async def test_func(x: int) -> int:
|
|
1134
|
+
return x * 2
|
|
1135
|
+
|
|
1136
|
+
mock_response = 100
|
|
1137
|
+
|
|
1138
|
+
with (
|
|
1139
|
+
patch.object(get_api_client(), "post_async", return_value=mock_response),
|
|
1140
|
+
patch("veris_ai.utils.logger.warning") as mock_logger,
|
|
1141
|
+
):
|
|
1142
|
+
# Function should still work despite combined callback failure
|
|
1143
|
+
result = await test_func(42)
|
|
1144
|
+
assert result == 100
|
|
1145
|
+
|
|
1146
|
+
# Wait for background tasks to complete
|
|
1147
|
+
await asyncio.sleep(0.01)
|
|
1148
|
+
|
|
1149
|
+
# Error should have been logged
|
|
1150
|
+
mock_logger.assert_called()
|
|
1151
|
+
assert "Combined callback execution failed" in str(mock_logger.call_args)
|
|
1152
|
+
|
|
1153
|
+
|
|
1154
|
+
@pytest.mark.asyncio
|
|
1155
|
+
async def test_mock_with_all_three_callbacks(simulation_env):
|
|
1156
|
+
"""Test mock decorator with input, output, and combined callbacks."""
|
|
1157
|
+
captured_inputs = []
|
|
1158
|
+
captured_outputs = []
|
|
1159
|
+
captured_combined = []
|
|
1160
|
+
|
|
1161
|
+
async def capture_input(**kwargs):
|
|
1162
|
+
captured_inputs.append(kwargs)
|
|
1163
|
+
|
|
1164
|
+
async def capture_output(result: int):
|
|
1165
|
+
captured_outputs.append(result)
|
|
1166
|
+
|
|
1167
|
+
async def capture_combined(a: int, b: int, mock_output: int = None):
|
|
1168
|
+
combined = {"sum": a + b, "product": a * b, "mock": mock_output}
|
|
1169
|
+
captured_combined.append(combined)
|
|
1170
|
+
|
|
1171
|
+
@veris.mock(
|
|
1172
|
+
mode="function",
|
|
1173
|
+
input_callback=capture_input,
|
|
1174
|
+
output_callback=capture_output,
|
|
1175
|
+
combined_callback=capture_combined,
|
|
1176
|
+
)
|
|
1177
|
+
async def add(a: int, b: int) -> int:
|
|
1178
|
+
return a + b
|
|
1179
|
+
|
|
1180
|
+
mock_response = 50
|
|
1181
|
+
|
|
1182
|
+
with patch.object(get_api_client(), "post_async", return_value=mock_response):
|
|
1183
|
+
result = await add(10, 20)
|
|
1184
|
+
assert result == 50
|
|
1185
|
+
# Wait for background tasks to complete
|
|
1186
|
+
await asyncio.sleep(0.01)
|
|
1187
|
+
|
|
1188
|
+
# All callbacks should have been called
|
|
1189
|
+
assert len(captured_inputs) == 1
|
|
1190
|
+
assert captured_inputs[0] == {"a": 10, "b": 20}
|
|
1191
|
+
|
|
1192
|
+
assert len(captured_outputs) == 1
|
|
1193
|
+
assert captured_outputs[0] == 50
|
|
1194
|
+
|
|
1195
|
+
assert len(captured_combined) == 1
|
|
1196
|
+
assert captured_combined[0] == {"sum": 30, "product": 200, "mock": 50}
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
def test_stub_with_all_three_callbacks(simulation_env):
|
|
1200
|
+
"""Test stub decorator with input, output, and combined callbacks."""
|
|
1201
|
+
captured_inputs = []
|
|
1202
|
+
captured_outputs = []
|
|
1203
|
+
captured_combined = []
|
|
1204
|
+
|
|
1205
|
+
def capture_input(**kwargs):
|
|
1206
|
+
captured_inputs.append(kwargs)
|
|
1207
|
+
|
|
1208
|
+
def capture_output(result: str):
|
|
1209
|
+
captured_outputs.append(result)
|
|
1210
|
+
|
|
1211
|
+
def capture_combined(name: str, value: int, mock_output: str = None):
|
|
1212
|
+
combined = f"{name}_{value}_{mock_output}"
|
|
1213
|
+
captured_combined.append(combined)
|
|
1214
|
+
|
|
1215
|
+
@veris.stub(
|
|
1216
|
+
return_value="stubbed",
|
|
1217
|
+
input_callback=capture_input,
|
|
1218
|
+
output_callback=capture_output,
|
|
1219
|
+
combined_callback=capture_combined,
|
|
1220
|
+
)
|
|
1221
|
+
def process(name: str, value: int) -> str:
|
|
1222
|
+
return f"{name}:{value}"
|
|
1223
|
+
|
|
1224
|
+
result = process("test", 42)
|
|
1225
|
+
assert result == "stubbed"
|
|
1226
|
+
|
|
1227
|
+
# All callbacks should have been called
|
|
1228
|
+
assert len(captured_inputs) == 1
|
|
1229
|
+
assert captured_inputs[0] == {"name": "test", "value": 42}
|
|
1230
|
+
|
|
1231
|
+
assert len(captured_outputs) == 1
|
|
1232
|
+
assert captured_outputs[0] == "stubbed"
|
|
1233
|
+
|
|
1234
|
+
assert len(captured_combined) == 1
|
|
1235
|
+
assert captured_combined[0] == "test_42_stubbed"
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
@pytest.mark.asyncio
|
|
1239
|
+
async def test_combined_callback_with_kwargs_accepts_all(simulation_env):
|
|
1240
|
+
"""Test combined callback with **kwargs accepts all parameters including mock_output."""
|
|
1241
|
+
captured_data = []
|
|
1242
|
+
|
|
1243
|
+
async def flexible_callback(**kwargs):
|
|
1244
|
+
"""Callback that accepts any parameters via **kwargs."""
|
|
1245
|
+
captured_data.append(kwargs)
|
|
1246
|
+
|
|
1247
|
+
@veris.mock(mode="function", combined_callback=flexible_callback)
|
|
1248
|
+
async def multi_param_func(a: int, b: str, c: float = 3.14) -> dict:
|
|
1249
|
+
return {"result": a}
|
|
1250
|
+
|
|
1251
|
+
mock_response = {"result": 999}
|
|
1252
|
+
|
|
1253
|
+
with patch.object(get_api_client(), "post_async", return_value=mock_response):
|
|
1254
|
+
result = await multi_param_func(10, "test", c=2.71)
|
|
1255
|
+
assert result == {"result": 999}
|
|
1256
|
+
# Wait for background tasks to complete
|
|
1257
|
+
await asyncio.sleep(0.01)
|
|
1258
|
+
|
|
1259
|
+
assert len(captured_data) == 1
|
|
1260
|
+
# Should have all input params plus mock_output
|
|
1261
|
+
assert captured_data[0]["a"] == 10
|
|
1262
|
+
assert captured_data[0]["b"] == "test"
|
|
1263
|
+
assert captured_data[0]["c"] == 2.71
|
|
1264
|
+
assert captured_data[0]["mock_output"] == {"result": 999}
|
|
1265
|
+
|
|
1266
|
+
|
|
1267
|
+
def test_combined_callback_user_example(simulation_env):
|
|
1268
|
+
"""Test the exact example from the user: add function with multiply_by_result."""
|
|
1269
|
+
multiplication_results = []
|
|
1270
|
+
|
|
1271
|
+
def multiply_by_result(a: int, b: int, d: int = None, mock_output: int = None):
|
|
1272
|
+
"""Multiply sum of provided inputs by the mock output."""
|
|
1273
|
+
# Sum the inputs that are not None
|
|
1274
|
+
input_sum = a + b + (d if d is not None else 0)
|
|
1275
|
+
# Multiply by mock output
|
|
1276
|
+
result = input_sum * (mock_output if mock_output is not None else 1)
|
|
1277
|
+
multiplication_results.append(result)
|
|
1278
|
+
|
|
1279
|
+
@veris.mock(mode="function", combined_callback=multiply_by_result)
|
|
1280
|
+
def add(a: int, b: int, c: int = None, d: int = None) -> int:
|
|
1281
|
+
"""Add numbers together."""
|
|
1282
|
+
total = a + b
|
|
1283
|
+
if c is not None:
|
|
1284
|
+
total += c
|
|
1285
|
+
if d is not None:
|
|
1286
|
+
total += d
|
|
1287
|
+
return total
|
|
1288
|
+
|
|
1289
|
+
# Mock the API to return 6 as the result
|
|
1290
|
+
mock_response = 6
|
|
1291
|
+
|
|
1292
|
+
with patch.object(get_api_client(), "post", return_value=mock_response):
|
|
1293
|
+
# Call add(1, 2, d=4)
|
|
1294
|
+
# Expected: inputs are 1, 2, 4 (c is None)
|
|
1295
|
+
# Mock returns 6
|
|
1296
|
+
# Combined callback should compute: (1 + 2 + 4) * 6 = 42
|
|
1297
|
+
result = add(1, 2, d=4)
|
|
1298
|
+
|
|
1299
|
+
# The function returns the mocked value
|
|
1300
|
+
assert result == 6
|
|
1301
|
+
|
|
1302
|
+
# The combined callback computed (1+2+4)*6
|
|
1303
|
+
assert len(multiplication_results) == 1
|
|
1304
|
+
assert multiplication_results[0] == 42
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|