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