0
# Utilities
1
2
Helper functions and utilities for JSON serialization, span context capture, and project management in OpenInference instrumentation.
3
4
## Capabilities
5
6
### JSON Serialization
7
8
Safe JSON serialization utility that handles edge cases and ensures non-ASCII characters are preserved.
9
10
```python { .api }
11
def safe_json_dumps(obj: Any, **kwargs: Any) -> str:
12
"""
13
A convenience wrapper around json.dumps that ensures any object can
14
be safely encoded without a TypeError and that non-ASCII Unicode
15
characters are not escaped.
16
17
Args:
18
obj: Object to serialize to JSON
19
**kwargs: Additional keyword arguments passed to json.dumps
20
21
Returns:
22
str: JSON string representation
23
"""
24
```
25
26
**Usage Example:**
27
28
```python
29
from openinference.instrumentation import safe_json_dumps
30
from datetime import datetime
31
32
# Handles complex objects safely
33
data = {
34
"text": "Hello 世界", # Unicode characters preserved
35
"timestamp": datetime.now(), # Converted to string
36
"numbers": [1, 2.5, float('inf')], # Special values handled
37
"nested": {"key": "value"}
38
}
39
40
json_str = safe_json_dumps(data)
41
print(json_str)
42
# Output: {"text": "Hello 世界", "timestamp": "2024-01-01 10:00:00.123456", ...}
43
44
# Works with custom kwargs
45
compact_json = safe_json_dumps(data, separators=(',', ':'))
46
pretty_json = safe_json_dumps(data, indent=2)
47
```
48
49
### Span Context Capture
50
51
Context manager for capturing OpenInference span contexts, useful for annotation and evaluation workflows.
52
53
```python { .api }
54
class capture_span_context:
55
"""
56
Context manager for capturing OpenInference span context.
57
Useful for getting span IDs for annotations, evaluations, or feedback.
58
"""
59
60
def __init__(self) -> None: ...
61
62
def __enter__(self) -> "capture_span_context": ...
63
64
def __exit__(
65
self,
66
_exc_type: Optional[Type[BaseException]],
67
_exc_value: Optional[BaseException],
68
_traceback: Optional[TracebackType],
69
) -> None: ...
70
71
def get_first_span_id(self) -> Optional[str]:
72
"""
73
Returns the first captured span ID, or None if no spans were captured.
74
This can be useful if the first span is the one that you want to annotate or evaluate.
75
76
Returns:
77
Optional[str]: First span ID as hex string, or None
78
"""
79
80
def get_last_span_id(self) -> Optional[str]:
81
"""
82
Returns the last captured span ID, or None if no spans were captured.
83
This can be useful if the last span is the one that you want to annotate or evaluate.
84
85
Returns:
86
Optional[str]: Last span ID as hex string, or None
87
"""
88
89
def get_span_contexts(self) -> Sequence[SpanContext]:
90
"""
91
Returns a sequence of all captured span contexts.
92
93
Returns:
94
Sequence[SpanContext]: All captured span contexts
95
"""
96
```
97
98
**Usage Example:**
99
100
```python
101
from openinference.instrumentation import capture_span_context
102
import openai
103
104
# Capture spans from LLM operations
105
with capture_span_context() as capture:
106
response = openai.chat.completions.create(
107
model="gpt-4",
108
messages=[{"role": "user", "content": "Hello!"}]
109
)
110
111
# Get the span ID for annotation
112
span_id = capture.get_last_span_id()
113
if span_id:
114
# Use span ID with Phoenix or other annotation systems
115
phoenix_client.annotations.add_span_annotation(
116
span_id=span_id,
117
annotation_name="feedback",
118
score=0.9,
119
label="helpful"
120
)
121
122
# Capture multiple spans
123
with capture_span_context() as capture:
124
# Multiple operations
125
embedding = openai.embeddings.create(...)
126
completion = openai.chat.completions.create(...)
127
128
# Get all span contexts
129
all_contexts = capture.get_span_contexts()
130
print(f"Captured {len(all_contexts)} spans")
131
132
# Get first and last
133
first_span = capture.get_first_span_id()
134
last_span = capture.get_last_span_id()
135
```
136
137
### Project Management
138
139
Context manager for dynamically changing the project associated with spans, intended for notebook environments.
140
141
```python { .api }
142
class dangerously_using_project:
143
"""
144
A context manager that switches the project for all spans created within the context.
145
146
This is intended for use in notebook environments where it's useful to be able to change the
147
project associated with spans on the fly.
148
149
Note: This should not be used in production environments or complex OpenTelemetry setups.
150
As dynamically modifying span resources in this way can lead to unexpected behavior.
151
152
Args:
153
project_name (str): The project name to associate with spans created within the context.
154
"""
155
156
def __init__(self, project_name: str) -> None: ...
157
158
def __enter__(self) -> None: ...
159
160
def __exit__(
161
self,
162
exc_type: Optional[type[BaseException]],
163
exc_value: Optional[BaseException],
164
traceback: Optional[types.TracebackType],
165
) -> None: ...
166
```
167
168
**Usage Example:**
169
170
```python
171
from openinference.instrumentation import dangerously_using_project
172
173
# Normal spans go to default project
174
tracer.start_span("normal-operation")
175
176
# Temporarily switch project for experimentation
177
with dangerously_using_project("experiment-project"):
178
# All spans created here will be associated with "experiment-project"
179
tracer.start_span("experimental-operation")
180
llm_call()
181
182
# Back to default project
183
tracer.start_span("another-normal-operation")
184
185
# Useful in Jupyter notebooks for organizing experiments
186
with dangerously_using_project("notebook-session-1"):
187
# Experiment 1
188
results_1 = run_experiment()
189
190
with dangerously_using_project("notebook-session-2"):
191
# Experiment 2
192
results_2 = run_experiment()
193
```
194
195
### Internal Helper Functions
196
197
Additional helper functions used internally (exported for advanced use cases).
198
199
```python { .api }
200
def get_span_id(span: Span) -> str:
201
"""
202
Extract span ID as hex string from OpenTelemetry span.
203
204
Args:
205
span (Span): OpenTelemetry span
206
207
Returns:
208
str: Span ID as hex string
209
"""
210
211
def get_trace_id(span: Span) -> str:
212
"""
213
Extract trace ID as hex string from OpenTelemetry span.
214
215
Args:
216
span (Span): OpenTelemetry span
217
218
Returns:
219
str: Trace ID as hex string
220
"""
221
```
222
223
**Usage Example:**
224
225
```python
226
from openinference.instrumentation.helpers import get_span_id, get_trace_id
227
from opentelemetry import trace
228
229
# Get current span IDs
230
current_span = trace.get_current_span()
231
if current_span:
232
span_id = get_span_id(current_span)
233
trace_id = get_trace_id(current_span)
234
print(f"Current span: {span_id} in trace: {trace_id}")
235
```
236
237
## Utility Features
238
239
### Safe JSON Handling
240
241
The `safe_json_dumps` function provides several safety features:
242
243
- **Fallback serialization**: Objects that can't be JSON-serialized are converted to strings
244
- **Unicode preservation**: Non-ASCII characters are not escaped (ensure_ascii=False)
245
- **Custom object handling**: Supports dataclasses, Pydantic models, and datetime objects
246
- **Error tolerance**: Never raises TypeError, always returns a valid JSON string
247
248
### Context Capture Benefits
249
250
The `capture_span_context` utility enables:
251
252
- **Span annotation**: Get span IDs for adding feedback and annotations
253
- **Evaluation workflows**: Link evaluation results to specific spans
254
- **Debugging**: Inspect which spans were created during operations
255
- **Phoenix integration**: Seamless integration with Phoenix tracing UI
256
- **Testing**: Verify span creation in unit tests
257
258
### Project Management Use Cases
259
260
The `dangerously_using_project` context manager is useful for:
261
262
- **Jupyter notebooks**: Organize experiments by project
263
- **Development testing**: Isolate test spans from production data
264
- **Multi-tenant scenarios**: Temporarily switch between tenants
265
- **A/B testing**: Separate spans for different test variants
266
267
**Warning**: Only use `dangerously_using_project` in development or notebook environments. It modifies OpenTelemetry internals and can cause issues in production systems.
268
269
## Integration Examples
270
271
### Phoenix Annotation Workflow
272
273
```python
274
from openinference.instrumentation import capture_span_context
275
import phoenix
276
277
with capture_span_context() as capture:
278
# Your LLM operations
279
response = llm.generate("Hello world")
280
281
# Add human feedback
282
span_id = capture.get_last_span_id()
283
if span_id:
284
phoenix.Client().log_evaluations(
285
span_ids=[span_id],
286
evaluations=[
287
phoenix.Evaluation(
288
name="helpfulness",
289
score=0.8,
290
explanation="Response was helpful"
291
)
292
]
293
)
294
```
295
296
### Testing Span Creation
297
298
```python
299
import pytest
300
from openinference.instrumentation import capture_span_context
301
302
def test_span_creation():
303
with capture_span_context() as capture:
304
# Your code that should create spans
305
my_function_that_creates_spans()
306
307
# Verify spans were created
308
assert len(capture.get_span_contexts()) > 0
309
assert capture.get_first_span_id() is not None
310
```