veris-ai 1.7.0__py3-none-any.whl → 1.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of veris-ai might be problematic. Click here for more details.
- veris_ai/README.md +27 -2
- veris_ai/__init__.py +54 -1
- veris_ai/agents_wrapper.py +266 -0
- veris_ai/api_client.py +68 -0
- veris_ai/logging.py +17 -83
- veris_ai/observability.py +8 -10
- veris_ai/tool_mock.py +23 -83
- veris_ai/utils.py +15 -0
- {veris_ai-1.7.0.dist-info → veris_ai-1.8.0.dist-info}/METADATA +42 -10
- veris_ai-1.8.0.dist-info/RECORD +17 -0
- veris_ai-1.7.0.dist-info/RECORD +0 -15
- {veris_ai-1.7.0.dist-info → veris_ai-1.8.0.dist-info}/WHEEL +0 -0
- {veris_ai-1.7.0.dist-info → veris_ai-1.8.0.dist-info}/licenses/LICENSE +0 -0
veris_ai/README.md
CHANGED
|
@@ -49,8 +49,33 @@ Environment variables are processed in [`tool_mock.py`](tool_mock.py):
|
|
|
49
49
|
- `VERIS_ENDPOINT_URL`: Mock server endpoint
|
|
50
50
|
- `VERIS_MOCK_TIMEOUT`: Request timeout (default: 90s)
|
|
51
51
|
- `ENV`: Set to `"simulation"` for mock mode
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
|
|
53
|
+
### Observability (OTLP / Logfire)
|
|
54
|
+
|
|
55
|
+
When using the observability helpers (`init_observability`, `instrument_fastapi_app`), configure the following environment variables so traces export correctly and are attributed to the right service name:
|
|
56
|
+
|
|
57
|
+
- `OTEL_SERVICE_NAME` — e.g. `simulation-server` (keep consistent with any `VERIS_SERVICE_NAME` you use)
|
|
58
|
+
- `OTEL_EXPORTER_OTLP_ENDPOINT` — e.g. `https://logfire-api.pydantic.dev`
|
|
59
|
+
- `LOGFIRE_TOKEN` — API token for Logfire
|
|
60
|
+
- `OTEL_EXPORTER_OTLP_HEADERS` — e.g. `Authorization=Bearer <LOGFIRE_TOKEN>` (quote the value)
|
|
61
|
+
|
|
62
|
+
Minimal shell setup:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
export OTEL_SERVICE_NAME="simulation-server"
|
|
66
|
+
export OTEL_EXPORTER_OTLP_ENDPOINT="https://logfire-api.pydantic.dev"
|
|
67
|
+
export LOGFIRE_TOKEN="<your-token>"
|
|
68
|
+
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=${LOGFIRE_TOKEN}"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then in code:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from veris_ai import init_observability, instrument_fastapi_app
|
|
75
|
+
init_observability()
|
|
76
|
+
app = FastAPI()
|
|
77
|
+
instrument_fastapi_app(app)
|
|
78
|
+
```
|
|
54
79
|
|
|
55
80
|
## Development Notes
|
|
56
81
|
|
veris_ai/__init__.py
CHANGED
|
@@ -1,12 +1,63 @@
|
|
|
1
1
|
"""Veris AI Python SDK."""
|
|
2
2
|
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
3
5
|
__version__ = "0.1.0"
|
|
4
6
|
|
|
5
7
|
# Import lightweight modules that only use base dependencies
|
|
6
8
|
from .jaeger_interface import JaegerClient
|
|
7
9
|
from .models import ResponseExpectation
|
|
8
|
-
from .tool_mock import veris
|
|
9
10
|
from .observability import init_observability, instrument_fastapi_app
|
|
11
|
+
from .tool_mock import veris
|
|
12
|
+
|
|
13
|
+
# Lazy import for modules with heavy dependencies
|
|
14
|
+
_veris_runner = None
|
|
15
|
+
_VerisConfig = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def veris_runner(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401
|
|
19
|
+
"""Lazy loader for the veris_runner function from agents_wrapper.
|
|
20
|
+
|
|
21
|
+
This function wraps OpenAI agents Runner.run to intercept tool calls
|
|
22
|
+
through the Veris SDK's mocking infrastructure.
|
|
23
|
+
|
|
24
|
+
This function requires the 'agents' extra dependencies:
|
|
25
|
+
pip install veris-ai[agents]
|
|
26
|
+
"""
|
|
27
|
+
global _veris_runner # noqa: PLW0603
|
|
28
|
+
if _veris_runner is None:
|
|
29
|
+
try:
|
|
30
|
+
from .agents_wrapper import veris_runner as _veris_runner_impl # noqa: PLC0415
|
|
31
|
+
|
|
32
|
+
_veris_runner = _veris_runner_impl
|
|
33
|
+
except ImportError as e:
|
|
34
|
+
error_msg = (
|
|
35
|
+
"The 'veris_runner' function requires additional dependencies. "
|
|
36
|
+
"Please install them with: pip install veris-ai[agents]"
|
|
37
|
+
)
|
|
38
|
+
raise ImportError(error_msg) from e
|
|
39
|
+
return _veris_runner(*args, **kwargs)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def __getattr__(name: str) -> Any: # noqa: ANN401
|
|
43
|
+
"""Lazy load VerisConfig class."""
|
|
44
|
+
global _VerisConfig # noqa: PLW0603
|
|
45
|
+
if name == "VerisConfig":
|
|
46
|
+
if _VerisConfig is None:
|
|
47
|
+
try:
|
|
48
|
+
from .agents_wrapper import VerisConfig as _VerisConfig_impl # noqa: PLC0415
|
|
49
|
+
|
|
50
|
+
_VerisConfig = _VerisConfig_impl
|
|
51
|
+
except ImportError as e:
|
|
52
|
+
error_msg = (
|
|
53
|
+
"The 'VerisConfig' class requires additional dependencies. "
|
|
54
|
+
"Please install them with: pip install veris-ai[agents]"
|
|
55
|
+
)
|
|
56
|
+
raise ImportError(error_msg) from e
|
|
57
|
+
return _VerisConfig
|
|
58
|
+
msg = f"module {__name__!r} has no attribute {name!r}"
|
|
59
|
+
raise AttributeError(msg)
|
|
60
|
+
|
|
10
61
|
|
|
11
62
|
__all__ = [
|
|
12
63
|
"veris",
|
|
@@ -14,4 +65,6 @@ __all__ = [
|
|
|
14
65
|
"ResponseExpectation",
|
|
15
66
|
"init_observability",
|
|
16
67
|
"instrument_fastapi_app",
|
|
68
|
+
"veris_runner",
|
|
69
|
+
"VerisConfig",
|
|
17
70
|
]
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""OpenAI Agents wrapper for automatic tool mocking via Veris SDK."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import inspect
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from agents import RunResult, Runner
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
from veris_ai import veris
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _wrap(
|
|
19
|
+
include_tools: list[str] | None = None,
|
|
20
|
+
exclude_tools: list[str] | None = None,
|
|
21
|
+
) -> Callable:
|
|
22
|
+
"""Private wrapper for OpenAI agents Runner to intercept tool calls through Veris SDK.
|
|
23
|
+
|
|
24
|
+
This function transparently intercepts tool calls from OpenAI agents and
|
|
25
|
+
routes them through the Veris SDK's mocking infrastructure.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
include_tools: Optional list of tool names to intercept (only these if provided)
|
|
29
|
+
exclude_tools: Optional list of tool names to NOT intercept (these run normally)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
A wrapped Runner.run function
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
ValueError: If both include_tools and exclude_tools are specified
|
|
36
|
+
ImportError: If agents package is not installed
|
|
37
|
+
"""
|
|
38
|
+
if include_tools and exclude_tools:
|
|
39
|
+
msg = "Cannot specify both include_tools and exclude_tools"
|
|
40
|
+
raise ValueError(msg)
|
|
41
|
+
|
|
42
|
+
def wrapped_run_func(run_func: Callable) -> Callable:
|
|
43
|
+
"""Inner wrapper that takes the actual Runner.run function."""
|
|
44
|
+
try:
|
|
45
|
+
from agents import FunctionTool # type: ignore[import-untyped] # noqa: PLC0415
|
|
46
|
+
except ImportError as e:
|
|
47
|
+
msg = "openai-agents package not installed. Install with: pip install veris-ai[agents]"
|
|
48
|
+
raise ImportError(msg) from e
|
|
49
|
+
|
|
50
|
+
async def wrapped_run(starting_agent: Any, input_text: str, **kwargs: Any) -> Any: # noqa: ANN401
|
|
51
|
+
"""Wrapped version of Runner.run that intercepts tool calls."""
|
|
52
|
+
# Patch all tools in the agent
|
|
53
|
+
original_tools = []
|
|
54
|
+
|
|
55
|
+
if hasattr(starting_agent, "tools") and starting_agent.tools:
|
|
56
|
+
for tool in starting_agent.tools:
|
|
57
|
+
if isinstance(tool, FunctionTool):
|
|
58
|
+
tool_name = getattr(tool, "name", None)
|
|
59
|
+
|
|
60
|
+
# Check if we should patch this tool
|
|
61
|
+
if tool_name and _should_intercept_tool(
|
|
62
|
+
tool_name, include_tools, exclude_tools
|
|
63
|
+
):
|
|
64
|
+
# Save the original invoke function
|
|
65
|
+
original_tools.append((tool, tool.on_invoke_tool)) # type: ignore[attr-defined]
|
|
66
|
+
|
|
67
|
+
# Create veris-wrapped version
|
|
68
|
+
tool.on_invoke_tool = _create_veris_wrapped_invoke( # type: ignore[attr-defined]
|
|
69
|
+
tool,
|
|
70
|
+
tool.on_invoke_tool, # type: ignore[attr-defined]
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
# Call the original Runner.run with the patched agent
|
|
75
|
+
return await run_func(starting_agent, input_text, **kwargs)
|
|
76
|
+
finally:
|
|
77
|
+
# Restore all original tool functions
|
|
78
|
+
for tool, original_invoke in original_tools:
|
|
79
|
+
tool.on_invoke_tool = original_invoke # type: ignore[attr-defined]
|
|
80
|
+
|
|
81
|
+
# Preserve function metadata
|
|
82
|
+
wrapped_run.__name__ = getattr(run_func, "__name__", "wrapped_run")
|
|
83
|
+
wrapped_run.__doc__ = getattr(run_func, "__doc__", "Wrapped Runner.run function")
|
|
84
|
+
|
|
85
|
+
# Also provide a sync version
|
|
86
|
+
def wrapped_run_sync(starting_agent: Any, input_text: str, **kwargs: Any) -> Any: # noqa: ANN401
|
|
87
|
+
"""Sync version of wrapped Runner.run."""
|
|
88
|
+
return asyncio.run(wrapped_run(starting_agent, input_text, **kwargs))
|
|
89
|
+
|
|
90
|
+
# Add sync version as an attribute
|
|
91
|
+
wrapped_run.run_sync = wrapped_run_sync # type: ignore[attr-defined]
|
|
92
|
+
|
|
93
|
+
return wrapped_run
|
|
94
|
+
|
|
95
|
+
return wrapped_run_func
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _should_intercept_tool(
|
|
99
|
+
tool_name: str,
|
|
100
|
+
include_tools: list[str] | None,
|
|
101
|
+
exclude_tools: list[str] | None,
|
|
102
|
+
) -> bool:
|
|
103
|
+
"""Determine if a tool should be intercepted based on include/exclude lists.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
tool_name: Name of the tool
|
|
107
|
+
include_tools: If provided, only these tools are intercepted
|
|
108
|
+
exclude_tools: If provided, these tools are NOT intercepted
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
True if the tool should be intercepted, False otherwise
|
|
112
|
+
"""
|
|
113
|
+
if include_tools:
|
|
114
|
+
return tool_name in include_tools
|
|
115
|
+
if exclude_tools:
|
|
116
|
+
return tool_name not in exclude_tools
|
|
117
|
+
return True
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _create_veris_wrapped_invoke( # noqa: C901
|
|
121
|
+
tool: Any, # noqa: ANN401
|
|
122
|
+
original_invoke: Callable,
|
|
123
|
+
) -> Callable:
|
|
124
|
+
"""Create a wrapped invoke function that uses veris.mock().
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
tool: The FunctionTool instance
|
|
128
|
+
original_invoke: The original on_invoke_tool function
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
A wrapped invoke function that routes through Veris SDK
|
|
132
|
+
"""
|
|
133
|
+
# Extract tool metadata
|
|
134
|
+
tool_name = tool.name
|
|
135
|
+
tool_schema = tool.params_json_schema if hasattr(tool, "params_json_schema") else {}
|
|
136
|
+
|
|
137
|
+
# Create a function that will be decorated with veris.mock()
|
|
138
|
+
@veris.mock(mode="tool")
|
|
139
|
+
async def veris_tool_function(**_kwargs: Any) -> Any: # noqa: ANN401
|
|
140
|
+
"""Mock function for tool execution."""
|
|
141
|
+
# This function's signature doesn't matter as veris.mock()
|
|
142
|
+
# will intercept and send the metadata to the endpoint
|
|
143
|
+
return f"Mock response for {tool_name}"
|
|
144
|
+
|
|
145
|
+
async def wrapped_invoke(context: Any, arguments: str) -> Any: # noqa: ANN401
|
|
146
|
+
"""Wrapped invoke function that routes through Veris SDK."""
|
|
147
|
+
# Only intercept if we have a session ID
|
|
148
|
+
if not veris.session_id:
|
|
149
|
+
# No session, run original
|
|
150
|
+
return await original_invoke(context, arguments)
|
|
151
|
+
|
|
152
|
+
# Parse arguments
|
|
153
|
+
try:
|
|
154
|
+
args_dict = json.loads(arguments) if arguments else {}
|
|
155
|
+
except json.JSONDecodeError:
|
|
156
|
+
args_dict = {"raw_arguments": arguments}
|
|
157
|
+
|
|
158
|
+
# Set up the function with proper metadata for veris.mock()
|
|
159
|
+
veris_tool_function.__name__ = tool_name
|
|
160
|
+
veris_tool_function.__doc__ = getattr(tool, "description", "")
|
|
161
|
+
|
|
162
|
+
# Add type hints based on schema if available
|
|
163
|
+
if tool_schema and "properties" in tool_schema:
|
|
164
|
+
# Create a simple signature based on schema
|
|
165
|
+
params = []
|
|
166
|
+
for param_name, param_info in tool_schema["properties"].items():
|
|
167
|
+
# Simplified type mapping
|
|
168
|
+
param_type: type[Any] = Any
|
|
169
|
+
if "type" in param_info:
|
|
170
|
+
type_map = {
|
|
171
|
+
"string": str,
|
|
172
|
+
"number": float,
|
|
173
|
+
"integer": int,
|
|
174
|
+
"boolean": bool,
|
|
175
|
+
"array": list,
|
|
176
|
+
"object": dict,
|
|
177
|
+
}
|
|
178
|
+
param_type = type_map.get(param_info["type"], Any)
|
|
179
|
+
params.append(
|
|
180
|
+
inspect.Parameter(
|
|
181
|
+
param_name,
|
|
182
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
183
|
+
annotation=param_type,
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
if params:
|
|
188
|
+
veris_tool_function.__signature__ = inspect.Signature(params)
|
|
189
|
+
|
|
190
|
+
# Call the veris-wrapped function with the arguments
|
|
191
|
+
try:
|
|
192
|
+
return await veris_tool_function(**args_dict)
|
|
193
|
+
except Exception as e:
|
|
194
|
+
# If mocking fails, fall back to original
|
|
195
|
+
logger.warning(f"Veris mock failed for {tool_name}, falling back to original: {e}")
|
|
196
|
+
return await original_invoke(context, arguments)
|
|
197
|
+
|
|
198
|
+
# Handle sync version if original is sync
|
|
199
|
+
if not asyncio.iscoroutinefunction(original_invoke):
|
|
200
|
+
|
|
201
|
+
def sync_wrapped_invoke(context: Any, arguments: str) -> Any: # noqa: ANN401
|
|
202
|
+
"""Sync version of wrapped invoke."""
|
|
203
|
+
return asyncio.run(wrapped_invoke(context, arguments))
|
|
204
|
+
|
|
205
|
+
return sync_wrapped_invoke
|
|
206
|
+
|
|
207
|
+
return wrapped_invoke
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class VerisConfig(BaseModel):
|
|
211
|
+
"""Configuration for the Veris SDK."""
|
|
212
|
+
|
|
213
|
+
include_tools: list[str] | None = None
|
|
214
|
+
exclude_tools: list[str] | None = None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def veris_runner(
|
|
218
|
+
starting_agent: Any, # noqa: ANN401
|
|
219
|
+
input_text: str,
|
|
220
|
+
veris_config: VerisConfig | None = None,
|
|
221
|
+
**kwargs: Any, # noqa: ANN401
|
|
222
|
+
) -> RunResult: # noqa: ANN401
|
|
223
|
+
"""Veris-wrapped version of OpenAI agents Runner.run.
|
|
224
|
+
|
|
225
|
+
This function wraps the OpenAI agents Runner.run to intercept tool calls
|
|
226
|
+
and route them through the Veris SDK's mocking infrastructure. It can be
|
|
227
|
+
used as a drop-in replacement for Runner.run with an additional veris_config parameter.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
starting_agent: The OpenAI agent to run
|
|
231
|
+
input_text: The input text to process
|
|
232
|
+
veris_config: Optional configuration for Veris SDK tool interception
|
|
233
|
+
**kwargs: Additional arguments to pass to Runner.run
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
The result from Runner.run
|
|
237
|
+
|
|
238
|
+
Example:
|
|
239
|
+
```python
|
|
240
|
+
from veris_ai import veris_runner, VerisConfig
|
|
241
|
+
from agents import Agent, FunctionTool
|
|
242
|
+
|
|
243
|
+
# Define your agent with tools
|
|
244
|
+
agent = Agent(...)
|
|
245
|
+
|
|
246
|
+
# Use veris_runner instead of Runner.run
|
|
247
|
+
result = await veris_runner(agent, "Process this input")
|
|
248
|
+
|
|
249
|
+
# Or with specific tool configuration
|
|
250
|
+
config = VerisConfig(include_tools=["calculator", "search"])
|
|
251
|
+
result = await veris_runner(agent, "Calculate 2+2", veris_config=config)
|
|
252
|
+
```
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
# Extract config values
|
|
256
|
+
include_tools = None
|
|
257
|
+
exclude_tools = None
|
|
258
|
+
if veris_config:
|
|
259
|
+
include_tools = veris_config.include_tools
|
|
260
|
+
exclude_tools = veris_config.exclude_tools
|
|
261
|
+
|
|
262
|
+
# Create the wrapped version of Runner.run with the config
|
|
263
|
+
wrapped_run = _wrap(include_tools=include_tools, exclude_tools=exclude_tools)(Runner.run)
|
|
264
|
+
|
|
265
|
+
# Execute the wrapped run function
|
|
266
|
+
return wrapped_run(starting_agent, input_text, **kwargs)
|
veris_ai/api_client.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Centralized API client for VERIS simulation endpoints."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SimulatorAPIClient:
|
|
13
|
+
"""Centralized client for making requests to VERIS simulation endpoints."""
|
|
14
|
+
|
|
15
|
+
def __init__(self) -> None:
|
|
16
|
+
"""Initialize the API client with configuration from environment variables."""
|
|
17
|
+
self.base_url = os.getenv("VERIS_API_URL", "https://simulation.api.veris.ai/")
|
|
18
|
+
self.api_key = os.getenv("VERIS_API_KEY")
|
|
19
|
+
self.timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
|
|
20
|
+
|
|
21
|
+
def _build_headers(self) -> dict[str, str] | None:
|
|
22
|
+
"""Build headers including OpenTelemetry tracing and API key."""
|
|
23
|
+
headers: dict[str, str] | None = None
|
|
24
|
+
# Add API key header if available
|
|
25
|
+
if self.api_key:
|
|
26
|
+
if headers is None:
|
|
27
|
+
headers = {}
|
|
28
|
+
headers["x-api-key"] = self.api_key
|
|
29
|
+
|
|
30
|
+
return headers
|
|
31
|
+
|
|
32
|
+
async def post_async(self, endpoint: str, payload: dict[str, Any]) -> Any: # noqa: ANN401
|
|
33
|
+
"""Make an async POST request to the specified endpoint."""
|
|
34
|
+
headers = self._build_headers()
|
|
35
|
+
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
36
|
+
response = await client.post(endpoint, json=payload, headers=headers)
|
|
37
|
+
response.raise_for_status()
|
|
38
|
+
return response.json() if response.content else None
|
|
39
|
+
|
|
40
|
+
def post_sync(self, endpoint: str, payload: dict[str, Any]) -> Any: # noqa: ANN401
|
|
41
|
+
"""Make a synchronous POST request to the specified endpoint."""
|
|
42
|
+
headers = self._build_headers()
|
|
43
|
+
with httpx.Client(timeout=self.timeout) as client:
|
|
44
|
+
response = client.post(endpoint, json=payload, headers=headers)
|
|
45
|
+
response.raise_for_status()
|
|
46
|
+
return response.json() if response.content else None
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def tool_mock_endpoint(self) -> str:
|
|
50
|
+
"""Get the tool mock endpoint URL."""
|
|
51
|
+
return f"{self.base_url}/v2/tool_mock"
|
|
52
|
+
|
|
53
|
+
def get_log_tool_call_endpoint(self, session_id: str) -> str:
|
|
54
|
+
"""Get the log tool call endpoint URL."""
|
|
55
|
+
return f"{self.base_url}/v2/simulations/{session_id}/log_tool_call"
|
|
56
|
+
|
|
57
|
+
def get_log_tool_response_endpoint(self, session_id: str) -> str:
|
|
58
|
+
"""Get the log tool response endpoint URL."""
|
|
59
|
+
return f"{self.base_url}/v2/simulations/{session_id}/log_tool_response"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Global singleton instance
|
|
63
|
+
_api_client = SimulatorAPIClient()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_api_client() -> SimulatorAPIClient:
|
|
67
|
+
"""Get the global API client instance."""
|
|
68
|
+
return _api_client
|
veris_ai/logging.py
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
|
-
import os
|
|
6
5
|
from typing import Any
|
|
7
6
|
|
|
8
|
-
import
|
|
7
|
+
from veris_ai.api_client import get_api_client
|
|
9
8
|
|
|
10
9
|
logger = logging.getLogger(__name__)
|
|
11
10
|
|
|
@@ -17,35 +16,18 @@ async def log_tool_call_async(
|
|
|
17
16
|
docstring: str,
|
|
18
17
|
) -> None:
|
|
19
18
|
"""Log tool call asynchronously to the VERIS logging endpoint."""
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
logger.warning("VERIS_ENDPOINT_URL not set, skipping tool call logging")
|
|
23
|
-
return
|
|
24
|
-
base_url = base_url.rstrip("/")
|
|
19
|
+
api_client = get_api_client()
|
|
20
|
+
endpoint = api_client.get_log_tool_call_endpoint(session_id)
|
|
25
21
|
|
|
26
|
-
endpoint = f"{base_url}/api/v2/simulations/{session_id}/log_tool_call"
|
|
27
22
|
payload = {
|
|
28
23
|
"function_name": function_name,
|
|
29
24
|
"parameters": parameters,
|
|
30
25
|
"docstring": docstring,
|
|
31
26
|
}
|
|
32
27
|
|
|
33
|
-
timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
|
|
34
|
-
|
|
35
28
|
try:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
from opentelemetry.propagate import get_global_textmap
|
|
39
|
-
|
|
40
|
-
headers = {}
|
|
41
|
-
get_global_textmap().inject(headers)
|
|
42
|
-
except Exception: # pragma: no cover - otel optional
|
|
43
|
-
headers = None
|
|
44
|
-
|
|
45
|
-
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
46
|
-
response = await client.post(endpoint, json=payload, headers=headers)
|
|
47
|
-
response.raise_for_status()
|
|
48
|
-
logger.debug(f"Tool call logged for {function_name}")
|
|
29
|
+
await api_client.post_async(endpoint, payload)
|
|
30
|
+
logger.debug(f"Tool call logged for {function_name}")
|
|
49
31
|
except Exception as e:
|
|
50
32
|
logger.warning(f"Failed to log tool call for {function_name}: {e}")
|
|
51
33
|
|
|
@@ -57,97 +39,49 @@ def log_tool_call_sync(
|
|
|
57
39
|
docstring: str,
|
|
58
40
|
) -> None:
|
|
59
41
|
"""Log tool call synchronously to the VERIS logging endpoint."""
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
logger.warning("VERIS_ENDPOINT_URL not set, skipping tool call logging")
|
|
63
|
-
return
|
|
42
|
+
api_client = get_api_client()
|
|
43
|
+
endpoint = api_client.get_log_tool_call_endpoint(session_id)
|
|
64
44
|
|
|
65
|
-
endpoint = f"{base_url}/api/v2/simulations/{session_id}/log_tool_call"
|
|
66
45
|
payload = {
|
|
67
46
|
"function_name": function_name,
|
|
68
47
|
"parameters": parameters,
|
|
69
48
|
"docstring": docstring,
|
|
70
49
|
}
|
|
71
50
|
|
|
72
|
-
timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
|
|
73
|
-
|
|
74
51
|
try:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
from opentelemetry.propagate import get_global_textmap # type: ignore[import-not-found]
|
|
78
|
-
|
|
79
|
-
headers = {}
|
|
80
|
-
get_global_textmap().inject(headers)
|
|
81
|
-
except Exception: # pragma: no cover - otel optional
|
|
82
|
-
headers = None
|
|
83
|
-
|
|
84
|
-
with httpx.Client(timeout=timeout) as client:
|
|
85
|
-
response = client.post(endpoint, json=payload, headers=headers)
|
|
86
|
-
response.raise_for_status()
|
|
87
|
-
logger.debug(f"Tool call logged for {function_name}")
|
|
52
|
+
api_client.post_sync(endpoint, payload)
|
|
53
|
+
logger.debug(f"Tool call logged for {function_name}")
|
|
88
54
|
except Exception as e:
|
|
89
55
|
logger.warning(f"Failed to log tool call for {function_name}: {e}")
|
|
90
56
|
|
|
91
57
|
|
|
92
58
|
async def log_tool_response_async(session_id: str, response: Any) -> None: # noqa: ANN401
|
|
93
59
|
"""Log tool response asynchronously to the VERIS logging endpoint."""
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
logger.warning("VERIS_ENDPOINT_URL not set, skipping tool response logging")
|
|
97
|
-
return
|
|
60
|
+
api_client = get_api_client()
|
|
61
|
+
endpoint = api_client.get_log_tool_response_endpoint(session_id)
|
|
98
62
|
|
|
99
|
-
endpoint = f"{base_url}/api/v2/simulations/{session_id}/log_tool_response"
|
|
100
63
|
payload = {
|
|
101
64
|
"response": json.dumps(response, default=str),
|
|
102
65
|
}
|
|
103
66
|
|
|
104
|
-
timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
|
|
105
|
-
|
|
106
67
|
try:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
from opentelemetry.propagate import get_global_textmap # type: ignore[import-not-found]
|
|
110
|
-
|
|
111
|
-
headers = {}
|
|
112
|
-
get_global_textmap().inject(headers)
|
|
113
|
-
except Exception: # pragma: no cover - otel optional
|
|
114
|
-
headers = None
|
|
115
|
-
|
|
116
|
-
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
117
|
-
http_response = await client.post(endpoint, json=payload, headers=headers)
|
|
118
|
-
http_response.raise_for_status()
|
|
119
|
-
logger.debug("Tool response logged")
|
|
68
|
+
await api_client.post_async(endpoint, payload)
|
|
69
|
+
logger.debug("Tool response logged")
|
|
120
70
|
except Exception as e:
|
|
121
71
|
logger.warning(f"Failed to log tool response: {e}")
|
|
122
72
|
|
|
123
73
|
|
|
124
74
|
def log_tool_response_sync(session_id: str, response: Any) -> None: # noqa: ANN401
|
|
125
75
|
"""Log tool response synchronously to the VERIS logging endpoint."""
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
logger.warning("VERIS_ENDPOINT_URL not set, skipping tool response logging")
|
|
129
|
-
return
|
|
76
|
+
api_client = get_api_client()
|
|
77
|
+
endpoint = api_client.get_log_tool_response_endpoint(session_id)
|
|
130
78
|
|
|
131
|
-
endpoint = f"{base_url}/api/v2/simulations/{session_id}/log_tool_response"
|
|
132
79
|
payload = {
|
|
133
80
|
"response": json.dumps(response, default=str),
|
|
134
81
|
}
|
|
135
82
|
|
|
136
|
-
timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
|
|
137
|
-
|
|
138
83
|
try:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
from opentelemetry.propagate import get_global_textmap # type: ignore[import-not-found]
|
|
142
|
-
|
|
143
|
-
headers = {}
|
|
144
|
-
get_global_textmap().inject(headers)
|
|
145
|
-
except Exception: # pragma: no cover - otel optional
|
|
146
|
-
headers = None
|
|
147
|
-
|
|
148
|
-
with httpx.Client(timeout=timeout) as client:
|
|
149
|
-
http_response = client.post(endpoint, json=payload, headers=headers)
|
|
150
|
-
http_response.raise_for_status()
|
|
151
|
-
logger.debug("Tool response logged")
|
|
84
|
+
api_client.post_sync(endpoint, payload)
|
|
85
|
+
logger.debug("Tool response logged")
|
|
152
86
|
except Exception as e:
|
|
153
87
|
logger.warning(f"Failed to log tool response: {e}")
|
veris_ai/observability.py
CHANGED
|
@@ -7,12 +7,13 @@ enable consistent tracing without duplicating setup code.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
-
import os
|
|
11
|
-
|
|
12
10
|
from fastapi import FastAPI
|
|
11
|
+
from logging import getLogger
|
|
12
|
+
|
|
13
|
+
logger = getLogger(__name__)
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
def init_observability(
|
|
16
|
+
def init_observability() -> None: # noqa: PLR0912
|
|
16
17
|
"""Initialize tracing/export and set W3C propagation.
|
|
17
18
|
|
|
18
19
|
- Initializes Traceloop if available (acts as OTel bootstrap/exporter)
|
|
@@ -22,17 +23,14 @@ def init_observability(service_name: str | None = None) -> None:
|
|
|
22
23
|
|
|
23
24
|
This function is safe to call even if instrumentation packages are not installed.
|
|
24
25
|
"""
|
|
25
|
-
|
|
26
|
-
resolved_service_name = service_name or os.getenv("VERIS_SERVICE_NAME", "veris-service")
|
|
27
|
-
|
|
28
|
-
# Initialize Traceloop SDK first (acts as OTel bootstrap)
|
|
29
26
|
try:
|
|
30
|
-
|
|
27
|
+
import logfire
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
logfire.configure(scrubbing=False)
|
|
30
|
+
logfire.instrument_openai_agents()
|
|
33
31
|
except Exception as e:
|
|
34
32
|
# Tracing is optional; continue without Traceloop
|
|
35
|
-
msg = "
|
|
33
|
+
msg = "Logfire not found: " + str(e)
|
|
36
34
|
raise RuntimeError(msg) from e
|
|
37
35
|
|
|
38
36
|
# Ensure W3C propagation (TraceContext + optional Baggage), tolerant to OTel versions
|
veris_ai/tool_mock.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
|
-
import os
|
|
5
4
|
from collections.abc import Callable
|
|
6
5
|
from contextlib import suppress
|
|
7
6
|
from contextvars import ContextVar
|
|
@@ -13,8 +12,6 @@ from typing import (
|
|
|
13
12
|
get_type_hints,
|
|
14
13
|
)
|
|
15
14
|
|
|
16
|
-
import httpx
|
|
17
|
-
|
|
18
15
|
from veris_ai.logging import (
|
|
19
16
|
log_tool_call_async,
|
|
20
17
|
log_tool_call_sync,
|
|
@@ -22,7 +19,8 @@ from veris_ai.logging import (
|
|
|
22
19
|
log_tool_response_sync,
|
|
23
20
|
)
|
|
24
21
|
from veris_ai.models import ResponseExpectation
|
|
25
|
-
from veris_ai.
|
|
22
|
+
from veris_ai.api_client import get_api_client
|
|
23
|
+
from veris_ai.utils import convert_to_type, extract_json_schema, get_function_parameters
|
|
26
24
|
|
|
27
25
|
logger = logging.getLogger(__name__)
|
|
28
26
|
|
|
@@ -113,27 +111,15 @@ class VerisSDK:
|
|
|
113
111
|
|
|
114
112
|
def create_mock_payload(
|
|
115
113
|
*args: tuple[object, ...],
|
|
116
|
-
**kwargs:
|
|
114
|
+
**kwargs: Any, # noqa: ANN401
|
|
117
115
|
) -> tuple[dict[str, Any], Any]:
|
|
118
116
|
"""Create the mock payload - shared logic for both sync and async."""
|
|
119
|
-
sig = inspect.signature(func)
|
|
120
117
|
type_hints = get_type_hints(func)
|
|
121
118
|
|
|
122
119
|
# Extract return type object (not just the name)
|
|
123
120
|
return_type_obj = type_hints.pop("return", Any)
|
|
124
121
|
# Create parameter info
|
|
125
|
-
|
|
126
|
-
bound_args = sig.bind(*args, **kwargs)
|
|
127
|
-
bound_args.apply_defaults()
|
|
128
|
-
_ = bound_args.arguments.pop("ctx", None)
|
|
129
|
-
_ = bound_args.arguments.pop("self", None)
|
|
130
|
-
_ = bound_args.arguments.pop("cls", None)
|
|
131
|
-
|
|
132
|
-
for param_name, param_value in bound_args.arguments.items():
|
|
133
|
-
params_info[param_name] = {
|
|
134
|
-
"value": str(param_value),
|
|
135
|
-
"type": str(type_hints.get(param_name, Any)),
|
|
136
|
-
}
|
|
122
|
+
parameters = get_function_parameters(func, args, kwargs)
|
|
137
123
|
# Get function docstring
|
|
138
124
|
docstring = inspect.getdoc(func) or ""
|
|
139
125
|
nonlocal expects_response
|
|
@@ -154,7 +140,7 @@ class VerisSDK:
|
|
|
154
140
|
"cache_response": bool(cache_response) if cache_response is not None else False,
|
|
155
141
|
"tool_call": {
|
|
156
142
|
"function_name": func.__name__,
|
|
157
|
-
"parameters":
|
|
143
|
+
"parameters": parameters,
|
|
158
144
|
"return_type": json.dumps(extract_json_schema(return_type_obj)),
|
|
159
145
|
"docstring": docstring,
|
|
160
146
|
},
|
|
@@ -165,7 +151,7 @@ class VerisSDK:
|
|
|
165
151
|
@wraps(func)
|
|
166
152
|
async def async_wrapper(
|
|
167
153
|
*args: tuple[object, ...],
|
|
168
|
-
**kwargs:
|
|
154
|
+
**kwargs: Any, # noqa: ANN401
|
|
169
155
|
) -> object:
|
|
170
156
|
# Check if we're in simulation mode
|
|
171
157
|
if not self.session_id:
|
|
@@ -178,17 +164,12 @@ class VerisSDK:
|
|
|
178
164
|
logger.info(f"Spying on function: {func.__name__}")
|
|
179
165
|
|
|
180
166
|
# Log the tool call
|
|
181
|
-
|
|
182
|
-
bound_args = sig.bind(*args, **kwargs)
|
|
183
|
-
bound_args.apply_defaults()
|
|
184
|
-
_ = bound_args.arguments.pop("ctx", None)
|
|
185
|
-
_ = bound_args.arguments.pop("self", None)
|
|
186
|
-
_ = bound_args.arguments.pop("cls", None)
|
|
167
|
+
parameters = get_function_parameters(func, args, kwargs)
|
|
187
168
|
|
|
188
169
|
await log_tool_call_async(
|
|
189
170
|
session_id=session_id,
|
|
190
171
|
function_name=func.__name__,
|
|
191
|
-
parameters=
|
|
172
|
+
parameters=parameters,
|
|
192
173
|
docstring=inspect.getdoc(func) or "",
|
|
193
174
|
)
|
|
194
175
|
|
|
@@ -201,33 +182,15 @@ class VerisSDK:
|
|
|
201
182
|
return result
|
|
202
183
|
|
|
203
184
|
# Regular mock mode
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
error_msg = "VERIS_ENDPOINT_URL environment variable is not set"
|
|
207
|
-
raise ValueError(error_msg)
|
|
208
|
-
endpoint = f"{base_url.rstrip('/')}/api/v2/tool_mock"
|
|
209
|
-
# Default timeout of 30 seconds
|
|
210
|
-
timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
|
|
185
|
+
api_client = get_api_client()
|
|
186
|
+
endpoint = api_client.tool_mock_endpoint
|
|
211
187
|
|
|
212
188
|
logger.info(f"Simulating function: {func.__name__}")
|
|
213
189
|
payload, return_type_obj = create_mock_payload(*args, **kwargs)
|
|
214
190
|
|
|
215
|
-
# Send request to endpoint
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
try:
|
|
219
|
-
from opentelemetry.propagate import get_global_textmap # type: ignore[import-not-found]
|
|
220
|
-
|
|
221
|
-
headers = {}
|
|
222
|
-
get_global_textmap().inject(headers)
|
|
223
|
-
except Exception: # pragma: no cover - otel optional
|
|
224
|
-
headers = None
|
|
225
|
-
|
|
226
|
-
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
227
|
-
response = await client.post(endpoint, json=payload, headers=headers)
|
|
228
|
-
response.raise_for_status()
|
|
229
|
-
mock_result = response.json()
|
|
230
|
-
logger.info(f"Mock response: {mock_result}")
|
|
191
|
+
# Send request to endpoint
|
|
192
|
+
mock_result = await api_client.post_async(endpoint, payload)
|
|
193
|
+
logger.info(f"Mock response: {mock_result}")
|
|
231
194
|
|
|
232
195
|
if isinstance(mock_result, str):
|
|
233
196
|
with suppress(json.JSONDecodeError):
|
|
@@ -252,7 +215,7 @@ class VerisSDK:
|
|
|
252
215
|
@wraps(func)
|
|
253
216
|
def sync_wrapper(
|
|
254
217
|
*args: tuple[object, ...],
|
|
255
|
-
**kwargs:
|
|
218
|
+
**kwargs: Any, # noqa: ANN401
|
|
256
219
|
) -> object:
|
|
257
220
|
# Check if we're in simulation mode
|
|
258
221
|
if not self.session_id:
|
|
@@ -265,17 +228,12 @@ class VerisSDK:
|
|
|
265
228
|
logger.info(f"Spying on function: {func.__name__}")
|
|
266
229
|
|
|
267
230
|
# Log the tool call
|
|
268
|
-
|
|
269
|
-
bound_args = sig.bind(*args, **kwargs)
|
|
270
|
-
bound_args.apply_defaults()
|
|
271
|
-
_ = bound_args.arguments.pop("ctx", None)
|
|
272
|
-
_ = bound_args.arguments.pop("self", None)
|
|
273
|
-
_ = bound_args.arguments.pop("cls", None)
|
|
231
|
+
parameters = get_function_parameters(func, args, kwargs)
|
|
274
232
|
|
|
275
233
|
log_tool_call_sync(
|
|
276
234
|
session_id=session_id,
|
|
277
235
|
function_name=func.__name__,
|
|
278
|
-
parameters=
|
|
236
|
+
parameters=parameters,
|
|
279
237
|
docstring=inspect.getdoc(func) or "",
|
|
280
238
|
)
|
|
281
239
|
|
|
@@ -288,33 +246,15 @@ class VerisSDK:
|
|
|
288
246
|
return result
|
|
289
247
|
|
|
290
248
|
# Regular mock mode
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
error_msg = "VERIS_ENDPOINT_URL environment variable is not set"
|
|
294
|
-
raise ValueError(error_msg)
|
|
295
|
-
endpoint = f"{base_url.rstrip('/')}/api/v2/tool_mock"
|
|
296
|
-
# Default timeout of 30 seconds
|
|
297
|
-
timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
|
|
249
|
+
api_client = get_api_client()
|
|
250
|
+
endpoint = api_client.tool_mock_endpoint
|
|
298
251
|
|
|
299
252
|
logger.info(f"Simulating function: {func.__name__}")
|
|
300
253
|
payload, return_type_obj = create_mock_payload(*args, **kwargs)
|
|
301
254
|
|
|
302
|
-
# Send request to endpoint
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
try:
|
|
306
|
-
from opentelemetry.propagate import get_global_textmap # type: ignore[import-not-found]
|
|
307
|
-
|
|
308
|
-
headers = {}
|
|
309
|
-
get_global_textmap().inject(headers)
|
|
310
|
-
except Exception: # pragma: no cover - otel optional
|
|
311
|
-
headers = None
|
|
312
|
-
|
|
313
|
-
with httpx.Client(timeout=timeout) as client:
|
|
314
|
-
response = client.post(endpoint, json=payload, headers=headers)
|
|
315
|
-
response.raise_for_status()
|
|
316
|
-
mock_result = response.json()
|
|
317
|
-
logger.info(f"Mock response: {mock_result}")
|
|
255
|
+
# Send request to endpoint
|
|
256
|
+
mock_result = api_client.post_sync(endpoint, payload)
|
|
257
|
+
logger.info(f"Mock response: {mock_result}")
|
|
318
258
|
|
|
319
259
|
if isinstance(mock_result, str):
|
|
320
260
|
with suppress(json.JSONDecodeError):
|
|
@@ -351,7 +291,7 @@ class VerisSDK:
|
|
|
351
291
|
@wraps(func)
|
|
352
292
|
async def async_wrapper(
|
|
353
293
|
*args: tuple[object, ...],
|
|
354
|
-
**kwargs:
|
|
294
|
+
**kwargs: Any, # noqa: ANN401
|
|
355
295
|
) -> object:
|
|
356
296
|
if not self.session_id:
|
|
357
297
|
# If not in simulation mode, execute the original function
|
|
@@ -360,7 +300,7 @@ class VerisSDK:
|
|
|
360
300
|
return return_value
|
|
361
301
|
|
|
362
302
|
@wraps(func)
|
|
363
|
-
def sync_wrapper(*args: tuple[object, ...], **kwargs:
|
|
303
|
+
def sync_wrapper(*args: tuple[object, ...], **kwargs: Any) -> object: # noqa: ANN401
|
|
364
304
|
if not self.session_id:
|
|
365
305
|
# If not in simulation mode, execute the original function
|
|
366
306
|
return func(*args, **kwargs)
|
veris_ai/utils.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import inspect
|
|
1
2
|
import sys
|
|
2
3
|
import types
|
|
3
4
|
import typing
|
|
4
5
|
from contextlib import suppress
|
|
5
6
|
from typing import Any, ForwardRef, Literal, NotRequired, Required, Union, get_args, get_origin
|
|
7
|
+
from collections.abc import Callable
|
|
6
8
|
|
|
7
9
|
from pydantic import BaseModel
|
|
8
10
|
|
|
@@ -272,3 +274,16 @@ def extract_json_schema(target_type: Any) -> dict: # noqa: PLR0911, PLR0912, C9
|
|
|
272
274
|
|
|
273
275
|
# Default case for unknown types
|
|
274
276
|
return {"type": "object"}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def get_function_parameters(
|
|
280
|
+
func: Callable, args: tuple[object, ...], kwargs: dict[str, object]
|
|
281
|
+
) -> dict[str, object]:
|
|
282
|
+
"""Get the parameters for a function."""
|
|
283
|
+
sig = inspect.signature(func)
|
|
284
|
+
bound_args = sig.bind(*args, **kwargs)
|
|
285
|
+
bound_args.apply_defaults()
|
|
286
|
+
_ = bound_args.arguments.pop("ctx", None)
|
|
287
|
+
_ = bound_args.arguments.pop("self", None)
|
|
288
|
+
_ = bound_args.arguments.pop("cls", None)
|
|
289
|
+
return bound_args.arguments
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: veris-ai
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.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
|
|
@@ -9,6 +9,7 @@ License-Expression: MIT
|
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Requires-Python: >=3.11
|
|
11
11
|
Requires-Dist: httpx>=0.24.0
|
|
12
|
+
Requires-Dist: logfire>=4.3.3
|
|
12
13
|
Requires-Dist: opentelemetry-api>=1.34.1
|
|
13
14
|
Requires-Dist: opentelemetry-exporter-otlp>=1.34.1
|
|
14
15
|
Requires-Dist: opentelemetry-instrumentation-fastapi>=0.55b1
|
|
@@ -19,11 +20,12 @@ Requires-Dist: opentelemetry-instrumentation>=0.55b1
|
|
|
19
20
|
Requires-Dist: opentelemetry-sdk>=1.34.1
|
|
20
21
|
Requires-Dist: pydantic>=2.0.0
|
|
21
22
|
Requires-Dist: requests>=2.31.0
|
|
22
|
-
|
|
23
|
+
Provides-Extra: agents
|
|
24
|
+
Requires-Dist: openai-agents>=0.0.1; extra == 'agents'
|
|
23
25
|
Provides-Extra: dev
|
|
24
26
|
Requires-Dist: black>=23.7.0; extra == 'dev'
|
|
25
27
|
Requires-Dist: mypy>=1.5.1; extra == 'dev'
|
|
26
|
-
Requires-Dist: openai-agents>=0.
|
|
28
|
+
Requires-Dist: openai-agents>=0.2.5; extra == 'dev'
|
|
27
29
|
Requires-Dist: pre-commit>=3.3.3; extra == 'dev'
|
|
28
30
|
Requires-Dist: pytest-asyncio>=0.21.1; extra == 'dev'
|
|
29
31
|
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
@@ -45,7 +47,7 @@ A Python package for Veris AI tools with simulation capabilities and FastAPI MCP
|
|
|
45
47
|
## Quick Reference
|
|
46
48
|
|
|
47
49
|
**Purpose**: Tool mocking, tracing, and FastAPI MCP integration for AI agent development
|
|
48
|
-
**Core Components**: [`tool_mock`](#function-mocking) • [`observability`](#sdk-observability-helpers) • [`fastapi_mcp`](#fastapi-mcp-integration) • [`jaeger_interface`](#jaeger-trace-interface)
|
|
50
|
+
**Core Components**: [`tool_mock`](#function-mocking) • [`api_client`](src/veris_ai/api_client.py) • [`observability`](#sdk-observability-helpers) • [`fastapi_mcp`](#fastapi-mcp-integration) • [`jaeger_interface`](#jaeger-trace-interface)
|
|
49
51
|
**Deep Dive**: [`Module Architecture`](src/veris_ai/README.md) • [`Testing Guide`](tests/README.md) • [`Usage Examples`](examples/README.md)
|
|
50
52
|
**Source of Truth**: Implementation details in [`src/veris_ai/`](src/veris_ai/) source code
|
|
51
53
|
|
|
@@ -84,13 +86,14 @@ from veris_ai import init_observability, instrument_fastapi_app # Provided by S
|
|
|
84
86
|
|
|
85
87
|
| Variable | Purpose | Default |
|
|
86
88
|
|----------|---------|---------|
|
|
87
|
-
| `
|
|
89
|
+
| `VERIS_API_KEY` | API authentication key | None |
|
|
88
90
|
| `VERIS_MOCK_TIMEOUT` | Request timeout (seconds) | `90.0` |
|
|
89
91
|
| `ENV` | Set to `"simulation"` for mock mode | Production |
|
|
90
|
-
| `VERIS_SERVICE_NAME` | Tracing service name | Auto-detected |
|
|
91
|
-
| `VERIS_OTLP_ENDPOINT` | OpenTelemetry collector | *Required for tracing* |
|
|
92
92
|
|
|
93
|
-
**Configuration
|
|
93
|
+
**Advanced Configuration** (rarely needed):
|
|
94
|
+
- `VERIS_API_URL`: Override default API endpoint (defaults to production)
|
|
95
|
+
|
|
96
|
+
**Configuration Details**: See [`src/veris_ai/api_client.py`](src/veris_ai/api_client.py) for API configuration and [`src/veris_ai/tool_mock.py`](src/veris_ai/tool_mock.py) for environment handling logic.
|
|
94
97
|
|
|
95
98
|
|
|
96
99
|
### SDK Observability Helpers
|
|
@@ -102,7 +105,7 @@ from fastapi import FastAPI
|
|
|
102
105
|
from veris_ai import init_observability, instrument_fastapi_app
|
|
103
106
|
|
|
104
107
|
# Initialize tracing/export early (no-op if dependencies are absent)
|
|
105
|
-
init_observability(
|
|
108
|
+
init_observability()
|
|
106
109
|
|
|
107
110
|
app = FastAPI()
|
|
108
111
|
|
|
@@ -110,6 +113,35 @@ app = FastAPI()
|
|
|
110
113
|
instrument_fastapi_app(app)
|
|
111
114
|
```
|
|
112
115
|
|
|
116
|
+
#### Observability Environment
|
|
117
|
+
|
|
118
|
+
Set these environment variables to enable exporting traces via OTLP (Logfire) and ensure consistent service naming:
|
|
119
|
+
|
|
120
|
+
| Variable | Example | Notes |
|
|
121
|
+
|----------|---------|-------|
|
|
122
|
+
| `OTEL_SERVICE_NAME` | `simulation-server` | Should match `VERIS_SERVICE_NAME` used elsewhere to keep traces aligned |
|
|
123
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | `https://logfire-api.pydantic.dev` | OTLP HTTP endpoint |
|
|
124
|
+
| `LOGFIRE_TOKEN` | `FILL_IN` | Logfire API token used by the exporter |
|
|
125
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | `'Authorization=FILL_IN'` | Include quotes to preserve the `=`; often `Authorization=Bearer <LOGFIRE_TOKEN>` |
|
|
126
|
+
|
|
127
|
+
Quick setup example:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
export OTEL_SERVICE_NAME="simulation-server"
|
|
131
|
+
export OTEL_EXPORTER_OTLP_ENDPOINT="https://logfire-api.pydantic.dev"
|
|
132
|
+
export LOGFIRE_TOKEN="<your-token>"
|
|
133
|
+
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=${LOGFIRE_TOKEN}"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Then initialize in code early in your process:
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from veris_ai import init_observability, instrument_fastapi_app
|
|
140
|
+
init_observability()
|
|
141
|
+
app = FastAPI()
|
|
142
|
+
instrument_fastapi_app(app)
|
|
143
|
+
```
|
|
144
|
+
|
|
113
145
|
What this enables:
|
|
114
146
|
- Sets global W3C propagator (TraceContext + Baggage)
|
|
115
147
|
- Optionally instruments FastAPI, requests, httpx, MCP client if installed
|
|
@@ -222,7 +254,7 @@ pytest --cov=veris_ai # Test with coverage
|
|
|
222
254
|
|
|
223
255
|
**Semantic Tag**: `module-architecture`
|
|
224
256
|
|
|
225
|
-
**Core Modules**: `tool_mock` (mocking), `jaeger_interface` (trace queries), `utils` (schema conversion)
|
|
257
|
+
**Core Modules**: `tool_mock` (mocking), `api_client` (centralized API), `jaeger_interface` (trace queries), `utils` (schema conversion)
|
|
226
258
|
|
|
227
259
|
**Complete Architecture**: See [`src/veris_ai/README.md`](src/veris_ai/README.md) for module overview, implementation flows, and configuration details.
|
|
228
260
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
veris_ai/README.md,sha256=0EC-yWB8FtqAhtcE5Q204bS1JWJPIvfpp1bPDo6iwwc,3601
|
|
2
|
+
veris_ai/__init__.py,sha256=mFEF2pHXOAXoXuBh9IOQgqpKGvzLOsy8b0vUpMRJggU,2260
|
|
3
|
+
veris_ai/agents_wrapper.py,sha256=-8R2yTkEQjHQJxkWBK5tqMHzmlPjfDpybD9_UpsSjLk,9971
|
|
4
|
+
veris_ai/api_client.py,sha256=afvVvg9DXSHgnr0p411yXo3_8UM7EeiIoqm1q_goYgI,2602
|
|
5
|
+
veris_ai/logging.py,sha256=Dr7dQtTK_WvUAtSpif79Srf5YpSLwDVlWs0XsQGOdu4,2595
|
|
6
|
+
veris_ai/models.py,sha256=6HINPxNFCakCVPcyEbUswWkXwb2K4lF0A8g8EvTMal4,213
|
|
7
|
+
veris_ai/observability.py,sha256=fDtWeUexfzaIQArE5YbWsja8Y-bcE_h0dXQWYbXbupY,4929
|
|
8
|
+
veris_ai/tool_mock.py,sha256=77KxBy0mAxC58sXYs_jPHBT_2oEkXybsUGKzOOLH93w,13334
|
|
9
|
+
veris_ai/utils.py,sha256=bSutoMYQAboo9nHZ4yAsOGIDEoc1zaoIXCoJr4yTvBw,9771
|
|
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.8.0.dist-info/METADATA,sha256=r79b39D4sUnlT2sJWNxbMk2Y1ES4pRVLkaO0K7BYsDc,9935
|
|
15
|
+
veris_ai-1.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
+
veris_ai-1.8.0.dist-info/licenses/LICENSE,sha256=2g4i20atAgtD5einaKzhQrIB-JrPhyQgD3bC0wkHcCI,1065
|
|
17
|
+
veris_ai-1.8.0.dist-info/RECORD,,
|
veris_ai-1.7.0.dist-info/RECORD
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
veris_ai/README.md,sha256=iMSwSIrBO2zEdUhImkhZucUnTNO0kSQ-_zxWjZCmp4I,2730
|
|
2
|
-
veris_ai/__init__.py,sha256=Gh52-XfFpsxX37uqp8vuX0V3Np7f-Rlf5k3MMANu6e0,425
|
|
3
|
-
veris_ai/logging.py,sha256=srEdVimcQSCsnwGhyzCydehD2JW1cQmwSRd3X20NQs0,5233
|
|
4
|
-
veris_ai/models.py,sha256=6HINPxNFCakCVPcyEbUswWkXwb2K4lF0A8g8EvTMal4,213
|
|
5
|
-
veris_ai/observability.py,sha256=fG5ixAiVDykLCdHUScKQNCsePLphKQ7PcaKkAGBVdF0,5113
|
|
6
|
-
veris_ai/tool_mock.py,sha256=C1BANT3LCMKOIwJm_nOFuNdHnaO9_tbjeDTtBcI41RI,16512
|
|
7
|
-
veris_ai/utils.py,sha256=aqFFNuNiBehil6874nOHtU7G_bWHbFpVuubcz2AIx6I,9267
|
|
8
|
-
veris_ai/jaeger_interface/README.md,sha256=kd9rKcE5xf3EyNaiHu0tjn-0oES9sfaK6Ih-OhhTyCM,2821
|
|
9
|
-
veris_ai/jaeger_interface/__init__.py,sha256=KD7NSiMYRG_2uF6dOLKkGG5lNQe4K9ptEwucwMT4_aw,1128
|
|
10
|
-
veris_ai/jaeger_interface/client.py,sha256=yJrh86wRR0Dk3Gq12DId99WogcMIVbL0QQFqVSevvlE,8772
|
|
11
|
-
veris_ai/jaeger_interface/models.py,sha256=e64VV6IvOEFuzRUgvDAMQFyOZMRb56I-PUPZLBZ3rX0,1864
|
|
12
|
-
veris_ai-1.7.0.dist-info/METADATA,sha256=dmWFBpwU2MK9tjnJtlrkQdqnR9C0yn0Lhb1TMWb-XvA,8679
|
|
13
|
-
veris_ai-1.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
-
veris_ai-1.7.0.dist-info/licenses/LICENSE,sha256=2g4i20atAgtD5einaKzhQrIB-JrPhyQgD3bC0wkHcCI,1065
|
|
15
|
-
veris_ai-1.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|