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

attribute-creation.mddocs/

0

# Attribute Creation

1

2

Attribute creation functions convert user-friendly Langfuse attributes into OpenTelemetry attribute format for span processors. These functions handle serialization, flattening, and formatting of trace and observation attributes.

3

4

## Core Functions

5

6

### createTraceAttributes

7

8

Converts Langfuse trace attributes into OpenTelemetry attributes.

9

10

```typescript { .api }

11

/**

12

* Creates OpenTelemetry attributes from Langfuse trace attributes.

13

*

14

* Converts user-friendly trace attributes into the internal OpenTelemetry

15

* attribute format required by the span processor.

16

*

17

* @param attributes - Langfuse trace attributes to convert

18

* @returns OpenTelemetry attributes object with non-null values

19

*/

20

function createTraceAttributes(

21

attributes?: LangfuseTraceAttributes

22

): Attributes;

23

24

interface LangfuseTraceAttributes {

25

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

26

name?: string;

27

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

28

userId?: string;

29

/** Session identifier for grouping related traces */

30

sessionId?: string;

31

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

32

version?: string;

33

/** Release identifier for deployment tracking */

34

release?: string;

35

/** Input data that initiated the trace */

36

input?: unknown;

37

/** Final output data from the trace */

38

output?: unknown;

39

/** Additional metadata for the trace */

40

metadata?: unknown;

41

/** Tags for categorizing and filtering traces */

42

tags?: string[];

43

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

44

public?: boolean;

45

/** Environment where the trace was captured */

46

environment?: string;

47

}

48

49

// OpenTelemetry Attributes type

50

type Attributes = Record<string, string | number | boolean | Array<string | number | boolean>>;

51

```

52

53

**Usage:**

54

55

```typescript

56

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

57

58

const otelAttributes = createTraceAttributes({

59

name: 'user-checkout-flow',

60

userId: 'user-123',

61

sessionId: 'session-456',

62

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

63

metadata: { version: '2.1.0', cartValue: 99.99 },

64

input: { items: [{ id: '1', name: 'Product A' }] },

65

output: { orderId: 'ord-789', success: true },

66

environment: 'production',

67

public: false

68

});

69

70

// Apply to span

71

span.setAttributes(otelAttributes);

72

```

73

74

### createObservationAttributes

75

76

Converts Langfuse observation attributes into OpenTelemetry attributes based on observation type.

77

78

```typescript { .api }

79

/**

80

* Creates OpenTelemetry attributes from Langfuse observation attributes.

81

*

82

* @param type - The observation type (span, generation, event, etc.)

83

* @param attributes - Langfuse observation attributes to convert

84

* @returns OpenTelemetry attributes object with non-null values

85

*/

86

function createObservationAttributes(

87

type: LangfuseObservationType,

88

attributes: LangfuseObservationAttributes

89

): Attributes;

90

91

type LangfuseObservationType =

92

| "span"

93

| "generation"

94

| "event"

95

| "embedding"

96

| "agent"

97

| "tool"

98

| "chain"

99

| "retriever"

100

| "evaluator"

101

| "guardrail";

102

103

interface LangfuseObservationAttributes {

104

/** Input data for the operation */

105

input?: unknown;

106

/** Output data from the operation */

107

output?: unknown;

108

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

109

metadata?: Record<string, unknown>;

110

/** Severity level */

111

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

112

/** Human-readable status message */

113

statusMessage?: string;

114

/** Version identifier */

115

version?: string;

116

/** Environment identifier */

117

environment?: string;

118

119

// Generation-specific attributes

120

/** Timestamp when model started generating completion */

121

completionStartTime?: Date;

122

/** Name of the language model used */

123

model?: string;

124

/** Parameters passed to the model */

125

modelParameters?: { [key: string]: string | number };

126

/** Token usage and other metrics */

127

usageDetails?: { [key: string]: number };

128

/** Cost breakdown */

129

costDetails?: { [key: string]: number };

130

/** Prompt information */

131

prompt?: { name: string; version: number; isFallback: boolean };

132

}

133

```

134

135

**Usage:**

136

137

```typescript

138

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

139

140

// Span attributes

141

const spanAttrs = createObservationAttributes('span', {

142

input: { userId: '123', action: 'checkout' },

143

output: { success: true, orderId: 'ord-456' },

144

metadata: { duration: 1500 },

145

level: 'DEFAULT'

146

});

147

148

// Generation attributes

149

const genAttrs = createObservationAttributes('generation', {

150

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

151

output: { role: 'assistant', content: 'Hi there!' },

152

model: 'gpt-4',

153

modelParameters: { temperature: 0.7, maxTokens: 500 },

154

usageDetails: {

155

promptTokens: 10,

156

completionTokens: 15,

157

totalTokens: 25

158

},

159

costDetails: { totalCost: 0.001 }

160

});

161

162

span.setAttributes(spanAttrs);

163

```

164

165

## Attribute Processing

166

167

### Serialization

168

169

Complex objects are automatically serialized to JSON strings.

170

171

```typescript

172

const attributes = createTraceAttributes({

173

input: {

174

userId: '123',

175

items: [

176

{ id: 'item-1', quantity: 2 },

177

{ id: 'item-2', quantity: 1 }

178

]

179

},

180

metadata: {

181

timestamp: new Date(),

182

config: { retries: 3, timeout: 5000 }

183

}

184

});

185

186

// Result (simplified):

187

// {

188

// 'langfuse.trace.input': '{"userId":"123","items":[{"id":"item-1","quantity":2},{"id":"item-2","quantity":1}]}',

189

// 'langfuse.metadata.timestamp': '"2024-01-01T00:00:00.000Z"',

190

// 'langfuse.metadata.config': '{"retries":3,"timeout":5000}'

191

// }

192

```

193

194

### Metadata Flattening

195

196

Metadata objects are flattened into dot-notation keys.

197

198

```typescript

199

const attributes = createTraceAttributes({

200

metadata: {

201

database: {

202

host: 'localhost',

203

port: 5432,

204

name: 'mydb'

205

},

206

cache: {

207

enabled: true,

208

ttl: 3600

209

}

210

}

211

});

212

213

// Result (simplified):

214

// {

215

// 'langfuse.metadata.database.host': 'localhost',

216

// 'langfuse.metadata.database.port': '5432',

217

// 'langfuse.metadata.database.name': 'mydb',

218

// 'langfuse.metadata.cache.enabled': 'true',

219

// 'langfuse.metadata.cache.ttl': '3600'

220

// }

221

```

222

223

### Null Value Filtering

224

225

Null and undefined values are automatically filtered out.

226

227

```typescript

228

const attributes = createTraceAttributes({

229

name: 'my-trace',

230

userId: 'user-123',

231

sessionId: undefined, // Excluded

232

version: null, // Excluded

233

tags: ['tag1', 'tag2']

234

});

235

236

// Result only includes non-null values:

237

// {

238

// 'langfuse.trace.name': 'my-trace',

239

// 'langfuse.trace.userId': 'user-123',

240

// 'langfuse.trace.tags': ['tag1', 'tag2']

241

// }

242

```

243

244

## Use Cases

245

246

### Direct Span Updates

247

248

Apply attributes directly to OpenTelemetry spans.

249

250

```typescript

251

import { trace } from '@opentelemetry/api';

252

import { createTraceAttributes, createObservationAttributes } from '@langfuse/tracing';

253

254

const tracer = trace.getTracer('my-tracer');

255

const span = tracer.startSpan('my-operation');

256

257

// Set trace attributes

258

span.setAttributes(createTraceAttributes({

259

name: 'user-workflow',

260

userId: 'user-123',

261

tags: ['production']

262

}));

263

264

// Set observation attributes

265

span.setAttributes(createObservationAttributes('span', {

266

input: { action: 'process' },

267

output: { success: true }

268

}));

269

270

span.end();

271

```

272

273

### Custom Span Processors

274

275

Use in custom span processors for attribute transformation.

276

277

```typescript

278

import { SpanProcessor, Span, ReadableSpan } from '@opentelemetry/sdk-trace-base';

279

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

280

281

class CustomSpanProcessor implements SpanProcessor {

282

onStart(span: Span): void {

283

// Add default attributes to all spans

284

const defaultAttrs = createObservationAttributes('span', {

285

metadata: {

286

processor: 'custom',

287

version: '1.0.0'

288

}

289

});

290

291

span.setAttributes(defaultAttrs);

292

}

293

294

onEnd(span: ReadableSpan): void {

295

// Process completed span

296

}

297

298

shutdown(): Promise<void> {

299

return Promise.resolve();

300

}

301

302

forceFlush(): Promise<void> {

303

return Promise.resolve();

304

}

305

}

306

```

307

308

### Attribute Transformation

309

310

Transform attributes before applying to spans.

311

312

```typescript

313

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

314

315

function enrichObservationAttributes(

316

type: LangfuseObservationType,

317

baseAttributes: LangfuseObservationAttributes,

318

enrichments: Record<string, unknown>

319

): Attributes {

320

// Merge base attributes with enrichments

321

const enrichedAttributes = {

322

...baseAttributes,

323

metadata: {

324

...baseAttributes.metadata,

325

...enrichments

326

}

327

};

328

329

return createObservationAttributes(type, enrichedAttributes);

330

}

331

332

const attributes = enrichObservationAttributes(

333

'generation',

334

{

335

model: 'gpt-4',

336

input: 'Hello'

337

},

338

{

339

requestId: 'req-123',

340

timestamp: Date.now(),

341

region: 'us-east-1'

342

}

343

);

344

```

345

346

### Testing and Validation

347

348

Validate attribute structure in tests.

349

350

```typescript

351

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

352

353

describe('Trace attributes', () => {

354

it('should create valid OpenTelemetry attributes', () => {

355

const attributes = createTraceAttributes({

356

name: 'test-trace',

357

userId: 'user-123',

358

tags: ['test', 'validation']

359

});

360

361

// Validate structure

362

expect(attributes).toHaveProperty('langfuse.trace.name', 'test-trace');

363

expect(attributes).toHaveProperty('langfuse.trace.userId', 'user-123');

364

expect(attributes).toHaveProperty('langfuse.trace.tags', ['test', 'validation']);

365

});

366

367

it('should filter out null values', () => {

368

const attributes = createTraceAttributes({

369

name: 'test-trace',

370

userId: undefined,

371

sessionId: null

372

});

373

374

// Should not include null/undefined

375

expect(attributes).not.toHaveProperty('langfuse.trace.userId');

376

expect(attributes).not.toHaveProperty('langfuse.trace.sessionId');

377

});

378

});

379

```

380

381

### Attribute Inspection

382

383

Inspect generated attributes for debugging.

384

385

```typescript

386

import { createTraceAttributes, createObservationAttributes } from '@langfuse/tracing';

387

388

function logAttributes(

389

name: string,

390

attributes: Attributes

391

): void {

392

console.log(`\n=== ${name} ===`);

393

for (const [key, value] of Object.entries(attributes)) {

394

console.log(`${key}: ${JSON.stringify(value)}`);

395

}

396

}

397

398

const traceAttrs = createTraceAttributes({

399

name: 'debug-trace',

400

userId: 'user-123',

401

metadata: { debug: true }

402

});

403

404

const obsAttrs = createObservationAttributes('span', {

405

input: { test: true },

406

output: { result: 'success' }

407

});

408

409

logAttributes('Trace Attributes', traceAttrs);

410

logAttributes('Observation Attributes', obsAttrs);

411

```

412

413

## Advanced Patterns

414

415

### Conditional Attribute Creation

416

417

Create attributes conditionally based on environment or configuration.

418

419

```typescript

420

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

421

422

function createConditionalTraceAttributes(

423

baseAttributes: LangfuseTraceAttributes,

424

options: { includeInput?: boolean; includeOutput?: boolean } = {}

425

): Attributes {

426

const attributes: LangfuseTraceAttributes = {

427

name: baseAttributes.name,

428

userId: baseAttributes.userId,

429

sessionId: baseAttributes.sessionId

430

};

431

432

// Conditionally include input/output

433

if (options.includeInput && baseAttributes.input) {

434

attributes.input = baseAttributes.input;

435

}

436

437

if (options.includeOutput && baseAttributes.output) {

438

attributes.output = baseAttributes.output;

439

}

440

441

return createTraceAttributes(attributes);

442

}

443

444

// Production: exclude sensitive data

445

const prodAttrs = createConditionalTraceAttributes(

446

{ name: 'trace', input: { sensitive: 'data' } },

447

{ includeInput: false }

448

);

449

450

// Development: include all data

451

const devAttrs = createConditionalTraceAttributes(

452

{ name: 'trace', input: { debug: 'info' } },

453

{ includeInput: true }

454

);

455

```

456

457

### Attribute Merging

458

459

Merge multiple attribute sets.

460

461

```typescript

462

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

463

464

function mergeObservationAttributes(

465

type: LangfuseObservationType,

466

...attributeSets: LangfuseObservationAttributes[]

467

): Attributes {

468

// Merge all attribute sets

469

const merged: LangfuseObservationAttributes = {};

470

471

for (const attrs of attributeSets) {

472

Object.assign(merged, attrs);

473

474

// Merge metadata deeply

475

if (attrs.metadata) {

476

merged.metadata = {

477

...merged.metadata,

478

...attrs.metadata

479

};

480

}

481

}

482

483

return createObservationAttributes(type, merged);

484

}

485

486

const baseAttrs = { level: 'DEFAULT' as const };

487

const inputAttrs = { input: { data: 'value' } };

488

const outputAttrs = { output: { result: 'success' } };

489

const metadataAttrs = { metadata: { timestamp: Date.now() } };

490

491

const combined = mergeObservationAttributes(

492

'span',

493

baseAttrs,

494

inputAttrs,

495

outputAttrs,

496

metadataAttrs

497

);

498

```

499

500

### Attribute Sanitization

501

502

Sanitize sensitive data before creating attributes.

503

504

```typescript

505

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

506

507

function sanitizeTraceAttributes(

508

attributes: LangfuseTraceAttributes

509

): Attributes {

510

const sanitized: LangfuseTraceAttributes = { ...attributes };

511

512

// Remove sensitive fields from input

513

if (sanitized.input && typeof sanitized.input === 'object') {

514

const input = { ...sanitized.input as any };

515

delete input.password;

516

delete input.apiKey;

517

delete input.token;

518

sanitized.input = input;

519

}

520

521

// Remove sensitive fields from output

522

if (sanitized.output && typeof sanitized.output === 'object') {

523

const output = { ...sanitized.output as any };

524

delete output.creditCard;

525

delete output.ssn;

526

sanitized.output = output;

527

}

528

529

return createTraceAttributes(sanitized);

530

}

531

532

const attributes = sanitizeTraceAttributes({

533

name: 'secure-trace',

534

input: {

535

username: 'user123',

536

password: 'secret123' // Will be removed

537

},

538

output: {

539

userId: '123',

540

token: 'jwt-token' // Will be removed

541

}

542

});

543

```

544

545

### Attribute Validation

546

547

Validate attributes before creation.

548

549

```typescript

550

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

551

552

function validateAndCreateAttributes(

553

type: LangfuseObservationType,

554

attributes: LangfuseObservationAttributes

555

): Attributes | null {

556

// Validate required fields

557

if (type === 'generation') {

558

if (!attributes.model) {

559

console.error('Generation requires model attribute');

560

return null;

561

}

562

}

563

564

// Validate level

565

if (attributes.level) {

566

const validLevels = ['DEBUG', 'DEFAULT', 'WARNING', 'ERROR'];

567

if (!validLevels.includes(attributes.level)) {

568

console.error(`Invalid level: ${attributes.level}`);

569

return null;

570

}

571

}

572

573

// Validate metadata

574

if (attributes.metadata) {

575

try {

576

JSON.stringify(attributes.metadata);

577

} catch (error) {

578

console.error('Metadata must be JSON serializable');

579

return null;

580

}

581

}

582

583

return createObservationAttributes(type, attributes);

584

}

585

```

586

587

## Best Practices

588

589

### Use Appropriate Functions

590

591

Use the correct function for trace vs observation attributes.

592

593

```typescript

594

// Good: Use createTraceAttributes for trace-level data

595

const traceAttrs = createTraceAttributes({

596

name: 'my-trace',

597

userId: 'user-123'

598

});

599

600

// Good: Use createObservationAttributes for observation-level data

601

const obsAttrs = createObservationAttributes('span', {

602

input: { data: 'value' },

603

output: { result: 'success' }

604

});

605

606

// Avoid: Using wrong function

607

const wrongAttrs = createObservationAttributes('span', {

608

name: 'trace-name' // This is a trace attribute

609

} as any);

610

```

611

612

### Handle Serialization Failures

613

614

Account for objects that may fail to serialize.

615

616

```typescript

617

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

618

619

function safeCreateAttributes(

620

type: LangfuseObservationType,

621

attributes: LangfuseObservationAttributes

622

): Attributes {

623

try {

624

return createObservationAttributes(type, attributes);

625

} catch (error) {

626

console.error('Failed to create attributes:', error);

627

628

// Return minimal valid attributes

629

return createObservationAttributes(type, {

630

metadata: { error: 'Failed to serialize attributes' }

631

});

632

}

633

}

634

```

635

636

### Keep Metadata Structured

637

638

Maintain consistent metadata structure for better querying.

639

640

```typescript

641

// Good: Structured metadata

642

const attributes = createTraceAttributes({

643

metadata: {

644

performance: {

645

duration: 1500,

646

memoryUsed: 128

647

},

648

context: {

649

region: 'us-east-1',

650

environment: 'production'

651

}

652

}

653

});

654

655

// Avoid: Flat, inconsistent structure

656

const attributes = createTraceAttributes({

657

metadata: {

658

durationMs: 1500,

659

memory: 128,

660

region: 'us-east-1',

661

env: 'prod'

662

}

663

});

664

```

665

666

### Avoid Excessive Nesting

667

668

Keep metadata nesting reasonable for readability.

669

670

```typescript

671

// Good: Reasonable depth

672

const attributes = createTraceAttributes({

673

metadata: {

674

database: { host: 'localhost', port: 5432 }

675

}

676

});

677

678

// Avoid: Excessive nesting

679

const attributes = createTraceAttributes({

680

metadata: {

681

system: {

682

subsystem: {

683

component: {

684

subcomponent: {

685

detail: { value: 'deeply-nested' }

686

}

687

}

688

}

689

}

690

}

691

});

692

```

693

694

### Use TypeScript Types

695

696

Leverage TypeScript for type safety.

697

698

```typescript

699

import {

700

createTraceAttributes,

701

createObservationAttributes,

702

LangfuseTraceAttributes,

703

LangfuseObservationAttributes,

704

LangfuseObservationType

705

} from '@langfuse/tracing';

706

707

function createTypedAttributes(

708

traceAttrs: LangfuseTraceAttributes,

709

obsType: LangfuseObservationType,

710

obsAttrs: LangfuseObservationAttributes

711

) {

712

const trace = createTraceAttributes(traceAttrs);

713

const observation = createObservationAttributes(obsType, obsAttrs);

714

715

return { trace, observation };

716

}

717

718

// TypeScript ensures correct structure

719

const attrs = createTypedAttributes(

720

{ name: 'trace', userId: 'user-123' },

721

'generation',

722

{ model: 'gpt-4', input: 'Hello' }

723

);

724

```

725