@posthog/ai 8.6.3 → 8.6.5

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.
@@ -41,14 +41,16 @@ const FILE_FAMILY_TYPES = new Set(['file', 'input_file', 'document', 'media', 'f
41
41
  const KNOWN_AUDIO_FORMATS = new Set(['wav', 'mp3', 'ogg', 'flac', 'm4a', 'aac', 'webm']);
42
42
  class MediaTypeContext {
43
43
  static EMPTY = new MediaTypeContext(undefined, undefined);
44
- constructor(parent, key) {
44
+ constructor(parent, key, explicitMediaType) {
45
45
  this.parent = parent;
46
46
  this.key = key;
47
+ this.explicitMediaType = explicitMediaType;
47
48
  }
48
49
  inferMediaType() {
49
50
  return this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey();
50
51
  }
51
52
  inferFromSiblingMime() {
53
+ if (this.explicitMediaType) return this.explicitMediaType;
52
54
  if (!this.parent) return undefined;
53
55
  for (const hint of MIME_HINT_KEYS) {
54
56
  const v = this.parent[hint];
@@ -83,7 +85,13 @@ class MediaTypeContext {
83
85
  if (key.includes('file') || key.includes('document')) return 'application/octet-stream';
84
86
  return undefined;
85
87
  }
88
+ hasExplicitBinaryMediaType() {
89
+ if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false;
90
+ const mediaType = this.inferFromSiblingMime();
91
+ return mediaType !== undefined && !mediaType.toLowerCase().startsWith('text/');
92
+ }
86
93
  signalsBinary() {
94
+ if (this.explicitMediaType) return true;
87
95
  if (this.parent) {
88
96
  for (const hint of MIME_HINT_KEYS) {
89
97
  if (typeof this.parent[hint] === 'string') return true;
@@ -105,10 +113,10 @@ class BinaryContentRedactor {
105
113
  constructor(recognizer = new Base64Recognizer()) {
106
114
  this.recognizer = recognizer;
107
115
  }
108
- redact(value) {
116
+ redact(value, mediaType) {
109
117
  if (this.isMultimodalEnabled()) return value;
110
118
  this.visited = new WeakSet();
111
- return this.walk(value, MediaTypeContext.EMPTY);
119
+ return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY);
112
120
  }
113
121
  walk(value, ctx) {
114
122
  if (value === null || value === undefined) return value;
@@ -132,8 +140,10 @@ class BinaryContentRedactor {
132
140
  return out;
133
141
  }
134
142
  redactString(value, ctx) {
135
- const minLength = ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
136
- const recognition = this.recognizer.recognize(value, minLength);
143
+ const hasExplicitBinaryMediaType = ctx.hasExplicitBinaryMediaType();
144
+ const recognitionValue = hasExplicitBinaryMediaType ? value.replace(/[\r\n]/g, '') : value;
145
+ const minLength = hasExplicitBinaryMediaType ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH) : ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
146
+ const recognition = this.recognizer.recognize(recognitionValue, minLength);
137
147
  switch (recognition.kind) {
138
148
  case 'data-url':
139
149
  return this.placeholderFor(recognition.mediaType);
@@ -534,7 +544,7 @@ function formatOpenAIResponsesInput(input, instructions) {
534
544
  return messages;
535
545
  }
536
546
 
537
- var version = "8.6.3";
547
+ var version = "8.6.5";
538
548
 
539
549
  const DEFAULT_MAX_DEPTH = 3;
540
550
  const MAX_STACK_LINES = 20;
@@ -667,6 +677,8 @@ const captureAiGeneration$1 = async (client, options) => {
667
677
  if (httpStatus === undefined) {
668
678
  if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
669
679
  httpStatus = options.error.status;
680
+ } else if (typeof options.error === 'object' && 'statusCode' in options.error && typeof options.error.statusCode === 'number') {
681
+ httpStatus = options.error.statusCode;
670
682
  } else {
671
683
  httpStatus = 500;
672
684
  }
@@ -832,7 +844,7 @@ function extractRequestId(result) {
832
844
  * Assembles the `$ai_provider_metadata` blob for OpenAI / Azure OpenAI events.
833
845
  * Provider-specific fields (system fingerprint, request id) live here rather
834
846
  * than in the shared, provider-agnostic `$ai_*` namespace. Only keys with a
835
- * truthy value are included, and `undefined` is returned when there is nothing
847
+ * meaningful value are included, and `undefined` is returned when there is nothing
836
848
  * to report so the property can be omitted from the event entirely.
837
849
  */
838
850
  function buildProviderMetadata(fields) {
@@ -843,8 +855,125 @@ function buildProviderMetadata(fields) {
843
855
  if (fields.requestId) {
844
856
  metadata.request_id = fields.requestId;
845
857
  }
858
+ if (fields.incompleteDetails != null) {
859
+ metadata.incomplete_details = fields.incompleteDetails;
860
+ }
846
861
  return Object.keys(metadata).length > 0 ? metadata : undefined;
847
862
  }
863
+ const TERMINAL_RESPONSE_STATUSES = new Set(['completed', 'failed', 'cancelled', 'incomplete']);
864
+
865
+ /**
866
+ * Checks whether a Responses API response has reached a status that should
867
+ * produce a final `$ai_generation` event.
868
+ */
869
+ function isTerminalResponse(response) {
870
+ return !!response?.status && TERMINAL_RESPONSE_STATUSES.has(response.status);
871
+ }
872
+
873
+ /**
874
+ * Returns an isolated copy of a failed Responses API error for `$ai_error`, or
875
+ * creates a fallback error when the provider omitted failure details.
876
+ */
877
+ function getResponseFailure(response) {
878
+ if (response?.status !== 'failed') {
879
+ return undefined;
880
+ }
881
+ return response.error ? {
882
+ ...response.error
883
+ } : new Error(`OpenAI response ${response.id} failed without error details`);
884
+ }
885
+
886
+ function addRequestId(result, response, requestIdHeader) {
887
+ if (!result || typeof result !== 'object' || Array.isArray(result)) {
888
+ return result;
889
+ }
890
+ return Object.defineProperty(result, '_request_id', {
891
+ value: response.headers.get(requestIdHeader),
892
+ enumerable: false
893
+ });
894
+ }
895
+ function getResponsePropsPromise(parentPromise) {
896
+ const responsePromise = parentPromise.responsePromise;
897
+ if (!responsePromise || typeof responsePromise.then !== 'function') {
898
+ return undefined;
899
+ }
900
+ return responsePromise;
901
+ }
902
+ function decorateProviderPromise(wrappedPromise, responsePropsPromise, requestIdHeader, preserveThenUnwrap) {
903
+ const providerPromise = wrappedPromise;
904
+ if (responsePropsPromise) {
905
+ providerPromise.asResponse = async () => (await responsePropsPromise).response;
906
+ providerPromise.withResponse = async () => {
907
+ const [props, data] = await Promise.all([responsePropsPromise, wrappedPromise]);
908
+ return {
909
+ response: props.response,
910
+ data,
911
+ request_id: props.response.headers.get(requestIdHeader)
912
+ };
913
+ };
914
+ }
915
+ if (preserveThenUnwrap) {
916
+ providerPromise._thenUnwrap = transform => {
917
+ if (!responsePropsPromise) {
918
+ throw new Error('The provider promise response metadata is unavailable');
919
+ }
920
+ const transformedPromise = Promise.all([wrappedPromise, responsePropsPromise]).then(([data, props]) => addRequestId(transform(data, props), props.response, requestIdHeader));
921
+ return decorateProviderPromise(transformedPromise, responsePropsPromise, requestIdHeader, true);
922
+ };
923
+ }
924
+ return providerPromise;
925
+ }
926
+
927
+ /**
928
+ * Keep the provider SDK helpers on a promise whose resolved value is instrumented.
929
+ * OpenAI's parse helpers compose create calls through `_thenUnwrap`, while both
930
+ * OpenAI and Anthropic expose the raw response through `asResponse` and
931
+ * `withResponse`.
932
+ */
933
+
934
+ function preserveProviderPromise(parentPromise, wrappedPromise, options = {}) {
935
+ const responsePropsPromise = getResponsePropsPromise(parentPromise);
936
+ const preserveThenUnwrap = typeof parentPromise._thenUnwrap === 'function';
937
+ const providerPromise = decorateProviderPromise(wrappedPromise, responsePropsPromise, options.requestIdHeader ?? 'x-request-id', preserveThenUnwrap);
938
+ if (!responsePropsPromise) {
939
+ const asResponse = parentPromise.asResponse?.bind(parentPromise);
940
+ if (asResponse) {
941
+ providerPromise.asResponse = asResponse;
942
+ }
943
+ const withResponse = parentPromise.withResponse?.bind(parentPromise);
944
+ if (withResponse) {
945
+ providerPromise.withResponse = async () => {
946
+ const [response, data] = await Promise.all([withResponse(), wrappedPromise]);
947
+ return {
948
+ ...response,
949
+ data
950
+ };
951
+ };
952
+ }
953
+ }
954
+ return providerPromise;
955
+ }
956
+
957
+ /**
958
+ * OpenAI's `Responses.parse` dispatches through `this._client.responses.create`.
959
+ * Temporarily use the provider's original `create` implementation so parsing a
960
+ * wrapped response does not capture the same request twice.
961
+ */
962
+ function callWithOriginalCreate(resource, originalCreate, callback) {
963
+ const resourceRecord = resource;
964
+ const hadOwnCreate = Object.prototype.hasOwnProperty.call(resource, 'create');
965
+ const wrappedCreate = resourceRecord['create'];
966
+ resourceRecord['create'] = originalCreate;
967
+ try {
968
+ return callback();
969
+ } finally {
970
+ if (hadOwnCreate) {
971
+ resourceRecord['create'] = wrappedCreate;
972
+ } else {
973
+ delete resourceRecord['create'];
974
+ }
975
+ }
976
+ }
848
977
 
849
978
  /**
850
979
  * Splits an SDK stream into a monitoring branch and a caller branch without
@@ -1101,7 +1230,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1101
1230
  const startTime = Date.now();
1102
1231
  const parentPromise = super.create(openAIParams, options);
1103
1232
  if (openAIParams.stream) {
1104
- return parentPromise.then(value => {
1233
+ const wrappedPromise = parentPromise.then(value => {
1105
1234
  if (Symbol.asyncIterator in value) {
1106
1235
  const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
1107
1236
  (async () => {
@@ -1232,7 +1361,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1232
1361
  model: openAIParams.model ?? modelFromResponse,
1233
1362
  provider: 'azure',
1234
1363
  input: sanitizeOpenAI(openAIParams.messages),
1235
- output: formattedOutput,
1364
+ output: sanitizeOpenAIResponse(formattedOutput),
1236
1365
  latency,
1237
1366
  timeToFirstToken,
1238
1367
  baseURL: this.baseURL,
@@ -1279,6 +1408,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1279
1408
  }
1280
1409
  return value;
1281
1410
  });
1411
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1282
1412
  } else {
1283
1413
  const wrappedPromise = parentPromise.then(async result => {
1284
1414
  if ('choices' in result) {
@@ -1287,8 +1417,8 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1287
1417
  ...posthogParams,
1288
1418
  model: openAIParams.model ?? result.model,
1289
1419
  provider: 'azure',
1290
- input: openAIParams.messages,
1291
- output: formatResponseOpenAI(result),
1420
+ input: sanitizeOpenAI(openAIParams.messages),
1421
+ output: sanitizeOpenAIResponse(formatResponseOpenAI(result)),
1292
1422
  latency,
1293
1423
  baseURL: this.baseURL,
1294
1424
  modelParameters: getModelParams(body, result.service_tier),
@@ -1313,7 +1443,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1313
1443
  ...posthogParams,
1314
1444
  model: openAIParams.model,
1315
1445
  provider: 'azure',
1316
- input: openAIParams.messages,
1446
+ input: sanitizeOpenAI(openAIParams.messages),
1317
1447
  output: [],
1318
1448
  latency: 0,
1319
1449
  baseURL: this.baseURL,
@@ -1327,7 +1457,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1327
1457
  });
1328
1458
  throw error;
1329
1459
  });
1330
- return wrappedPromise;
1460
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1331
1461
  }
1332
1462
  }
1333
1463
  };
@@ -1353,7 +1483,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1353
1483
  const startTime = Date.now();
1354
1484
  const parentPromise = super.create(openAIParams, options);
1355
1485
  if (openAIParams.stream) {
1356
- return parentPromise.then(value => {
1486
+ const wrappedPromise = parentPromise.then(value => {
1357
1487
  if (Symbol.asyncIterator in value) {
1358
1488
  const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
1359
1489
  (async () => {
@@ -1369,6 +1499,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1369
1499
  inputTokens: 0,
1370
1500
  outputTokens: 0
1371
1501
  };
1502
+ let terminalResponse;
1372
1503
  for await (const chunk of stream1) {
1373
1504
  // Track first token time on content delta events
1374
1505
  if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
@@ -1385,9 +1516,10 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1385
1516
  if (chunk.response.service_tier != null) {
1386
1517
  serviceTierFromResponse = chunk.response.service_tier;
1387
1518
  }
1388
- }
1389
- if (chunk.type === 'response.completed' && 'response' in chunk && chunk.response?.output && chunk.response.output.length > 0) {
1390
- finalContent = chunk.response.output;
1519
+ if (isTerminalResponse(chunk.response)) {
1520
+ terminalResponse = chunk.response;
1521
+ finalContent = chunk.response.output ?? [];
1522
+ }
1391
1523
  }
1392
1524
  if ('response' in chunk && chunk.response?.usage) {
1393
1525
  usage = {
@@ -1404,22 +1536,27 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1404
1536
  ...posthogParams,
1405
1537
  model: openAIParams.model ?? modelFromResponse,
1406
1538
  provider: 'azure',
1407
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1408
- output: finalContent,
1539
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1540
+ output: sanitizeOpenAIResponse(finalContent),
1409
1541
  latency,
1410
1542
  timeToFirstToken,
1411
1543
  baseURL: this.baseURL,
1412
1544
  modelParameters: getModelParams(body, serviceTierFromResponse),
1413
1545
  httpStatus: 200,
1414
1546
  usage,
1415
- completionId: completionIdFromResponse
1547
+ stopReason: terminalResponse?.status ?? undefined,
1548
+ completionId: completionIdFromResponse,
1549
+ providerMetadata: buildProviderMetadata({
1550
+ incompleteDetails: terminalResponse?.incomplete_details
1551
+ }),
1552
+ error: getResponseFailure(terminalResponse)
1416
1553
  });
1417
1554
  } catch (error) {
1418
1555
  await captureAiGeneration(this.phClient, {
1419
1556
  ...posthogParams,
1420
1557
  model: openAIParams.model,
1421
1558
  provider: 'azure',
1422
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1559
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1423
1560
  output: [],
1424
1561
  latency: 0,
1425
1562
  baseURL: this.baseURL,
@@ -1443,6 +1580,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1443
1580
  }
1444
1581
  return value;
1445
1582
  });
1583
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1446
1584
  } else {
1447
1585
  const wrappedPromise = parentPromise.then(async result => {
1448
1586
  if ('output' in result) {
@@ -1451,8 +1589,8 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1451
1589
  ...posthogParams,
1452
1590
  model: openAIParams.model ?? result.model,
1453
1591
  provider: 'azure',
1454
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1455
- output: result.output,
1592
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1593
+ output: sanitizeOpenAIResponse(result.output),
1456
1594
  latency,
1457
1595
  baseURL: this.baseURL,
1458
1596
  modelParameters: getModelParams(body, result.service_tier),
@@ -1463,10 +1601,13 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1463
1601
  reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1464
1602
  cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
1465
1603
  },
1604
+ stopReason: result.status ?? undefined,
1466
1605
  completionId: result.id,
1467
1606
  providerMetadata: buildProviderMetadata({
1468
- requestId: extractRequestId(result)
1469
- })
1607
+ requestId: extractRequestId(result),
1608
+ incompleteDetails: result.incomplete_details
1609
+ }),
1610
+ error: getResponseFailure(result)
1470
1611
  });
1471
1612
  }
1472
1613
  return result;
@@ -1476,7 +1617,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1476
1617
  ...posthogParams,
1477
1618
  model: openAIParams.model,
1478
1619
  provider: 'azure',
1479
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1620
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1480
1621
  output: [],
1481
1622
  latency: 0,
1482
1623
  baseURL: this.baseURL,
@@ -1490,7 +1631,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1490
1631
  });
1491
1632
  throw error;
1492
1633
  });
1493
- return wrappedPromise;
1634
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1494
1635
  }
1495
1636
  }
1496
1637
  parse(body, options) {
@@ -1499,15 +1640,15 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1499
1640
  posthogParams
1500
1641
  } = extractPosthogParams(body);
1501
1642
  const startTime = Date.now();
1502
- const parentPromise = super.parse(openAIParams, options);
1643
+ const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
1503
1644
  const wrappedPromise = parentPromise.then(async result => {
1504
1645
  const latency = (Date.now() - startTime) / 1000;
1505
1646
  await captureAiGeneration(this.phClient, {
1506
1647
  ...posthogParams,
1507
1648
  model: openAIParams.model ?? result.model,
1508
1649
  provider: 'azure',
1509
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1510
- output: result.output,
1650
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1651
+ output: sanitizeOpenAIResponse(result.output),
1511
1652
  latency,
1512
1653
  baseURL: this.baseURL,
1513
1654
  modelParameters: getModelParams(body, result.service_tier),
@@ -1518,10 +1659,13 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1518
1659
  reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1519
1660
  cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
1520
1661
  },
1662
+ stopReason: result.status ?? undefined,
1521
1663
  completionId: result.id,
1522
1664
  providerMetadata: buildProviderMetadata({
1523
- requestId: extractRequestId(result)
1524
- })
1665
+ requestId: extractRequestId(result),
1666
+ incompleteDetails: result.incomplete_details
1667
+ }),
1668
+ error: getResponseFailure(result)
1525
1669
  });
1526
1670
  return result;
1527
1671
  }, async error => {
@@ -1529,7 +1673,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1529
1673
  ...posthogParams,
1530
1674
  model: openAIParams.model,
1531
1675
  provider: 'azure',
1532
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1676
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1533
1677
  output: [],
1534
1678
  latency: 0,
1535
1679
  baseURL: this.baseURL,
@@ -1543,7 +1687,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1543
1687
  });
1544
1688
  throw error;
1545
1689
  });
1546
- return wrappedPromise;
1690
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1547
1691
  }
1548
1692
  };
1549
1693
  let WrappedEmbeddings$1 = class WrappedEmbeddings extends AzureOpenAI.Embeddings {
@@ -1598,7 +1742,7 @@ let WrappedEmbeddings$1 = class WrappedEmbeddings extends AzureOpenAI.Embeddings
1598
1742
  });
1599
1743
  throw error;
1600
1744
  });
1601
- return wrappedPromise;
1745
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1602
1746
  }
1603
1747
  };
1604
1748
 
@@ -1619,28 +1763,8 @@ async function captureAiGenerationAfterSuccess(...args) {
1619
1763
  captureAiGenerationInBackground(...args);
1620
1764
  }
1621
1765
  }
1622
- function preserveAPIPromiseHelpers(parentPromise, wrappedPromise) {
1623
- const apiPromise = wrappedPromise;
1624
- if (typeof parentPromise.asResponse === 'function') {
1625
- apiPromise.asResponse = () => parentPromise.asResponse();
1626
- }
1627
- if (typeof parentPromise.withResponse === 'function') {
1628
- apiPromise.withResponse = async () => {
1629
- const [response, data] = await Promise.all([parentPromise.withResponse(), wrappedPromise]);
1630
- // swaps the `data` payload inside the SDK's generic withResponse shape; the compiler
1631
- // cannot relate the Input- and Output-instantiated conditional types
1632
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
1633
- return {
1634
- ...response,
1635
- data
1636
- };
1637
- };
1638
- }
1639
- return apiPromise;
1640
- }
1641
- const TERMINAL_RESPONSE_STATUSES = new Set(['completed', 'failed', 'cancelled', 'incomplete']);
1642
1766
  function isPendingBackgroundResponse(params, response) {
1643
- return params.background === true && !response.usage && !!response.status && !TERMINAL_RESPONSE_STATUSES.has(response.status);
1767
+ return params.background === true && !response.usage && !!response.status && !isTerminalResponse(response);
1644
1768
  }
1645
1769
  class PostHogOpenAI extends OpenAI {
1646
1770
  constructor(config) {
@@ -1828,7 +1952,7 @@ class WrappedCompletions extends Completions {
1828
1952
  model: openAIParams.model ?? modelFromResponse,
1829
1953
  provider: 'openai',
1830
1954
  input: sanitizeOpenAI(openAIParams.messages),
1831
- output: formattedOutput,
1955
+ output: sanitizeOpenAIResponse(formattedOutput),
1832
1956
  latency,
1833
1957
  timeToFirstToken,
1834
1958
  baseURL: this.baseURL,
@@ -1884,7 +2008,7 @@ class WrappedCompletions extends Completions {
1884
2008
  }
1885
2009
  return value;
1886
2010
  });
1887
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2011
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1888
2012
  } else {
1889
2013
  const wrappedPromise = parentPromise.then(async result => {
1890
2014
  if ('choices' in result) {
@@ -1896,7 +2020,7 @@ class WrappedCompletions extends Completions {
1896
2020
  model: openAIParams.model ?? result.model,
1897
2021
  provider: 'openai',
1898
2022
  input: sanitizeOpenAI(openAIParams.messages),
1899
- output: formattedOutput,
2023
+ output: sanitizeOpenAIResponse(formattedOutput),
1900
2024
  latency,
1901
2025
  baseURL: this.baseURL,
1902
2026
  modelParameters: getModelParams(body, result.service_tier),
@@ -1939,7 +2063,7 @@ class WrappedCompletions extends Completions {
1939
2063
  });
1940
2064
  throw error;
1941
2065
  });
1942
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2066
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1943
2067
  }
1944
2068
  }
1945
2069
  }
@@ -1984,6 +2108,7 @@ class WrappedResponses extends Responses {
1984
2108
  webSearchCount: 0
1985
2109
  };
1986
2110
  let rawUsageData;
2111
+ let terminalResponse;
1987
2112
  for await (const chunk of stream1) {
1988
2113
  // Track first token time on content delta events
1989
2114
  if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
@@ -2004,10 +2129,9 @@ class WrappedResponses extends Responses {
2004
2129
  if (chunkWebSearchCount > 0 && chunkWebSearchCount > (usage.webSearchCount ?? 0)) {
2005
2130
  usage.webSearchCount = chunkWebSearchCount;
2006
2131
  }
2007
- }
2008
- if (chunk.type === 'response.completed' && 'response' in chunk && chunk.response?.output && chunk.response.output.length > 0) {
2009
- finalContent = chunk.response.output;
2010
- if (chunk.response.status) {
2132
+ if (isTerminalResponse(chunk.response)) {
2133
+ terminalResponse = chunk.response;
2134
+ finalContent = chunk.response.output ?? [];
2011
2135
  stopReason = chunk.response.status;
2012
2136
  }
2013
2137
  }
@@ -2030,7 +2154,7 @@ class WrappedResponses extends Responses {
2030
2154
  model: openAIParams.model ?? modelFromResponse,
2031
2155
  provider: 'openai',
2032
2156
  input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2033
- output: finalContent,
2157
+ output: sanitizeOpenAIResponse(finalContent),
2034
2158
  latency,
2035
2159
  timeToFirstToken,
2036
2160
  baseURL: this.baseURL,
@@ -2046,7 +2170,11 @@ class WrappedResponses extends Responses {
2046
2170
  },
2047
2171
  stopReason,
2048
2172
  tools: availableTools,
2049
- completionId: completionIdFromResponse
2173
+ completionId: completionIdFromResponse,
2174
+ providerMetadata: buildProviderMetadata({
2175
+ incompleteDetails: terminalResponse?.incomplete_details
2176
+ }),
2177
+ error: getResponseFailure(terminalResponse)
2050
2178
  });
2051
2179
  } catch (error) {
2052
2180
  await captureAiGeneration(this.phClient, {
@@ -2077,7 +2205,7 @@ class WrappedResponses extends Responses {
2077
2205
  }
2078
2206
  return value;
2079
2207
  });
2080
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2208
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2081
2209
  } else {
2082
2210
  const wrappedPromise = parentPromise.then(async result => {
2083
2211
  if ('output' in result) {
@@ -2094,7 +2222,7 @@ class WrappedResponses extends Responses {
2094
2222
  model: openAIParams.model ?? result.model,
2095
2223
  provider: 'openai',
2096
2224
  input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2097
- output: formattedOutput,
2225
+ output: sanitizeOpenAIResponse(formattedOutput),
2098
2226
  latency,
2099
2227
  baseURL: this.baseURL,
2100
2228
  modelParameters: getModelParams(body, result.service_tier),
@@ -2111,8 +2239,10 @@ class WrappedResponses extends Responses {
2111
2239
  tools: availableTools,
2112
2240
  completionId: result.id,
2113
2241
  providerMetadata: buildProviderMetadata({
2114
- requestId: extractRequestId(result)
2115
- })
2242
+ requestId: extractRequestId(result),
2243
+ incompleteDetails: result.incomplete_details
2244
+ }),
2245
+ error: getResponseFailure(result)
2116
2246
  });
2117
2247
  }
2118
2248
  return result;
@@ -2136,7 +2266,7 @@ class WrappedResponses extends Responses {
2136
2266
  });
2137
2267
  throw error;
2138
2268
  });
2139
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2269
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2140
2270
  }
2141
2271
  }
2142
2272
  parse(body, options) {
@@ -2145,64 +2275,57 @@ class WrappedResponses extends Responses {
2145
2275
  posthogParams
2146
2276
  } = extractPosthogParams(body);
2147
2277
  const startTime = Date.now();
2148
- const originalCreate = super.create.bind(this);
2149
- const originalSelfRecord = this;
2150
- const tempCreate = originalSelfRecord['create'];
2151
- originalSelfRecord['create'] = originalCreate;
2152
- try {
2153
- const parentPromise = super.parse(openAIParams, options);
2154
- const wrappedPromise = parentPromise.then(async result => {
2155
- if (isPendingBackgroundResponse(openAIParams, result)) {
2156
- return result;
2157
- }
2158
- const latency = (Date.now() - startTime) / 1000;
2159
- await captureAiGeneration(this.phClient, {
2160
- ...posthogParams,
2161
- model: openAIParams.model ?? result.model,
2162
- provider: 'openai',
2163
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2164
- output: result.output,
2165
- latency,
2166
- baseURL: this.baseURL,
2167
- modelParameters: getModelParams(body, result.service_tier),
2168
- httpStatus: 200,
2169
- usage: {
2170
- inputTokens: result.usage?.input_tokens ?? 0,
2171
- outputTokens: result.usage?.output_tokens ?? 0,
2172
- reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
2173
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
2174
- rawUsage: result.usage
2175
- },
2176
- stopReason: result.status ?? undefined,
2177
- completionId: result.id,
2178
- providerMetadata: buildProviderMetadata({
2179
- requestId: extractRequestId(result)
2180
- })
2181
- });
2278
+ const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
2279
+ const wrappedPromise = parentPromise.then(async result => {
2280
+ if (isPendingBackgroundResponse(openAIParams, result)) {
2182
2281
  return result;
2183
- }, async error => {
2184
- await captureAiGeneration(this.phClient, {
2185
- ...posthogParams,
2186
- model: openAIParams.model,
2187
- provider: 'openai',
2188
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2189
- output: [],
2190
- latency: 0,
2191
- baseURL: this.baseURL,
2192
- modelParameters: getModelParams(body),
2193
- usage: {
2194
- inputTokens: 0,
2195
- outputTokens: 0
2196
- },
2197
- error
2198
- });
2199
- throw error;
2282
+ }
2283
+ const latency = (Date.now() - startTime) / 1000;
2284
+ await captureAiGeneration(this.phClient, {
2285
+ ...posthogParams,
2286
+ model: openAIParams.model ?? result.model,
2287
+ provider: 'openai',
2288
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2289
+ output: sanitizeOpenAIResponse(result.output),
2290
+ latency,
2291
+ baseURL: this.baseURL,
2292
+ modelParameters: getModelParams(body, result.service_tier),
2293
+ httpStatus: 200,
2294
+ usage: {
2295
+ inputTokens: result.usage?.input_tokens ?? 0,
2296
+ outputTokens: result.usage?.output_tokens ?? 0,
2297
+ reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
2298
+ cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
2299
+ rawUsage: result.usage
2300
+ },
2301
+ stopReason: result.status ?? undefined,
2302
+ completionId: result.id,
2303
+ providerMetadata: buildProviderMetadata({
2304
+ requestId: extractRequestId(result),
2305
+ incompleteDetails: result.incomplete_details
2306
+ }),
2307
+ error: getResponseFailure(result)
2200
2308
  });
2201
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2202
- } finally {
2203
- // Restore our wrapped create method
2204
- originalSelfRecord['create'] = tempCreate;
2205
- }
2309
+ return result;
2310
+ }, async error => {
2311
+ await captureAiGeneration(this.phClient, {
2312
+ ...posthogParams,
2313
+ model: openAIParams.model,
2314
+ provider: 'openai',
2315
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2316
+ output: [],
2317
+ latency: 0,
2318
+ baseURL: this.baseURL,
2319
+ modelParameters: getModelParams(body),
2320
+ usage: {
2321
+ inputTokens: 0,
2322
+ outputTokens: 0
2323
+ },
2324
+ error
2325
+ });
2326
+ throw error;
2327
+ });
2328
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2206
2329
  }
2207
2330
  }
2208
2331
  class WrappedEmbeddings extends Embeddings {
@@ -2259,7 +2382,7 @@ class WrappedEmbeddings extends Embeddings {
2259
2382
  });
2260
2383
  throw error;
2261
2384
  });
2262
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2385
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2263
2386
  }
2264
2387
  }
2265
2388
  class WrappedAudio extends Audio {
@@ -2334,7 +2457,7 @@ class WrappedTranscriptions extends Transcriptions {
2334
2457
  model: openAIParams.model,
2335
2458
  provider: 'openai',
2336
2459
  input: openAIParams.prompt,
2337
- output: finalContent,
2460
+ output: sanitizeOpenAIResponse(finalContent),
2338
2461
  latency,
2339
2462
  timeToFirstToken,
2340
2463
  baseURL: this.baseURL,
@@ -2369,7 +2492,7 @@ class WrappedTranscriptions extends Transcriptions {
2369
2492
  }
2370
2493
  return value;
2371
2494
  });
2372
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2495
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2373
2496
  } else {
2374
2497
  const wrappedPromise = parentPromise.then(async result => {
2375
2498
  if (result && typeof result === 'object' && 'text' in result) {
@@ -2379,7 +2502,7 @@ class WrappedTranscriptions extends Transcriptions {
2379
2502
  model: openAIParams.model,
2380
2503
  provider: 'openai',
2381
2504
  input: openAIParams.prompt,
2382
- output: result.text,
2505
+ output: sanitizeOpenAIResponse(result.text),
2383
2506
  latency,
2384
2507
  baseURL: this.baseURL,
2385
2508
  modelParameters: getModelParams(body),
@@ -2410,7 +2533,7 @@ class WrappedTranscriptions extends Transcriptions {
2410
2533
  });
2411
2534
  throw error;
2412
2535
  });
2413
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2536
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2414
2537
  }
2415
2538
  }
2416
2539
  }