veris-ai 1.9.0__tar.gz → 1.10.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.

Files changed (41) hide show
  1. {veris_ai-1.9.0 → veris_ai-1.10.0}/CLAUDE.md +32 -10
  2. {veris_ai-1.9.0 → veris_ai-1.10.0}/PKG-INFO +34 -6
  3. {veris_ai-1.9.0 → veris_ai-1.10.0}/README.md +32 -5
  4. {veris_ai-1.9.0 → veris_ai-1.10.0}/examples/README.md +9 -6
  5. {veris_ai-1.9.0 → veris_ai-1.10.0}/pyproject.toml +2 -1
  6. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/README.md +12 -11
  7. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/agents_wrapper.py +2 -1
  8. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/observability.py +8 -25
  9. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/tool_mock.py +57 -8
  10. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/utils.py +19 -4
  11. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/README.md +2 -2
  12. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/test_mcp_protocol_server_mocked.py +0 -2
  13. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/test_tool_mock.py +8 -2
  14. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/test_veris_runner_tool_options.py +1 -2
  15. {veris_ai-1.9.0 → veris_ai-1.10.0}/uv.lock +12 -1
  16. veris_ai-1.9.0/src/veris_ai/logging.py +0 -46
  17. {veris_ai-1.9.0 → veris_ai-1.10.0}/.cursor/rules/documentation-management.mdc +0 -0
  18. {veris_ai-1.9.0 → veris_ai-1.10.0}/.github/workflows/release.yml +0 -0
  19. {veris_ai-1.9.0 → veris_ai-1.10.0}/.github/workflows/test.yml +0 -0
  20. {veris_ai-1.9.0 → veris_ai-1.10.0}/.gitignore +0 -0
  21. {veris_ai-1.9.0 → veris_ai-1.10.0}/.pre-commit-config.yaml +0 -0
  22. {veris_ai-1.9.0 → veris_ai-1.10.0}/CHANGELOG.md +0 -0
  23. {veris_ai-1.9.0 → veris_ai-1.10.0}/LICENSE +0 -0
  24. {veris_ai-1.9.0 → veris_ai-1.10.0}/examples/__init__.py +0 -0
  25. {veris_ai-1.9.0 → veris_ai-1.10.0}/examples/import_options.py +0 -0
  26. {veris_ai-1.9.0 → veris_ai-1.10.0}/examples/openai_agents_example.py +0 -0
  27. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/__init__.py +0 -0
  28. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/api_client.py +0 -0
  29. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/jaeger_interface/README.md +0 -0
  30. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/jaeger_interface/__init__.py +0 -0
  31. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/jaeger_interface/client.py +0 -0
  32. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/jaeger_interface/models.py +0 -0
  33. {veris_ai-1.9.0 → veris_ai-1.10.0}/src/veris_ai/models.py +0 -0
  34. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/__init__.py +0 -0
  35. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/conftest.py +0 -0
  36. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/fixtures/__init__.py +0 -0
  37. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/fixtures/http_server.py +0 -0
  38. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/fixtures/simple_app.py +0 -0
  39. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/test_agents_wrapper_extract.py +0 -0
  40. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/test_agents_wrapper_simple.py +0 -0
  41. {veris_ai-1.9.0 → veris_ai-1.10.0}/tests/test_utils.py +0 -0
@@ -6,11 +6,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
6
6
 
7
7
  This is the Veris AI Python SDK - a package that provides simulation capabilities through decorator-based function mocking and FastAPI MCP (Model Context Protocol) integration. The core functionality revolves around:
8
8
  - `VerisSDK` class in `src/veris_ai/tool_mock.py:27` - enables environment-aware execution where functions can be mocked in simulation mode, spied on, or executed normally in production
9
- - Spy mode (`@veris.mock(mode="spy")`) - executes original functions while logging calls and responses to Redis via logging endpoints
9
+ - `@veris.spy()` decorator - executes original functions while logging calls and responses via logging endpoints
10
10
  - `convert_to_type()` function in `src/veris_ai/utils.py:5` - handles sophisticated type conversion from mock responses
11
11
  - `FastApiMCPParams` model in `src/veris_ai/models.py:1` - provides configuration for integrating FastAPI applications with the Model Context Protocol
12
12
  - `set_fastapi_mcp()` method in `src/veris_ai/tool_mock.py:54` - configures FastAPI MCP server with automatic OAuth2-based session management
13
13
  - Logging utilities in `src/veris_ai/logging.py` - provide async and sync functions for logging tool calls and responses to VERIS endpoints
14
+ - `SimulatorAPIClient` class in `src/veris_ai/api_client.py` - centralized client for making requests to VERIS simulation endpoints with automatic authentication
14
15
 
15
16
  ## Development Commands
16
17
 
@@ -18,12 +19,18 @@ This project uses `uv` as the package manager and follows modern Python tooling
18
19
 
19
20
  ### Setup
20
21
  ```bash
22
+ # Install base package
23
+ uv add veris-ai
24
+
21
25
  # Install with development dependencies
22
26
  uv add "veris-ai[dev]"
23
27
 
24
28
  # Install with FastAPI MCP integration
25
29
  uv add "veris-ai[fastapi]"
26
30
 
31
+ # Install with all extras
32
+ uv add "veris-ai[dev,fastapi,observability,agents]"
33
+
27
34
  # Set Python version (requires 3.11+)
28
35
  pyenv local 3.11.0
29
36
  ```
@@ -75,10 +82,12 @@ uv build
75
82
  - Main SDK class that provides decorator functionality:
76
83
  - `@veris.mock()`: Dynamic mocking that calls external endpoints for responses
77
84
  - `@veris.stub()`: Simple stubbing with fixed return values
78
- - Environment detection: Uses `ENV` environment variable to determine simulation vs production mode
85
+ - `@veris.spy()`: Logging decorator that executes original function and logs the call/response
86
+ - Session-based activation: Uses session ID presence to determine mocking behavior
79
87
  - HTTP communication with mock endpoints via `httpx` (for mock decorator)
80
88
  - Context extraction for session management via context variables
81
89
  - Delegates type conversion to the utils module
90
+ - Automatic API endpoint configuration with production defaults
82
91
 
83
92
  **API Surface** (`src/veris_ai/__init__.py:5`)
84
93
  - Exports single `veris` instance for public use
@@ -101,10 +110,18 @@ uv build
101
110
 
102
111
  ### Environment Configuration
103
112
 
104
- Required environment variables:
105
- - `VERIS_ENDPOINT_URL`: Base endpoint URL (required)
106
- - `VERIS_MOCK_TIMEOUT`: Request timeout in seconds (optional, default: 30.0)
107
- - `ENV`: Set to "simulation" to enable mocking, anything else runs original functions
113
+ Environment variables:
114
+ - `VERIS_API_KEY`: API authentication key for VERIS services (optional, but recommended for production)
115
+ - `VERIS_MOCK_TIMEOUT`: Request timeout in seconds (optional, default: 90.0)
116
+
117
+ **Note**: The SDK automatically connects to the production VERIS API endpoint (`https://simulation.api.veris.ai/`). Only override `VERIS_API_URL` if you need to use a custom endpoint (rarely needed).
118
+
119
+ ### Session-Based Activation
120
+
121
+ The SDK activates mocking based on session ID presence:
122
+ - **With session ID**: Routes calls to mock/simulator endpoint
123
+ - **Without session ID**: Executes original function
124
+ - Session IDs can be set manually via `veris.set_session_id()` or extracted automatically from OAuth2 tokens in FastAPI MCP integration
108
125
 
109
126
  ### Type System
110
127
 
@@ -123,8 +140,8 @@ The SDK handles sophisticated type conversion from mock responses:
123
140
 
124
141
  **Key Test Fixtures**:
125
142
  - `mock_context`: Provides mock context with session ID
126
- - `simulation_env`: Sets up simulation environment variables
127
- - `production_env`: Sets up production environment variables
143
+ - `simulation_env`: Sets up simulation mode with session ID
144
+ - `production_env`: Sets up production mode without session ID
128
145
 
129
146
  **Test Coverage Areas**:
130
147
  - Environment-based behavior switching
@@ -164,9 +181,14 @@ The SDK handles sophisticated type conversion from mock responses:
164
181
  ## Key Implementation Details
165
182
 
166
183
  - **Decorator Pattern**: Functions are wrapped to intercept calls in simulation mode
167
- - `mock()`: Sends function metadata to external endpoint for dynamic responses
168
- - `stub()`: Returns predetermined values without external calls
184
+ - `@veris.mock()`: Sends function metadata to external endpoint for dynamic responses
185
+ - `@veris.stub()`: Returns predetermined values without external calls
186
+ - `@veris.spy()`: Executes original function while logging calls and responses
169
187
  - **Session Management**: Extracts session ID from context for request correlation
188
+ - **API Client**: Centralized `SimulatorAPIClient` handles all API communication
189
+ - Automatic endpoint configuration with production defaults
190
+ - Built-in authentication via `VERIS_API_KEY` header
191
+ - Configurable timeout with `VERIS_MOCK_TIMEOUT`
170
192
  - **Error Handling**: Comprehensive HTTP and type conversion error handling
171
193
  - **Async Support**: Built with async/await pattern throughout
172
194
  - **Type Safety**: Full type hints and runtime type conversion validation
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: veris-ai
3
- Version: 1.9.0
3
+ Version: 1.10.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,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)
@@ -158,29 +158,57 @@ End-to-end propagation with the simulator:
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 mode: Returns simulated responses in ENV=simulation
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 mode: Executes function but logs calls/responses
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 mode: Returns fixed value in simulation
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**: In simulation mode, decorators intercept calls to mock endpoints. In production, functions execute normally.
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
 
@@ -48,7 +48,6 @@ from veris_ai import Runner, VerisConfig # Requires agents extras
48
48
  |----------|---------|---------|
49
49
  | `VERIS_API_KEY` | API authentication key | None |
50
50
  | `VERIS_MOCK_TIMEOUT` | Request timeout (seconds) | `90.0` |
51
- | `ENV` | Set to `"simulation"` for mock mode | Production |
52
51
 
53
52
  **Advanced Configuration** (rarely needed):
54
53
  - `VERIS_API_URL`: Override default API endpoint (defaults to production)
@@ -116,29 +115,57 @@ End-to-end propagation with the simulator:
116
115
 
117
116
  **Semantic Tag**: `tool-mocking`
118
117
 
118
+ ### Session-Based Activation
119
+
120
+ The SDK uses session-based activation to determine when to enable mocking. Choose one of these methods to set a session ID:
121
+
122
+ **Option 1: Manual Setting**
123
+ ```python
124
+ from veris_ai import veris
125
+
126
+ # Explicitly set a session ID
127
+ veris.set_session_id("your-session-id")
128
+
129
+ # Now decorated functions will use mock responses
130
+ result = await your_mocked_function()
131
+
132
+ # Clear session to disable mocking
133
+ veris.clear_session_id()
134
+ ```
135
+
136
+ **Option 2: Automatic Extraction (FastAPI MCP)**
137
+ ```python
138
+ # When using FastAPI MCP integration, session IDs are
139
+ # automatically extracted from OAuth2 bearer tokens
140
+ veris.set_fastapi_mcp(...)
141
+ # No manual session management needed
142
+ ```
143
+
144
+ **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.
145
+
119
146
  ### Core Decorators
120
147
 
121
148
  ```python
122
149
  from veris_ai import veris
123
150
 
124
- # Mock mode: Returns simulated responses in ENV=simulation
151
+ # Mock decorator: Returns simulated responses when session ID is set
125
152
  @veris.mock()
126
153
  async def your_function(param1: str, param2: int) -> dict:
127
154
  """Function documentation for LLM context."""
128
155
  return {"result": "actual implementation"}
129
156
 
130
- # Spy mode: Executes function but logs calls/responses
157
+ # Spy decorator: Executes function and logs calls/responses
131
158
  @veris.spy()
132
159
  async def monitored_function(data: str) -> dict:
133
160
  return process_data(data)
134
161
 
135
- # Stub mode: Returns fixed value in simulation
162
+ # Stub decorator: Returns fixed value in simulation
136
163
  @veris.stub(return_value={"status": "success"})
137
164
  async def get_data() -> dict:
138
165
  return await fetch_from_api()
139
166
  ```
140
167
 
141
- **Behavior**: In simulation mode, decorators intercept calls to mock endpoints. In production, functions execute normally.
168
+ **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.
142
169
 
143
170
  **Implementation**: See [`src/veris_ai/tool_mock.py`](src/veris_ai/tool_mock.py) for decorator logic and API integration.
144
171
 
@@ -63,9 +63,9 @@ if os.getenv("USE_FASTAPI") == "true":
63
63
  **Semantic Tag**: `integration-patterns`
64
64
 
65
65
  ### Decorator Usage
66
- - **Function Mocking**: `@veris.mock()` for simulation mode
67
- - **Spy Mode**: `@veris.spy()` for logging
68
- - **Stub Mode**: `@veris.stub(return_value={})` for fixed responses
66
+ - **Mock Decorator**: `@veris.mock()` for simulation mode
67
+ - **Spy Decorator**: `@veris.spy()` for logging calls and responses
68
+ - **Stub Decorator**: `@veris.stub(return_value={})` for fixed responses
69
69
 
70
70
  ### Error Handling
71
71
  - **Graceful Import Failures**: Try/catch blocks for optional features
@@ -73,7 +73,8 @@ if os.getenv("USE_FASTAPI") == "true":
73
73
  - **Feature Detection**: Runtime capability checking
74
74
 
75
75
  ### Configuration Management
76
- - **Environment Variables**: `VERIS_ENDPOINT_URL`, `ENV=simulation`
76
+ - **Environment Variables**: `VERIS_API_KEY` for authentication, `VERIS_MOCK_TIMEOUT` for request timeout
77
+ - **Session Management**: Use `veris.set_session_id()` to enable mocking
77
78
  - **Conditional Setup**: Feature flags for optional components
78
79
  - **Service Configuration**: Dynamic service naming and endpoints
79
80
 
@@ -86,13 +87,15 @@ if os.getenv("USE_FASTAPI") == "true":
86
87
  cd examples/
87
88
  python import_options.py
88
89
 
89
- # With specific environment setup
90
- ENV=simulation VERIS_ENDPOINT_URL=http://localhost:8000 python import_options.py
90
+ # With API key for production
91
+ VERIS_API_KEY=your-api-key python import_options.py
91
92
 
92
93
  # Testing with different feature flags
93
94
  ENABLE_TRACING=true USE_FASTAPI=true python import_options.py
94
95
  ```
95
96
 
97
+ **Note**: To enable mocking in your code, call `veris.set_session_id("your-session-id")`.
98
+
96
99
  w### Observability Environment (optional)
97
100
 
98
101
  If you want traces exported while running examples, set the following before execution:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "veris-ai"
7
- version = "1.9.0"
7
+ version = "1.10.0"
8
8
  description = "A Python package for Veris AI tools"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -25,6 +25,7 @@ dependencies = [
25
25
  "opentelemetry-instrumentation-mcp>=0.44.1",
26
26
  "opentelemetry-api>=1.34.1",
27
27
  "logfire>=4.3.3",
28
+ "tenacity>=9.1.2",
28
29
  ]
29
30
 
30
31
  [project.optional-dependencies]
@@ -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
- | [`logging.py`](logging.py) | Logging configuration | `setup_logging()` | 116 |
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
 
@@ -26,16 +27,16 @@ This module contains the core implementation of the Veris AI Python SDK. Each co
26
27
 
27
28
  ### Mock Flow
28
29
  1. **Decoration**: `@veris.mock()` captures function metadata
29
- 2. **Environment Check**: `ENV=simulation` determines behavior
30
- 3. **API Call**: POST to `{VERIS_ENDPOINT_URL}/api/v2/tool_mock`
30
+ 2. **Session Check**: Presence of session ID determines behavior
31
+ 3. **API Call**: POST to VERIS API endpoint `/v2/tool_mock` (auto-configured)
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 `/api/v2/log_tool_call`
37
+ 1. **Pre-execution Logging**: Call details sent to `/v2/simulations/{session_id}/log_tool_call`
37
38
  2. **Function Execution**: Original function runs normally
38
- 3. **Post-execution Logging**: Response sent to `/api/v2/log_tool_response`
39
+ 3. **Post-execution Logging**: Response sent to `/v2/simulations/{session_id}/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 [`tool_mock.py`](tool_mock.py):
48
+ Environment variables are processed in [`api_client.py`](api_client.py):
48
49
 
49
- - `VERIS_ENDPOINT_URL`: Mock server endpoint
50
- - `VERIS_MOCK_TIMEOUT`: Request timeout (default: 90s)
51
- - `ENV`: Set to `"simulation"` for mock mode
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
 
@@ -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
@@ -28,6 +28,12 @@ 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
38
  # Tracing is optional; continue without Traceloop
33
39
  msg = "Logfire not found: " + str(e)
@@ -96,25 +102,6 @@ def init_observability() -> None: # noqa: PLR0912
96
102
  msg = "OpenTelemetry not found: " + str(e)
97
103
  raise RuntimeError(msg) from e
98
104
 
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
105
 
119
106
  def instrument_fastapi_app(app: FastAPI) -> None:
120
107
  """Instrument a FastAPI app so inbound HTTP requests continue W3C traces.
@@ -122,10 +109,6 @@ def instrument_fastapi_app(app: FastAPI) -> None:
122
109
  Safe to call even if the fastapi instrumentation package is not installed.
123
110
  """
124
111
 
125
- try:
126
- from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor # type: ignore[import-not-found]
112
+ import logfire
127
113
 
128
- FastAPIInstrumentor.instrument_app(app)
129
- except Exception as e:
130
- msg = "OpenTelemetry not found: " + str(e)
131
- raise RuntimeError(msg) from e
114
+ logfire.instrument_fastapi(app)
@@ -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
- # If not in simulation mode, execute the original function
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
- # If not in simulation mode, execute the original function
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": json.loads(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()
@@ -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 Any, ForwardRef, Literal, NotRequired, Required, Union, get_args, get_origin
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
- return json.dumps(bound_args.arguments)
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
@@ -14,7 +14,7 @@ Test suite for the Veris AI Python SDK with comprehensive coverage of mocking, t
14
14
 
15
15
  | Test File | Coverage | Key Areas | Lines |
16
16
  |-----------|----------|-----------|-------|
17
- | [`test_tool_mock.py`](test_tool_mock.py) | VerisSDK, decorators | Mock/spy modes, FastAPI MCP | 530 |
17
+ | [`test_tool_mock.py`](test_tool_mock.py) | VerisSDK, decorators | Mock/spy/stub decorators, FastAPI MCP | 530 |
18
18
  | [`test_utils.py`](test_utils.py) | Type conversion utilities | JSON schema extraction | 438 |
19
19
  | [`test_mcp_protocol_server_mocked.py`](test_mcp_protocol_server_mocked.py) | MCP integration | Protocol server mocking | 215 |
20
20
  | [`conftest.py`](conftest.py) | Test configuration | Pytest fixtures, setup | 60 |
@@ -24,7 +24,7 @@ Test suite for the Veris AI Python SDK with comprehensive coverage of mocking, t
24
24
  **Semantic Tag**: `test-fixtures`
25
25
 
26
26
  ### Core Fixtures (conftest.py)
27
- - **Environment setup**: Mock `VERIS_ENDPOINT_URL` and `ENV` variables
27
+ - **Environment setup**: Mock environment variables and session IDs
28
28
  - **Async client**: HTTP client for testing API interactions
29
29
  - **Session management**: Isolation between test runs
30
30
 
@@ -23,8 +23,6 @@ SERVER_NAME = "Test MCP Server"
23
23
 
24
24
  def run_server_with_mock(server_port: int) -> None: # noqa: C901
25
25
  """Run server with mocked veris API client methods."""
26
- # Ensure we're in simulation mode
27
- os.environ["ENV"] = "simulation"
28
26
  os.environ["VERIS_API_URL"] = "http://test-endpoint"
29
27
 
30
28
  # Create a mock function for the API client
@@ -257,7 +257,10 @@ async def test_spy_decorator_with_session(simulation_env):
257
257
  mock_log_call.assert_called_once_with(
258
258
  session_id="spy-session-123",
259
259
  function_name="test_func",
260
- parameters='{"x": 42, "y": "hello"}',
260
+ parameters={
261
+ "x": {"value": "42", "type": "<class 'int'>"},
262
+ "y": {"value": "hello", "type": "<class 'str'>"},
263
+ },
261
264
  docstring="A test function that returns a dict.",
262
265
  )
263
266
  mock_log_response.assert_called_once_with(
@@ -312,7 +315,10 @@ def test_spy_decorator_sync_function(simulation_env):
312
315
  mock_log_call.assert_called_once_with(
313
316
  session_id="sync-spy-session",
314
317
  function_name="sync_func",
315
- parameters='{"a": 10, "b": 20}',
318
+ parameters={
319
+ "a": {"value": "10", "type": "<class 'int'>"},
320
+ "b": {"value": "20", "type": "<class 'int'>"},
321
+ },
316
322
  docstring="Adds two numbers.",
317
323
  )
318
324
  mock_log_response.assert_called_once_with(session_id="sync-spy-session", response=30)
@@ -131,8 +131,7 @@ def mock_api_client_with_options():
131
131
  @pytest.mark.asyncio
132
132
  async def test_tool_options_passed_to_mock_call(mock_api_client_with_options, multi_tool_agent):
133
133
  """Verify that ToolCallOptions are correctly passed to the mock_tool_call function."""
134
- # Set up simulation environment
135
- os.environ["ENV"] = "simulation"
134
+ # Set up session for mocking
136
135
  veris.set_session_id("test-session-tool-options")
137
136
 
138
137
  try:
@@ -1433,6 +1433,15 @@ wheels = [
1433
1433
  { url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" },
1434
1434
  ]
1435
1435
 
1436
+ [[package]]
1437
+ name = "tenacity"
1438
+ version = "9.1.2"
1439
+ source = { registry = "https://pypi.org/simple" }
1440
+ sdist = { url = "https://files.pythonhosted.org/packages/0a/d4/2b0cd0fe285e14b36db076e78c93766ff1d529d70408bd1d2a5a84f1d929/tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb", size = 48036, upload-time = "2025-04-02T08:25:09.966Z" }
1441
+ wheels = [
1442
+ { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" },
1443
+ ]
1444
+
1436
1445
  [[package]]
1437
1446
  name = "tomli"
1438
1447
  version = "2.2.1"
@@ -1556,7 +1565,7 @@ wheels = [
1556
1565
 
1557
1566
  [[package]]
1558
1567
  name = "veris-ai"
1559
- version = "1.8.2"
1568
+ version = "1.9.0"
1560
1569
  source = { editable = "." }
1561
1570
  dependencies = [
1562
1571
  { name = "httpx" },
@@ -1571,6 +1580,7 @@ dependencies = [
1571
1580
  { name = "opentelemetry-sdk" },
1572
1581
  { name = "pydantic" },
1573
1582
  { name = "requests" },
1583
+ { name = "tenacity" },
1574
1584
  ]
1575
1585
 
1576
1586
  [package.optional-dependencies]
@@ -1637,6 +1647,7 @@ requires-dist = [
1637
1647
  { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.1.0" },
1638
1648
  { name = "requests", specifier = ">=2.31.0" },
1639
1649
  { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.11.4" },
1650
+ { name = "tenacity", specifier = ">=9.1.2" },
1640
1651
  { name = "wrapt", marker = "extra == 'instrument'" },
1641
1652
  ]
1642
1653
  provides-extras = ["dev", "fastapi", "agents", "instrument"]
@@ -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}")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes