veris-ai 1.9.0__py3-none-any.whl → 1.10.1__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.
- veris_ai/README.md +11 -10
- veris_ai/agents_wrapper.py +2 -1
- veris_ai/api_client.py +31 -9
- veris_ai/observability.py +9 -27
- veris_ai/tool_mock.py +57 -8
- veris_ai/utils.py +19 -4
- {veris_ai-1.9.0.dist-info → veris_ai-1.10.1.dist-info}/METADATA +87 -7
- veris_ai-1.10.1.dist-info/RECORD +16 -0
- veris_ai/logging.py +0 -46
- veris_ai-1.9.0.dist-info/RECORD +0 -17
- {veris_ai-1.9.0.dist-info → veris_ai-1.10.1.dist-info}/WHEEL +0 -0
- {veris_ai-1.9.0.dist-info → veris_ai-1.10.1.dist-info}/licenses/LICENSE +0 -0
veris_ai/README.md
CHANGED
|
@@ -14,9 +14,10 @@ This module contains the core implementation of the Veris AI Python SDK. Each co
|
|
|
14
14
|
|
|
15
15
|
| Module | Purpose | Key Classes/Functions | Lines |
|
|
16
16
|
|--------|---------|----------------------|-------|
|
|
17
|
-
| [`tool_mock.py`](tool_mock.py) | Function mocking & FastAPI MCP | `VerisSDK`, `@mock`, `@stub` | 327 |
|
|
18
|
-
| [`utils.py`](utils.py) | Type utilities & JSON schema | `extract_json_schema()` | 272 |
|
|
19
|
-
| [`
|
|
17
|
+
| [`tool_mock.py`](tool_mock.py) | Function mocking & FastAPI MCP | `VerisSDK`, `@mock`, `@stub`, `@spy` | 327 |
|
|
18
|
+
| [`utils.py`](utils.py) | Type utilities & JSON schema | `extract_json_schema()`, `convert_to_type()` | 272 |
|
|
19
|
+
| [`api_client.py`](api_client.py) | Centralized API client | `SimulatorAPIClient` | 62 |
|
|
20
|
+
| [`logging.py`](logging.py) | Tool call/response logging | `log_tool_call()`, `log_tool_response()` | 116 |
|
|
20
21
|
| [`models.py`](models.py) | Data models | Type definitions | 12 |
|
|
21
22
|
| [`jaeger_interface/`](jaeger_interface/) | Jaeger Query API wrapper | `JaegerClient` | See module README |
|
|
22
23
|
|
|
@@ -27,15 +28,15 @@ This module contains the core implementation of the Veris AI Python SDK. Each co
|
|
|
27
28
|
### Mock Flow
|
|
28
29
|
1. **Decoration**: `@veris.mock()` captures function metadata
|
|
29
30
|
2. **Environment Check**: `ENV=simulation` determines behavior
|
|
30
|
-
3. **API Call**: POST to `{VERIS_ENDPOINT_URL}/
|
|
31
|
+
3. **API Call**: POST to `{VERIS_ENDPOINT_URL}/v2/tool_mock`
|
|
31
32
|
4. **Type Conversion**: Response converted using `extract_json_schema()`
|
|
32
33
|
|
|
33
34
|
**Implementation**: [`tool_mock.py:200-250`](tool_mock.py)
|
|
34
35
|
|
|
35
36
|
### Spy Flow
|
|
36
|
-
1. **Pre-execution Logging**: Call details sent to `/
|
|
37
|
+
1. **Pre-execution Logging**: Call details sent to `/v2/log_tool_call`
|
|
37
38
|
2. **Function Execution**: Original function runs normally
|
|
38
|
-
3. **Post-execution Logging**: Response sent to `/
|
|
39
|
+
3. **Post-execution Logging**: Response sent to `/v2/log_tool_response`
|
|
39
40
|
|
|
40
41
|
**Implementation**: [`tool_mock.py:250-300`](tool_mock.py)
|
|
41
42
|
|
|
@@ -44,11 +45,11 @@ This module contains the core implementation of the Veris AI Python SDK. Each co
|
|
|
44
45
|
|
|
45
46
|
**Semantic Tag**: `module-config`
|
|
46
47
|
|
|
47
|
-
Environment variables are processed in [`
|
|
48
|
+
Environment variables are processed in [`api_client.py`](api_client.py):
|
|
48
49
|
|
|
49
|
-
- `
|
|
50
|
-
- `VERIS_MOCK_TIMEOUT`: Request timeout (default: 90s)
|
|
51
|
-
- `
|
|
50
|
+
- `VERIS_API_KEY`: API authentication key (optional, but recommended)
|
|
51
|
+
- `VERIS_MOCK_TIMEOUT`: Request timeout (default: 90s)
|
|
52
|
+
- `VERIS_API_URL`: Override default API endpoint (rarely needed - defaults to production)
|
|
52
53
|
|
|
53
54
|
### Observability (OTLP / Logfire)
|
|
54
55
|
|
veris_ai/agents_wrapper.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""OpenAI Agents wrapper for automatic tool mocking via Veris SDK."""
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import logging
|
|
4
5
|
from collections.abc import Callable
|
|
5
6
|
from typing import Any
|
|
@@ -88,7 +89,7 @@ def _wrap(
|
|
|
88
89
|
return mock_tool_call(
|
|
89
90
|
the_func,
|
|
90
91
|
session_id,
|
|
91
|
-
parameters,
|
|
92
|
+
json.loads(parameters),
|
|
92
93
|
tool_options.get(tool_name_inner),
|
|
93
94
|
)
|
|
94
95
|
# Fall back to original if we couldn't extract the function
|
veris_ai/api_client.py
CHANGED
|
@@ -11,28 +11,50 @@ logger = logging.getLogger(__name__)
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class SimulatorAPIClient:
|
|
14
|
-
"""Centralized client for making requests to VERIS simulation endpoints.
|
|
14
|
+
"""Centralized client for making requests to VERIS simulation endpoints.
|
|
15
|
+
|
|
16
|
+
Note:
|
|
17
|
+
This client intentionally reads configuration (base URL and API key)
|
|
18
|
+
from environment variables at call-time instead of at construction
|
|
19
|
+
time. This allows tests to patch environment variables and have those
|
|
20
|
+
changes reflected without recreating the singleton.
|
|
21
|
+
"""
|
|
15
22
|
|
|
16
23
|
def __init__(self) -> None:
|
|
17
|
-
"""Initialize the API client with
|
|
18
|
-
self.base_url = os.getenv("VERIS_API_URL", "https://simulation.api.veris.ai/")
|
|
19
|
-
self.api_key = os.getenv("VERIS_API_KEY")
|
|
24
|
+
"""Initialize the API client with static timeout configuration."""
|
|
20
25
|
self.timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
|
|
21
26
|
|
|
27
|
+
def _get_base_url(self) -> str:
|
|
28
|
+
"""Resolve the base URL from environment.
|
|
29
|
+
|
|
30
|
+
Behavior:
|
|
31
|
+
- If VERIS_API_URL is unset, default to the dev simulator URL.
|
|
32
|
+
- If VERIS_API_URL is set to an empty string, treat it as empty
|
|
33
|
+
(do not fall back). This supports tests expecting connection
|
|
34
|
+
failures when an invalid endpoint is provided.
|
|
35
|
+
"""
|
|
36
|
+
return os.getenv("VERIS_API_URL") or "https://simulator.api.veris.ai"
|
|
37
|
+
|
|
22
38
|
def _build_headers(self) -> dict[str, str] | None:
|
|
23
39
|
"""Build headers including OpenTelemetry tracing and API key."""
|
|
24
40
|
headers: dict[str, str] | None = None
|
|
25
41
|
# Add API key header if available
|
|
26
|
-
|
|
42
|
+
api_key = os.getenv("VERIS_API_KEY")
|
|
43
|
+
if api_key:
|
|
27
44
|
if headers is None:
|
|
28
45
|
headers = {}
|
|
29
|
-
headers["x-api-key"] =
|
|
46
|
+
headers["x-api-key"] = api_key
|
|
30
47
|
|
|
31
48
|
return headers
|
|
32
49
|
|
|
33
50
|
def post(self, endpoint: str, payload: dict[str, Any]) -> Any: # noqa: ANN401
|
|
34
51
|
"""Make a synchronous POST request to the specified endpoint."""
|
|
35
52
|
headers = self._build_headers()
|
|
53
|
+
# Validate endpoint URL; raise ConnectError for non-absolute URLs to
|
|
54
|
+
# mirror connection failures in tests when base URL is intentionally invalid.
|
|
55
|
+
if not endpoint.startswith(("http://", "https://")):
|
|
56
|
+
raise httpx.ConnectError("Invalid endpoint URL (not absolute): {endpoint}")
|
|
57
|
+
|
|
36
58
|
with httpx.Client(timeout=self.timeout) as client:
|
|
37
59
|
response = client.post(endpoint, json=payload, headers=headers)
|
|
38
60
|
response.raise_for_status()
|
|
@@ -41,15 +63,15 @@ class SimulatorAPIClient:
|
|
|
41
63
|
@property
|
|
42
64
|
def tool_mock_endpoint(self) -> str:
|
|
43
65
|
"""Get the tool mock endpoint URL."""
|
|
44
|
-
return urljoin(self.
|
|
66
|
+
return urljoin(self._get_base_url(), "v2/tool_mock")
|
|
45
67
|
|
|
46
68
|
def get_log_tool_call_endpoint(self, session_id: str) -> str:
|
|
47
69
|
"""Get the log tool call endpoint URL."""
|
|
48
|
-
return urljoin(self.
|
|
70
|
+
return urljoin(self._get_base_url(), f"v2/simulations/{session_id}/log_tool_call")
|
|
49
71
|
|
|
50
72
|
def get_log_tool_response_endpoint(self, session_id: str) -> str:
|
|
51
73
|
"""Get the log tool response endpoint URL."""
|
|
52
|
-
return urljoin(self.
|
|
74
|
+
return urljoin(self._get_base_url(), f"v2/simulations/{session_id}/log_tool_response")
|
|
53
75
|
|
|
54
76
|
|
|
55
77
|
# Global singleton instance
|
veris_ai/observability.py
CHANGED
|
@@ -16,7 +16,7 @@ logger = getLogger(__name__)
|
|
|
16
16
|
def init_observability() -> None: # noqa: PLR0912
|
|
17
17
|
"""Initialize tracing/export and set W3C propagation.
|
|
18
18
|
|
|
19
|
-
- Initializes
|
|
19
|
+
- Initializes logfire if available (acts as OTel bootstrap/exporter)
|
|
20
20
|
- Sets global propagator to TraceContext + Baggage (W3C)
|
|
21
21
|
- Instruments MCP, requests, httpx if instrumentation packages are present
|
|
22
22
|
- Adds a request hook to capture outbound traceparent for debugging
|
|
@@ -28,8 +28,13 @@ def init_observability() -> None: # noqa: PLR0912
|
|
|
28
28
|
|
|
29
29
|
logfire.configure(scrubbing=False)
|
|
30
30
|
logfire.instrument_openai_agents()
|
|
31
|
+
try:
|
|
32
|
+
logfire.instrument_redis()
|
|
33
|
+
except Exception:
|
|
34
|
+
logger.warning("Failed to instrument redis")
|
|
35
|
+
logfire.instrument_mcp()
|
|
36
|
+
|
|
31
37
|
except Exception as e:
|
|
32
|
-
# Tracing is optional; continue without Traceloop
|
|
33
38
|
msg = "Logfire not found: " + str(e)
|
|
34
39
|
raise RuntimeError(msg) from e
|
|
35
40
|
|
|
@@ -96,25 +101,6 @@ def init_observability() -> None: # noqa: PLR0912
|
|
|
96
101
|
msg = "OpenTelemetry not found: " + str(e)
|
|
97
102
|
raise RuntimeError(msg) from e
|
|
98
103
|
|
|
99
|
-
try:
|
|
100
|
-
from opentelemetry.instrumentation.httpx import (
|
|
101
|
-
HTTPXClientInstrumentor, # type: ignore[import-not-found]
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
HTTPXClientInstrumentor().instrument(request_hook=_log_request_headers)
|
|
105
|
-
except Exception as e:
|
|
106
|
-
msg = "OpenTelemetry not found: " + str(e)
|
|
107
|
-
raise RuntimeError(msg) from e
|
|
108
|
-
|
|
109
|
-
# Optionally enable MCP-specific spans
|
|
110
|
-
try:
|
|
111
|
-
from opentelemetry.instrumentation.mcp import McpInstrumentor # type: ignore[import-not-found]
|
|
112
|
-
|
|
113
|
-
McpInstrumentor().instrument()
|
|
114
|
-
except Exception as e:
|
|
115
|
-
msg = "OpenTelemetry not found: " + str(e)
|
|
116
|
-
raise RuntimeError(msg) from e
|
|
117
|
-
|
|
118
104
|
|
|
119
105
|
def instrument_fastapi_app(app: FastAPI) -> None:
|
|
120
106
|
"""Instrument a FastAPI app so inbound HTTP requests continue W3C traces.
|
|
@@ -122,10 +108,6 @@ def instrument_fastapi_app(app: FastAPI) -> None:
|
|
|
122
108
|
Safe to call even if the fastapi instrumentation package is not installed.
|
|
123
109
|
"""
|
|
124
110
|
|
|
125
|
-
|
|
126
|
-
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor # type: ignore[import-not-found]
|
|
111
|
+
import logfire
|
|
127
112
|
|
|
128
|
-
|
|
129
|
-
except Exception as e:
|
|
130
|
-
msg = "OpenTelemetry not found: " + str(e)
|
|
131
|
-
raise RuntimeError(msg) from e
|
|
113
|
+
logfire.instrument_fastapi(app)
|
veris_ai/tool_mock.py
CHANGED
|
@@ -5,6 +5,7 @@ from collections.abc import Callable
|
|
|
5
5
|
from contextlib import suppress
|
|
6
6
|
from contextvars import ContextVar
|
|
7
7
|
from functools import wraps
|
|
8
|
+
import tenacity
|
|
8
9
|
from typing import (
|
|
9
10
|
Any,
|
|
10
11
|
Literal,
|
|
@@ -13,10 +14,6 @@ from typing import (
|
|
|
13
14
|
)
|
|
14
15
|
|
|
15
16
|
|
|
16
|
-
from veris_ai.logging import (
|
|
17
|
-
log_tool_call,
|
|
18
|
-
log_tool_response,
|
|
19
|
-
)
|
|
20
17
|
from veris_ai.models import ResponseExpectation, ToolCallOptions
|
|
21
18
|
from veris_ai.api_client import get_api_client
|
|
22
19
|
from veris_ai.utils import convert_to_type, extract_json_schema, get_function_parameters
|
|
@@ -173,6 +170,9 @@ class VerisSDK:
|
|
|
173
170
|
"""Async wrapper."""
|
|
174
171
|
session_id = _session_id_context.get()
|
|
175
172
|
if not session_id:
|
|
173
|
+
logger.info(
|
|
174
|
+
f"No session ID found, executing original function: {func.__name__}"
|
|
175
|
+
)
|
|
176
176
|
return await func(*args, **kwargs)
|
|
177
177
|
parameters = get_function_parameters(func, args, kwargs)
|
|
178
178
|
return mock_tool_call(
|
|
@@ -190,6 +190,9 @@ class VerisSDK:
|
|
|
190
190
|
"""Sync wrapper."""
|
|
191
191
|
session_id = _session_id_context.get()
|
|
192
192
|
if not session_id:
|
|
193
|
+
logger.info(
|
|
194
|
+
f"No session ID found, executing original function: {func.__name__}"
|
|
195
|
+
)
|
|
193
196
|
return func(*args, **kwargs)
|
|
194
197
|
parameters = get_function_parameters(func, args, kwargs)
|
|
195
198
|
return mock_tool_call(
|
|
@@ -217,7 +220,9 @@ class VerisSDK:
|
|
|
217
220
|
**kwargs: Any, # noqa: ANN401
|
|
218
221
|
) -> object:
|
|
219
222
|
if not self.session_id:
|
|
220
|
-
|
|
223
|
+
logger.info(
|
|
224
|
+
f"No session ID found, executing original function: {func.__name__}"
|
|
225
|
+
)
|
|
221
226
|
return await func(*args, **kwargs)
|
|
222
227
|
logger.info(f"Stubbing function: {func.__name__}")
|
|
223
228
|
return return_value
|
|
@@ -225,7 +230,9 @@ class VerisSDK:
|
|
|
225
230
|
@wraps(func)
|
|
226
231
|
def sync_wrapper(*args: tuple[object, ...], **kwargs: Any) -> object: # noqa: ANN401
|
|
227
232
|
if not self.session_id:
|
|
228
|
-
|
|
233
|
+
logger.info(
|
|
234
|
+
f"No session ID found, executing original function: {func.__name__}"
|
|
235
|
+
)
|
|
229
236
|
return func(*args, **kwargs)
|
|
230
237
|
logger.info(f"Stubbing function: {func.__name__}")
|
|
231
238
|
return return_value
|
|
@@ -236,10 +243,15 @@ class VerisSDK:
|
|
|
236
243
|
return decorator
|
|
237
244
|
|
|
238
245
|
|
|
246
|
+
@tenacity.retry(
|
|
247
|
+
stop=tenacity.stop_after_attempt(3),
|
|
248
|
+
wait=tenacity.wait_exponential(multiplier=1, min=4, max=10),
|
|
249
|
+
reraise=True,
|
|
250
|
+
)
|
|
239
251
|
def mock_tool_call(
|
|
240
252
|
func: Callable,
|
|
241
253
|
session_id: str,
|
|
242
|
-
parameters: str,
|
|
254
|
+
parameters: dict[str, dict[str, str]],
|
|
243
255
|
options: ToolCallOptions | None = None,
|
|
244
256
|
) -> object:
|
|
245
257
|
"""Mock tool call."""
|
|
@@ -262,7 +274,7 @@ def mock_tool_call(
|
|
|
262
274
|
"cache_response": bool(options.cache_response),
|
|
263
275
|
"tool_call": {
|
|
264
276
|
"function_name": func.__name__,
|
|
265
|
-
"parameters":
|
|
277
|
+
"parameters": parameters,
|
|
266
278
|
"return_type": json.dumps(extract_json_schema(return_type_obj)),
|
|
267
279
|
"docstring": docstring,
|
|
268
280
|
},
|
|
@@ -278,4 +290,41 @@ def mock_tool_call(
|
|
|
278
290
|
return convert_to_type(mock_result, return_type_obj)
|
|
279
291
|
|
|
280
292
|
|
|
293
|
+
def log_tool_call(
|
|
294
|
+
session_id: str,
|
|
295
|
+
function_name: str,
|
|
296
|
+
parameters: dict[str, dict[str, str]],
|
|
297
|
+
docstring: str,
|
|
298
|
+
) -> None:
|
|
299
|
+
"""Log tool call synchronously to the VERIS logging endpoint."""
|
|
300
|
+
api_client = get_api_client()
|
|
301
|
+
endpoint = api_client.get_log_tool_call_endpoint(session_id)
|
|
302
|
+
payload = {
|
|
303
|
+
"function_name": function_name,
|
|
304
|
+
"parameters": parameters,
|
|
305
|
+
"docstring": docstring,
|
|
306
|
+
}
|
|
307
|
+
try:
|
|
308
|
+
api_client.post(endpoint, payload)
|
|
309
|
+
logger.debug(f"Tool call logged for {function_name}")
|
|
310
|
+
except Exception as e:
|
|
311
|
+
logger.warning(f"Failed to log tool call for {function_name}: {e}")
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def log_tool_response(session_id: str, response: Any) -> None: # noqa: ANN401
|
|
315
|
+
"""Log tool response synchronously to the VERIS logging endpoint."""
|
|
316
|
+
api_client = get_api_client()
|
|
317
|
+
endpoint = api_client.get_log_tool_response_endpoint(session_id)
|
|
318
|
+
|
|
319
|
+
payload = {
|
|
320
|
+
"response": json.dumps(response, default=str),
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
api_client.post(endpoint, payload)
|
|
325
|
+
logger.debug("Tool response logged")
|
|
326
|
+
except Exception as e:
|
|
327
|
+
logger.warning(f"Failed to log tool response: {e}")
|
|
328
|
+
|
|
329
|
+
|
|
281
330
|
veris = VerisSDK()
|
veris_ai/utils.py
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import inspect
|
|
2
|
-
import json
|
|
3
2
|
import sys
|
|
4
3
|
import types
|
|
5
4
|
import typing
|
|
6
5
|
from contextlib import suppress
|
|
7
|
-
from typing import
|
|
6
|
+
from typing import (
|
|
7
|
+
Any,
|
|
8
|
+
ForwardRef,
|
|
9
|
+
Literal,
|
|
10
|
+
NotRequired,
|
|
11
|
+
Required,
|
|
12
|
+
Union,
|
|
13
|
+
get_args,
|
|
14
|
+
get_origin,
|
|
15
|
+
get_type_hints,
|
|
16
|
+
)
|
|
8
17
|
from collections.abc import Callable
|
|
9
18
|
|
|
10
19
|
from pydantic import BaseModel
|
|
@@ -279,12 +288,18 @@ def extract_json_schema(target_type: Any) -> dict: # noqa: PLR0911, PLR0912, C9
|
|
|
279
288
|
|
|
280
289
|
def get_function_parameters(
|
|
281
290
|
func: Callable, args: tuple[object, ...], kwargs: dict[str, object]
|
|
282
|
-
) -> str:
|
|
291
|
+
) -> dict[str, dict[str, str]]:
|
|
283
292
|
"""Get the parameters for a function."""
|
|
293
|
+
params_info = {}
|
|
284
294
|
sig = inspect.signature(func)
|
|
285
295
|
bound_args = sig.bind(*args, **kwargs)
|
|
286
296
|
bound_args.apply_defaults()
|
|
287
297
|
_ = bound_args.arguments.pop("ctx", None)
|
|
288
298
|
_ = bound_args.arguments.pop("self", None)
|
|
289
299
|
_ = bound_args.arguments.pop("cls", None)
|
|
290
|
-
|
|
300
|
+
for param_name, param_value in bound_args.arguments.items():
|
|
301
|
+
params_info[param_name] = {
|
|
302
|
+
"value": str(param_value),
|
|
303
|
+
"type": str(get_type_hints(func).get(param_name, Any)),
|
|
304
|
+
}
|
|
305
|
+
return params_info
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: veris-ai
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.10.1
|
|
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,6 +20,7 @@ Requires-Dist: opentelemetry-instrumentation>=0.55b1
|
|
|
20
20
|
Requires-Dist: opentelemetry-sdk>=1.34.1
|
|
21
21
|
Requires-Dist: pydantic>=2.0.0
|
|
22
22
|
Requires-Dist: requests>=2.31.0
|
|
23
|
+
Requires-Dist: tenacity>=9.1.2
|
|
23
24
|
Provides-Extra: agents
|
|
24
25
|
Requires-Dist: openai-agents>=0.0.1; extra == 'agents'
|
|
25
26
|
Provides-Extra: dev
|
|
@@ -90,7 +91,6 @@ from veris_ai import Runner, VerisConfig # Requires agents extras
|
|
|
90
91
|
|----------|---------|---------|
|
|
91
92
|
| `VERIS_API_KEY` | API authentication key | None |
|
|
92
93
|
| `VERIS_MOCK_TIMEOUT` | Request timeout (seconds) | `90.0` |
|
|
93
|
-
| `ENV` | Set to `"simulation"` for mock mode | Production |
|
|
94
94
|
|
|
95
95
|
**Advanced Configuration** (rarely needed):
|
|
96
96
|
- `VERIS_API_URL`: Override default API endpoint (defaults to production)
|
|
@@ -151,39 +151,119 @@ What this enables:
|
|
|
151
151
|
|
|
152
152
|
End-to-end propagation with the simulator:
|
|
153
153
|
- The simulator injects W3C headers when connecting to your FastAPI MCP endpoints
|
|
154
|
-
- The SDK injects W3C headers on `/
|
|
154
|
+
- The SDK injects W3C headers on `/v2/tool_mock` and logging requests back to the simulator
|
|
155
155
|
- Result: customer agent spans and tool mocks appear under the same distributed trace
|
|
156
156
|
|
|
157
157
|
## Function Mocking
|
|
158
158
|
|
|
159
159
|
**Semantic Tag**: `tool-mocking`
|
|
160
160
|
|
|
161
|
+
### Session-Based Activation
|
|
162
|
+
|
|
163
|
+
The SDK uses session-based activation to determine when to enable mocking. Choose one of these methods to set a session ID:
|
|
164
|
+
|
|
165
|
+
**Option 1: Manual Setting**
|
|
166
|
+
```python
|
|
167
|
+
from veris_ai import veris
|
|
168
|
+
|
|
169
|
+
# Explicitly set a session ID
|
|
170
|
+
veris.set_session_id("your-session-id")
|
|
171
|
+
|
|
172
|
+
# Now decorated functions will use mock responses
|
|
173
|
+
result = await your_mocked_function()
|
|
174
|
+
|
|
175
|
+
# Clear session to disable mocking
|
|
176
|
+
veris.clear_session_id()
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Option 2: Automatic Extraction (FastAPI MCP)**
|
|
180
|
+
```python
|
|
181
|
+
# When using FastAPI MCP integration, session IDs are
|
|
182
|
+
# automatically extracted from OAuth2 bearer tokens
|
|
183
|
+
veris.set_fastapi_mcp(...)
|
|
184
|
+
# No manual session management needed
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**How it works internally**: Regardless of which method you use, session IDs are stored in Python context variables (`contextvars`). This ensures proper isolation between concurrent requests and automatic propagation through the call stack.
|
|
188
|
+
|
|
161
189
|
### Core Decorators
|
|
162
190
|
|
|
163
191
|
```python
|
|
164
192
|
from veris_ai import veris
|
|
165
193
|
|
|
166
|
-
# Mock
|
|
194
|
+
# Mock decorator: Returns simulated responses when session ID is set
|
|
167
195
|
@veris.mock()
|
|
168
196
|
async def your_function(param1: str, param2: int) -> dict:
|
|
169
197
|
"""Function documentation for LLM context."""
|
|
170
198
|
return {"result": "actual implementation"}
|
|
171
199
|
|
|
172
|
-
# Spy
|
|
200
|
+
# Spy decorator: Executes function and logs calls/responses
|
|
173
201
|
@veris.spy()
|
|
174
202
|
async def monitored_function(data: str) -> dict:
|
|
175
203
|
return process_data(data)
|
|
176
204
|
|
|
177
|
-
# Stub
|
|
205
|
+
# Stub decorator: Returns fixed value in simulation
|
|
178
206
|
@veris.stub(return_value={"status": "success"})
|
|
179
207
|
async def get_data() -> dict:
|
|
180
208
|
return await fetch_from_api()
|
|
181
209
|
```
|
|
182
210
|
|
|
183
|
-
**Behavior**:
|
|
211
|
+
**Behavior**: When a session ID is set, decorators activate their respective behaviors (mock responses, logging, or stubbed values). Without a session ID, functions execute normally.
|
|
184
212
|
|
|
185
213
|
**Implementation**: See [`src/veris_ai/tool_mock.py`](src/veris_ai/tool_mock.py) for decorator logic and API integration.
|
|
186
214
|
|
|
215
|
+
### Core Instrument
|
|
216
|
+
|
|
217
|
+
Instrument OpenAI agents without changing tool code by using the SDK's Runner (extends OpenAI's Runner) and an optional VerisConfig for fine control.
|
|
218
|
+
|
|
219
|
+
Requirements:
|
|
220
|
+
- Install extras: `uv add "veris-ai[agents]"`
|
|
221
|
+
- Set `ENV=simulation` to enable mock behavior (otherwise passes through)
|
|
222
|
+
|
|
223
|
+
Minimal usage:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
from veris_ai import Runner
|
|
227
|
+
|
|
228
|
+
result = await Runner.run(agent, "What's 10 + 5?")
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Select tools to intercept (include/exclude):
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from veris_ai import Runner, VerisConfig
|
|
235
|
+
|
|
236
|
+
config = VerisConfig(include_tools=["calculator", "search_web"]) # or exclude_tools=[...]
|
|
237
|
+
result = await Runner.run(agent, "Process this", veris_config=config)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Per‑tool behavior via ToolCallOptions:
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
from veris_ai import Runner, VerisConfig, ToolCallOptions, ResponseExpectation
|
|
244
|
+
|
|
245
|
+
config = VerisConfig(
|
|
246
|
+
tool_options={
|
|
247
|
+
"calculator": ToolCallOptions(
|
|
248
|
+
response_expectation=ResponseExpectation.REQUIRED,
|
|
249
|
+
cache_response=True,
|
|
250
|
+
mode="tool",
|
|
251
|
+
),
|
|
252
|
+
"search_web": ToolCallOptions(
|
|
253
|
+
response_expectation=ResponseExpectation.NONE,
|
|
254
|
+
cache_response=False,
|
|
255
|
+
mode="spy",
|
|
256
|
+
),
|
|
257
|
+
}
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
result = await Runner.run(agent, "Calculate and search", veris_config=config)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Notes:
|
|
264
|
+
- Runner is a drop‑in enhancement over OpenAI's Runner (full details in [OpenAI Agents Integration](#openai-agents-integration))
|
|
265
|
+
- See complete examples in [`examples/openai_agents_example.py`](examples/openai_agents_example.py)
|
|
266
|
+
|
|
187
267
|
## OpenAI Agents Integration
|
|
188
268
|
|
|
189
269
|
**Semantic Tag**: `openai-agents`
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
veris_ai/README.md,sha256=g3yobd0u9kWEBDitowrYv49tavo8mTHXJkpuypBhZb8,3806
|
|
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=385u11QuKq4Qk_WkI4Z5yOxAWJcV57VCGLYU5NIfnmo,3201
|
|
5
|
+
veris_ai/models.py,sha256=xKeheSJQle2tBeJG1DsGJzMDwv24p5jECjX6RAa39n4,495
|
|
6
|
+
veris_ai/observability.py,sha256=5E4ox8TFFmWTKaWFHz-5kdt58KDH8hfMMSWKBacnfzg,4146
|
|
7
|
+
veris_ai/tool_mock.py,sha256=rU_ZwYWYIfgiOojEjmi5BdBsNzP3RphLPmMG1pyYUVw,11954
|
|
8
|
+
veris_ai/utils.py,sha256=hJetCiN8Bubhy0nqSoS1C2awN9cdkKuHM1v7YhtwtTs,10066
|
|
9
|
+
veris_ai/jaeger_interface/README.md,sha256=kd9rKcE5xf3EyNaiHu0tjn-0oES9sfaK6Ih-OhhTyCM,2821
|
|
10
|
+
veris_ai/jaeger_interface/__init__.py,sha256=KD7NSiMYRG_2uF6dOLKkGG5lNQe4K9ptEwucwMT4_aw,1128
|
|
11
|
+
veris_ai/jaeger_interface/client.py,sha256=yJrh86wRR0Dk3Gq12DId99WogcMIVbL0QQFqVSevvlE,8772
|
|
12
|
+
veris_ai/jaeger_interface/models.py,sha256=e64VV6IvOEFuzRUgvDAMQFyOZMRb56I-PUPZLBZ3rX0,1864
|
|
13
|
+
veris_ai-1.10.1.dist-info/METADATA,sha256=15xKxjJP4i8aJZihl4nzyDzEasyASlx-B_0Ox3ZMTfU,15991
|
|
14
|
+
veris_ai-1.10.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
+
veris_ai-1.10.1.dist-info/licenses/LICENSE,sha256=2g4i20atAgtD5einaKzhQrIB-JrPhyQgD3bC0wkHcCI,1065
|
|
16
|
+
veris_ai-1.10.1.dist-info/RECORD,,
|
veris_ai/logging.py
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"""Logging utilities for VERIS tool calls and responses."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import logging
|
|
5
|
-
from typing import Any
|
|
6
|
-
|
|
7
|
-
from veris_ai.api_client import get_api_client
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def log_tool_call(
|
|
13
|
-
session_id: str,
|
|
14
|
-
function_name: str,
|
|
15
|
-
parameters: str,
|
|
16
|
-
docstring: str,
|
|
17
|
-
) -> None:
|
|
18
|
-
"""Log tool call synchronously to the VERIS logging endpoint."""
|
|
19
|
-
api_client = get_api_client()
|
|
20
|
-
endpoint = api_client.get_log_tool_call_endpoint(session_id)
|
|
21
|
-
payload = {
|
|
22
|
-
"function_name": function_name,
|
|
23
|
-
"parameters": json.loads(parameters),
|
|
24
|
-
"docstring": docstring,
|
|
25
|
-
}
|
|
26
|
-
try:
|
|
27
|
-
api_client.post(endpoint, payload)
|
|
28
|
-
logger.debug(f"Tool call logged for {function_name}")
|
|
29
|
-
except Exception as e:
|
|
30
|
-
logger.warning(f"Failed to log tool call for {function_name}: {e}")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def log_tool_response(session_id: str, response: Any) -> None: # noqa: ANN401
|
|
34
|
-
"""Log tool response synchronously to the VERIS logging endpoint."""
|
|
35
|
-
api_client = get_api_client()
|
|
36
|
-
endpoint = api_client.get_log_tool_response_endpoint(session_id)
|
|
37
|
-
|
|
38
|
-
payload = {
|
|
39
|
-
"response": json.dumps(response, default=str),
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
try:
|
|
43
|
-
api_client.post(endpoint, payload)
|
|
44
|
-
logger.debug("Tool response logged")
|
|
45
|
-
except Exception as e:
|
|
46
|
-
logger.warning(f"Failed to log tool response: {e}")
|
veris_ai-1.9.0.dist-info/RECORD
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
veris_ai/README.md,sha256=0EC-yWB8FtqAhtcE5Q204bS1JWJPIvfpp1bPDo6iwwc,3601
|
|
2
|
-
veris_ai/__init__.py,sha256=enl_gEa6BQAjWvzCtsn_hFVJVVlJZ_dMsLL--E5W3nU,1907
|
|
3
|
-
veris_ai/agents_wrapper.py,sha256=L4llszfyZkG4Xjo8OTeVfm-IIdESAcW51ukxceKc88U,11907
|
|
4
|
-
veris_ai/api_client.py,sha256=Gu_ovqaW-hZ6yPee2GVconMeg_xOxlt7IKu0HJQNiD0,2192
|
|
5
|
-
veris_ai/logging.py,sha256=ENduOHnc5UmzciTDZDJCZqUOXb5We-bdtwN9f9Qqwl0,1360
|
|
6
|
-
veris_ai/models.py,sha256=xKeheSJQle2tBeJG1DsGJzMDwv24p5jECjX6RAa39n4,495
|
|
7
|
-
veris_ai/observability.py,sha256=fDtWeUexfzaIQArE5YbWsja8Y-bcE_h0dXQWYbXbupY,4929
|
|
8
|
-
veris_ai/tool_mock.py,sha256=L2p6Yg8S98floahDTrf6cnmDqC6Dq3YKA7Q6RRvv7BQ,10253
|
|
9
|
-
veris_ai/utils.py,sha256=Ud4k2jKAJ6-nqSsFXIJWYrOmPGmvl5RSp36cQtgnMpg,9781
|
|
10
|
-
veris_ai/jaeger_interface/README.md,sha256=kd9rKcE5xf3EyNaiHu0tjn-0oES9sfaK6Ih-OhhTyCM,2821
|
|
11
|
-
veris_ai/jaeger_interface/__init__.py,sha256=KD7NSiMYRG_2uF6dOLKkGG5lNQe4K9ptEwucwMT4_aw,1128
|
|
12
|
-
veris_ai/jaeger_interface/client.py,sha256=yJrh86wRR0Dk3Gq12DId99WogcMIVbL0QQFqVSevvlE,8772
|
|
13
|
-
veris_ai/jaeger_interface/models.py,sha256=e64VV6IvOEFuzRUgvDAMQFyOZMRb56I-PUPZLBZ3rX0,1864
|
|
14
|
-
veris_ai-1.9.0.dist-info/METADATA,sha256=8c5TObTTfXNUiV0uTiTUu1sAZs7VOcrki1Q-00sGn_8,13495
|
|
15
|
-
veris_ai-1.9.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
-
veris_ai-1.9.0.dist-info/licenses/LICENSE,sha256=2g4i20atAgtD5einaKzhQrIB-JrPhyQgD3bC0wkHcCI,1065
|
|
17
|
-
veris_ai-1.9.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|