@posthog/ai 8.8.0 → 8.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -340,6 +340,11 @@ const formatResponseOpenAI = response => {
340
340
  arguments: item.arguments || {}
341
341
  }
342
342
  });
343
+ } else if (item.type === 'image_generation_call' && item.result) {
344
+ content.push({
345
+ type: 'image',
346
+ image: item.result
347
+ });
343
348
  }
344
349
  }
345
350
  if (content.length > 0) {
@@ -572,7 +577,7 @@ function formatOpenAIResponsesInput(input, instructions) {
572
577
  return messages;
573
578
  }
574
579
 
575
- var version = "8.8.0";
580
+ var version = "8.9.0";
576
581
 
577
582
  const DEFAULT_MAX_DEPTH = 3;
578
583
  const MAX_STACK_LINES = 20;
@@ -1318,6 +1323,340 @@ function monitoredStreamTee(source, createStream) {
1318
1323
  return [monitoringStream, callerStream];
1319
1324
  }
1320
1325
 
1326
+ /** Pure state accumulator for OpenAI-compatible Chat Completions chunks. */
1327
+ class OpenAIChatStreamAccumulator {
1328
+ accumulatedContent = '';
1329
+ usage = {
1330
+ inputTokens: 0,
1331
+ outputTokens: 0,
1332
+ webSearchCount: 0
1333
+ };
1334
+ toolCalls = new Map();
1335
+ consume(chunk, receivedAt = Date.now()) {
1336
+ this.model ||= chunk.model || undefined;
1337
+ this.completionId ||= chunk.id || undefined;
1338
+ this.systemFingerprint ||= chunk.system_fingerprint || undefined;
1339
+ if (chunk.service_tier != null) {
1340
+ this.serviceTier = chunk.service_tier;
1341
+ }
1342
+ const choice = chunk.choices?.[0];
1343
+ if (choice?.finish_reason) {
1344
+ this.stopReason = choice.finish_reason;
1345
+ }
1346
+ const webSearchCount = calculateWebSearchCount(chunk);
1347
+ if (webSearchCount > (this.usage.webSearchCount ?? 0)) {
1348
+ this.usage.webSearchCount = webSearchCount;
1349
+ }
1350
+ if (choice?.delta?.content) {
1351
+ this.firstTokenTime ??= receivedAt;
1352
+ this.accumulatedContent += choice.delta.content;
1353
+ }
1354
+ if (Array.isArray(choice?.delta?.tool_calls)) {
1355
+ this.firstTokenTime ??= receivedAt;
1356
+ for (const toolCall of choice.delta.tool_calls) {
1357
+ if (toolCall.index === undefined) {
1358
+ continue;
1359
+ }
1360
+ const current = this.toolCalls.get(toolCall.index) ?? {
1361
+ id: '',
1362
+ name: '',
1363
+ arguments: ''
1364
+ };
1365
+ if (toolCall.id) {
1366
+ current.id = toolCall.id;
1367
+ }
1368
+ if (toolCall.function?.name) {
1369
+ current.name = toolCall.function.name;
1370
+ }
1371
+ if (toolCall.function?.arguments) {
1372
+ current.arguments += toolCall.function.arguments;
1373
+ }
1374
+ this.toolCalls.set(toolCall.index, current);
1375
+ }
1376
+ }
1377
+ if (chunk.usage) {
1378
+ this.usage = {
1379
+ ...this.usage,
1380
+ inputTokens: chunk.usage.prompt_tokens ?? 0,
1381
+ outputTokens: chunk.usage.completion_tokens ?? 0,
1382
+ reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? 0,
1383
+ cacheReadInputTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0,
1384
+ cacheCreationInputTokens: extractCacheWriteTokens(chunk.usage.prompt_tokens_details),
1385
+ rawUsage: chunk.usage
1386
+ };
1387
+ }
1388
+ }
1389
+ result() {
1390
+ const content = [];
1391
+ if (this.accumulatedContent) {
1392
+ content.push({
1393
+ type: 'text',
1394
+ text: this.accumulatedContent
1395
+ });
1396
+ }
1397
+ for (const toolCall of this.toolCalls.values()) {
1398
+ if (toolCall.name) {
1399
+ content.push({
1400
+ type: 'function',
1401
+ id: toolCall.id,
1402
+ function: {
1403
+ name: toolCall.name,
1404
+ arguments: toolCall.arguments
1405
+ }
1406
+ });
1407
+ }
1408
+ }
1409
+ return {
1410
+ output: [{
1411
+ role: 'assistant',
1412
+ content: content.length > 0 ? content : [{
1413
+ type: 'text',
1414
+ text: ''
1415
+ }]
1416
+ }],
1417
+ model: this.model,
1418
+ completionId: this.completionId,
1419
+ systemFingerprint: this.systemFingerprint,
1420
+ serviceTier: this.serviceTier,
1421
+ firstTokenTime: this.firstTokenTime,
1422
+ stopReason: this.stopReason,
1423
+ usage: {
1424
+ ...this.usage
1425
+ }
1426
+ };
1427
+ }
1428
+ }
1429
+ /** Pure state accumulator for OpenAI-compatible Responses stream events. */
1430
+ class OpenAIResponsesStreamAccumulator {
1431
+ output = [];
1432
+ usage = {
1433
+ inputTokens: 0,
1434
+ outputTokens: 0,
1435
+ webSearchCount: 0
1436
+ };
1437
+ consume(event, receivedAt = Date.now()) {
1438
+ if (this.firstTokenTime === undefined && isResponseTokenChunk(event)) {
1439
+ this.firstTokenTime = receivedAt;
1440
+ }
1441
+ if (!('response' in event) || !event.response) {
1442
+ return;
1443
+ }
1444
+ const response = event.response;
1445
+ this.model ||= response.model || undefined;
1446
+ this.completionId ||= response.id || undefined;
1447
+ if (response.service_tier != null) {
1448
+ this.serviceTier = response.service_tier;
1449
+ }
1450
+ const webSearchCount = calculateWebSearchCount(response);
1451
+ if (webSearchCount > (this.usage.webSearchCount ?? 0)) {
1452
+ this.usage.webSearchCount = webSearchCount;
1453
+ }
1454
+ if (response.usage) {
1455
+ this.usage = {
1456
+ ...this.usage,
1457
+ inputTokens: response.usage.input_tokens ?? 0,
1458
+ outputTokens: response.usage.output_tokens ?? 0,
1459
+ reasoningTokens: response.usage.output_tokens_details?.reasoning_tokens ?? 0,
1460
+ cacheReadInputTokens: response.usage.input_tokens_details?.cached_tokens ?? 0,
1461
+ cacheCreationInputTokens: extractCacheWriteTokens(response.usage.input_tokens_details),
1462
+ rawUsage: response.usage
1463
+ };
1464
+ }
1465
+ if (isTerminalResponse(response)) {
1466
+ this.terminalResponse = response;
1467
+ this.output = response.output ?? [];
1468
+ this.stopReason = response.status;
1469
+ }
1470
+ }
1471
+ result() {
1472
+ return {
1473
+ output: [...this.output],
1474
+ model: this.model,
1475
+ completionId: this.completionId,
1476
+ serviceTier: this.serviceTier,
1477
+ firstTokenTime: this.firstTokenTime,
1478
+ stopReason: this.stopReason,
1479
+ usage: {
1480
+ ...this.usage
1481
+ },
1482
+ terminalResponse: this.terminalResponse
1483
+ };
1484
+ }
1485
+ }
1486
+
1487
+ function captureAiGenerationInBackground(...args) {
1488
+ void captureAiGeneration(...args).catch(() => undefined);
1489
+ }
1490
+
1491
+ /** Preserve immediate delivery while isolating normal telemetry from provider latency/failures. */
1492
+ async function captureAiGenerationAfterSuccess(...args) {
1493
+ if (args[1].captureImmediate) {
1494
+ await captureAiGeneration(...args);
1495
+ } else {
1496
+ captureAiGenerationInBackground(...args);
1497
+ }
1498
+ }
1499
+ function buildChatUsage(usage, webSearchSource) {
1500
+ return {
1501
+ inputTokens: usage?.prompt_tokens ?? 0,
1502
+ outputTokens: usage?.completion_tokens ?? 0,
1503
+ reasoningTokens: usage?.completion_tokens_details?.reasoning_tokens ?? 0,
1504
+ cacheReadInputTokens: usage?.prompt_tokens_details?.cached_tokens ?? 0,
1505
+ cacheCreationInputTokens: extractCacheWriteTokens(usage?.prompt_tokens_details),
1506
+ webSearchCount: calculateWebSearchCount(webSearchSource),
1507
+ rawUsage: usage
1508
+ };
1509
+ }
1510
+ function buildResponsesUsage(usage, webSearchSource) {
1511
+ return {
1512
+ inputTokens: usage?.input_tokens ?? 0,
1513
+ outputTokens: usage?.output_tokens ?? 0,
1514
+ reasoningTokens: usage?.output_tokens_details?.reasoning_tokens ?? 0,
1515
+ cacheReadInputTokens: usage?.input_tokens_details?.cached_tokens ?? 0,
1516
+ cacheCreationInputTokens: extractCacheWriteTokens(usage?.input_tokens_details),
1517
+ webSearchCount: calculateWebSearchCount(webSearchSource),
1518
+ rawUsage: usage
1519
+ };
1520
+ }
1521
+ function buildChatSuccessOptions(context, result) {
1522
+ return {
1523
+ ...context.monitoring,
1524
+ model: context.params.model ?? result.model,
1525
+ provider: context.provider,
1526
+ input: sanitizeOpenAI(context.params.messages, context.client),
1527
+ output: sanitizeOpenAIResponse(result.output, context.client),
1528
+ latency: result.latency,
1529
+ timeToFirstToken: result.timeToFirstToken,
1530
+ baseURL: context.baseURL,
1531
+ modelParameters: getModelParams(context.modelParametersSource, result.serviceTier),
1532
+ httpStatus: 200,
1533
+ usage: result.usage,
1534
+ stopReason: result.stopReason,
1535
+ tools: extractAvailableToolCalls('openai', context.params),
1536
+ completionId: result.completionId,
1537
+ providerMetadata: buildProviderMetadata({
1538
+ systemFingerprint: result.systemFingerprint,
1539
+ requestId: result.requestId
1540
+ })
1541
+ };
1542
+ }
1543
+ function buildChatErrorOptions(context, error, metadata = {}) {
1544
+ return {
1545
+ ...context.monitoring,
1546
+ model: context.params.model,
1547
+ provider: context.provider,
1548
+ input: sanitizeOpenAI(context.params.messages, context.client),
1549
+ output: [],
1550
+ latency: 0,
1551
+ baseURL: context.baseURL,
1552
+ modelParameters: getModelParams(context.modelParametersSource),
1553
+ usage: {
1554
+ inputTokens: 0,
1555
+ outputTokens: 0
1556
+ },
1557
+ completionId: metadata.completionId,
1558
+ providerMetadata: buildProviderMetadata({
1559
+ systemFingerprint: metadata.systemFingerprint
1560
+ }),
1561
+ error
1562
+ };
1563
+ }
1564
+ function buildSanitizedResponsesInput(context) {
1565
+ return formatOpenAIResponsesInput(sanitizeOpenAIResponse(context.params.input, context.client), sanitizeOpenAIResponse(context.params.instructions, context.client));
1566
+ }
1567
+ function buildResponsesSuccessOptions(context, result) {
1568
+ const response = result.response;
1569
+ return {
1570
+ ...context.monitoring,
1571
+ model: context.params.model ?? response.model,
1572
+ provider: context.provider,
1573
+ input: buildSanitizedResponsesInput(context),
1574
+ output: sanitizeOpenAIResponse(result.output, context.client),
1575
+ latency: result.latency,
1576
+ timeToFirstToken: result.timeToFirstToken,
1577
+ baseURL: context.baseURL,
1578
+ modelParameters: getModelParams(context.modelParametersSource, response.service_tier),
1579
+ httpStatus: 200,
1580
+ usage: result.usage ?? buildResponsesUsage(response.usage, response),
1581
+ stopReason: response.status ?? undefined,
1582
+ tools: result.includeTools ? extractAvailableToolCalls('openai', context.params) : undefined,
1583
+ completionId: response.id,
1584
+ providerMetadata: buildProviderMetadata({
1585
+ requestId: result.includeRequestId ? extractRequestId(response) : undefined,
1586
+ incompleteDetails: response.incomplete_details
1587
+ }),
1588
+ error: getResponseFailure({
1589
+ id: response.id,
1590
+ status: response.status,
1591
+ error: response.error ?? null
1592
+ })
1593
+ };
1594
+ }
1595
+ function buildBackgroundResponseOptions(context, response) {
1596
+ return buildResponsesSuccessOptions(context, {
1597
+ response,
1598
+ output: formatResponseOpenAI({
1599
+ output: response.output
1600
+ }),
1601
+ latency: getBackgroundResponseLatency(response),
1602
+ includeTools: true,
1603
+ includeRequestId: true
1604
+ });
1605
+ }
1606
+ function buildResponsesErrorOptions(context, error, completionId) {
1607
+ return {
1608
+ ...context.monitoring,
1609
+ model: context.params.model,
1610
+ provider: context.provider,
1611
+ input: buildSanitizedResponsesInput(context),
1612
+ output: [],
1613
+ latency: 0,
1614
+ baseURL: context.baseURL,
1615
+ modelParameters: getModelParams(context.modelParametersSource),
1616
+ usage: {
1617
+ inputTokens: 0,
1618
+ outputTokens: 0
1619
+ },
1620
+ completionId,
1621
+ error
1622
+ };
1623
+ }
1624
+ function buildEmbeddingSuccessOptions(context, usage, latency) {
1625
+ return {
1626
+ eventType: AIEvent.Embedding,
1627
+ ...context.monitoring,
1628
+ model: context.params.model,
1629
+ provider: context.provider,
1630
+ input: withPrivacyMode(context.client, context.monitoring.privacyMode, context.params.input),
1631
+ output: null,
1632
+ latency,
1633
+ baseURL: context.baseURL,
1634
+ modelParameters: getModelParams(context.modelParametersSource),
1635
+ httpStatus: 200,
1636
+ usage: {
1637
+ inputTokens: usage?.prompt_tokens ?? 0,
1638
+ rawUsage: usage
1639
+ }
1640
+ };
1641
+ }
1642
+ function buildEmbeddingErrorOptions(context, error) {
1643
+ return {
1644
+ eventType: AIEvent.Embedding,
1645
+ ...context.monitoring,
1646
+ model: context.params.model,
1647
+ provider: context.provider,
1648
+ input: withPrivacyMode(context.client, context.monitoring.privacyMode, context.params.input),
1649
+ output: null,
1650
+ latency: 0,
1651
+ baseURL: context.baseURL,
1652
+ modelParameters: getModelParams(context.modelParametersSource),
1653
+ usage: {
1654
+ inputTokens: 0
1655
+ },
1656
+ error
1657
+ };
1658
+ }
1659
+
1321
1660
  class PostHogAzureOpenAI extends AzureOpenAI {
1322
1661
  constructor(config) {
1323
1662
  const {
@@ -1363,169 +1702,37 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1363
1702
  if (Symbol.asyncIterator in value) {
1364
1703
  const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
1365
1704
  (async () => {
1366
- // Hoisted so the catch block can surface whatever was accumulated
1367
- // from the streamed chunks before the failure.
1368
- let completionIdFromResponse;
1369
- let systemFingerprintFromResponse;
1705
+ const accumulator = new OpenAIChatStreamAccumulator();
1370
1706
  try {
1371
- const contentBlocks = [];
1372
- let accumulatedContent = '';
1373
- let modelFromResponse;
1374
- let serviceTierFromResponse;
1375
- let firstTokenTime;
1376
- let usage = {
1377
- inputTokens: 0,
1378
- outputTokens: 0
1379
- };
1380
-
1381
- // Map to track in-progress tool calls
1382
- const toolCallsInProgress = new Map();
1383
1707
  for await (const chunk of stream1) {
1384
- // Extract model and completion metadata from chunk (Chat Completions chunks carry these fields)
1385
- if (!modelFromResponse && chunk.model) {
1386
- modelFromResponse = chunk.model;
1387
- }
1388
- if (!completionIdFromResponse && chunk.id) {
1389
- completionIdFromResponse = chunk.id;
1390
- }
1391
- if (!systemFingerprintFromResponse && chunk.system_fingerprint) {
1392
- systemFingerprintFromResponse = chunk.system_fingerprint;
1393
- }
1394
- if (chunk.service_tier != null) {
1395
- serviceTierFromResponse = chunk.service_tier;
1396
- }
1397
- const choice = chunk?.choices?.[0];
1398
-
1399
- // Handle text content
1400
- const deltaContent = choice?.delta?.content;
1401
- if (deltaContent) {
1402
- if (firstTokenTime === undefined) {
1403
- firstTokenTime = Date.now();
1404
- }
1405
- accumulatedContent += deltaContent;
1406
- }
1407
-
1408
- // Handle tool calls
1409
- const deltaToolCalls = choice?.delta?.tool_calls;
1410
- if (deltaToolCalls && Array.isArray(deltaToolCalls)) {
1411
- if (firstTokenTime === undefined) {
1412
- firstTokenTime = Date.now();
1413
- }
1414
- for (const toolCall of deltaToolCalls) {
1415
- const index = toolCall.index;
1416
- if (index !== undefined) {
1417
- if (!toolCallsInProgress.has(index)) {
1418
- // New tool call
1419
- toolCallsInProgress.set(index, {
1420
- id: toolCall.id || '',
1421
- name: toolCall.function?.name || '',
1422
- arguments: ''
1423
- });
1424
- }
1425
- const inProgressCall = toolCallsInProgress.get(index);
1426
- if (inProgressCall) {
1427
- // Update tool call data
1428
- if (toolCall.id) {
1429
- inProgressCall.id = toolCall.id;
1430
- }
1431
- if (toolCall.function?.name) {
1432
- inProgressCall.name = toolCall.function.name;
1433
- }
1434
- if (toolCall.function?.arguments) {
1435
- inProgressCall.arguments += toolCall.function.arguments;
1436
- }
1437
- }
1438
- }
1439
- }
1440
- }
1441
-
1442
- // Handle usage information
1443
- if (chunk.usage) {
1444
- usage = {
1445
- inputTokens: chunk.usage.prompt_tokens ?? 0,
1446
- outputTokens: chunk.usage.completion_tokens ?? 0,
1447
- reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? 0,
1448
- cacheReadInputTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0,
1449
- cacheCreationInputTokens: extractCacheWriteTokens(chunk.usage.prompt_tokens_details)
1450
- };
1451
- }
1452
- }
1453
-
1454
- // Build final content blocks
1455
- if (accumulatedContent) {
1456
- contentBlocks.push({
1457
- type: 'text',
1458
- text: accumulatedContent
1459
- });
1460
- }
1461
-
1462
- // Add completed tool calls to content blocks
1463
- for (const toolCall of toolCallsInProgress.values()) {
1464
- if (toolCall.name) {
1465
- contentBlocks.push({
1466
- type: 'function',
1467
- id: toolCall.id,
1468
- function: {
1469
- name: toolCall.name,
1470
- arguments: toolCall.arguments
1471
- }
1472
- });
1473
- }
1708
+ accumulator.consume(chunk);
1474
1709
  }
1475
-
1476
- // Format output to match non-streaming version
1477
- const formattedOutput = contentBlocks.length > 0 ? [{
1478
- role: 'assistant',
1479
- content: contentBlocks
1480
- }] : [{
1481
- role: 'assistant',
1482
- content: [{
1483
- type: 'text',
1484
- text: ''
1485
- }]
1486
- }];
1487
- const latency = (Date.now() - startTime) / 1000;
1488
- const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1489
- await captureAiGeneration(this.phClient, {
1490
- ...posthogParams,
1491
- model: openAIParams.model ?? modelFromResponse,
1710
+ const accumulated = accumulator.result();
1711
+ await captureAiGeneration(this.phClient, buildChatSuccessOptions({
1712
+ client: this.phClient,
1492
1713
  provider: 'azure',
1493
- input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1494
- output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
1495
- latency,
1496
- timeToFirstToken,
1497
1714
  baseURL: this.baseURL,
1498
- modelParameters: getModelParams(body, serviceTierFromResponse),
1499
- httpStatus: 200,
1500
- usage,
1501
- completionId: completionIdFromResponse,
1502
- providerMetadata: buildProviderMetadata({
1503
- systemFingerprint: systemFingerprintFromResponse
1504
- })
1505
- });
1715
+ params: openAIParams,
1716
+ monitoring: posthogParams,
1717
+ modelParametersSource: body
1718
+ }, {
1719
+ ...accumulated,
1720
+ latency: (Date.now() - startTime) / 1000,
1721
+ timeToFirstToken: accumulated.firstTokenTime === undefined ? undefined : (accumulated.firstTokenTime - startTime) / 1000
1722
+ }));
1506
1723
  } catch (error) {
1507
- await captureAiGeneration(this.phClient, {
1508
- ...posthogParams,
1509
- model: openAIParams.model,
1724
+ const accumulated = accumulator.result();
1725
+ await captureAiGeneration(this.phClient, buildChatErrorOptions({
1726
+ client: this.phClient,
1510
1727
  provider: 'azure',
1511
- input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1512
- output: [],
1513
- latency: 0,
1514
1728
  baseURL: this.baseURL,
1515
- modelParameters: getModelParams(body),
1516
- usage: {
1517
- inputTokens: 0,
1518
- outputTokens: 0
1519
- },
1520
- // If the stream fails mid-flight, surface whatever completion
1521
- // metadata the consumed chunks already provided so the error
1522
- // event can still be correlated to OpenAI's Logs dashboard.
1523
- completionId: completionIdFromResponse,
1524
- providerMetadata: buildProviderMetadata({
1525
- systemFingerprint: systemFingerprintFromResponse
1526
- }),
1527
- error: error
1528
- });
1729
+ params: openAIParams,
1730
+ monitoring: posthogParams,
1731
+ modelParametersSource: body
1732
+ }, error, {
1733
+ completionId: accumulated.completionId,
1734
+ systemFingerprint: accumulated.systemFingerprint
1735
+ }));
1529
1736
  throw error;
1530
1737
  }
1531
1738
  })().catch(() => {
@@ -1542,50 +1749,35 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1542
1749
  } else {
1543
1750
  const wrappedPromise = parentPromise.then(async result => {
1544
1751
  if ('choices' in result) {
1545
- const latency = (Date.now() - startTime) / 1000;
1546
- await captureAiGeneration(this.phClient, {
1547
- ...posthogParams,
1548
- model: openAIParams.model ?? result.model,
1752
+ await captureAiGenerationAfterSuccess(this.phClient, buildChatSuccessOptions({
1753
+ client: this.phClient,
1549
1754
  provider: 'azure',
1550
- input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1551
- output: sanitizeOpenAIResponse(formatResponseOpenAI(result), this.phClient),
1552
- latency,
1553
1755
  baseURL: this.baseURL,
1554
- modelParameters: getModelParams(body, result.service_tier),
1555
- httpStatus: 200,
1556
- usage: {
1557
- inputTokens: result.usage?.prompt_tokens ?? 0,
1558
- outputTokens: result.usage?.completion_tokens ?? 0,
1559
- reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
1560
- cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0,
1561
- cacheCreationInputTokens: extractCacheWriteTokens(result.usage?.prompt_tokens_details)
1562
- },
1756
+ params: openAIParams,
1757
+ monitoring: posthogParams,
1758
+ modelParametersSource: body
1759
+ }, {
1760
+ output: formatResponseOpenAI(result),
1761
+ model: result.model,
1762
+ serviceTier: result.service_tier ?? undefined,
1763
+ latency: (Date.now() - startTime) / 1000,
1764
+ usage: buildChatUsage(result.usage, result),
1765
+ stopReason: result.choices[0]?.finish_reason ?? undefined,
1563
1766
  completionId: result.id,
1564
- providerMetadata: buildProviderMetadata({
1565
- systemFingerprint: result.system_fingerprint,
1566
- requestId: extractRequestId(result)
1567
- })
1568
- });
1767
+ systemFingerprint: result.system_fingerprint,
1768
+ requestId: result._request_id
1769
+ }));
1569
1770
  }
1570
1771
  return result;
1571
1772
  }, async error => {
1572
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1573
- await captureAiGeneration(this.phClient, {
1574
- ...posthogParams,
1575
- model: openAIParams.model,
1773
+ await captureAiGeneration(this.phClient, buildChatErrorOptions({
1774
+ client: this.phClient,
1576
1775
  provider: 'azure',
1577
- input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1578
- output: [],
1579
- latency: 0,
1580
1776
  baseURL: this.baseURL,
1581
- modelParameters: getModelParams(body),
1582
- httpStatus,
1583
- usage: {
1584
- inputTokens: 0,
1585
- outputTokens: 0
1586
- },
1587
- error
1588
- });
1777
+ params: openAIParams,
1778
+ monitoring: posthogParams,
1779
+ modelParametersSource: body
1780
+ }, error));
1589
1781
  throw error;
1590
1782
  });
1591
1783
  return preserveProviderPromise(parentPromise, wrappedPromise);
@@ -1604,32 +1796,14 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1604
1796
  openAIParams,
1605
1797
  posthogParams
1606
1798
  } = context;
1607
- await captureAiGeneration(this.phClient, {
1608
- ...posthogParams,
1609
- model: openAIParams.model ?? result.model,
1799
+ await captureAiGenerationAfterSuccess(this.phClient, buildBackgroundResponseOptions({
1800
+ client: this.phClient,
1610
1801
  provider: 'azure',
1611
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1612
- output: result.output,
1613
- latency: getBackgroundResponseLatency(result),
1614
1802
  baseURL: this.baseURL,
1615
- modelParameters: getModelParams(openAIParams, result.service_tier),
1616
- httpStatus: 200,
1617
- usage: {
1618
- inputTokens: result.usage?.input_tokens ?? 0,
1619
- outputTokens: result.usage?.output_tokens ?? 0,
1620
- reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1621
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
1622
- cacheCreationInputTokens: extractCacheWriteTokens(result.usage?.input_tokens_details),
1623
- rawUsage: result.usage
1624
- },
1625
- stopReason: result.status ?? undefined,
1626
- completionId: result.id,
1627
- providerMetadata: buildProviderMetadata({
1628
- requestId: extractRequestId(result),
1629
- incompleteDetails: result.incomplete_details
1630
- }),
1631
- error: getResponseFailure(result)
1632
- });
1803
+ params: openAIParams,
1804
+ monitoring: posthogParams,
1805
+ modelParametersSource: openAIParams
1806
+ }, result));
1633
1807
  }
1634
1808
 
1635
1809
  // --- Overload #1: Non-streaming
@@ -1651,108 +1825,61 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1651
1825
  if (Symbol.asyncIterator in value) {
1652
1826
  const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
1653
1827
  (async () => {
1654
- // Hoisted so the catch block can surface the completion ID that
1655
- // was accumulated from the streamed chunks before the failure.
1656
- let completionIdFromResponse;
1828
+ const accumulator = new OpenAIResponsesStreamAccumulator();
1657
1829
  try {
1658
- let finalContent = [];
1659
- let modelFromResponse;
1660
- let serviceTierFromResponse;
1661
- let firstTokenTime;
1662
- let usage = {
1663
- inputTokens: 0,
1664
- outputTokens: 0
1665
- };
1666
- let terminalResponse;
1667
1830
  for await (const chunk of stream1) {
1668
- // Track first token time on content delta events
1669
- if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
1670
- firstTokenTime = Date.now();
1671
- }
1672
- if ('response' in chunk && chunk.response) {
1673
- // Extract model and completion ID from the response object in the chunk (for stored prompts)
1674
- if (!modelFromResponse && chunk.response.model) {
1675
- modelFromResponse = chunk.response.model;
1676
- }
1677
- if (!completionIdFromResponse && chunk.response.id) {
1678
- completionIdFromResponse = chunk.response.id;
1679
- }
1680
- if (openAIParams.background === true && !this.backgroundResponses.get(chunk.response.id)) {
1681
- this.backgroundResponses.set(chunk.response.id, {
1682
- openAIParams,
1683
- posthogParams
1684
- });
1685
- }
1686
- if (chunk.response.service_tier != null) {
1687
- serviceTierFromResponse = chunk.response.service_tier;
1688
- }
1689
- if (isTerminalResponse(chunk.response)) {
1690
- terminalResponse = chunk.response;
1691
- finalContent = chunk.response.output ?? [];
1692
- }
1693
- }
1694
- if ('response' in chunk && chunk.response?.usage) {
1695
- usage = {
1696
- inputTokens: chunk.response.usage.input_tokens ?? 0,
1697
- outputTokens: chunk.response.usage.output_tokens ?? 0,
1698
- reasoningTokens: chunk.response.usage.output_tokens_details?.reasoning_tokens ?? 0,
1699
- cacheReadInputTokens: chunk.response.usage.input_tokens_details?.cached_tokens ?? 0,
1700
- cacheCreationInputTokens: extractCacheWriteTokens(chunk.response.usage.input_tokens_details)
1701
- };
1831
+ accumulator.consume(chunk);
1832
+ if (openAIParams.background === true && 'response' in chunk && chunk.response && !this.backgroundResponses.get(chunk.response.id)) {
1833
+ this.backgroundResponses.set(chunk.response.id, {
1834
+ openAIParams,
1835
+ posthogParams
1836
+ });
1702
1837
  }
1703
1838
  }
1839
+ const accumulated = accumulator.result();
1704
1840
  if (openAIParams.background === true) {
1705
- if (terminalResponse) {
1706
- const context = this.backgroundResponses.take(terminalResponse.id);
1841
+ if (accumulated.terminalResponse) {
1842
+ const context = this.backgroundResponses.take(accumulated.terminalResponse.id);
1707
1843
  if (context) {
1708
- await this.captureBackgroundResponse(terminalResponse, context).catch(() => undefined);
1844
+ await this.captureBackgroundResponse(accumulated.terminalResponse, context).catch(() => undefined);
1709
1845
  }
1710
1846
  }
1711
1847
  return;
1712
1848
  }
1713
- const latency = (Date.now() - startTime) / 1000;
1714
- const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1715
- await captureAiGeneration(this.phClient, {
1716
- ...posthogParams,
1717
- model: openAIParams.model ?? modelFromResponse,
1849
+ const response = accumulated.terminalResponse ?? {
1850
+ id: accumulated.completionId ?? '',
1851
+ model: accumulated.model ?? openAIParams.model,
1852
+ status: accumulated.stopReason,
1853
+ service_tier: accumulated.serviceTier
1854
+ };
1855
+ await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
1856
+ client: this.phClient,
1718
1857
  provider: 'azure',
1719
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1720
- output: sanitizeOpenAIResponse(finalContent, this.phClient),
1721
- latency,
1722
- timeToFirstToken,
1723
1858
  baseURL: this.baseURL,
1724
- modelParameters: getModelParams(body, serviceTierFromResponse),
1725
- httpStatus: 200,
1726
- usage,
1727
- stopReason: terminalResponse?.status ?? undefined,
1728
- completionId: completionIdFromResponse,
1729
- providerMetadata: buildProviderMetadata({
1730
- incompleteDetails: terminalResponse?.incomplete_details
1731
- }),
1732
- error: getResponseFailure(terminalResponse)
1733
- });
1859
+ params: openAIParams,
1860
+ monitoring: posthogParams,
1861
+ modelParametersSource: body
1862
+ }, {
1863
+ response,
1864
+ output: accumulated.output,
1865
+ latency: (Date.now() - startTime) / 1000,
1866
+ timeToFirstToken: accumulated.firstTokenTime === undefined ? undefined : (accumulated.firstTokenTime - startTime) / 1000,
1867
+ usage: accumulated.usage,
1868
+ includeTools: true
1869
+ }));
1734
1870
  } catch (error) {
1735
- if (openAIParams.background === true && completionIdFromResponse && this.backgroundResponses.get(completionIdFromResponse)) {
1871
+ const accumulated = accumulator.result();
1872
+ if (openAIParams.background === true && accumulated.completionId && this.backgroundResponses.get(accumulated.completionId)) {
1736
1873
  throw error;
1737
1874
  }
1738
- await captureAiGeneration(this.phClient, {
1739
- ...posthogParams,
1740
- model: openAIParams.model,
1875
+ await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
1876
+ client: this.phClient,
1741
1877
  provider: 'azure',
1742
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1743
- output: [],
1744
- latency: 0,
1745
1878
  baseURL: this.baseURL,
1746
- modelParameters: getModelParams(body),
1747
- usage: {
1748
- inputTokens: 0,
1749
- outputTokens: 0
1750
- },
1751
- // Surface the completion ID from any chunks consumed before
1752
- // the stream failed so the error event remains correlatable.
1753
- completionId: completionIdFromResponse,
1754
- error: error
1755
- });
1879
+ params: openAIParams,
1880
+ monitoring: posthogParams,
1881
+ modelParametersSource: body
1882
+ }, error, accumulated.completionId));
1756
1883
  throw error;
1757
1884
  }
1758
1885
  })().catch(() => {
@@ -1774,53 +1901,33 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1774
1901
  });
1775
1902
  return result;
1776
1903
  }
1777
- const latency = (Date.now() - startTime) / 1000;
1778
- await captureAiGeneration(this.phClient, {
1779
- ...posthogParams,
1780
- model: openAIParams.model ?? result.model,
1904
+ await captureAiGenerationAfterSuccess(this.phClient, buildResponsesSuccessOptions({
1905
+ client: this.phClient,
1781
1906
  provider: 'azure',
1782
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1783
- output: sanitizeOpenAIResponse(result.output, this.phClient),
1784
- latency,
1785
1907
  baseURL: this.baseURL,
1786
- modelParameters: getModelParams(body, result.service_tier),
1787
- httpStatus: 200,
1788
- usage: {
1789
- inputTokens: result.usage?.input_tokens ?? 0,
1790
- outputTokens: result.usage?.output_tokens ?? 0,
1791
- reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1792
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
1793
- cacheCreationInputTokens: extractCacheWriteTokens(result.usage?.input_tokens_details),
1794
- rawUsage: result.usage
1795
- },
1796
- stopReason: result.status ?? undefined,
1797
- completionId: result.id,
1798
- providerMetadata: buildProviderMetadata({
1799
- requestId: extractRequestId(result),
1800
- incompleteDetails: result.incomplete_details
1908
+ params: openAIParams,
1909
+ monitoring: posthogParams,
1910
+ modelParametersSource: body
1911
+ }, {
1912
+ response: result,
1913
+ output: formatResponseOpenAI({
1914
+ output: result.output
1801
1915
  }),
1802
- error: getResponseFailure(result)
1803
- });
1916
+ latency: (Date.now() - startTime) / 1000,
1917
+ includeTools: true,
1918
+ includeRequestId: true
1919
+ }));
1804
1920
  }
1805
1921
  return result;
1806
1922
  }, async error => {
1807
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1808
- await captureAiGeneration(this.phClient, {
1809
- ...posthogParams,
1810
- model: openAIParams.model,
1923
+ await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
1924
+ client: this.phClient,
1811
1925
  provider: 'azure',
1812
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1813
- output: [],
1814
- latency: 0,
1815
1926
  baseURL: this.baseURL,
1816
- modelParameters: getModelParams(body),
1817
- httpStatus,
1818
- usage: {
1819
- inputTokens: 0,
1820
- outputTokens: 0
1821
- },
1822
- error
1823
- });
1927
+ params: openAIParams,
1928
+ monitoring: posthogParams,
1929
+ modelParametersSource: body
1930
+ }, error));
1824
1931
  throw error;
1825
1932
  });
1826
1933
  return preserveProviderPromise(parentPromise, wrappedPromise);
@@ -1890,51 +1997,29 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1890
1997
  });
1891
1998
  return result;
1892
1999
  }
1893
- const latency = (Date.now() - startTime) / 1000;
1894
- await captureAiGeneration(this.phClient, {
1895
- ...posthogParams,
1896
- model: openAIParams.model ?? result.model,
2000
+ await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
2001
+ client: this.phClient,
1897
2002
  provider: 'azure',
1898
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1899
- output: sanitizeOpenAIResponse(result.output, this.phClient),
1900
- latency,
1901
2003
  baseURL: this.baseURL,
1902
- modelParameters: getModelParams(body, result.service_tier),
1903
- httpStatus: 200,
1904
- usage: {
1905
- inputTokens: result.usage?.input_tokens ?? 0,
1906
- outputTokens: result.usage?.output_tokens ?? 0,
1907
- reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1908
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
1909
- cacheCreationInputTokens: extractCacheWriteTokens(result.usage?.input_tokens_details),
1910
- rawUsage: result.usage
1911
- },
1912
- stopReason: result.status ?? undefined,
1913
- completionId: result.id,
1914
- providerMetadata: buildProviderMetadata({
1915
- requestId: extractRequestId(result),
1916
- incompleteDetails: result.incomplete_details
1917
- }),
1918
- error: getResponseFailure(result)
1919
- });
2004
+ params: openAIParams,
2005
+ monitoring: posthogParams,
2006
+ modelParametersSource: body
2007
+ }, {
2008
+ response: result,
2009
+ output: result.output,
2010
+ latency: (Date.now() - startTime) / 1000,
2011
+ includeRequestId: true
2012
+ }));
1920
2013
  return result;
1921
2014
  }, async error => {
1922
- await captureAiGeneration(this.phClient, {
1923
- ...posthogParams,
1924
- model: openAIParams.model,
2015
+ await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
2016
+ client: this.phClient,
1925
2017
  provider: 'azure',
1926
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1927
- output: [],
1928
- latency: 0,
1929
2018
  baseURL: this.baseURL,
1930
- modelParameters: getModelParams(body),
1931
- httpStatus: error?.status ? error.status : 500,
1932
- usage: {
1933
- inputTokens: 0,
1934
- outputTokens: 0
1935
- },
1936
- error
1937
- });
2019
+ params: openAIParams,
2020
+ monitoring: posthogParams,
2021
+ modelParametersSource: body
2022
+ }, error));
1938
2023
  throw error;
1939
2024
  });
1940
2025
  return preserveProviderPromise(parentPromise, wrappedPromise);
@@ -1954,42 +2039,24 @@ let WrappedEmbeddings$1 = class WrappedEmbeddings extends AzureOpenAI.Embeddings
1954
2039
  const startTime = Date.now();
1955
2040
  const parentPromise = super.create(openAIParams, options);
1956
2041
  const wrappedPromise = parentPromise.then(async result => {
1957
- const latency = (Date.now() - startTime) / 1000;
1958
- await captureAiGeneration(this.phClient, {
1959
- eventType: AIEvent.Embedding,
1960
- ...posthogParams,
1961
- model: openAIParams.model,
2042
+ await captureAiGeneration(this.phClient, buildEmbeddingSuccessOptions({
2043
+ client: this.phClient,
1962
2044
  provider: 'azure',
1963
- input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
1964
- output: null,
1965
- // Embeddings don't have output content
1966
- latency,
1967
2045
  baseURL: this.baseURL,
1968
- modelParameters: getModelParams(body),
1969
- httpStatus: 200,
1970
- usage: {
1971
- inputTokens: result.usage?.prompt_tokens ?? 0
1972
- }
1973
- });
2046
+ params: openAIParams,
2047
+ monitoring: posthogParams,
2048
+ modelParametersSource: body
2049
+ }, result.usage, (Date.now() - startTime) / 1000));
1974
2050
  return result;
1975
2051
  }, async error => {
1976
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1977
- await captureAiGeneration(this.phClient, {
1978
- eventType: AIEvent.Embedding,
1979
- ...posthogParams,
1980
- model: openAIParams.model,
2052
+ await captureAiGeneration(this.phClient, buildEmbeddingErrorOptions({
2053
+ client: this.phClient,
1981
2054
  provider: 'azure',
1982
- input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
1983
- output: null,
1984
- latency: 0,
1985
2055
  baseURL: this.baseURL,
1986
- modelParameters: getModelParams(body),
1987
- httpStatus,
1988
- usage: {
1989
- inputTokens: 0
1990
- },
1991
- error
1992
- });
2056
+ params: openAIParams,
2057
+ monitoring: posthogParams,
2058
+ modelParametersSource: body
2059
+ }, error));
1993
2060
  throw error;
1994
2061
  });
1995
2062
  return preserveProviderPromise(parentPromise, wrappedPromise);
@@ -2002,17 +2069,6 @@ const Responses = OpenAI.Responses;
2002
2069
  const Embeddings = OpenAI.Embeddings;
2003
2070
  const Audio = OpenAI.Audio;
2004
2071
  const Transcriptions = OpenAI.Audio.Transcriptions;
2005
- function captureAiGenerationInBackground(...args) {
2006
- void captureAiGeneration(...args).catch(() => undefined);
2007
- }
2008
- async function captureAiGenerationAfterSuccess(...args) {
2009
- const [, options] = args;
2010
- if (options.captureImmediate) {
2011
- await captureAiGeneration(...args);
2012
- } else {
2013
- captureAiGenerationInBackground(...args);
2014
- }
2015
- }
2016
2072
  class PostHogOpenAI extends OpenAI {
2017
2073
  constructor(config) {
2018
2074
  const {
@@ -2059,192 +2115,37 @@ class WrappedCompletions extends Completions {
2059
2115
  if (Symbol.asyncIterator in value) {
2060
2116
  const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
2061
2117
  (async () => {
2062
- // Hoisted so the catch block can surface whatever was accumulated
2063
- // from the streamed chunks before the failure.
2064
- let completionIdFromResponse;
2065
- let systemFingerprintFromResponse;
2118
+ const accumulator = new OpenAIChatStreamAccumulator();
2066
2119
  try {
2067
- const contentBlocks = [];
2068
- let accumulatedContent = '';
2069
- let modelFromResponse;
2070
- let serviceTierFromResponse;
2071
- let firstTokenTime;
2072
- let stopReason;
2073
- let usage = {
2074
- inputTokens: 0,
2075
- outputTokens: 0,
2076
- webSearchCount: 0
2077
- };
2078
-
2079
- // Map to track in-progress tool calls
2080
- const toolCallsInProgress = new Map();
2081
- let rawUsageData;
2082
2120
  for await (const chunk of stream1) {
2083
- // Extract model and completion metadata from chunk (Chat Completions chunks carry these fields)
2084
- if (!modelFromResponse && chunk.model) {
2085
- modelFromResponse = chunk.model;
2086
- }
2087
- if (!completionIdFromResponse && chunk.id) {
2088
- completionIdFromResponse = chunk.id;
2089
- }
2090
- if (!systemFingerprintFromResponse && chunk.system_fingerprint) {
2091
- systemFingerprintFromResponse = chunk.system_fingerprint;
2092
- }
2093
- if (chunk.service_tier != null) {
2094
- serviceTierFromResponse = chunk.service_tier;
2095
- }
2096
- const choice = chunk?.choices?.[0];
2097
- if (choice?.finish_reason) {
2098
- stopReason = choice.finish_reason;
2099
- }
2100
- const chunkWebSearchCount = calculateWebSearchCount(chunk);
2101
- if (chunkWebSearchCount > 0 && chunkWebSearchCount > (usage.webSearchCount ?? 0)) {
2102
- usage.webSearchCount = chunkWebSearchCount;
2103
- }
2104
-
2105
- // Handle text content
2106
- const deltaContent = choice?.delta?.content;
2107
- if (deltaContent) {
2108
- if (firstTokenTime === undefined) {
2109
- firstTokenTime = Date.now();
2110
- }
2111
- accumulatedContent += deltaContent;
2112
- }
2113
-
2114
- // Handle tool calls
2115
- const deltaToolCalls = choice?.delta?.tool_calls;
2116
- if (deltaToolCalls && Array.isArray(deltaToolCalls)) {
2117
- if (firstTokenTime === undefined) {
2118
- firstTokenTime = Date.now();
2119
- }
2120
- for (const toolCall of deltaToolCalls) {
2121
- const index = toolCall.index;
2122
- if (index !== undefined) {
2123
- if (!toolCallsInProgress.has(index)) {
2124
- // New tool call
2125
- toolCallsInProgress.set(index, {
2126
- id: toolCall.id || '',
2127
- name: toolCall.function?.name || '',
2128
- arguments: ''
2129
- });
2130
- }
2131
- const inProgressCall = toolCallsInProgress.get(index);
2132
- if (inProgressCall) {
2133
- // Update tool call data
2134
- if (toolCall.id) {
2135
- inProgressCall.id = toolCall.id;
2136
- }
2137
- if (toolCall.function?.name) {
2138
- inProgressCall.name = toolCall.function.name;
2139
- }
2140
- if (toolCall.function?.arguments) {
2141
- inProgressCall.arguments += toolCall.function.arguments;
2142
- }
2143
- }
2144
- }
2145
- }
2146
- }
2147
-
2148
- // Handle usage information
2149
- if (chunk.usage) {
2150
- rawUsageData = chunk.usage;
2151
- usage = {
2152
- ...usage,
2153
- inputTokens: chunk.usage.prompt_tokens ?? 0,
2154
- outputTokens: chunk.usage.completion_tokens ?? 0,
2155
- reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? 0,
2156
- cacheReadInputTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0,
2157
- cacheCreationInputTokens: extractCacheWriteTokens(chunk.usage.prompt_tokens_details)
2158
- };
2159
- }
2160
- }
2161
-
2162
- // Build final content blocks
2163
- if (accumulatedContent) {
2164
- contentBlocks.push({
2165
- type: 'text',
2166
- text: accumulatedContent
2167
- });
2168
- }
2169
-
2170
- // Add completed tool calls to content blocks
2171
- for (const toolCall of toolCallsInProgress.values()) {
2172
- if (toolCall.name) {
2173
- contentBlocks.push({
2174
- type: 'function',
2175
- id: toolCall.id,
2176
- function: {
2177
- name: toolCall.name,
2178
- arguments: toolCall.arguments
2179
- }
2180
- });
2181
- }
2121
+ accumulator.consume(chunk);
2182
2122
  }
2183
-
2184
- // Format output to match non-streaming version
2185
- const formattedOutput = contentBlocks.length > 0 ? [{
2186
- role: 'assistant',
2187
- content: contentBlocks
2188
- }] : [{
2189
- role: 'assistant',
2190
- content: [{
2191
- type: 'text',
2192
- text: ''
2193
- }]
2194
- }];
2195
- const latency = (Date.now() - startTime) / 1000;
2196
- const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
2197
- const availableTools = extractAvailableToolCalls('openai', openAIParams);
2198
- await captureAiGeneration(this.phClient, {
2199
- ...posthogParams,
2200
- model: openAIParams.model ?? modelFromResponse,
2123
+ const accumulated = accumulator.result();
2124
+ await captureAiGeneration(this.phClient, buildChatSuccessOptions({
2125
+ client: this.phClient,
2201
2126
  provider: 'openai',
2202
- input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2203
- output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
2204
- latency,
2205
- timeToFirstToken,
2206
2127
  baseURL: this.baseURL,
2207
- modelParameters: getModelParams(body, serviceTierFromResponse),
2208
- httpStatus: 200,
2209
- usage: {
2210
- inputTokens: usage.inputTokens,
2211
- outputTokens: usage.outputTokens,
2212
- reasoningTokens: usage.reasoningTokens,
2213
- cacheReadInputTokens: usage.cacheReadInputTokens,
2214
- cacheCreationInputTokens: usage.cacheCreationInputTokens,
2215
- webSearchCount: usage.webSearchCount,
2216
- rawUsage: rawUsageData
2217
- },
2218
- stopReason,
2219
- tools: availableTools,
2220
- completionId: completionIdFromResponse,
2221
- providerMetadata: buildProviderMetadata({
2222
- systemFingerprint: systemFingerprintFromResponse
2223
- })
2224
- });
2128
+ params: openAIParams,
2129
+ monitoring: posthogParams,
2130
+ modelParametersSource: body
2131
+ }, {
2132
+ ...accumulated,
2133
+ latency: (Date.now() - startTime) / 1000,
2134
+ timeToFirstToken: accumulated.firstTokenTime === undefined ? undefined : (accumulated.firstTokenTime - startTime) / 1000
2135
+ }));
2225
2136
  } catch (error) {
2226
- await captureAiGeneration(this.phClient, {
2227
- ...posthogParams,
2228
- model: openAIParams.model,
2137
+ const accumulated = accumulator.result();
2138
+ await captureAiGeneration(this.phClient, buildChatErrorOptions({
2139
+ client: this.phClient,
2229
2140
  provider: 'openai',
2230
- input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2231
- output: [],
2232
- latency: 0,
2233
2141
  baseURL: this.baseURL,
2234
- modelParameters: getModelParams(body),
2235
- usage: {
2236
- inputTokens: 0,
2237
- outputTokens: 0
2238
- },
2239
- // If the stream fails mid-flight, surface whatever completion
2240
- // metadata the consumed chunks already provided so the error
2241
- // event can still be correlated to OpenAI's Logs dashboard.
2242
- completionId: completionIdFromResponse,
2243
- providerMetadata: buildProviderMetadata({
2244
- systemFingerprint: systemFingerprintFromResponse
2245
- }),
2246
- error
2247
- });
2142
+ params: openAIParams,
2143
+ monitoring: posthogParams,
2144
+ modelParametersSource: body
2145
+ }, error, {
2146
+ completionId: accumulated.completionId,
2147
+ systemFingerprint: accumulated.systemFingerprint
2148
+ }));
2248
2149
  throw error;
2249
2150
  }
2250
2151
  })().catch(() => {
@@ -2261,56 +2162,35 @@ class WrappedCompletions extends Completions {
2261
2162
  } else {
2262
2163
  const wrappedPromise = parentPromise.then(async result => {
2263
2164
  if ('choices' in result) {
2264
- const latency = (Date.now() - startTime) / 1000;
2265
- const availableTools = extractAvailableToolCalls('openai', openAIParams);
2266
- const formattedOutput = formatResponseOpenAI(result);
2267
- await captureAiGenerationAfterSuccess(this.phClient, {
2268
- ...posthogParams,
2269
- model: openAIParams.model ?? result.model,
2165
+ await captureAiGenerationAfterSuccess(this.phClient, buildChatSuccessOptions({
2166
+ client: this.phClient,
2270
2167
  provider: 'openai',
2271
- input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2272
- output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
2273
- latency,
2274
2168
  baseURL: this.baseURL,
2275
- modelParameters: getModelParams(body, result.service_tier),
2276
- httpStatus: 200,
2277
- usage: {
2278
- inputTokens: result.usage?.prompt_tokens ?? 0,
2279
- outputTokens: result.usage?.completion_tokens ?? 0,
2280
- reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
2281
- cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0,
2282
- cacheCreationInputTokens: extractCacheWriteTokens(result.usage?.prompt_tokens_details),
2283
- webSearchCount: calculateWebSearchCount(result),
2284
- rawUsage: result.usage
2285
- },
2169
+ params: openAIParams,
2170
+ monitoring: posthogParams,
2171
+ modelParametersSource: body
2172
+ }, {
2173
+ output: formatResponseOpenAI(result),
2174
+ model: result.model,
2175
+ serviceTier: result.service_tier ?? undefined,
2176
+ latency: (Date.now() - startTime) / 1000,
2177
+ usage: buildChatUsage(result.usage, result),
2286
2178
  stopReason: result.choices[0]?.finish_reason ?? undefined,
2287
- tools: availableTools,
2288
2179
  completionId: result.id,
2289
- providerMetadata: buildProviderMetadata({
2290
- systemFingerprint: result.system_fingerprint,
2291
- requestId: extractRequestId(result)
2292
- })
2293
- });
2180
+ systemFingerprint: result.system_fingerprint,
2181
+ requestId: extractRequestId(result)
2182
+ }));
2294
2183
  }
2295
2184
  return result;
2296
2185
  }, async error => {
2297
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
2298
- await captureAiGeneration(this.phClient, {
2299
- ...posthogParams,
2300
- model: openAIParams.model,
2186
+ await captureAiGeneration(this.phClient, buildChatErrorOptions({
2187
+ client: this.phClient,
2301
2188
  provider: 'openai',
2302
- input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2303
- output: [],
2304
- latency: 0,
2305
2189
  baseURL: this.baseURL,
2306
- modelParameters: getModelParams(body),
2307
- httpStatus,
2308
- usage: {
2309
- inputTokens: 0,
2310
- outputTokens: 0
2311
- },
2312
- error
2313
- });
2190
+ params: openAIParams,
2191
+ monitoring: posthogParams,
2192
+ modelParametersSource: body
2193
+ }, error));
2314
2194
  throw error;
2315
2195
  });
2316
2196
  return preserveProviderPromise(parentPromise, wrappedPromise);
@@ -2329,36 +2209,14 @@ class WrappedResponses extends Responses {
2329
2209
  openAIParams,
2330
2210
  posthogParams
2331
2211
  } = context;
2332
- await captureAiGenerationAfterSuccess(this.phClient, {
2333
- ...posthogParams,
2334
- model: openAIParams.model ?? result.model,
2212
+ await captureAiGenerationAfterSuccess(this.phClient, buildBackgroundResponseOptions({
2213
+ client: this.phClient,
2335
2214
  provider: 'openai',
2336
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2337
- output: formatResponseOpenAI({
2338
- output: result.output
2339
- }),
2340
- latency: getBackgroundResponseLatency(result),
2341
2215
  baseURL: this.baseURL,
2342
- modelParameters: getModelParams(openAIParams, result.service_tier),
2343
- httpStatus: 200,
2344
- usage: {
2345
- inputTokens: result.usage?.input_tokens ?? 0,
2346
- outputTokens: result.usage?.output_tokens ?? 0,
2347
- reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
2348
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
2349
- cacheCreationInputTokens: extractCacheWriteTokens(result.usage?.input_tokens_details),
2350
- webSearchCount: calculateWebSearchCount(result),
2351
- rawUsage: result.usage
2352
- },
2353
- stopReason: result.status ?? undefined,
2354
- tools: extractAvailableToolCalls('openai', openAIParams),
2355
- completionId: result.id,
2356
- providerMetadata: buildProviderMetadata({
2357
- requestId: extractRequestId(result),
2358
- incompleteDetails: result.incomplete_details
2359
- }),
2360
- error: getResponseFailure(result)
2361
- });
2216
+ params: openAIParams,
2217
+ monitoring: posthogParams,
2218
+ modelParametersSource: openAIParams
2219
+ }, result));
2362
2220
  }
2363
2221
 
2364
2222
  // --- Overload #1: Non-streaming
@@ -2380,128 +2238,61 @@ class WrappedResponses extends Responses {
2380
2238
  if (Symbol.asyncIterator in value) {
2381
2239
  const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
2382
2240
  (async () => {
2383
- // Hoisted so the catch block can surface the completion ID that
2384
- // was accumulated from the streamed chunks before the failure.
2385
- let completionIdFromResponse;
2241
+ const accumulator = new OpenAIResponsesStreamAccumulator();
2386
2242
  try {
2387
- let finalContent = [];
2388
- let modelFromResponse;
2389
- let serviceTierFromResponse;
2390
- let firstTokenTime;
2391
- let stopReason;
2392
- let usage = {
2393
- inputTokens: 0,
2394
- outputTokens: 0,
2395
- webSearchCount: 0
2396
- };
2397
- let rawUsageData;
2398
- let terminalResponse;
2399
2243
  for await (const chunk of stream1) {
2400
- // Track first token time on content delta events
2401
- if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
2402
- firstTokenTime = Date.now();
2403
- }
2404
- if ('response' in chunk && chunk.response) {
2405
- // Extract model and completion ID from the response object in the chunk (for stored prompts)
2406
- if (!modelFromResponse && chunk.response.model) {
2407
- modelFromResponse = chunk.response.model;
2408
- }
2409
- if (!completionIdFromResponse && chunk.response.id) {
2410
- completionIdFromResponse = chunk.response.id;
2411
- }
2412
- if (openAIParams.background === true && !this.backgroundResponses.get(chunk.response.id)) {
2413
- this.backgroundResponses.set(chunk.response.id, {
2414
- openAIParams,
2415
- posthogParams
2416
- });
2417
- }
2418
- if (chunk.response.service_tier != null) {
2419
- serviceTierFromResponse = chunk.response.service_tier;
2420
- }
2421
- const chunkWebSearchCount = calculateWebSearchCount(chunk.response);
2422
- if (chunkWebSearchCount > 0 && chunkWebSearchCount > (usage.webSearchCount ?? 0)) {
2423
- usage.webSearchCount = chunkWebSearchCount;
2424
- }
2425
- if (isTerminalResponse(chunk.response)) {
2426
- terminalResponse = chunk.response;
2427
- finalContent = chunk.response.output ?? [];
2428
- stopReason = chunk.response.status;
2429
- }
2430
- }
2431
- if ('response' in chunk && chunk.response?.usage) {
2432
- rawUsageData = chunk.response.usage;
2433
- usage = {
2434
- ...usage,
2435
- inputTokens: chunk.response.usage.input_tokens ?? 0,
2436
- outputTokens: chunk.response.usage.output_tokens ?? 0,
2437
- reasoningTokens: chunk.response.usage.output_tokens_details?.reasoning_tokens ?? 0,
2438
- cacheReadInputTokens: chunk.response.usage.input_tokens_details?.cached_tokens ?? 0,
2439
- cacheCreationInputTokens: extractCacheWriteTokens(chunk.response.usage.input_tokens_details)
2440
- };
2244
+ accumulator.consume(chunk);
2245
+ if (openAIParams.background === true && 'response' in chunk && chunk.response && !this.backgroundResponses.get(chunk.response.id)) {
2246
+ this.backgroundResponses.set(chunk.response.id, {
2247
+ openAIParams,
2248
+ posthogParams
2249
+ });
2441
2250
  }
2442
2251
  }
2252
+ const accumulated = accumulator.result();
2443
2253
  if (openAIParams.background === true) {
2444
- if (terminalResponse) {
2445
- const context = this.backgroundResponses.take(terminalResponse.id);
2254
+ if (accumulated.terminalResponse) {
2255
+ const context = this.backgroundResponses.take(accumulated.terminalResponse.id);
2446
2256
  if (context) {
2447
- await this.captureBackgroundResponse(terminalResponse, context).catch(() => undefined);
2257
+ await this.captureBackgroundResponse(accumulated.terminalResponse, context).catch(() => undefined);
2448
2258
  }
2449
2259
  }
2450
2260
  return;
2451
2261
  }
2452
- const latency = (Date.now() - startTime) / 1000;
2453
- const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
2454
- const availableTools = extractAvailableToolCalls('openai', openAIParams);
2455
- await captureAiGeneration(this.phClient, {
2456
- ...posthogParams,
2457
- model: openAIParams.model ?? modelFromResponse,
2262
+ const response = accumulated.terminalResponse ?? {
2263
+ id: accumulated.completionId ?? '',
2264
+ model: accumulated.model ?? openAIParams.model,
2265
+ status: accumulated.stopReason,
2266
+ service_tier: accumulated.serviceTier
2267
+ };
2268
+ await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
2269
+ client: this.phClient,
2458
2270
  provider: 'openai',
2459
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2460
- output: sanitizeOpenAIResponse(finalContent, this.phClient),
2461
- latency,
2462
- timeToFirstToken,
2463
2271
  baseURL: this.baseURL,
2464
- modelParameters: getModelParams(body, serviceTierFromResponse),
2465
- httpStatus: 200,
2466
- usage: {
2467
- inputTokens: usage.inputTokens,
2468
- outputTokens: usage.outputTokens,
2469
- reasoningTokens: usage.reasoningTokens,
2470
- cacheReadInputTokens: usage.cacheReadInputTokens,
2471
- cacheCreationInputTokens: usage.cacheCreationInputTokens,
2472
- webSearchCount: usage.webSearchCount,
2473
- rawUsage: rawUsageData
2474
- },
2475
- stopReason,
2476
- tools: availableTools,
2477
- completionId: completionIdFromResponse,
2478
- providerMetadata: buildProviderMetadata({
2479
- incompleteDetails: terminalResponse?.incomplete_details
2480
- }),
2481
- error: getResponseFailure(terminalResponse)
2482
- });
2272
+ params: openAIParams,
2273
+ monitoring: posthogParams,
2274
+ modelParametersSource: body
2275
+ }, {
2276
+ response,
2277
+ output: accumulated.output,
2278
+ latency: (Date.now() - startTime) / 1000,
2279
+ timeToFirstToken: accumulated.firstTokenTime === undefined ? undefined : (accumulated.firstTokenTime - startTime) / 1000,
2280
+ usage: accumulated.usage,
2281
+ includeTools: true
2282
+ }));
2483
2283
  } catch (error) {
2484
- if (openAIParams.background === true && completionIdFromResponse && this.backgroundResponses.get(completionIdFromResponse)) {
2284
+ const accumulated = accumulator.result();
2285
+ if (openAIParams.background === true && accumulated.completionId && this.backgroundResponses.get(accumulated.completionId)) {
2485
2286
  throw error;
2486
2287
  }
2487
- await captureAiGeneration(this.phClient, {
2488
- ...posthogParams,
2489
- model: openAIParams.model,
2288
+ await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
2289
+ client: this.phClient,
2490
2290
  provider: 'openai',
2491
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2492
- output: [],
2493
- latency: 0,
2494
2291
  baseURL: this.baseURL,
2495
- modelParameters: getModelParams(body),
2496
- usage: {
2497
- inputTokens: 0,
2498
- outputTokens: 0
2499
- },
2500
- // Surface the completion ID from any chunks consumed before
2501
- // the stream failed so the error event remains correlatable.
2502
- completionId: completionIdFromResponse,
2503
- error
2504
- });
2292
+ params: openAIParams,
2293
+ monitoring: posthogParams,
2294
+ modelParametersSource: body
2295
+ }, error, accumulated.completionId));
2505
2296
  throw error;
2506
2297
  }
2507
2298
  })().catch(() => {
@@ -2523,59 +2314,33 @@ class WrappedResponses extends Responses {
2523
2314
  });
2524
2315
  return result;
2525
2316
  }
2526
- const latency = (Date.now() - startTime) / 1000;
2527
- const availableTools = extractAvailableToolCalls('openai', openAIParams);
2528
- const formattedOutput = formatResponseOpenAI({
2529
- output: result.output
2530
- });
2531
- await captureAiGenerationAfterSuccess(this.phClient, {
2532
- ...posthogParams,
2533
- model: openAIParams.model ?? result.model,
2317
+ await captureAiGenerationAfterSuccess(this.phClient, buildResponsesSuccessOptions({
2318
+ client: this.phClient,
2534
2319
  provider: 'openai',
2535
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2536
- output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
2537
- latency,
2538
2320
  baseURL: this.baseURL,
2539
- modelParameters: getModelParams(body, result.service_tier),
2540
- httpStatus: 200,
2541
- usage: {
2542
- inputTokens: result.usage?.input_tokens ?? 0,
2543
- outputTokens: result.usage?.output_tokens ?? 0,
2544
- reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
2545
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
2546
- cacheCreationInputTokens: extractCacheWriteTokens(result.usage?.input_tokens_details),
2547
- webSearchCount: calculateWebSearchCount(result),
2548
- rawUsage: result.usage
2549
- },
2550
- stopReason: result.status ?? undefined,
2551
- tools: availableTools,
2552
- completionId: result.id,
2553
- providerMetadata: buildProviderMetadata({
2554
- requestId: extractRequestId(result),
2555
- incompleteDetails: result.incomplete_details
2321
+ params: openAIParams,
2322
+ monitoring: posthogParams,
2323
+ modelParametersSource: body
2324
+ }, {
2325
+ response: result,
2326
+ output: formatResponseOpenAI({
2327
+ output: result.output
2556
2328
  }),
2557
- error: getResponseFailure(result)
2558
- });
2329
+ latency: (Date.now() - startTime) / 1000,
2330
+ includeTools: true,
2331
+ includeRequestId: true
2332
+ }));
2559
2333
  }
2560
2334
  return result;
2561
2335
  }, async error => {
2562
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
2563
- await captureAiGeneration(this.phClient, {
2564
- ...posthogParams,
2565
- model: openAIParams.model,
2336
+ await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
2337
+ client: this.phClient,
2566
2338
  provider: 'openai',
2567
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2568
- output: [],
2569
- latency: 0,
2570
2339
  baseURL: this.baseURL,
2571
- modelParameters: getModelParams(body),
2572
- httpStatus,
2573
- usage: {
2574
- inputTokens: 0,
2575
- outputTokens: 0
2576
- },
2577
- error
2578
- });
2340
+ params: openAIParams,
2341
+ monitoring: posthogParams,
2342
+ modelParametersSource: body
2343
+ }, error));
2579
2344
  throw error;
2580
2345
  });
2581
2346
  return preserveProviderPromise(parentPromise, wrappedPromise);
@@ -2645,50 +2410,29 @@ class WrappedResponses extends Responses {
2645
2410
  });
2646
2411
  return result;
2647
2412
  }
2648
- const latency = (Date.now() - startTime) / 1000;
2649
- await captureAiGeneration(this.phClient, {
2650
- ...posthogParams,
2651
- model: openAIParams.model ?? result.model,
2413
+ await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
2414
+ client: this.phClient,
2652
2415
  provider: 'openai',
2653
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2654
- output: sanitizeOpenAIResponse(result.output, this.phClient),
2655
- latency,
2656
2416
  baseURL: this.baseURL,
2657
- modelParameters: getModelParams(body, result.service_tier),
2658
- httpStatus: 200,
2659
- usage: {
2660
- inputTokens: result.usage?.input_tokens ?? 0,
2661
- outputTokens: result.usage?.output_tokens ?? 0,
2662
- reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
2663
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
2664
- cacheCreationInputTokens: extractCacheWriteTokens(result.usage?.input_tokens_details),
2665
- rawUsage: result.usage
2666
- },
2667
- stopReason: result.status ?? undefined,
2668
- completionId: result.id,
2669
- providerMetadata: buildProviderMetadata({
2670
- requestId: extractRequestId(result),
2671
- incompleteDetails: result.incomplete_details
2672
- }),
2673
- error: getResponseFailure(result)
2674
- });
2417
+ params: openAIParams,
2418
+ monitoring: posthogParams,
2419
+ modelParametersSource: body
2420
+ }, {
2421
+ response: result,
2422
+ output: result.output,
2423
+ latency: (Date.now() - startTime) / 1000,
2424
+ includeRequestId: true
2425
+ }));
2675
2426
  return result;
2676
2427
  }, async error => {
2677
- await captureAiGeneration(this.phClient, {
2678
- ...posthogParams,
2679
- model: openAIParams.model,
2428
+ await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
2429
+ client: this.phClient,
2680
2430
  provider: 'openai',
2681
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2682
- output: [],
2683
- latency: 0,
2684
2431
  baseURL: this.baseURL,
2685
- modelParameters: getModelParams(body),
2686
- usage: {
2687
- inputTokens: 0,
2688
- outputTokens: 0
2689
- },
2690
- error
2691
- });
2432
+ params: openAIParams,
2433
+ monitoring: posthogParams,
2434
+ modelParametersSource: body
2435
+ }, error));
2692
2436
  throw error;
2693
2437
  });
2694
2438
  return preserveProviderPromise(parentPromise, wrappedPromise);
@@ -2708,44 +2452,24 @@ class WrappedEmbeddings extends Embeddings {
2708
2452
  const startTime = Date.now();
2709
2453
  const parentPromise = super.create(openAIParams, options);
2710
2454
  const wrappedPromise = parentPromise.then(async result => {
2711
- const latency = (Date.now() - startTime) / 1000;
2712
- await captureAiGeneration(this.phClient, {
2713
- ...posthogParams,
2714
- eventType: AIEvent.Embedding,
2715
- model: openAIParams.model,
2455
+ await captureAiGeneration(this.phClient, buildEmbeddingSuccessOptions({
2456
+ client: this.phClient,
2716
2457
  provider: 'openai',
2717
- input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
2718
- output: null,
2719
- // Embeddings don't have output content
2720
- latency,
2721
2458
  baseURL: this.baseURL,
2722
- modelParameters: getModelParams(body),
2723
- httpStatus: 200,
2724
- usage: {
2725
- inputTokens: result.usage?.prompt_tokens ?? 0,
2726
- rawUsage: result.usage
2727
- }
2728
- });
2459
+ params: openAIParams,
2460
+ monitoring: posthogParams,
2461
+ modelParametersSource: body
2462
+ }, result.usage, (Date.now() - startTime) / 1000));
2729
2463
  return result;
2730
2464
  }, async error => {
2731
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
2732
- await captureAiGeneration(this.phClient, {
2733
- eventType: AIEvent.Embedding,
2734
- ...posthogParams,
2735
- model: openAIParams.model,
2465
+ await captureAiGeneration(this.phClient, buildEmbeddingErrorOptions({
2466
+ client: this.phClient,
2736
2467
  provider: 'openai',
2737
- input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
2738
- output: null,
2739
- // Embeddings don't have output content
2740
- latency: 0,
2741
2468
  baseURL: this.baseURL,
2742
- modelParameters: getModelParams(body),
2743
- httpStatus,
2744
- usage: {
2745
- inputTokens: 0
2746
- },
2747
- error
2748
- });
2469
+ params: openAIParams,
2470
+ monitoring: posthogParams,
2471
+ modelParametersSource: body
2472
+ }, error));
2749
2473
  throw error;
2750
2474
  });
2751
2475
  return preserveProviderPromise(parentPromise, wrappedPromise);