or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

active-observations.mdattribute-creation.mdcontext-management.mdindex.mdmanual-observations.mdobservation-types.mdobserve-decorator.mdotel-span-attributes.mdtrace-id-generation.mdtracer-provider.md

context-management.mddocs/

0

# Context Management

1

2

Context management functions provide utilities for updating and accessing the currently active trace and observation within the OpenTelemetry context. These functions enable you to modify trace attributes from anywhere within an active observation context.

3

4

## Core Functions

5

6

### updateActiveTrace

7

8

Updates the currently active trace with new attributes from within any observation context.

9

10

```typescript { .api }

11

/**

12

* Updates the currently active trace with new attributes.

13

*

14

* This function finds the currently active OpenTelemetry span and updates

15

* it with trace-level attributes. If no active span is found, a warning is logged.

16

*

17

* @param attributes - Trace attributes to set

18

*/

19

function updateActiveTrace(attributes: LangfuseTraceAttributes): void;

20

21

interface LangfuseTraceAttributes {

22

/** Human-readable name for the trace */

23

name?: string;

24

/** Identifier for the user associated with this trace */

25

userId?: string;

26

/** Session identifier for grouping related traces */

27

sessionId?: string;

28

/** Version identifier for the code/application */

29

version?: string;

30

/** Release identifier for deployment tracking */

31

release?: string;

32

/** Input data that initiated the trace */

33

input?: unknown;

34

/** Final output data from the trace */

35

output?: unknown;

36

/** Additional metadata for the trace */

37

metadata?: unknown;

38

/** Tags for categorizing and filtering traces */

39

tags?: string[];

40

/** Whether this trace should be publicly visible */

41

public?: boolean;

42

/** Environment where the trace was captured */

43

environment?: string;

44

}

45

```

46

47

**Usage:**

48

49

```typescript

50

import { startActiveObservation, updateActiveTrace } from '@langfuse/tracing';

51

52

await startActiveObservation('user-workflow', async (observation) => {

53

// Update trace with user context

54

updateActiveTrace({

55

name: 'checkout-flow',

56

userId: 'user-123',

57

sessionId: 'session-456',

58

tags: ['checkout', 'payment'],

59

metadata: { cartValue: 99.99 }

60

});

61

62

// Perform operations...

63

const result = await processCheckout();

64

65

// Update trace with final output

66

updateActiveTrace({

67

output: {

68

orderId: result.orderId,

69

success: true

70

}

71

});

72

});

73

```

74

75

### updateActiveObservation

76

77

Updates the currently active observation with new attributes from within the observation context.

78

79

```typescript { .api }

80

/**

81

* Updates the currently active observation with new attributes.

82

*

83

* This function finds the currently active OpenTelemetry span in the execution context

84

* and updates it with Langfuse-specific attributes. It supports all observation types

85

* through TypeScript overloads for type safety.

86

*

87

* **Important**: When `asType` is omitted or undefined, the observation type attribute

88

* is NOT updated, preserving the existing observation type. This prevents inadvertently

89

* overwriting the type to "span". Only specify `asType` when you explicitly want to

90

* change the observation type (which is rarely needed).

91

*

92

* @param attributes - Observation-specific attributes to update

93

* @param options - Configuration specifying observation type (defaults to 'span')

94

*/

95

function updateActiveObservation(

96

attributes: LangfuseObservationAttributes,

97

options?: { asType?: LangfuseObservationType }

98

): void;

99

100

// Type-specific overloads for type safety

101

function updateActiveObservation(

102

attributes: LangfuseSpanAttributes,

103

options?: { asType: "span" }

104

): void;

105

106

function updateActiveObservation(

107

attributes: LangfuseGenerationAttributes,

108

options: { asType: "generation" }

109

): void;

110

111

function updateActiveObservation(

112

attributes: LangfuseAgentAttributes,

113

options: { asType: "agent" }

114

): void;

115

116

// ... additional overloads for tool, chain, retriever, evaluator, guardrail, embedding

117

```

118

119

**Usage:**

120

121

```typescript

122

import { startActiveObservation, updateActiveObservation } from '@langfuse/tracing';

123

124

// Update active span (default)

125

await startActiveObservation('data-processing', async () => {

126

const result = await processData(inputData);

127

128

updateActiveObservation({

129

output: { processedRecords: result.count },

130

metadata: { processingTime: result.duration }

131

});

132

});

133

134

// Update active generation with LLM-specific attributes

135

await startActiveObservation('llm-call', async () => {

136

const response = await openai.chat.completions.create({

137

model: 'gpt-4',

138

messages: [{ role: 'user', content: 'Hello' }]

139

});

140

141

updateActiveObservation({

142

output: response.choices[0].message,

143

usageDetails: {

144

promptTokens: response.usage.prompt_tokens,

145

completionTokens: response.usage.completion_tokens,

146

totalTokens: response.usage.total_tokens

147

},

148

costDetails: { totalCost: 0.025, currency: 'USD' }

149

}, { asType: 'generation' });

150

}, { asType: 'generation' });

151

```

152

153

### getActiveTraceId

154

155

Gets the trace ID of the currently active span.

156

157

```typescript { .api }

158

/**

159

* Gets the current active trace ID.

160

*

161

* If there is no span in the current context, returns undefined.

162

*

163

* @returns The trace ID of the currently active span, or undefined if no span is active

164

*/

165

function getActiveTraceId(): string | undefined;

166

```

167

168

**Usage:**

169

170

```typescript

171

import { startActiveObservation, getActiveTraceId } from '@langfuse/tracing';

172

173

await startActiveObservation('operation', async () => {

174

const traceId = getActiveTraceId();

175

176

if (traceId) {

177

console.log('Current trace ID:', traceId);

178

// Use trace ID for correlation with external systems

179

await logToExternalSystem({ traceId, event: 'operation-started' });

180

}

181

});

182

```

183

184

### getActiveSpanId

185

186

Gets the observation ID (span ID) of the currently active span.

187

188

```typescript { .api }

189

/**

190

* Gets the current active observation ID.

191

*

192

* If there is no OTEL span in the current context, returns undefined.

193

*

194

* @returns The ID of the currently active OTEL span, or undefined if no OTEL span is active

195

*/

196

function getActiveSpanId(): string | undefined;

197

```

198

199

**Usage:**

200

201

```typescript

202

import { startActiveObservation, getActiveSpanId } from '@langfuse/tracing';

203

204

await startActiveObservation('parent-operation', async () => {

205

const parentSpanId = getActiveSpanId();

206

207

await startActiveObservation('child-operation', async () => {

208

const childSpanId = getActiveSpanId();

209

210

console.log('Parent span ID:', parentSpanId);

211

console.log('Child span ID:', childSpanId);

212

});

213

});

214

```

215

216

## Use Cases

217

218

### Dynamic User Context

219

220

Update trace with user information discovered during execution.

221

222

```typescript

223

import { startActiveObservation, updateActiveTrace } from '@langfuse/tracing';

224

225

async function handleRequest(request: Request) {

226

return await startActiveObservation('handle-request', async () => {

227

// Initially, we don't know the user

228

const authToken = request.headers.get('Authorization');

229

const user = await authenticateUser(authToken);

230

231

// Update trace with authenticated user

232

updateActiveTrace({

233

userId: user.id,

234

sessionId: user.sessionId,

235

metadata: {

236

userRole: user.role,

237

accountType: user.accountType

238

}

239

});

240

241

// Continue processing with user context

242

return await processRequest(request, user);

243

});

244

}

245

```

246

247

### Progressive Attribute Updates

248

249

Update observations as more information becomes available.

250

251

```typescript

252

await startActiveObservation('multi-step-process', async () => {

253

// Step 1: Initialize

254

updateActiveObservation({

255

input: { phase: 'initialization' },

256

metadata: { startTime: Date.now() }

257

});

258

259

const config = await loadConfiguration();

260

261

// Step 2: Processing

262

updateActiveObservation({

263

metadata: {

264

configLoaded: true,

265

configVersion: config.version

266

}

267

});

268

269

const result = await processWithConfig(config);

270

271

// Step 3: Completion

272

updateActiveObservation({

273

output: result,

274

metadata: {

275

completionTime: Date.now(),

276

recordsProcessed: result.count

277

}

278

});

279

});

280

```

281

282

### Error Context Enhancement

283

284

Add detailed error context to active observations.

285

286

```typescript

287

await startActiveObservation('risky-operation', async () => {

288

try {

289

const result = await performRiskyOperation();

290

291

updateActiveObservation({

292

output: result,

293

level: 'DEFAULT'

294

});

295

296

return result;

297

} catch (error) {

298

// Enhance error with context

299

updateActiveObservation({

300

level: 'ERROR',

301

statusMessage: error.message,

302

output: {

303

error: error.message,

304

errorCode: error.code,

305

stack: error.stack

306

},

307

metadata: {

308

recoveryAttempted: false,

309

criticalFailure: true

310

}

311

});

312

313

throw error;

314

}

315

});

316

```

317

318

### Cross-Cutting Concerns

319

320

Add tracing information from middleware or decorators.

321

322

```typescript

323

// Middleware example

324

async function timingMiddleware(

325

handler: () => Promise<any>

326

) {

327

const startTime = Date.now();

328

329

try {

330

const result = await handler();

331

332

const duration = Date.now() - startTime;

333

updateActiveObservation({

334

metadata: {

335

duration,

336

performance: duration < 1000 ? 'fast' : 'slow'

337

}

338

});

339

340

return result;

341

} catch (error) {

342

const duration = Date.now() - startTime;

343

updateActiveObservation({

344

level: 'ERROR',

345

metadata: {

346

duration,

347

failedAfter: duration

348

}

349

});

350

351

throw error;

352

}

353

}

354

355

// Usage in traced operation

356

await startActiveObservation('operation', async () => {

357

return await timingMiddleware(async () => {

358

return await doWork();

359

});

360

});

361

```

362

363

### External System Correlation

364

365

Use trace and span IDs to correlate with external logging systems.

366

367

```typescript

368

import { startActiveObservation, getActiveTraceId, getActiveSpanId } from '@langfuse/tracing';

369

370

async function callExternalAPI(endpoint: string, data: any) {

371

return await startActiveObservation('external-api-call', async () => {

372

const traceId = getActiveTraceId();

373

const spanId = getActiveSpanId();

374

375

// Include trace context in external API call

376

const response = await fetch(endpoint, {

377

method: 'POST',

378

headers: {

379

'Content-Type': 'application/json',

380

'X-Trace-Id': traceId || 'unknown',

381

'X-Span-Id': spanId || 'unknown'

382

},

383

body: JSON.stringify(data)

384

});

385

386

// Log correlation info

387

console.log(`API call to ${endpoint}`, {

388

traceId,

389

spanId,

390

statusCode: response.status

391

});

392

393

updateActiveObservation({

394

output: {

395

statusCode: response.status,

396

correlationId: response.headers.get('X-Correlation-Id')

397

},

398

metadata: {

399

endpoint,

400

traceId,

401

spanId

402

}

403

});

404

405

return response.json();

406

});

407

}

408

```

409

410

### Nested Context Updates

411

412

Update trace and observation from deeply nested function calls.

413

414

```typescript

415

async function deeplyNestedOperation() {

416

// Can update trace/observation from anywhere in the call stack

417

updateActiveTrace({

418

tags: ['nested-operation'],

419

metadata: { depth: 'level-3' }

420

});

421

422

updateActiveObservation({

423

metadata: { nestedCallCompleted: true }

424

});

425

}

426

427

async function middleOperation() {

428

await deeplyNestedOperation();

429

430

updateActiveObservation({

431

metadata: { middleCompleted: true }

432

});

433

}

434

435

await startActiveObservation('top-level', async () => {

436

await middleOperation();

437

438

updateActiveObservation({

439

output: { allLevelsCompleted: true }

440

});

441

});

442

```

443

444

### Conditional Trace Updates

445

446

Update trace attributes based on runtime conditions.

447

448

```typescript

449

await startActiveObservation('conditional-workflow', async (obs) => {

450

const user = await getCurrentUser();

451

452

// Update trace based on user type

453

if (user.role === 'admin') {

454

updateActiveTrace({

455

tags: ['admin-operation'],

456

metadata: { elevated: true }

457

});

458

} else if (user.role === 'premium') {

459

updateActiveTrace({

460

tags: ['premium-operation'],

461

metadata: { accountTier: 'premium' }

462

});

463

}

464

465

// Perform operation

466

const result = await processForUser(user);

467

468

// Update based on result

469

if (result.requiresReview) {

470

updateActiveTrace({

471

tags: ['requires-review'],

472

public: false // Keep sensitive operations private

473

});

474

}

475

});

476

```

477

478

## Type Safety

479

480

The `updateActiveObservation` function provides type-specific overloads for each observation type.

481

482

```typescript

483

// Span attributes (default)

484

updateActiveObservation({

485

input: { data: 'value' },

486

output: { result: 'success' },

487

metadata: { custom: 'info' }

488

});

489

490

// Generation attributes

491

updateActiveObservation({

492

model: 'gpt-4',

493

modelParameters: { temperature: 0.7 },

494

usageDetails: { totalTokens: 500 },

495

costDetails: { totalCost: 0.025 }

496

}, { asType: 'generation' });

497

498

// Agent attributes

499

updateActiveObservation({

500

output: {

501

toolsUsed: ['search', 'calculator'],

502

iterationsRequired: 3

503

},

504

metadata: { efficiency: 0.85 }

505

}, { asType: 'agent' });

506

507

// Tool attributes

508

updateActiveObservation({

509

output: {

510

result: searchResults,

511

latency: 1200

512

},

513

metadata: { cacheHit: false }

514

}, { asType: 'tool' });

515

```

516

517

## Context Requirements

518

519

All context management functions require an active OpenTelemetry span context:

520

521

### Valid Context

522

523

```typescript

524

// ✓ Valid: Inside startActiveObservation

525

await startActiveObservation('operation', async () => {

526

updateActiveTrace({ userId: 'user-123' }); // Works

527

updateActiveObservation({ output: { result: 'success' } }); // Works

528

const traceId = getActiveTraceId(); // Returns trace ID

529

});

530

531

// ✓ Valid: Inside observe

532

const tracedFn = observe(async () => {

533

updateActiveTrace({ tags: ['processed'] }); // Works

534

updateActiveObservation({ level: 'DEFAULT' }); // Works

535

});

536

537

// ✓ Valid: Inside startObservation with manual context

538

const span = startObservation('operation');

539

context.with(

540

trace.setSpan(context.active(), span.otelSpan),

541

() => {

542

updateActiveObservation({ output: { done: true } }); // Works

543

}

544

);

545

span.end();

546

```

547

548

### Invalid Context

549

550

```typescript

551

// ✗ Invalid: Outside any observation context

552

updateActiveTrace({ userId: 'user-123' }); // Warning logged, no effect

553

554

updateActiveObservation({ output: { result: 'success' } }); // Warning logged, no effect

555

556

const traceId = getActiveTraceId(); // Returns undefined

557

```

558

559

## Best Practices

560

561

### Use for Dynamic Context

562

563

Update traces when context is discovered during execution, not known upfront.

564

565

```typescript

566

// Good: Update when information becomes available

567

await startActiveObservation('api-request', async () => {

568

const token = extractToken(request);

569

const user = await validateToken(token);

570

571

updateActiveTrace({

572

userId: user.id,

573

sessionId: user.sessionId

574

});

575

576

return await processRequest(user);

577

});

578

579

// Less ideal: If you know the context upfront, pass it to the observation

580

await startActiveObservation('api-request', async (obs) => {

581

obs.updateTrace({

582

userId: knownUser.id,

583

sessionId: knownUser.sessionId

584

});

585

586

return await processRequest(knownUser);

587

});

588

```

589

590

### Prefer Observation.update() When Available

591

592

When you have a reference to the observation, use its `update()` method directly.

593

594

```typescript

595

// Preferred: Direct update when you have the reference

596

await startActiveObservation('operation', async (observation) => {

597

observation.update({ output: { result: 'success' } });

598

observation.updateTrace({ tags: ['completed'] });

599

});

600

601

// Use updateActiveObservation when you don't have the reference

602

async function helperFunction() {

603

// Called from within an observation but doesn't have the reference

604

updateActiveObservation({ metadata: { helperCalled: true } });

605

}

606

607

await startActiveObservation('main-operation', async () => {

608

await helperFunction();

609

});

610

```

611

612

### Handle Missing Context Gracefully

613

614

Check for active context before using IDs.

615

616

```typescript

617

function logOperationContext() {

618

const traceId = getActiveTraceId();

619

const spanId = getActiveSpanId();

620

621

if (traceId && spanId) {

622

console.log('Operation context:', { traceId, spanId });

623

} else {

624

console.log('No active tracing context');

625

}

626

}

627

628

// Works in both traced and untraced contexts

629

await startActiveObservation('operation', async () => {

630

logOperationContext(); // Logs trace and span IDs

631

});

632

633

logOperationContext(); // Logs "No active tracing context"

634

```

635

636

### Batch Related Updates

637

638

Group related attribute updates together for clarity.

639

640

```typescript

641

// Good: Batch related updates

642

await startActiveObservation('operation', async () => {

643

const result = await complexOperation();

644

645

updateActiveObservation({

646

output: result,

647

level: 'DEFAULT',

648

metadata: {

649

duration: result.duration,

650

itemsProcessed: result.count,

651

efficiency: result.efficiency

652

}

653

});

654

});

655

656

// Avoid: Multiple separate updates

657

await startActiveObservation('operation', async () => {

658

const result = await complexOperation();

659

660

updateActiveObservation({ output: result });

661

updateActiveObservation({ level: 'DEFAULT' });

662

updateActiveObservation({ metadata: { duration: result.duration } });

663

updateActiveObservation({ metadata: { itemsProcessed: result.count } });

664

});

665

```

666

667

### Consistent Attribute Types

668

669

Maintain consistent attribute types across updates.

670

671

```typescript

672

// Good: Consistent types

673

updateActiveObservation({

674

metadata: {

675

count: 10, // number

676

status: 'success', // string

677

enabled: true // boolean

678

}

679

});

680

681

// Later update maintains types

682

updateActiveObservation({

683

metadata: {

684

count: 20, // still number

685

status: 'complete', // still string

686

enabled: false // still boolean

687

}

688

});

689

```

690