veris-ai 1.5.0__py3-none-any.whl → 1.7.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
@@ -15,7 +15,6 @@ This module contains the core implementation of the Veris AI Python SDK. Each co
15
15
  | Module | Purpose | Key Classes/Functions | Lines |
16
16
  |--------|---------|----------------------|-------|
17
17
  | [`tool_mock.py`](tool_mock.py) | Function mocking & FastAPI MCP | `VerisSDK`, `@mock`, `@stub` | 327 |
18
- | [`braintrust_tracing.py`](braintrust_tracing.py) | Dual tracing instrumentation | `instrument()` | 283 |
19
18
  | [`utils.py`](utils.py) | Type utilities & JSON schema | `extract_json_schema()` | 272 |
20
19
  | [`logging.py`](logging.py) | Logging configuration | `setup_logging()` | 116 |
21
20
  | [`models.py`](models.py) | Data models | Type definitions | 12 |
@@ -40,12 +39,6 @@ This module contains the core implementation of the Veris AI Python SDK. Each co
40
39
 
41
40
  **Implementation**: [`tool_mock.py:250-300`](tool_mock.py)
42
41
 
43
- ### Tracing Flow
44
- 1. **Dual Setup**: Braintrust + OpenTelemetry instrumentation
45
- 2. **Session Tagging**: Bearer tokens → session IDs
46
- 3. **Span Attribution**: All operations tagged with `veris.session_id`
47
-
48
- **Implementation**: [`braintrust_tracing.py:50-150`](braintrust_tracing.py)
49
42
 
50
43
  ## Configuration
51
44
 
veris_ai/__init__.py CHANGED
@@ -1,37 +1,17 @@
1
1
  """Veris AI Python SDK."""
2
2
 
3
- from typing import Any
4
-
5
3
  __version__ = "0.1.0"
6
4
 
7
5
  # Import lightweight modules that only use base dependencies
8
6
  from .jaeger_interface import JaegerClient
9
7
  from .models import ResponseExpectation
10
8
  from .tool_mock import veris
11
-
12
- # Lazy import for modules with heavy dependencies
13
- _instrument = None
14
-
15
-
16
- def instrument(*args: Any, **kwargs: Any) -> Any: # noqa: ANN401
17
- """Lazy loader for the instrument function from braintrust_tracing.
18
-
19
- This function requires the 'instrument' extra dependencies:
20
- pip install veris-ai[instrument]
21
- """
22
- global _instrument # noqa: PLW0603
23
- if _instrument is None:
24
- try:
25
- from .braintrust_tracing import instrument as _instrument_impl # noqa: PLC0415
26
-
27
- _instrument = _instrument_impl
28
- except ImportError as e:
29
- error_msg = (
30
- "The 'instrument' function requires additional dependencies. "
31
- "Please install them with: pip install veris-ai[instrument]"
32
- )
33
- raise ImportError(error_msg) from e
34
- return _instrument(*args, **kwargs)
35
-
36
-
37
- __all__ = ["veris", "JaegerClient", "instrument", "ResponseExpectation"]
9
+ from .observability import init_observability, instrument_fastapi_app
10
+
11
+ __all__ = [
12
+ "veris",
13
+ "JaegerClient",
14
+ "ResponseExpectation",
15
+ "init_observability",
16
+ "instrument_fastapi_app",
17
+ ]
@@ -34,6 +34,6 @@ are fully typed using *pydantic* models so that IDEs can provide proper
34
34
  autocomplete and type checking.
35
35
  """
36
36
 
37
- from .client import JaegerClient as JaegerClient # noqa: F401
37
+ from .client import JaegerClient
38
38
 
39
39
  __all__ = ["JaegerClient"]
veris_ai/logging.py CHANGED
@@ -21,6 +21,7 @@ async def log_tool_call_async(
21
21
  if not base_url:
22
22
  logger.warning("VERIS_ENDPOINT_URL not set, skipping tool call logging")
23
23
  return
24
+ base_url = base_url.rstrip("/")
24
25
 
25
26
  endpoint = f"{base_url}/api/v2/simulations/{session_id}/log_tool_call"
26
27
  payload = {
@@ -32,8 +33,17 @@ async def log_tool_call_async(
32
33
  timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
33
34
 
34
35
  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
+
35
45
  async with httpx.AsyncClient(timeout=timeout) as client:
36
- response = await client.post(endpoint, json=payload)
46
+ response = await client.post(endpoint, json=payload, headers=headers)
37
47
  response.raise_for_status()
38
48
  logger.debug(f"Tool call logged for {function_name}")
39
49
  except Exception as e:
@@ -62,15 +72,24 @@ def log_tool_call_sync(
62
72
  timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
63
73
 
64
74
  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
+
65
84
  with httpx.Client(timeout=timeout) as client:
66
- response = client.post(endpoint, json=payload)
85
+ response = client.post(endpoint, json=payload, headers=headers)
67
86
  response.raise_for_status()
68
87
  logger.debug(f"Tool call logged for {function_name}")
69
88
  except Exception as e:
70
89
  logger.warning(f"Failed to log tool call for {function_name}: {e}")
71
90
 
72
91
 
73
- async def log_tool_response_async(session_id: str, response: object) -> None:
92
+ async def log_tool_response_async(session_id: str, response: Any) -> None: # noqa: ANN401
74
93
  """Log tool response asynchronously to the VERIS logging endpoint."""
75
94
  base_url = os.getenv("VERIS_ENDPOINT_URL")
76
95
  if not base_url:
@@ -85,15 +104,24 @@ async def log_tool_response_async(session_id: str, response: object) -> None:
85
104
  timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
86
105
 
87
106
  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
+
88
116
  async with httpx.AsyncClient(timeout=timeout) as client:
89
- http_response = await client.post(endpoint, json=payload)
117
+ http_response = await client.post(endpoint, json=payload, headers=headers)
90
118
  http_response.raise_for_status()
91
119
  logger.debug("Tool response logged")
92
120
  except Exception as e:
93
121
  logger.warning(f"Failed to log tool response: {e}")
94
122
 
95
123
 
96
- def log_tool_response_sync(session_id: str, response: object) -> None:
124
+ def log_tool_response_sync(session_id: str, response: Any) -> None: # noqa: ANN401
97
125
  """Log tool response synchronously to the VERIS logging endpoint."""
98
126
  base_url = os.getenv("VERIS_ENDPOINT_URL")
99
127
  if not base_url:
@@ -108,8 +136,17 @@ def log_tool_response_sync(session_id: str, response: object) -> None:
108
136
  timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
109
137
 
110
138
  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
+
111
148
  with httpx.Client(timeout=timeout) as client:
112
- http_response = client.post(endpoint, json=payload)
149
+ http_response = client.post(endpoint, json=payload, headers=headers)
113
150
  http_response.raise_for_status()
114
151
  logger.debug("Tool response logged")
115
152
  except Exception as e:
@@ -0,0 +1,133 @@
1
+ """Observability helpers for services using veris-ai.
2
+
3
+ Provides optional-safe initialization for OpenTelemetry propagation/export and
4
+ client/server instrumentation. Services can import and call these helpers to
5
+ enable consistent tracing without duplicating setup code.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+
12
+ from fastapi import FastAPI
13
+
14
+
15
+ def init_observability(service_name: str | None = None) -> None:
16
+ """Initialize tracing/export and set W3C propagation.
17
+
18
+ - Initializes Traceloop if available (acts as OTel bootstrap/exporter)
19
+ - Sets global propagator to TraceContext + Baggage (W3C)
20
+ - Instruments MCP, requests, httpx if instrumentation packages are present
21
+ - Adds a request hook to capture outbound traceparent for debugging
22
+
23
+ This function is safe to call even if instrumentation packages are not installed.
24
+ """
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
+ try:
30
+ from traceloop.sdk import Traceloop # type: ignore[import-not-found, import-untyped]
31
+
32
+ Traceloop.init(app_name=resolved_service_name, disable_batch=True)
33
+ except Exception as e:
34
+ # Tracing is optional; continue without Traceloop
35
+ msg = "Traceloop not found: " + str(e)
36
+ raise RuntimeError(msg) from e
37
+
38
+ # Ensure W3C propagation (TraceContext + optional Baggage), tolerant to OTel versions
39
+ try:
40
+ from opentelemetry.propagate import set_global_textmap
41
+ from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
42
+ from collections.abc import Mapping # noqa: TC003
43
+ from opentelemetry.trace import Span
44
+ from requests import PreparedRequest
45
+ import httpx
46
+
47
+ # Import baggage propagator
48
+ baggage = None
49
+ try:
50
+ from opentelemetry.baggage.propagation import W3CBaggagePropagator
51
+
52
+ baggage = W3CBaggagePropagator()
53
+ except Exception as e:
54
+ msg = "OpenTelemetry not found: " + str(e)
55
+ raise RuntimeError(msg) from e
56
+
57
+ # Import composite propagator
58
+ try:
59
+ from opentelemetry.propagators.composite import CompositeHTTPPropagator
60
+
61
+ propagators: list[W3CBaggagePropagator | TraceContextTextMapPropagator] = [
62
+ TraceContextTextMapPropagator()
63
+ ]
64
+ if baggage:
65
+ propagators.append(baggage)
66
+ set_global_textmap(CompositeHTTPPropagator(propagators))
67
+ except Exception as e:
68
+ msg = "OpenTelemetry not found: " + str(e)
69
+ raise RuntimeError(msg) from e
70
+ except Exception as e:
71
+ # OpenTelemetry not installed or incompatible; continue without changing global propagator
72
+ msg = "OpenTelemetry not found: " + str(e)
73
+ raise RuntimeError(msg) from e
74
+
75
+ # Instrument HTTP clients and capture outbound traceparent for debugging
76
+ def _log_request_headers(
77
+ span: Span, request: PreparedRequest | httpx.Request | dict[str, Mapping[str, str]]
78
+ ) -> None:
79
+ try:
80
+ traceparent = None
81
+ if hasattr(request, "headers"):
82
+ traceparent = request.headers.get("traceparent")
83
+ elif isinstance(request, dict):
84
+ traceparent = request.get("headers", {}).get("traceparent")
85
+ if traceparent:
86
+ span.set_attribute("debug.traceparent", traceparent)
87
+ except Exception as e:
88
+ msg = "OpenTelemetry not found: " + str(e)
89
+ raise RuntimeError(msg) from e
90
+
91
+ try:
92
+ from opentelemetry.instrumentation.requests import (
93
+ RequestsInstrumentor, # type: ignore[import-not-found]
94
+ )
95
+
96
+ RequestsInstrumentor().instrument(request_hook=_log_request_headers)
97
+ except Exception as e:
98
+ msg = "OpenTelemetry not found: " + str(e)
99
+ raise RuntimeError(msg) from e
100
+
101
+ try:
102
+ from opentelemetry.instrumentation.httpx import (
103
+ HTTPXClientInstrumentor, # type: ignore[import-not-found]
104
+ )
105
+
106
+ HTTPXClientInstrumentor().instrument(request_hook=_log_request_headers)
107
+ except Exception as e:
108
+ msg = "OpenTelemetry not found: " + str(e)
109
+ raise RuntimeError(msg) from e
110
+
111
+ # Optionally enable MCP-specific spans
112
+ try:
113
+ from opentelemetry.instrumentation.mcp import McpInstrumentor # type: ignore[import-not-found]
114
+
115
+ McpInstrumentor().instrument()
116
+ except Exception as e:
117
+ msg = "OpenTelemetry not found: " + str(e)
118
+ raise RuntimeError(msg) from e
119
+
120
+
121
+ def instrument_fastapi_app(app: FastAPI) -> None:
122
+ """Instrument a FastAPI app so inbound HTTP requests continue W3C traces.
123
+
124
+ Safe to call even if the fastapi instrumentation package is not installed.
125
+ """
126
+
127
+ try:
128
+ from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor # type: ignore[import-not-found]
129
+
130
+ FastAPIInstrumentor.instrument_app(app)
131
+ except Exception as e:
132
+ msg = "OpenTelemetry not found: " + str(e)
133
+ raise RuntimeError(msg) from e
veris_ai/tool_mock.py CHANGED
@@ -172,57 +172,82 @@ class VerisSDK:
172
172
  # If not in simulation mode, execute the original function
173
173
  return await func(*args, **kwargs)
174
174
 
175
- # Handle spy mode - execute original function and log
176
- if mode == "spy":
177
- logger.info(f"Spying on function: {func.__name__}")
178
-
179
- # Log the tool call
180
- sig = inspect.signature(func)
181
- bound_args = sig.bind(*args, **kwargs)
182
- bound_args.apply_defaults()
183
- _ = bound_args.arguments.pop("ctx", None)
184
- _ = bound_args.arguments.pop("self", None)
185
- _ = bound_args.arguments.pop("cls", None)
186
-
187
- await log_tool_call_async(
188
- session_id=self.session_id,
189
- function_name=func.__name__,
190
- parameters=bound_args.arguments,
191
- docstring=inspect.getdoc(func) or "",
192
- )
193
-
194
- # Execute the original function
195
- result = await func(*args, **kwargs)
196
-
197
- # Log the response
198
- await log_tool_response_async(session_id=self.session_id, response=result)
199
-
200
- return result
201
-
202
- # Regular mock mode
203
- base_url = os.getenv("VERIS_ENDPOINT_URL")
204
- if not base_url:
205
- error_msg = "VERIS_ENDPOINT_URL environment variable is not set"
206
- raise ValueError(error_msg)
207
- endpoint = f"{base_url.rstrip('/')}/api/v2/tool_mock"
208
- # Default timeout of 30 seconds
209
- timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
210
-
211
- logger.info(f"Simulating function: {func.__name__}")
212
- payload, return_type_obj = create_mock_payload(*args, **kwargs)
213
-
214
- # Send request to endpoint with timeout
215
- async with httpx.AsyncClient(timeout=timeout) as client:
216
- response = await client.post(endpoint, json=payload)
217
- response.raise_for_status()
218
- mock_result = response.json()
219
- logger.info(f"Mock response: {mock_result}")
220
-
221
- if isinstance(mock_result, str):
222
- with suppress(json.JSONDecodeError):
223
- mock_result = json.loads(mock_result)
224
- return convert_to_type(mock_result, return_type_obj)
225
- return convert_to_type(mock_result, return_type_obj)
175
+ async def _execute_mock_logic(session_id: str) -> object:
176
+ # Handle spy mode - execute original function and log
177
+ if mode == "spy":
178
+ logger.info(f"Spying on function: {func.__name__}")
179
+
180
+ # 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)
187
+
188
+ await log_tool_call_async(
189
+ session_id=session_id,
190
+ function_name=func.__name__,
191
+ parameters=bound_args.arguments,
192
+ docstring=inspect.getdoc(func) or "",
193
+ )
194
+
195
+ # Execute the original function
196
+ result = await func(*args, **kwargs)
197
+
198
+ # Log the response
199
+ await log_tool_response_async(session_id=session_id, response=result)
200
+
201
+ return result
202
+
203
+ # 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"))
211
+
212
+ logger.info(f"Simulating function: {func.__name__}")
213
+ payload, return_type_obj = create_mock_payload(*args, **kwargs)
214
+
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}")
231
+
232
+ if isinstance(mock_result, str):
233
+ with suppress(json.JSONDecodeError):
234
+ mock_result = json.loads(mock_result)
235
+ return convert_to_type(mock_result, return_type_obj)
236
+ return convert_to_type(mock_result, return_type_obj)
237
+
238
+ # Create a top-level span for the simulated mock call if OpenTelemetry is available
239
+ try:
240
+ from opentelemetry import trace # type: ignore[import-not-found]
241
+
242
+ tracer = trace.get_tracer("veris_ai.tool_mock")
243
+ span_name = f"mock.{func.__name__}"
244
+ with tracer.start_as_current_span(span_name) as span: # type: ignore[attr-defined]
245
+ span.set_attribute("veris_ai.session.id", self.session_id or "") # type: ignore[attr-defined]
246
+ span.set_attribute("veris_ai.mock.mode", mode) # type: ignore[attr-defined]
247
+ return await _execute_mock_logic(self.session_id)
248
+ except Exception:
249
+ # If OpenTelemetry is not available, run without span
250
+ return await _execute_mock_logic(self.session_id)
226
251
 
227
252
  @wraps(func)
228
253
  def sync_wrapper(
@@ -234,57 +259,82 @@ class VerisSDK:
234
259
  # If not in simulation mode, execute the original function
235
260
  return func(*args, **kwargs)
236
261
 
237
- # Handle spy mode - execute original function and log
238
- if mode == "spy":
239
- logger.info(f"Spying on function: {func.__name__}")
240
-
241
- # Log the tool call
242
- sig = inspect.signature(func)
243
- bound_args = sig.bind(*args, **kwargs)
244
- bound_args.apply_defaults()
245
- _ = bound_args.arguments.pop("ctx", None)
246
- _ = bound_args.arguments.pop("self", None)
247
- _ = bound_args.arguments.pop("cls", None)
248
-
249
- log_tool_call_sync(
250
- session_id=self.session_id,
251
- function_name=func.__name__,
252
- parameters=bound_args.arguments,
253
- docstring=inspect.getdoc(func) or "",
254
- )
255
-
256
- # Execute the original function
257
- result = func(*args, **kwargs)
258
-
259
- # Log the response
260
- log_tool_response_sync(session_id=self.session_id, response=result)
261
-
262
- return result
263
-
264
- # Regular mock mode
265
- base_url = os.getenv("VERIS_ENDPOINT_URL")
266
- if not base_url:
267
- error_msg = "VERIS_ENDPOINT_URL environment variable is not set"
268
- raise ValueError(error_msg)
269
- endpoint = f"{base_url.rstrip('/')}/api/v2/tool_mock"
270
- # Default timeout of 30 seconds
271
- timeout = float(os.getenv("VERIS_MOCK_TIMEOUT", "90.0"))
272
-
273
- logger.info(f"Simulating function: {func.__name__}")
274
- payload, return_type_obj = create_mock_payload(*args, **kwargs)
275
-
276
- # Send request to endpoint with timeout (synchronous)
277
- with httpx.Client(timeout=timeout) as client:
278
- response = client.post(endpoint, json=payload)
279
- response.raise_for_status()
280
- mock_result = response.json()
281
- logger.info(f"Mock response: {mock_result}")
282
-
283
- if isinstance(mock_result, str):
284
- with suppress(json.JSONDecodeError):
285
- mock_result = json.loads(mock_result)
286
- return convert_to_type(mock_result, return_type_obj)
287
- return convert_to_type(mock_result, return_type_obj)
262
+ def _execute_mock_logic(session_id: str) -> object:
263
+ # Handle spy mode - execute original function and log
264
+ if mode == "spy":
265
+ logger.info(f"Spying on function: {func.__name__}")
266
+
267
+ # 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)
274
+
275
+ log_tool_call_sync(
276
+ session_id=session_id,
277
+ function_name=func.__name__,
278
+ parameters=bound_args.arguments,
279
+ docstring=inspect.getdoc(func) or "",
280
+ )
281
+
282
+ # Execute the original function
283
+ result = func(*args, **kwargs)
284
+
285
+ # Log the response
286
+ log_tool_response_sync(session_id=session_id, response=result)
287
+
288
+ return result
289
+
290
+ # 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"))
298
+
299
+ logger.info(f"Simulating function: {func.__name__}")
300
+ payload, return_type_obj = create_mock_payload(*args, **kwargs)
301
+
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}")
318
+
319
+ if isinstance(mock_result, str):
320
+ with suppress(json.JSONDecodeError):
321
+ mock_result = json.loads(mock_result)
322
+ return convert_to_type(mock_result, return_type_obj)
323
+ return convert_to_type(mock_result, return_type_obj)
324
+
325
+ # Create a top-level span for the simulated mock call if OpenTelemetry is available
326
+ try:
327
+ from opentelemetry import trace # type: ignore[import-not-found]
328
+
329
+ tracer = trace.get_tracer("veris_ai.tool_mock")
330
+ span_name = f"mock.{func.__name__}"
331
+ with tracer.start_as_current_span(span_name) as span: # type: ignore[attr-defined]
332
+ span.set_attribute("veris_ai.session.id", self.session_id or "") # type: ignore[attr-defined]
333
+ span.set_attribute("veris_ai.mock.mode", mode) # type: ignore[attr-defined]
334
+ return _execute_mock_logic(self.session_id)
335
+ except Exception:
336
+ # If OpenTelemetry is not available, run without span
337
+ return _execute_mock_logic(self.session_id)
288
338
 
289
339
  # Return the appropriate wrapper based on whether the function is async
290
340
  return async_wrapper if is_async else sync_wrapper
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: veris-ai
3
- Version: 1.5.0
3
+ Version: 1.7.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,8 +9,17 @@ 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: opentelemetry-api>=1.34.1
13
+ Requires-Dist: opentelemetry-exporter-otlp>=1.34.1
14
+ Requires-Dist: opentelemetry-instrumentation-fastapi>=0.55b1
15
+ Requires-Dist: opentelemetry-instrumentation-httpx>=0.55b1
16
+ Requires-Dist: opentelemetry-instrumentation-mcp>=0.44.1
17
+ Requires-Dist: opentelemetry-instrumentation-requests>=0.55b1
18
+ Requires-Dist: opentelemetry-instrumentation>=0.55b1
19
+ Requires-Dist: opentelemetry-sdk>=1.34.1
12
20
  Requires-Dist: pydantic>=2.0.0
13
21
  Requires-Dist: requests>=2.31.0
22
+ Requires-Dist: traceloop-sdk>=0.45.4
14
23
  Provides-Extra: dev
15
24
  Requires-Dist: black>=23.7.0; extra == 'dev'
16
25
  Requires-Dist: mypy>=1.5.1; extra == 'dev'
@@ -24,12 +33,7 @@ Provides-Extra: fastapi
24
33
  Requires-Dist: fastapi; extra == 'fastapi'
25
34
  Requires-Dist: fastapi-mcp>=0.4.0; extra == 'fastapi'
26
35
  Provides-Extra: instrument
27
- Requires-Dist: braintrust; extra == 'instrument'
28
36
  Requires-Dist: opentelemetry-api; extra == 'instrument'
29
- Requires-Dist: opentelemetry-exporter-otlp; extra == 'instrument'
30
- Requires-Dist: opentelemetry-exporter-otlp-proto-common; extra == 'instrument'
31
- Requires-Dist: opentelemetry-exporter-otlp-proto-grpc; extra == 'instrument'
32
- Requires-Dist: opentelemetry-exporter-otlp-proto-http; extra == 'instrument'
33
37
  Requires-Dist: opentelemetry-sdk; extra == 'instrument'
34
38
  Requires-Dist: wrapt; extra == 'instrument'
35
39
  Description-Content-Type: text/markdown
@@ -41,7 +45,7 @@ A Python package for Veris AI tools with simulation capabilities and FastAPI MCP
41
45
  ## Quick Reference
42
46
 
43
47
  **Purpose**: Tool mocking, tracing, and FastAPI MCP integration for AI agent development
44
- **Core Components**: [`tool_mock`](#function-mocking) • [`jaeger_interface`](#jaeger-trace-interface) • [`braintrust_tracing`](#tracing-integration) • [`fastapi_mcp`](#fastapi-mcp-integration)
48
+ **Core Components**: [`tool_mock`](#function-mocking) • [`observability`](#sdk-observability-helpers) • [`fastapi_mcp`](#fastapi-mcp-integration) • [`jaeger_interface`](#jaeger-trace-interface)
45
49
  **Deep Dive**: [`Module Architecture`](src/veris_ai/README.md) • [`Testing Guide`](tests/README.md) • [`Usage Examples`](examples/README.md)
46
50
  **Source of Truth**: Implementation details in [`src/veris_ai/`](src/veris_ai/) source code
47
51
 
@@ -58,7 +62,7 @@ uv add "veris-ai[dev,fastapi,instrument]"
58
62
  **Installation Profiles**:
59
63
  - `dev`: Development tools (ruff, pytest, mypy)
60
64
  - `fastapi`: FastAPI MCP integration
61
- - `instrument`: Braintrust/OpenTelemetry tracing
65
+ - `observability`: OpenTelemetry tracing
62
66
 
63
67
  ## Import Patterns
64
68
 
@@ -69,7 +73,7 @@ uv add "veris-ai[dev,fastapi,instrument]"
69
73
  from veris_ai import veris, JaegerClient
70
74
 
71
75
  # Optional features (require extras)
72
- from veris_ai import braintrust_tracing # Requires [instrument]
76
+ from veris_ai import init_observability, instrument_fastapi_app # Provided by SDK observability helpers
73
77
  ```
74
78
 
75
79
  **Complete Import Strategies**: See [`examples/README.md`](examples/README.md) for five different import approaches, conditional features, and integration patterns.
@@ -88,22 +92,33 @@ from veris_ai import braintrust_tracing # Requires [instrument]
88
92
 
89
93
  **Configuration Details**: See [`src/veris_ai/tool_mock.py`](src/veris_ai/tool_mock.py) for environment handling logic.
90
94
 
91
- ## Tracing Integration
92
95
 
93
- **Semantic Tag**: `distributed-tracing`
96
+ ### SDK Observability Helpers
94
97
 
95
- Parallel tracing to Braintrust and Jaeger/OpenTelemetry for monitoring and evaluation.
98
+ The SDK provides optional-safe observability helpers that standardize OpenTelemetry setup and W3C context propagation across services.
96
99
 
97
100
  ```python
98
- from veris_ai import braintrust_tracing
101
+ from fastapi import FastAPI
102
+ from veris_ai import init_observability, instrument_fastapi_app
103
+
104
+ # Initialize tracing/export early (no-op if dependencies are absent)
105
+ init_observability(service_name="my-customer-service")
106
+
107
+ app = FastAPI()
99
108
 
100
- # Enable dual tracing
101
- braintrust_tracing.instrument(service_name="my-service", otlp_endpoint="http://localhost:4317")
109
+ # Ensure inbound HTTP requests continue W3C traces
110
+ instrument_fastapi_app(app)
102
111
  ```
103
112
 
104
- **Session Management**: Automatic session ID extraction from bearer tokens. Manual session control via `veris.set_session_id()` and `veris.clear_session_id()`.
113
+ What this enables:
114
+ - Sets global W3C propagator (TraceContext + Baggage)
115
+ - Optionally instruments FastAPI, requests, httpx, MCP client if installed
116
+ - Includes request hooks to attach outbound `traceparent` on HTTP calls for continuity
105
117
 
106
- **Implementation Details**: See [`src/veris_ai/braintrust_tracing.py`](src/veris_ai/braintrust_tracing.py) for instrumentation logic.
118
+ End-to-end propagation with the simulator:
119
+ - The simulator injects W3C headers when connecting to your FastAPI MCP endpoints
120
+ - The SDK injects W3C headers on `/api/v2/tool_mock` and logging requests back to the simulator
121
+ - Result: customer agent spans and tool mocks appear under the same distributed trace
107
122
 
108
123
  ## Function Mocking
109
124
 
@@ -207,7 +222,7 @@ pytest --cov=veris_ai # Test with coverage
207
222
 
208
223
  **Semantic Tag**: `module-architecture`
209
224
 
210
- **Core Modules**: `tool_mock` (mocking), `jaeger_interface` (trace queries), `braintrust_tracing` (dual tracing), `utils` (schema conversion)
225
+ **Core Modules**: `tool_mock` (mocking), `jaeger_interface` (trace queries), `utils` (schema conversion)
211
226
 
212
227
  **Complete Architecture**: See [`src/veris_ai/README.md`](src/veris_ai/README.md) for module overview, implementation flows, and configuration details.
213
228
 
@@ -0,0 +1,15 @@
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,,
@@ -1,282 +0,0 @@
1
- """Non-invasive Braintrust + Jaeger (OTEL) instrumentation helper for the `openai-agents` SDK.
2
-
3
- Typical usage
4
- -------------
5
- >>> from our_sdk import braintrust_tracing
6
- >>> braintrust_tracing.instrument(service_name="openai-agent")
7
-
8
- After calling :func:`instrument`, any later call to
9
- ``agents.set_trace_processors([...])`` will be transparently patched so that:
10
- • the list always contains a BraintrustTracingProcessor (for Braintrust UI)
11
- • *and* an OpenTelemetry bridge processor that mirrors every span to the
12
- global OTEL tracer provider (Jaeger by default).
13
-
14
- Goal: deliver full OTEL compatibility while keeping the official Braintrust
15
- SDK integration unchanged – **no code modifications required** besides the
16
- single `instrument()` call.
17
- """
18
-
19
- from __future__ import annotations
20
-
21
- import json
22
- import logging
23
- import os
24
- from typing import Any, cast
25
-
26
- import wrapt # type: ignore[import-untyped, import-not-found]
27
- from braintrust.wrappers.openai import (
28
- BraintrustTracingProcessor, # type: ignore[import-untyped, import-not-found]
29
- )
30
- from opentelemetry import context as otel_context # type: ignore[import-untyped, import-not-found]
31
- from opentelemetry import trace # type: ignore[import-untyped, import-not-found]
32
- from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
33
- OTLPSpanExporter, # type: ignore[import-untyped, import-not-found]
34
- )
35
- from opentelemetry.sdk.resources import ( # type: ignore[import-untyped, import-not-found]
36
- SERVICE_NAME,
37
- Resource,
38
- )
39
- from opentelemetry.sdk.trace import TracerProvider # type: ignore[import-untyped, import-not-found]
40
- from opentelemetry.sdk.trace.export import (
41
- BatchSpanProcessor, # type: ignore[import-untyped, import-not-found]
42
- )
43
- from opentelemetry.trace import SpanKind # type: ignore[import-untyped, import-not-found]
44
-
45
- from veris_ai.tool_mock import _session_id_context
46
-
47
- # ---------------------------------------------------------------------------
48
- # Optional import of *agents* – we fail lazily at runtime if missing.
49
- # ---------------------------------------------------------------------------
50
- try:
51
- import agents # type: ignore[import-untyped] # noqa: TC002
52
- from agents import TracingProcessor # type: ignore[import-untyped]
53
-
54
- try:
55
- from agents.tracing import get_trace_provider # type: ignore[import-untyped]
56
- except ImportError:
57
- # Fallback for newer versions that have GLOBAL_TRACE_PROVIDER instead
58
- from agents.tracing import ( # type: ignore[import-untyped, attr-defined, import-not-found]
59
- GLOBAL_TRACE_PROVIDER, # type: ignore[import-untyped, attr-defined, import-not-found]
60
- )
61
-
62
- get_trace_provider = lambda: GLOBAL_TRACE_PROVIDER # type: ignore[no-any-return] # noqa: E731
63
- except ModuleNotFoundError as exc: # pragma: no cover
64
- _IMPORT_ERR: ModuleNotFoundError | None = exc
65
- TracingProcessor = object # type: ignore[assignment, misc]
66
- get_trace_provider = None # type: ignore[assignment]
67
- else:
68
- _IMPORT_ERR = None
69
-
70
- __all__ = ["instrument"]
71
-
72
- logger = logging.getLogger(__name__)
73
-
74
- # ---------------------------------------------------------------------------
75
- # Internal helper – OTEL bridge processor
76
- # ---------------------------------------------------------------------------
77
-
78
-
79
- class AgentsOTELBridgeProcessor(TracingProcessor): # type: ignore[misc]
80
- """Mirrors every Agents span into a dedicated OTEL tracer provider."""
81
-
82
- def __init__(
83
- self,
84
- braintrust_processor: BraintrustTracingProcessor,
85
- *,
86
- service_name: str, # noqa: ARG002
87
- tracer_provider: trace.TracerProvider,
88
- ) -> None: # noqa: D401,E501
89
- self._braintrust = braintrust_processor
90
- self._tracer = tracer_provider.get_tracer(__name__)
91
- self._otel_spans: dict[str, trace.Span] = {}
92
- self._provider = tracer_provider
93
-
94
- # ----------------------------- utils ---------------------------------
95
- @staticmethod
96
- def _flatten(prefix: str, obj: Any, out: dict[str, Any]) -> None: # noqa: PLR0911, ANN401
97
- """Flatten complex objects into OTEL-compatible primitives."""
98
- if isinstance(obj, dict):
99
- for k, v in obj.items():
100
- AgentsOTELBridgeProcessor._flatten(f"{prefix}.{k}" if prefix else str(k), v, out)
101
- elif isinstance(obj, str | int | float | bool) or obj is None:
102
- out[prefix] = obj
103
- elif isinstance(obj, list | tuple):
104
- try:
105
- if all(isinstance(i, str | int | float | bool) or i is None for i in obj):
106
- out[prefix] = list(obj)
107
- else:
108
- out[prefix] = json.dumps(obj, default=str)
109
- except Exception: # pragma: no cover – defensive
110
- out[prefix] = json.dumps(obj, default=str)
111
- else:
112
- out[prefix] = str(obj)
113
-
114
- def _log_data_attributes(self, span_obj: agents.tracing.Span) -> dict[str, Any]: # type: ignore[name-defined]
115
- data = self._braintrust._log_data(span_obj) # pyright: ignore[reportPrivateUsage] # noqa: SLF001
116
- flat: dict[str, Any] = {}
117
- self._flatten("bt", data, flat)
118
-
119
- # Add session_id if available
120
- session_id = _session_id_context.get()
121
- if session_id:
122
- flat["veris.session_id"] = session_id
123
-
124
- return {k: v for k, v in flat.items() if v is not None}
125
-
126
- # --------------------- Agents lifecycle hooks ------------------------
127
- def on_trace_start(self, trace_obj: Any) -> None: # noqa: ANN401, D102
128
- # Get session_id at trace start
129
- session_id = _session_id_context.get()
130
- attributes = {"veris.session_id": session_id} if session_id else {}
131
-
132
- otel_span = self._tracer.start_span(
133
- name=trace_obj.name or "agent-trace",
134
- kind=SpanKind.INTERNAL,
135
- attributes=attributes,
136
- )
137
- self._otel_spans[trace_obj.trace_id] = otel_span
138
- logger.info(f"VERIS AI BraintrustTracingProcessor: on_trace_start: {trace_obj.trace_id}")
139
-
140
- def on_trace_end(self, trace_obj: Any) -> None: # noqa: ANN401, D102
141
- span = self._otel_spans.pop(trace_obj.trace_id, None)
142
- if span:
143
- span.end()
144
-
145
- def on_span_start(self, span: Any) -> None: # noqa: ANN401, D102
146
- parent_otel = (
147
- self._otel_spans.get(span.parent_id)
148
- if span.parent_id
149
- else self._otel_spans.get(span.trace_id)
150
- )
151
- parent_ctx = (
152
- trace.set_span_in_context(parent_otel) if parent_otel else otel_context.get_current()
153
- )
154
-
155
- # Get session_id at span start
156
- session_id = _session_id_context.get()
157
- attributes = {"veris.session_id": session_id} if session_id else {}
158
-
159
- child = self._tracer.start_span(
160
- name=span.span_data.__class__.__name__,
161
- context=parent_ctx,
162
- kind=SpanKind.INTERNAL,
163
- attributes=attributes,
164
- )
165
- self._otel_spans[span.span_id] = child
166
- logger.info(f"VERIS AI BraintrustTracingProcessor: on_span_start: {span.span_id}")
167
-
168
- def on_span_end(self, span: Any) -> None: # noqa: ANN401, D102
169
- child = self._otel_spans.pop(span.span_id, None)
170
- logger.info(f"VERIS AI BraintrustTracingProcessor: on_span_end: {span.span_id}")
171
- if child:
172
- for k, v in self._log_data_attributes(span).items():
173
- try:
174
- child.set_attribute(k, v)
175
- except Exception: # pragma: no cover – bad value type # noqa: S112
176
- continue
177
- child.end()
178
-
179
- # --------------------- house-keeping ---------------------------------
180
- def shutdown(self) -> None: # noqa: D401
181
- provider = cast("TracerProvider", self._provider)
182
- provider.shutdown() # type: ignore[attr-defined]
183
-
184
- def force_flush(self) -> None: # noqa: D401
185
- provider = cast("TracerProvider", self._provider)
186
- provider.force_flush() # type: ignore[attr-defined]
187
-
188
-
189
- # ---------------------------------------------------------------------------
190
- # Public entry point
191
- # ---------------------------------------------------------------------------
192
-
193
- _PATCHED: bool = False # ensure idempotent patching
194
-
195
-
196
- def instrument(
197
- *,
198
- service_name: str | None = None,
199
- otlp_endpoint: str | None = None,
200
- ) -> None:
201
- """Bootstrap Braintrust + OTEL instrumentation and patch Agents SDK.
202
-
203
- Invoke once at any point before `set_trace_processors` is called.
204
- """
205
- global _PATCHED # noqa: PLW0603
206
- if _PATCHED:
207
- return # already done
208
-
209
- if _IMPORT_ERR is not None or get_trace_provider is None: # pragma: no cover
210
- error_msg = "The `agents` package is required but not installed"
211
- raise RuntimeError(error_msg) from _IMPORT_ERR
212
-
213
- # ------------------ 0. Validate inputs -----------------------------
214
- # Resolve service name ─ explicit argument → env var → error
215
- if not service_name or not str(service_name).strip():
216
- service_name = os.getenv("VERIS_SERVICE_NAME")
217
-
218
- if not service_name or not str(service_name).strip():
219
- error_msg = (
220
- "`service_name` must be provided either as an argument or via the "
221
- "VERIS_SERVICE_NAME environment variable"
222
- )
223
- raise ValueError(error_msg)
224
-
225
- # Resolve OTLP endpoint ─ explicit argument → env var → error
226
- if not otlp_endpoint or not str(otlp_endpoint).strip():
227
- otlp_endpoint = os.getenv("VERIS_OTLP_ENDPOINT")
228
-
229
- if not otlp_endpoint or not str(otlp_endpoint).strip():
230
- error_msg = (
231
- "`otlp_endpoint` must be provided either as an argument or via the "
232
- "VERIS_OTLP_ENDPOINT environment variable"
233
- )
234
- raise ValueError(error_msg)
235
-
236
- logger.info(f"service_name: {service_name}")
237
- logger.info(f"otlp_endpoint: {otlp_endpoint}")
238
-
239
- # ------------------ 1. Configure OTEL provider ---------------------
240
- # We create our own provider instance and do NOT set it globally.
241
- # This avoids conflicts with any other OTEL setup in the application.
242
- otel_provider = TracerProvider(resource=Resource.create({SERVICE_NAME: service_name}))
243
- otel_provider.add_span_processor(
244
- BatchSpanProcessor(OTLPSpanExporter(endpoint=otlp_endpoint, insecure=True)),
245
- )
246
-
247
- # ------------------ 2. Define wrapper for patching -------------------
248
- def _wrapper(wrapped: Any, instance: Any, args: Any, kwargs: Any) -> Any: # noqa: ANN401, ARG001
249
- """This function wraps `TraceProvider.set_processors`."""
250
- processors = args[0] if args else []
251
-
252
- # Find the user's Braintrust processor to pass to our bridge.
253
- bt_processor = next(
254
- (p for p in processors if isinstance(p, BraintrustTracingProcessor)), None
255
- )
256
-
257
- # If no Braintrust processor is present, our bridge is useless.
258
- # Also, if a bridge is already there, don't add another one.
259
- has_bridge = any(isinstance(p, AgentsOTELBridgeProcessor) for p in processors)
260
- if not bt_processor or has_bridge:
261
- return wrapped(*args, **kwargs)
262
-
263
- # Create the bridge and add it to the list of processors.
264
- bridge = AgentsOTELBridgeProcessor(
265
- bt_processor,
266
- service_name=service_name,
267
- tracer_provider=otel_provider,
268
- )
269
- new_processors = list(processors) + [bridge]
270
-
271
- # Call the original function with the augmented list.
272
- new_args = (new_processors,) + args[1:]
273
- logger.info(f"VERIS AI BraintrustTracingProcessor: {new_args}")
274
- return wrapped(*new_args, **kwargs)
275
-
276
- # ------------------ 3. Patch the provider instance -------------------
277
- # This is more robust than patching the function, as it's independent
278
- # of how the user imports `set_trace_processors`.
279
- provider_instance = get_trace_provider()
280
- wrapt.wrap_function_wrapper(provider_instance, "set_processors", _wrapper)
281
-
282
- _PATCHED = True
@@ -1,15 +0,0 @@
1
- veris_ai/README.md,sha256=q0Qn6gu85HWU189SmyOygY_mkNj0l10wlAVy9kj4udo,3120
2
- veris_ai/__init__.py,sha256=Vp5yf9ZRchw0mmPwrRuNebI5cb2NGwpJGfr41l86q1U,1177
3
- veris_ai/braintrust_tracing.py,sha256=0i-HR6IuK3-Q5ujMjT1FojxESRZLgqEvJ44bfJfDHaw,11938
4
- veris_ai/logging.py,sha256=2855E8s_k6yTnzFK10EQR4WQngQk3VuWFPJW-MYiw3k,3837
5
- veris_ai/models.py,sha256=6HINPxNFCakCVPcyEbUswWkXwb2K4lF0A8g8EvTMal4,213
6
- veris_ai/tool_mock.py,sha256=p10k6dCZAIqG8YoefR3QyvtkInnWNPMRO_07LI5Wks0,13218
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=d873a0zq3eUYU2Y77MtdjCwIARjAsAP7WDqGXDMWpYs,1158
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.5.0.dist-info/METADATA,sha256=l-x_bBgu3DWDVgG4IFeek26y1U1ANP6F4Cz-yrm9beQ,8151
13
- veris_ai-1.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- veris_ai-1.5.0.dist-info/licenses/LICENSE,sha256=2g4i20atAgtD5einaKzhQrIB-JrPhyQgD3bC0wkHcCI,1065
15
- veris_ai-1.5.0.dist-info/RECORD,,