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 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
- - `VERIS_SERVICE_NAME`: Tracing service identifier
53
- - `VERIS_OTLP_ENDPOINT`: OpenTelemetry collector endpoint
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 httpx
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
- base_url = os.getenv("VERIS_ENDPOINT_URL")
21
- if not base_url:
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
- headers: dict[str, str] | None = None
37
- try:
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
- base_url = os.getenv("VERIS_ENDPOINT_URL")
61
- if not base_url:
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
- headers: dict[str, str] | None = None
76
- try:
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
- base_url = os.getenv("VERIS_ENDPOINT_URL")
95
- if not base_url:
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
- headers: dict[str, str] | None = None
108
- try:
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
- base_url = os.getenv("VERIS_ENDPOINT_URL")
127
- if not base_url:
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
- headers: dict[str, str] | None = None
140
- try:
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(service_name: str | None = None) -> None:
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
- from traceloop.sdk import Traceloop # type: ignore[import-not-found, import-untyped]
27
+ import logfire
31
28
 
32
- Traceloop.init(app_name=resolved_service_name, disable_batch=True)
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 = "Traceloop not found: " + str(e)
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.utils import convert_to_type, extract_json_schema
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: dict[str, object],
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
- params_info = {}
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": params_info,
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: dict[str, object],
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
- sig = inspect.signature(func)
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=bound_args.arguments,
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
- base_url = os.getenv("VERIS_ENDPOINT_URL")
205
- if not base_url:
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 with timeout
216
- # Inject current trace context headers if OpenTelemetry is available
217
- headers: dict[str, str] | None = None
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: dict[str, object],
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
- sig = inspect.signature(func)
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=bound_args.arguments,
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
- base_url = os.getenv("VERIS_ENDPOINT_URL")
292
- if not base_url:
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 with timeout (synchronous)
303
- # Inject current trace context headers if OpenTelemetry is available
304
- headers: dict[str, str] | None = None
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: dict[str, object],
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: dict[str, object]) -> object:
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.7.0
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
- Requires-Dist: traceloop-sdk>=0.45.4
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.0.1; extra == 'dev'
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
- | `VERIS_ENDPOINT_URL` | Mock server endpoint | *Required* |
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 Details**: See [`src/veris_ai/tool_mock.py`](src/veris_ai/tool_mock.py) for environment handling logic.
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(service_name="my-customer-service")
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,,
@@ -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,,