or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mddatasets.mdindex.mdmedia.mdopenai-integration.mdprompts.mdpublic-api.mdtracing.md

openai-integration.mddocs/

0

# OpenAI Integration

1

2

Automatic tracing for OpenAI SDK calls using a proxy wrapper. The `observeOpenAI` function wraps your OpenAI client to automatically capture all API calls with inputs, outputs, token usage, and timing information.

3

4

## Capabilities

5

6

### ObserveOpenAI Function

7

8

Wraps an OpenAI SDK instance with automatic Langfuse tracing.

9

10

```typescript { .api }

11

/**

12

* Wraps an OpenAI SDK instance with automatic Langfuse tracing

13

* @param sdk - The OpenAI SDK instance to wrap

14

* @param langfuseConfig - Optional tracing configuration

15

* @returns Wrapped SDK with tracing and lifecycle methods

16

*/

17

function observeOpenAI<SDKType extends object>(

18

sdk: SDKType,

19

langfuseConfig?: LangfuseConfig

20

): SDKType & LangfuseExtension;

21

22

interface LangfuseExtension {

23

/**

24

* Flushes all pending Langfuse events

25

* @returns Promise that resolves when all events are sent

26

*/

27

flushAsync(): Promise<void>;

28

29

/**

30

* Shuts down the Langfuse client

31

* @returns Promise that resolves when shutdown is complete

32

*/

33

shutdownAsync(): Promise<void>;

34

}

35

```

36

37

**Usage Example:**

38

39

```typescript

40

import OpenAI from 'openai';

41

import { observeOpenAI } from 'langfuse';

42

43

const client = new OpenAI({

44

apiKey: process.env.OPENAI_API_KEY

45

});

46

47

// Wrap the client with tracing

48

const tracedClient = observeOpenAI(client, {

49

traceName: 'openai-chat',

50

userId: 'user-123',

51

sessionId: 'session-456'

52

});

53

54

// Use normally - all calls are automatically traced

55

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

56

messages: [

57

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

58

{ role: 'user', content: 'Hello!' }

59

],

60

model: 'gpt-3.5-turbo'

61

});

62

63

// Flush events to Langfuse

64

await tracedClient.flushAsync();

65

```

66

67

### Configuration Options

68

69

The `LangfuseConfig` type supports two modes: creating new traces or nesting under existing traces.

70

71

```typescript { .api }

72

type LangfuseConfig = (LangfuseNewTraceConfig | LangfuseWithParentConfig) & {

73

/** Optional name for the generation */

74

generationName?: string;

75

/** Optional prompt client for linking */

76

langfusePrompt?: LangfusePromptClient;

77

};

78

79

interface LangfuseNewTraceConfig {

80

/** Custom trace ID */

81

traceId?: string;

82

/** Trace name */

83

traceName?: string;

84

/** Session ID */

85

sessionId?: string;

86

/** User ID */

87

userId?: string;

88

/** Release version */

89

release?: string;

90

/** Version identifier */

91

version?: string;

92

/** Custom metadata */

93

metadata?: any;

94

/** Tags for filtering */

95

tags?: string[];

96

/** Client initialization parameters */

97

clientInitParams?: LangfuseInitParams;

98

}

99

100

interface LangfuseWithParentConfig {

101

/** Parent trace, span, or generation to nest under */

102

parent: LangfuseParent;

103

/** Custom metadata */

104

metadata?: any;

105

/** Version identifier */

106

version?: string;

107

/** Prompt name (deprecated, use langfusePrompt) */

108

promptName?: string;

109

/** Prompt version (deprecated, use langfusePrompt) */

110

promptVersion?: number;

111

}

112

113

type LangfuseParent =

114

| LangfuseTraceClient

115

| LangfuseSpanClient

116

| LangfuseGenerationClient;

117

118

interface LangfuseInitParams {

119

/** Langfuse public key */

120

publicKey?: string;

121

/** Langfuse secret key */

122

secretKey?: string;

123

/** Base URL for Langfuse API */

124

baseUrl?: string;

125

/** Additional Langfuse options */

126

[key: string]: any;

127

}

128

```

129

130

## Usage Patterns

131

132

### Basic Tracing

133

134

Simple wrapper for automatic tracing of all OpenAI calls.

135

136

```typescript

137

import OpenAI from 'openai';

138

import { observeOpenAI } from 'langfuse';

139

140

const client = new OpenAI();

141

const tracedClient = observeOpenAI(client);

142

143

// All methods are automatically traced

144

const completion = await tracedClient.chat.completions.create({

145

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

146

model: 'gpt-3.5-turbo'

147

});

148

149

await tracedClient.flushAsync();

150

```

151

152

### Named Traces

153

154

Provide custom trace names for better organization.

155

156

```typescript

157

const tracedClient = observeOpenAI(client, {

158

traceName: 'customer-support-chat',

159

userId: 'user-123',

160

sessionId: 'session-456',

161

metadata: {

162

environment: 'production',

163

region: 'us-east-1'

164

},

165

tags: ['support', 'chat']

166

});

167

168

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

169

messages: [{ role: 'user', content: 'I need help' }],

170

model: 'gpt-4'

171

});

172

173

await tracedClient.flushAsync();

174

```

175

176

### Custom Generation Names

177

178

Override the default generation name for specific calls.

179

180

```typescript

181

const tracedClient = observeOpenAI(client, {

182

traceName: 'document-processing',

183

generationName: 'document-summarization'

184

});

185

186

const summary = await tracedClient.chat.completions.create({

187

messages: [

188

{ role: 'system', content: 'Summarize the following document' },

189

{ role: 'user', content: documentText }

190

],

191

model: 'gpt-4'

192

});

193

194

await tracedClient.flushAsync();

195

```

196

197

### Nesting Under Existing Traces

198

199

Nest OpenAI calls under existing Langfuse traces for hierarchical tracing.

200

201

```typescript

202

import { Langfuse, observeOpenAI } from 'langfuse';

203

204

const langfuse = new Langfuse();

205

const client = new OpenAI();

206

207

// Create a parent trace

208

const trace = langfuse.trace({

209

name: 'rag-pipeline',

210

userId: 'user-123'

211

});

212

213

// Create a span for the retrieval step

214

const retrievalSpan = trace.span({

215

name: 'document-retrieval'

216

});

217

218

// ... perform retrieval ...

219

220

retrievalSpan.end({

221

output: { documents: retrievedDocs }

222

});

223

224

// Wrap OpenAI client to nest under the trace

225

const tracedClient = observeOpenAI(client, {

226

parent: trace,

227

generationName: 'answer-generation'

228

});

229

230

// OpenAI call will be nested under the trace

231

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

232

messages: [

233

{ role: 'system', content: 'Answer based on the context' },

234

{ role: 'user', content: query }

235

],

236

model: 'gpt-4'

237

});

238

239

trace.update({

240

output: { answer: response.choices[0].message.content }

241

});

242

243

await langfuse.flushAsync();

244

```

245

246

### Nesting Under Spans

247

248

Nest OpenAI calls under specific spans for detailed tracing.

249

250

```typescript

251

const trace = langfuse.trace({ name: 'multi-step-process' });

252

253

const step1Span = trace.span({ name: 'step-1' });

254

255

// Nest OpenAI call under this span

256

const tracedClient = observeOpenAI(client, {

257

parent: step1Span,

258

generationName: 'step-1-generation'

259

});

260

261

const result = await tracedClient.chat.completions.create({

262

messages: [{ role: 'user', content: 'Step 1 prompt' }],

263

model: 'gpt-3.5-turbo'

264

});

265

266

step1Span.end({

267

output: { result: result.choices[0].message.content }

268

});

269

270

await langfuse.flushAsync();

271

```

272

273

### Linking Prompts

274

275

Link prompt templates to OpenAI generations for version tracking.

276

277

```typescript

278

import { Langfuse, observeOpenAI } from 'langfuse';

279

280

const langfuse = new Langfuse();

281

const client = new OpenAI();

282

283

// Fetch a prompt

284

const prompt = await langfuse.getPrompt('chat-template', undefined, {

285

type: 'chat'

286

});

287

288

// Compile the prompt

289

const messages = prompt.compile(

290

{ topic: 'AI', tone: 'professional' },

291

{ history: [] }

292

);

293

294

// Wrap OpenAI with prompt linking

295

const tracedClient = observeOpenAI(client, {

296

traceName: 'templated-chat',

297

langfusePrompt: prompt

298

});

299

300

// The generation will be linked to the prompt version

301

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

302

messages: messages,

303

model: 'gpt-4'

304

});

305

306

await tracedClient.flushAsync();

307

```

308

309

### Streaming Responses

310

311

Automatic tracing works with streaming responses.

312

313

```typescript

314

const tracedClient = observeOpenAI(client, {

315

traceName: 'streaming-chat'

316

});

317

318

const stream = await tracedClient.chat.completions.create({

319

messages: [{ role: 'user', content: 'Tell me a story' }],

320

model: 'gpt-4',

321

stream: true

322

});

323

324

let fullContent = '';

325

326

for await (const chunk of stream) {

327

const content = chunk.choices[0]?.delta?.content || '';

328

fullContent += content;

329

process.stdout.write(content);

330

}

331

332

// Streaming responses are automatically captured

333

await tracedClient.flushAsync();

334

```

335

336

### Multiple OpenAI Calls

337

338

Each wrapped client can make multiple calls, all traced under the same configuration.

339

340

```typescript

341

const tracedClient = observeOpenAI(client, {

342

traceName: 'multi-call-workflow',

343

sessionId: 'session-789'

344

});

345

346

// First call

347

const classification = await tracedClient.chat.completions.create({

348

messages: [

349

{ role: 'system', content: 'Classify the intent' },

350

{ role: 'user', content: userMessage }

351

],

352

model: 'gpt-3.5-turbo'

353

});

354

355

// Second call (both under same trace)

356

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

357

messages: [

358

{ role: 'system', content: 'Generate response' },

359

{ role: 'user', content: userMessage }

360

],

361

model: 'gpt-4'

362

});

363

364

await tracedClient.flushAsync();

365

```

366

367

### Embeddings and Other Methods

368

369

All OpenAI SDK methods are automatically traced, not just chat completions.

370

371

```typescript

372

const tracedClient = observeOpenAI(client, {

373

traceName: 'embedding-pipeline'

374

});

375

376

// Embeddings are automatically traced

377

const embeddings = await tracedClient.embeddings.create({

378

input: 'Text to embed',

379

model: 'text-embedding-ada-002'

380

});

381

382

// Completions are traced

383

const completion = await tracedClient.completions.create({

384

prompt: 'Once upon a time',

385

model: 'gpt-3.5-turbo-instruct',

386

max_tokens: 100

387

});

388

389

await tracedClient.flushAsync();

390

```

391

392

### Custom Client Initialization

393

394

Provide custom Langfuse client initialization parameters.

395

396

```typescript

397

const tracedClient = observeOpenAI(client, {

398

traceName: 'custom-config',

399

clientInitParams: {

400

publicKey: 'custom-public-key',

401

secretKey: 'custom-secret-key',

402

baseUrl: 'https://custom-langfuse.com',

403

flushAt: 1, // Flush after every event

404

flushInterval: 5000

405

}

406

});

407

408

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

409

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

410

model: 'gpt-3.5-turbo'

411

});

412

413

await tracedClient.flushAsync();

414

```

415

416

## Complete OpenAI Integration Example

417

418

```typescript

419

import OpenAI from 'openai';

420

import { Langfuse, observeOpenAI } from 'langfuse';

421

422

// Initialize clients

423

const langfuse = new Langfuse();

424

const openai = new OpenAI();

425

426

// Create a parent trace for the entire workflow

427

const trace = langfuse.trace({

428

name: 'rag-qa-system',

429

userId: 'user-456',

430

sessionId: 'session-123',

431

tags: ['production', 'qa']

432

});

433

434

// Step 1: Generate query embedding

435

const embeddingClient = observeOpenAI(openai, {

436

parent: trace,

437

generationName: 'query-embedding'

438

});

439

440

const queryEmbedding = await embeddingClient.embeddings.create({

441

input: 'What is machine learning?',

442

model: 'text-embedding-ada-002'

443

});

444

445

// Step 2: Retrieve documents (simulated)

446

const retrievalSpan = trace.span({

447

name: 'document-retrieval',

448

input: { query: 'What is machine learning?' }

449

});

450

451

const documents = await retrieveDocuments(queryEmbedding.data[0].embedding);

452

453

retrievalSpan.end({

454

output: { documentCount: documents.length }

455

});

456

457

// Step 3: Generate answer with context

458

const prompt = await langfuse.getPrompt('qa-with-context', undefined, {

459

type: 'chat'

460

});

461

462

const messages = prompt.compile(

463

{

464

context: documents.join('\n'),

465

question: 'What is machine learning?'

466

},

467

{ history: [] }

468

);

469

470

const answerClient = observeOpenAI(openai, {

471

parent: trace,

472

generationName: 'answer-generation',

473

langfusePrompt: prompt

474

});

475

476

const answer = await answerClient.chat.completions.create({

477

messages: messages,

478

model: 'gpt-4',

479

temperature: 0.7,

480

max_tokens: 500

481

});

482

483

// Update trace with final output

484

trace.update({

485

output: {

486

answer: answer.choices[0].message.content,

487

model: 'gpt-4',

488

promptVersion: prompt.version

489

}

490

});

491

492

// Add a quality score

493

trace.score({

494

name: 'answer-quality',

495

value: 0.95,

496

comment: 'High quality answer with proper context'

497

});

498

499

// Flush all events

500

await langfuse.flushAsync();

501

502

// Get trace URL

503

console.log('View trace:', trace.getTraceUrl());

504

```

505

506

## Error Handling

507

508

The wrapper preserves errors from the OpenAI SDK while still capturing trace information.

509

510

```typescript

511

const tracedClient = observeOpenAI(client, {

512

traceName: 'error-handling-example'

513

});

514

515

try {

516

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

517

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

518

model: 'invalid-model' // This will cause an error

519

});

520

} catch (error) {

521

console.error('OpenAI error:', error);

522

// Error is captured in the trace with ERROR level

523

} finally {

524

await tracedClient.flushAsync();

525

}

526

```

527

528

## Performance Considerations

529

530

The `observeOpenAI` wrapper adds minimal overhead:

531

532

- **Proxy Pattern**: Uses JavaScript Proxy for transparent method interception

533

- **Async Tracing**: Events are queued and sent asynchronously without blocking OpenAI calls

534

- **Batching**: Multiple events are batched together for efficient network usage

535

- **Caching**: Prompt caching reduces API calls to Langfuse

536

537

**Best Practices:**

538

539

```typescript

540

// Good: Create one wrapper per workflow

541

const tracedClient = observeOpenAI(client, { traceName: 'workflow' });

542

await tracedClient.chat.completions.create(/* ... */);

543

await tracedClient.chat.completions.create(/* ... */);

544

await tracedClient.flushAsync();

545

546

// Avoid: Creating multiple wrappers for single calls

547

// This works but creates unnecessary overhead

548

const client1 = observeOpenAI(client, { traceName: 'call1' });

549

await client1.chat.completions.create(/* ... */);

550

await client1.flushAsync();

551

552

const client2 = observeOpenAI(client, { traceName: 'call2' });

553

await client2.chat.completions.create(/* ... */);

554

await client2.flushAsync();

555

```

556