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

manual-observations.mddocs/

0

# Manual Observations

1

2

Manual observation creation with `startObservation()` provides explicit control over the lifecycle of observations in your Langfuse traces. This approach gives you fine-grained control over when observations start and end, making it ideal for complex workflows where automatic lifecycle management isn't suitable.

3

4

## Core Function

5

6

### startObservation

7

8

Creates and starts a new Langfuse observation with automatic TypeScript type inference based on observation type.

9

10

```typescript { .api }

11

/**

12

* Creates and starts a new Langfuse observation with automatic TypeScript type inference.

13

*

14

* @param name - Descriptive name for the observation (e.g., 'openai-gpt-4', 'vector-search')

15

* @param attributes - Type-specific attributes (input, output, metadata, etc.)

16

* @param options - Configuration options including observation type and timing

17

* @returns Strongly-typed observation object based on `asType` parameter

18

*/

19

function startObservation(

20

name: string,

21

attributes?: LangfuseObservationAttributes,

22

options?: StartObservationOpts

23

): LangfuseObservation;

24

25

// Type-specific overloads for automatic type inference

26

function startObservation(

27

name: string,

28

attributes: LangfuseGenerationAttributes,

29

options: { asType: "generation" }

30

): LangfuseGeneration;

31

32

function startObservation(

33

name: string,

34

attributes: LangfuseEventAttributes,

35

options: { asType: "event" }

36

): LangfuseEvent;

37

38

function startObservation(

39

name: string,

40

attributes: LangfuseAgentAttributes,

41

options: { asType: "agent" }

42

): LangfuseAgent;

43

44

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

45

46

// Options interface

47

interface StartObservationOpts {

48

/** Type of observation to create. Defaults to 'span' */

49

asType?: LangfuseObservationType;

50

/** Custom start time for the observation */

51

startTime?: Date;

52

/** Parent span context to attach this observation to */

53

parentSpanContext?: SpanContext;

54

}

55

56

type LangfuseObservationType =

57

| "span"

58

| "generation"

59

| "event"

60

| "embedding"

61

| "agent"

62

| "tool"

63

| "chain"

64

| "retriever"

65

| "evaluator"

66

| "guardrail";

67

```

68

69

## Observation Types

70

71

### Span (Default)

72

73

General-purpose observation for tracking operations, functions, and workflows.

74

75

```typescript

76

import { startObservation } from '@langfuse/tracing';

77

78

// Default span creation

79

const span = startObservation('user-authentication', {

80

input: { username: 'john_doe', method: 'oauth' },

81

metadata: { provider: 'google' }

82

});

83

84

try {

85

const user = await authenticateUser(credentials);

86

span.update({

87

output: { userId: user.id, success: true }

88

});

89

} catch (error) {

90

span.update({

91

level: 'ERROR',

92

statusMessage: error.message,

93

output: { success: false, error: error.message }

94

});

95

} finally {

96

span.end();

97

}

98

```

99

100

### Generation

101

102

Specialized observation for LLM calls and AI model interactions.

103

104

```typescript

105

const generation = startObservation('openai-gpt-4', {

106

input: [

107

{ role: 'system', content: 'You are a helpful assistant.' },

108

{ role: 'user', content: 'Explain quantum computing' }

109

],

110

model: 'gpt-4-turbo',

111

modelParameters: {

112

temperature: 0.7,

113

maxTokens: 500

114

}

115

}, { asType: 'generation' });

116

117

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

118

model: 'gpt-4-turbo',

119

messages: generation.attributes.input,

120

temperature: 0.7,

121

max_tokens: 500

122

});

123

124

generation.update({

125

output: response.choices[0].message,

126

usageDetails: {

127

promptTokens: response.usage.prompt_tokens,

128

completionTokens: response.usage.completion_tokens,

129

totalTokens: response.usage.total_tokens

130

},

131

costDetails: {

132

totalCost: 0.025,

133

currency: 'USD'

134

}

135

});

136

137

generation.end();

138

```

139

140

### Agent

141

142

Observation for AI agent workflows with tool usage and autonomous operations.

143

144

```typescript

145

const agent = startObservation('research-agent', {

146

input: {

147

task: 'Research renewable energy trends',

148

tools: ['web-search', 'summarizer'],

149

maxIterations: 3

150

},

151

metadata: {

152

model: 'gpt-4',

153

strategy: 'react'

154

}

155

}, { asType: 'agent' });

156

157

// Agent performs multiple operations

158

const searchResults = await performWebSearch('renewable energy 2024');

159

const summary = await summarizeResults(searchResults);

160

161

agent.update({

162

output: {

163

completed: true,

164

toolsUsed: ['web-search', 'summarizer'],

165

iterationsRequired: 2,

166

finalResult: summary

167

},

168

metadata: {

169

efficiency: 0.85,

170

qualityScore: 0.92

171

}

172

});

173

174

agent.end();

175

```

176

177

### Tool

178

179

Observation for individual tool calls and external API interactions.

180

181

```typescript

182

const tool = startObservation('web-search', {

183

input: {

184

query: 'latest AI developments',

185

maxResults: 10

186

},

187

metadata: {

188

provider: 'google-api',

189

timeout: 5000

190

}

191

}, { asType: 'tool' });

192

193

try {

194

const results = await webSearch('latest AI developments');

195

196

tool.update({

197

output: {

198

results: results,

199

count: results.length,

200

relevanceScore: 0.89

201

},

202

metadata: {

203

latency: 1200,

204

cacheHit: false

205

}

206

});

207

} catch (error) {

208

tool.update({

209

level: 'ERROR',

210

statusMessage: 'Search failed',

211

output: { error: error.message }

212

});

213

} finally {

214

tool.end();

215

}

216

```

217

218

### Chain

219

220

Observation for structured multi-step workflows and process chains.

221

222

```typescript

223

const chain = startObservation('rag-pipeline', {

224

input: {

225

query: 'What is renewable energy?',

226

steps: ['retrieval', 'generation']

227

},

228

metadata: {

229

vectorDb: 'pinecone',

230

model: 'gpt-4'

231

}

232

}, { asType: 'chain' });

233

234

// Execute pipeline steps

235

const docs = await retrieveDocuments('renewable energy');

236

const response = await generateResponse(query, docs);

237

238

chain.update({

239

output: {

240

finalResponse: response,

241

stepsCompleted: 2,

242

documentsUsed: docs.length,

243

pipelineEfficiency: 0.87

244

}

245

});

246

247

chain.end();

248

```

249

250

### Retriever

251

252

Observation for document retrieval and search operations.

253

254

```typescript

255

const retriever = startObservation('vector-search', {

256

input: {

257

query: 'machine learning applications',

258

topK: 10,

259

similarityThreshold: 0.7

260

},

261

metadata: {

262

vectorDB: 'pinecone',

263

embeddingModel: 'text-embedding-ada-002',

264

similarity: 'cosine'

265

}

266

}, { asType: 'retriever' });

267

268

const results = await vectorDB.search({

269

query: 'machine learning applications',

270

topK: 10

271

});

272

273

retriever.update({

274

output: {

275

documents: results,

276

count: results.length,

277

avgSimilarity: 0.89

278

},

279

metadata: {

280

searchLatency: 150,

281

cacheHit: false

282

}

283

});

284

285

retriever.end();

286

```

287

288

### Evaluator

289

290

Observation for quality assessment and evaluation operations.

291

292

```typescript

293

const evaluator = startObservation('response-quality-eval', {

294

input: {

295

response: 'Machine learning is a subset of artificial intelligence...',

296

reference: 'Expected high-quality explanation',

297

criteria: ['accuracy', 'completeness', 'clarity']

298

},

299

metadata: {

300

evaluator: 'custom-bert-scorer',

301

threshold: 0.8

302

}

303

}, { asType: 'evaluator' });

304

305

const evaluation = await evaluateResponse({

306

response: inputText,

307

criteria: ['accuracy', 'completeness', 'clarity']

308

});

309

310

evaluator.update({

311

output: {

312

overallScore: 0.87,

313

criteriaScores: {

314

accuracy: 0.92,

315

completeness: 0.85,

316

clarity: 0.90

317

},

318

passed: true,

319

grade: 'excellent'

320

}

321

});

322

323

evaluator.end();

324

```

325

326

### Guardrail

327

328

Observation for safety checks and compliance enforcement.

329

330

```typescript

331

const guardrail = startObservation('content-safety-check', {

332

input: {

333

content: userMessage,

334

policies: ['no-toxicity', 'no-hate-speech', 'no-pii'],

335

strictMode: true

336

},

337

metadata: {

338

guardrailVersion: 'v2.1',

339

confidence: 0.95

340

}

341

}, { asType: 'guardrail' });

342

343

const safetyCheck = await checkContentSafety({

344

text: userMessage,

345

policies: ['no-toxicity', 'no-hate-speech']

346

});

347

348

guardrail.update({

349

output: {

350

safe: safetyCheck.safe,

351

riskScore: 0.15,

352

violations: [],

353

action: 'allow'

354

}

355

});

356

357

guardrail.end();

358

```

359

360

### Embedding

361

362

Observation for text embedding and vector generation operations.

363

364

```typescript

365

const embedding = startObservation('text-embedder', {

366

input: {

367

texts: [

368

'Machine learning is a subset of AI',

369

'Deep learning uses neural networks'

370

],

371

batchSize: 2

372

},

373

model: 'text-embedding-ada-002',

374

metadata: {

375

dimensions: 1536,

376

normalization: 'l2'

377

}

378

}, { asType: 'embedding' });

379

380

const embedResult = await generateEmbeddings({

381

texts: embedding.attributes.input.texts,

382

model: 'text-embedding-ada-002'

383

});

384

385

embedding.update({

386

output: {

387

embeddings: embedResult.vectors,

388

count: embedResult.vectors.length,

389

dimensions: 1536

390

},

391

usageDetails: {

392

totalTokens: embedResult.tokenCount

393

},

394

metadata: {

395

processingTime: 340

396

}

397

});

398

399

embedding.end();

400

```

401

402

### Event

403

404

Observation for point-in-time occurrences or log entries (automatically ended).

405

406

```typescript

407

// Events are automatically ended at creation

408

const event = startObservation('user-login', {

409

input: {

410

userId: '123',

411

method: 'oauth',

412

timestamp: new Date().toISOString()

413

},

414

level: 'DEFAULT',

415

metadata: {

416

ip: '192.168.1.1',

417

userAgent: 'Chrome/120.0',

418

sessionId: 'sess_456'

419

}

420

}, { asType: 'event' });

421

422

// No need to call event.end() - events are automatically ended

423

```

424

425

## Nested Observations

426

427

All observation types support creating child observations to build hierarchical traces.

428

429

```typescript

430

// Parent observation

431

const workflow = startObservation('ai-pipeline', {

432

input: { query: 'Explain quantum computing' }

433

});

434

435

// Create child retriever

436

const retrieval = workflow.startObservation('document-search', {

437

input: { query: 'quantum computing', topK: 5 }

438

}, { asType: 'retriever' });

439

440

const docs = await searchDocuments('quantum computing', 5);

441

retrieval.update({ output: { documents: docs, count: docs.length } });

442

retrieval.end();

443

444

// Create child generation

445

const generation = workflow.startObservation('response-generation', {

446

input: { query: 'Explain quantum computing', context: docs },

447

model: 'gpt-4'

448

}, { asType: 'generation' });

449

450

const response = await llm.generate({ prompt, context: docs });

451

generation.update({

452

output: response,

453

usageDetails: { totalTokens: 500 }

454

});

455

generation.end();

456

457

// Update parent with final results

458

workflow.update({

459

output: {

460

finalResponse: response,

461

childOperations: 2,

462

success: true

463

}

464

});

465

workflow.end();

466

```

467

468

## Observation Class Methods

469

470

All observation classes share common methods for lifecycle management and updates.

471

472

### update()

473

474

Updates the observation with new attributes. Returns the observation for method chaining.

475

476

```typescript { .api }

477

// For span observations

478

update(attributes: LangfuseSpanAttributes): LangfuseSpan;

479

480

// For generation observations

481

update(attributes: LangfuseGenerationAttributes): LangfuseGeneration;

482

483

// Each observation type has its corresponding update signature

484

```

485

486

**Example:**

487

488

```typescript

489

span.update({

490

output: { result: 'success' },

491

level: 'DEFAULT',

492

metadata: { duration: 150 }

493

});

494

```

495

496

### end()

497

498

Marks the observation as complete with optional end timestamp.

499

500

```typescript { .api }

501

/**

502

* Ends the observation, marking it as complete.

503

*

504

* @param endTime - Optional end time, defaults to current time

505

*/

506

end(endTime?: Date | number): void;

507

```

508

509

**Example:**

510

511

```typescript

512

const span = startObservation('operation');

513

// ... perform operation

514

span.end(); // End with current timestamp

515

516

// Or specify custom end time

517

const customEndTime = new Date();

518

span.end(customEndTime);

519

```

520

521

### updateTrace()

522

523

Updates the parent trace with trace-level attributes from within an observation.

524

525

```typescript { .api }

526

/**

527

* Updates the parent trace with new attributes.

528

*

529

* @param attributes - Trace attributes to set

530

* @returns This observation for method chaining

531

*/

532

updateTrace(attributes: LangfuseTraceAttributes): LangfuseObservation;

533

534

interface LangfuseTraceAttributes {

535

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

536

name?: string;

537

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

538

userId?: string;

539

/** Session identifier for grouping related traces */

540

sessionId?: string;

541

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

542

version?: string;

543

/** Release identifier for deployment tracking */

544

release?: string;

545

/** Input data that initiated the trace */

546

input?: unknown;

547

/** Final output data from the trace */

548

output?: unknown;

549

/** Additional metadata for the trace */

550

metadata?: unknown;

551

/** Tags for categorizing and filtering traces */

552

tags?: string[];

553

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

554

public?: boolean;

555

/** Environment where the trace was captured */

556

environment?: string;

557

}

558

```

559

560

**Example:**

561

562

```typescript

563

const observation = startObservation('api-request');

564

565

observation.updateTrace({

566

name: 'user-checkout',

567

userId: 'user-123',

568

sessionId: 'session-456',

569

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

570

metadata: { version: '2.1.0' }

571

});

572

573

observation.end();

574

```

575

576

### startObservation()

577

578

Creates a new child observation within this observation's context.

579

580

```typescript { .api }

581

/**

582

* Creates a new child observation within this observation's context.

583

*

584

* @param name - Descriptive name for the child observation

585

* @param attributes - Type-specific attributes

586

* @param options - Configuration including observation type

587

* @returns Strongly-typed observation instance based on `asType`

588

*/

589

startObservation(

590

name: string,

591

attributes?: LangfuseObservationAttributes,

592

options?: { asType?: LangfuseObservationType }

593

): LangfuseObservation;

594

```

595

596

**Example:**

597

598

```typescript

599

const parent = startObservation('workflow');

600

601

// Create child with default type (span)

602

const child1 = parent.startObservation('step-1', {

603

input: { data: 'value' }

604

});

605

606

// Create child with specific type

607

const child2 = parent.startObservation('llm-call', {

608

model: 'gpt-4',

609

input: 'prompt'

610

}, { asType: 'generation' });

611

612

child1.end();

613

child2.end();

614

parent.end();

615

```

616

617

## Common Observation Attributes

618

619

All observation types support these base attributes:

620

621

```typescript { .api }

622

interface LangfuseSpanAttributes {

623

/** Input data for the operation being tracked */

624

input?: unknown;

625

/** Output data from the operation */

626

output?: unknown;

627

/** Additional metadata as key-value pairs */

628

metadata?: Record<string, unknown>;

629

/** Severity level of the observation */

630

level?: "DEBUG" | "DEFAULT" | "WARNING" | "ERROR";

631

/** Human-readable status message */

632

statusMessage?: string;

633

/** Version identifier for the code/model being tracked */

634

version?: string;

635

/** Environment where the operation is running */

636

environment?: string;

637

}

638

```

639

640

## Generation-Specific Attributes

641

642

Generation and embedding observations support additional LLM-specific attributes:

643

644

```typescript { .api }

645

interface LangfuseGenerationAttributes extends LangfuseSpanAttributes {

646

/** Timestamp when the model started generating completion */

647

completionStartTime?: Date;

648

/** Name of the language model used */

649

model?: string;

650

/** Parameters passed to the model */

651

modelParameters?: {

652

[key: string]: string | number;

653

};

654

/** Token usage and other model-specific usage metrics */

655

usageDetails?: {

656

[key: string]: number;

657

} | OpenAiUsage;

658

/** Cost breakdown for the generation */

659

costDetails?: {

660

[key: string]: number;

661

};

662

/** Information about the prompt used from Langfuse prompt management */

663

prompt?: {

664

name: string;

665

version: number;

666

isFallback: boolean;

667

};

668

}

669

670

interface OpenAiUsage {

671

promptTokens?: number;

672

completionTokens?: number;

673

totalTokens?: number;

674

}

675

```

676

677

## Options Configuration

678

679

### Start Time

680

681

Specify a custom start time for backdating observations:

682

683

```typescript

684

const observation = startObservation('operation', {

685

input: { data: 'value' }

686

}, {

687

startTime: new Date('2024-01-01T10:00:00Z')

688

});

689

```

690

691

### Parent Context

692

693

Manually specify parent span context for custom hierarchies:

694

695

```typescript

696

import { startObservation } from '@langfuse/tracing';

697

698

const parent = startObservation('parent');

699

const parentContext = parent.otelSpan.spanContext();

700

701

// Create sibling observation by using same parent context

702

const child = startObservation('child', {

703

input: { step: 'processing' }

704

}, {

705

parentSpanContext: parentContext

706

});

707

708

child.end();

709

parent.end();

710

```

711

712

## Best Practices

713

714

### Always End Observations

715

716

Manual observations require explicit `end()` calls. Use try-finally blocks to ensure proper cleanup:

717

718

```typescript

719

const observation = startObservation('operation');

720

721

try {

722

// Perform operation

723

const result = await performWork();

724

observation.update({ output: result });

725

} catch (error) {

726

observation.update({

727

level: 'ERROR',

728

statusMessage: error.message

729

});

730

throw error;

731

} finally {

732

observation.end(); // Always end the observation

733

}

734

```

735

736

### Progressive Updates

737

738

Update observations progressively as information becomes available:

739

740

```typescript

741

const generation = startObservation('llm-call', {

742

model: 'gpt-4',

743

modelParameters: { temperature: 0.7 }

744

}, { asType: 'generation' });

745

746

// Set input

747

generation.update({

748

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

749

});

750

751

// Call API

752

const response = await llm.call();

753

754

// Update with output

755

generation.update({

756

output: response.message

757

});

758

759

// Add usage details

760

generation.update({

761

usageDetails: response.usage

762

});

763

764

generation.end();

765

```

766

767

### Meaningful Names

768

769

Use descriptive names that indicate the operation's purpose:

770

771

```typescript

772

// Good: Specific and descriptive

773

const retrieval = startObservation('pinecone-vector-search', {}, { asType: 'retriever' });

774

775

// Avoid: Generic and unclear

776

const span = startObservation('search');

777

```

778

779

### Structured Metadata

780

781

Use consistent metadata structure for better analytics:

782

783

```typescript

784

const observation = startObservation('api-call', {

785

input: request,

786

metadata: {

787

service: 'payment-gateway',

788

version: '2.1.0',

789

environment: 'production',

790

region: 'us-east-1'

791

}

792

});

793

```

794