or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

attribute-generation.mdconfiguration.mdcontext-management.mdindex.mdtracer-spans.mdtype-definitions.mdutilities.md

utilities.mddocs/

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

```