@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.
@@ -45,14 +45,16 @@ const FILE_FAMILY_TYPES = new Set(['file', 'input_file', 'document', 'media', 'f
45
45
  const KNOWN_AUDIO_FORMATS = new Set(['wav', 'mp3', 'ogg', 'flac', 'm4a', 'aac', 'webm']);
46
46
  class MediaTypeContext {
47
47
  static EMPTY = new MediaTypeContext(undefined, undefined);
48
- constructor(parent, key) {
48
+ constructor(parent, key, explicitMediaType) {
49
49
  this.parent = parent;
50
50
  this.key = key;
51
+ this.explicitMediaType = explicitMediaType;
51
52
  }
52
53
  inferMediaType() {
53
54
  return this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey();
54
55
  }
55
56
  inferFromSiblingMime() {
57
+ if (this.explicitMediaType) return this.explicitMediaType;
56
58
  if (!this.parent) return undefined;
57
59
  for (const hint of MIME_HINT_KEYS) {
58
60
  const v = this.parent[hint];
@@ -87,7 +89,13 @@ class MediaTypeContext {
87
89
  if (key.includes('file') || key.includes('document')) return 'application/octet-stream';
88
90
  return undefined;
89
91
  }
92
+ hasExplicitBinaryMediaType() {
93
+ if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false;
94
+ const mediaType = this.inferFromSiblingMime();
95
+ return mediaType !== undefined && !mediaType.toLowerCase().startsWith('text/');
96
+ }
90
97
  signalsBinary() {
98
+ if (this.explicitMediaType) return true;
91
99
  if (this.parent) {
92
100
  for (const hint of MIME_HINT_KEYS) {
93
101
  if (typeof this.parent[hint] === 'string') return true;
@@ -109,10 +117,10 @@ class BinaryContentRedactor {
109
117
  constructor(recognizer = new Base64Recognizer()) {
110
118
  this.recognizer = recognizer;
111
119
  }
112
- redact(value) {
120
+ redact(value, mediaType) {
113
121
  if (this.isMultimodalEnabled()) return value;
114
122
  this.visited = new WeakSet();
115
- return this.walk(value, MediaTypeContext.EMPTY);
123
+ return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY);
116
124
  }
117
125
  walk(value, ctx) {
118
126
  if (value === null || value === undefined) return value;
@@ -136,8 +144,10 @@ class BinaryContentRedactor {
136
144
  return out;
137
145
  }
138
146
  redactString(value, ctx) {
139
- const minLength = ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
140
- const recognition = this.recognizer.recognize(value, minLength);
147
+ const hasExplicitBinaryMediaType = ctx.hasExplicitBinaryMediaType();
148
+ const recognitionValue = hasExplicitBinaryMediaType ? value.replace(/[\r\n]/g, '') : value;
149
+ const minLength = hasExplicitBinaryMediaType ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH) : ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
150
+ const recognition = this.recognizer.recognize(recognitionValue, minLength);
141
151
  switch (recognition.kind) {
142
152
  case 'data-url':
143
153
  return this.placeholderFor(recognition.mediaType);
@@ -538,7 +548,7 @@ function formatOpenAIResponsesInput(input, instructions) {
538
548
  return messages;
539
549
  }
540
550
 
541
- var version = "8.6.3";
551
+ var version = "8.6.5";
542
552
 
543
553
  const DEFAULT_MAX_DEPTH = 3;
544
554
  const MAX_STACK_LINES = 20;
@@ -671,6 +681,8 @@ const captureAiGeneration$1 = async (client, options) => {
671
681
  if (httpStatus === undefined) {
672
682
  if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
673
683
  httpStatus = options.error.status;
684
+ } else if (typeof options.error === 'object' && 'statusCode' in options.error && typeof options.error.statusCode === 'number') {
685
+ httpStatus = options.error.statusCode;
674
686
  } else {
675
687
  httpStatus = 500;
676
688
  }
@@ -836,7 +848,7 @@ function extractRequestId(result) {
836
848
  * Assembles the `$ai_provider_metadata` blob for OpenAI / Azure OpenAI events.
837
849
  * Provider-specific fields (system fingerprint, request id) live here rather
838
850
  * than in the shared, provider-agnostic `$ai_*` namespace. Only keys with a
839
- * truthy value are included, and `undefined` is returned when there is nothing
851
+ * meaningful value are included, and `undefined` is returned when there is nothing
840
852
  * to report so the property can be omitted from the event entirely.
841
853
  */
842
854
  function buildProviderMetadata(fields) {
@@ -847,8 +859,125 @@ function buildProviderMetadata(fields) {
847
859
  if (fields.requestId) {
848
860
  metadata.request_id = fields.requestId;
849
861
  }
862
+ if (fields.incompleteDetails != null) {
863
+ metadata.incomplete_details = fields.incompleteDetails;
864
+ }
850
865
  return Object.keys(metadata).length > 0 ? metadata : undefined;
851
866
  }
867
+ const TERMINAL_RESPONSE_STATUSES = new Set(['completed', 'failed', 'cancelled', 'incomplete']);
868
+
869
+ /**
870
+ * Checks whether a Responses API response has reached a status that should
871
+ * produce a final `$ai_generation` event.
872
+ */
873
+ function isTerminalResponse(response) {
874
+ return !!response?.status && TERMINAL_RESPONSE_STATUSES.has(response.status);
875
+ }
876
+
877
+ /**
878
+ * Returns an isolated copy of a failed Responses API error for `$ai_error`, or
879
+ * creates a fallback error when the provider omitted failure details.
880
+ */
881
+ function getResponseFailure(response) {
882
+ if (response?.status !== 'failed') {
883
+ return undefined;
884
+ }
885
+ return response.error ? {
886
+ ...response.error
887
+ } : new Error(`OpenAI response ${response.id} failed without error details`);
888
+ }
889
+
890
+ function addRequestId(result, response, requestIdHeader) {
891
+ if (!result || typeof result !== 'object' || Array.isArray(result)) {
892
+ return result;
893
+ }
894
+ return Object.defineProperty(result, '_request_id', {
895
+ value: response.headers.get(requestIdHeader),
896
+ enumerable: false
897
+ });
898
+ }
899
+ function getResponsePropsPromise(parentPromise) {
900
+ const responsePromise = parentPromise.responsePromise;
901
+ if (!responsePromise || typeof responsePromise.then !== 'function') {
902
+ return undefined;
903
+ }
904
+ return responsePromise;
905
+ }
906
+ function decorateProviderPromise(wrappedPromise, responsePropsPromise, requestIdHeader, preserveThenUnwrap) {
907
+ const providerPromise = wrappedPromise;
908
+ if (responsePropsPromise) {
909
+ providerPromise.asResponse = async () => (await responsePropsPromise).response;
910
+ providerPromise.withResponse = async () => {
911
+ const [props, data] = await Promise.all([responsePropsPromise, wrappedPromise]);
912
+ return {
913
+ response: props.response,
914
+ data,
915
+ request_id: props.response.headers.get(requestIdHeader)
916
+ };
917
+ };
918
+ }
919
+ if (preserveThenUnwrap) {
920
+ providerPromise._thenUnwrap = transform => {
921
+ if (!responsePropsPromise) {
922
+ throw new Error('The provider promise response metadata is unavailable');
923
+ }
924
+ const transformedPromise = Promise.all([wrappedPromise, responsePropsPromise]).then(([data, props]) => addRequestId(transform(data, props), props.response, requestIdHeader));
925
+ return decorateProviderPromise(transformedPromise, responsePropsPromise, requestIdHeader, true);
926
+ };
927
+ }
928
+ return providerPromise;
929
+ }
930
+
931
+ /**
932
+ * Keep the provider SDK helpers on a promise whose resolved value is instrumented.
933
+ * OpenAI's parse helpers compose create calls through `_thenUnwrap`, while both
934
+ * OpenAI and Anthropic expose the raw response through `asResponse` and
935
+ * `withResponse`.
936
+ */
937
+
938
+ function preserveProviderPromise(parentPromise, wrappedPromise, options = {}) {
939
+ const responsePropsPromise = getResponsePropsPromise(parentPromise);
940
+ const preserveThenUnwrap = typeof parentPromise._thenUnwrap === 'function';
941
+ const providerPromise = decorateProviderPromise(wrappedPromise, responsePropsPromise, options.requestIdHeader ?? 'x-request-id', preserveThenUnwrap);
942
+ if (!responsePropsPromise) {
943
+ const asResponse = parentPromise.asResponse?.bind(parentPromise);
944
+ if (asResponse) {
945
+ providerPromise.asResponse = asResponse;
946
+ }
947
+ const withResponse = parentPromise.withResponse?.bind(parentPromise);
948
+ if (withResponse) {
949
+ providerPromise.withResponse = async () => {
950
+ const [response, data] = await Promise.all([withResponse(), wrappedPromise]);
951
+ return {
952
+ ...response,
953
+ data
954
+ };
955
+ };
956
+ }
957
+ }
958
+ return providerPromise;
959
+ }
960
+
961
+ /**
962
+ * OpenAI's `Responses.parse` dispatches through `this._client.responses.create`.
963
+ * Temporarily use the provider's original `create` implementation so parsing a
964
+ * wrapped response does not capture the same request twice.
965
+ */
966
+ function callWithOriginalCreate(resource, originalCreate, callback) {
967
+ const resourceRecord = resource;
968
+ const hadOwnCreate = Object.prototype.hasOwnProperty.call(resource, 'create');
969
+ const wrappedCreate = resourceRecord['create'];
970
+ resourceRecord['create'] = originalCreate;
971
+ try {
972
+ return callback();
973
+ } finally {
974
+ if (hadOwnCreate) {
975
+ resourceRecord['create'] = wrappedCreate;
976
+ } else {
977
+ delete resourceRecord['create'];
978
+ }
979
+ }
980
+ }
852
981
 
853
982
  /**
854
983
  * Splits an SDK stream into a monitoring branch and a caller branch without
@@ -1105,7 +1234,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1105
1234
  const startTime = Date.now();
1106
1235
  const parentPromise = super.create(openAIParams, options);
1107
1236
  if (openAIParams.stream) {
1108
- return parentPromise.then(value => {
1237
+ const wrappedPromise = parentPromise.then(value => {
1109
1238
  if (Symbol.asyncIterator in value) {
1110
1239
  const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new streaming.Stream(iterator, controller));
1111
1240
  (async () => {
@@ -1236,7 +1365,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1236
1365
  model: openAIParams.model ?? modelFromResponse,
1237
1366
  provider: 'azure',
1238
1367
  input: sanitizeOpenAI(openAIParams.messages),
1239
- output: formattedOutput,
1368
+ output: sanitizeOpenAIResponse(formattedOutput),
1240
1369
  latency,
1241
1370
  timeToFirstToken,
1242
1371
  baseURL: this.baseURL,
@@ -1283,6 +1412,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1283
1412
  }
1284
1413
  return value;
1285
1414
  });
1415
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1286
1416
  } else {
1287
1417
  const wrappedPromise = parentPromise.then(async result => {
1288
1418
  if ('choices' in result) {
@@ -1291,8 +1421,8 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1291
1421
  ...posthogParams,
1292
1422
  model: openAIParams.model ?? result.model,
1293
1423
  provider: 'azure',
1294
- input: openAIParams.messages,
1295
- output: formatResponseOpenAI(result),
1424
+ input: sanitizeOpenAI(openAIParams.messages),
1425
+ output: sanitizeOpenAIResponse(formatResponseOpenAI(result)),
1296
1426
  latency,
1297
1427
  baseURL: this.baseURL,
1298
1428
  modelParameters: getModelParams(body, result.service_tier),
@@ -1317,7 +1447,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1317
1447
  ...posthogParams,
1318
1448
  model: openAIParams.model,
1319
1449
  provider: 'azure',
1320
- input: openAIParams.messages,
1450
+ input: sanitizeOpenAI(openAIParams.messages),
1321
1451
  output: [],
1322
1452
  latency: 0,
1323
1453
  baseURL: this.baseURL,
@@ -1331,7 +1461,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1331
1461
  });
1332
1462
  throw error;
1333
1463
  });
1334
- return wrappedPromise;
1464
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1335
1465
  }
1336
1466
  }
1337
1467
  };
@@ -1357,7 +1487,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1357
1487
  const startTime = Date.now();
1358
1488
  const parentPromise = super.create(openAIParams, options);
1359
1489
  if (openAIParams.stream) {
1360
- return parentPromise.then(value => {
1490
+ const wrappedPromise = parentPromise.then(value => {
1361
1491
  if (Symbol.asyncIterator in value) {
1362
1492
  const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new streaming.Stream(iterator, controller));
1363
1493
  (async () => {
@@ -1373,6 +1503,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1373
1503
  inputTokens: 0,
1374
1504
  outputTokens: 0
1375
1505
  };
1506
+ let terminalResponse;
1376
1507
  for await (const chunk of stream1) {
1377
1508
  // Track first token time on content delta events
1378
1509
  if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
@@ -1389,9 +1520,10 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1389
1520
  if (chunk.response.service_tier != null) {
1390
1521
  serviceTierFromResponse = chunk.response.service_tier;
1391
1522
  }
1392
- }
1393
- if (chunk.type === 'response.completed' && 'response' in chunk && chunk.response?.output && chunk.response.output.length > 0) {
1394
- finalContent = chunk.response.output;
1523
+ if (isTerminalResponse(chunk.response)) {
1524
+ terminalResponse = chunk.response;
1525
+ finalContent = chunk.response.output ?? [];
1526
+ }
1395
1527
  }
1396
1528
  if ('response' in chunk && chunk.response?.usage) {
1397
1529
  usage = {
@@ -1408,22 +1540,27 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1408
1540
  ...posthogParams,
1409
1541
  model: openAIParams.model ?? modelFromResponse,
1410
1542
  provider: 'azure',
1411
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1412
- output: finalContent,
1543
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1544
+ output: sanitizeOpenAIResponse(finalContent),
1413
1545
  latency,
1414
1546
  timeToFirstToken,
1415
1547
  baseURL: this.baseURL,
1416
1548
  modelParameters: getModelParams(body, serviceTierFromResponse),
1417
1549
  httpStatus: 200,
1418
1550
  usage,
1419
- completionId: completionIdFromResponse
1551
+ stopReason: terminalResponse?.status ?? undefined,
1552
+ completionId: completionIdFromResponse,
1553
+ providerMetadata: buildProviderMetadata({
1554
+ incompleteDetails: terminalResponse?.incomplete_details
1555
+ }),
1556
+ error: getResponseFailure(terminalResponse)
1420
1557
  });
1421
1558
  } catch (error) {
1422
1559
  await captureAiGeneration(this.phClient, {
1423
1560
  ...posthogParams,
1424
1561
  model: openAIParams.model,
1425
1562
  provider: 'azure',
1426
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1563
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1427
1564
  output: [],
1428
1565
  latency: 0,
1429
1566
  baseURL: this.baseURL,
@@ -1447,6 +1584,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1447
1584
  }
1448
1585
  return value;
1449
1586
  });
1587
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1450
1588
  } else {
1451
1589
  const wrappedPromise = parentPromise.then(async result => {
1452
1590
  if ('output' in result) {
@@ -1455,8 +1593,8 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1455
1593
  ...posthogParams,
1456
1594
  model: openAIParams.model ?? result.model,
1457
1595
  provider: 'azure',
1458
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1459
- output: result.output,
1596
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1597
+ output: sanitizeOpenAIResponse(result.output),
1460
1598
  latency,
1461
1599
  baseURL: this.baseURL,
1462
1600
  modelParameters: getModelParams(body, result.service_tier),
@@ -1467,10 +1605,13 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1467
1605
  reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1468
1606
  cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
1469
1607
  },
1608
+ stopReason: result.status ?? undefined,
1470
1609
  completionId: result.id,
1471
1610
  providerMetadata: buildProviderMetadata({
1472
- requestId: extractRequestId(result)
1473
- })
1611
+ requestId: extractRequestId(result),
1612
+ incompleteDetails: result.incomplete_details
1613
+ }),
1614
+ error: getResponseFailure(result)
1474
1615
  });
1475
1616
  }
1476
1617
  return result;
@@ -1480,7 +1621,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1480
1621
  ...posthogParams,
1481
1622
  model: openAIParams.model,
1482
1623
  provider: 'azure',
1483
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1624
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1484
1625
  output: [],
1485
1626
  latency: 0,
1486
1627
  baseURL: this.baseURL,
@@ -1494,7 +1635,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1494
1635
  });
1495
1636
  throw error;
1496
1637
  });
1497
- return wrappedPromise;
1638
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1498
1639
  }
1499
1640
  }
1500
1641
  parse(body, options) {
@@ -1503,15 +1644,15 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1503
1644
  posthogParams
1504
1645
  } = extractPosthogParams(body);
1505
1646
  const startTime = Date.now();
1506
- const parentPromise = super.parse(openAIParams, options);
1647
+ const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
1507
1648
  const wrappedPromise = parentPromise.then(async result => {
1508
1649
  const latency = (Date.now() - startTime) / 1000;
1509
1650
  await captureAiGeneration(this.phClient, {
1510
1651
  ...posthogParams,
1511
1652
  model: openAIParams.model ?? result.model,
1512
1653
  provider: 'azure',
1513
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1514
- output: result.output,
1654
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1655
+ output: sanitizeOpenAIResponse(result.output),
1515
1656
  latency,
1516
1657
  baseURL: this.baseURL,
1517
1658
  modelParameters: getModelParams(body, result.service_tier),
@@ -1522,10 +1663,13 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1522
1663
  reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1523
1664
  cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
1524
1665
  },
1666
+ stopReason: result.status ?? undefined,
1525
1667
  completionId: result.id,
1526
1668
  providerMetadata: buildProviderMetadata({
1527
- requestId: extractRequestId(result)
1528
- })
1669
+ requestId: extractRequestId(result),
1670
+ incompleteDetails: result.incomplete_details
1671
+ }),
1672
+ error: getResponseFailure(result)
1529
1673
  });
1530
1674
  return result;
1531
1675
  }, async error => {
@@ -1533,7 +1677,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1533
1677
  ...posthogParams,
1534
1678
  model: openAIParams.model,
1535
1679
  provider: 'azure',
1536
- input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1680
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1537
1681
  output: [],
1538
1682
  latency: 0,
1539
1683
  baseURL: this.baseURL,
@@ -1547,7 +1691,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1547
1691
  });
1548
1692
  throw error;
1549
1693
  });
1550
- return wrappedPromise;
1694
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1551
1695
  }
1552
1696
  };
1553
1697
  let WrappedEmbeddings$1 = class WrappedEmbeddings extends openai.AzureOpenAI.Embeddings {
@@ -1602,7 +1746,7 @@ let WrappedEmbeddings$1 = class WrappedEmbeddings extends openai.AzureOpenAI.Emb
1602
1746
  });
1603
1747
  throw error;
1604
1748
  });
1605
- return wrappedPromise;
1749
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1606
1750
  }
1607
1751
  };
1608
1752
 
@@ -1623,28 +1767,8 @@ async function captureAiGenerationAfterSuccess(...args) {
1623
1767
  captureAiGenerationInBackground(...args);
1624
1768
  }
1625
1769
  }
1626
- function preserveAPIPromiseHelpers(parentPromise, wrappedPromise) {
1627
- const apiPromise = wrappedPromise;
1628
- if (typeof parentPromise.asResponse === 'function') {
1629
- apiPromise.asResponse = () => parentPromise.asResponse();
1630
- }
1631
- if (typeof parentPromise.withResponse === 'function') {
1632
- apiPromise.withResponse = async () => {
1633
- const [response, data] = await Promise.all([parentPromise.withResponse(), wrappedPromise]);
1634
- // swaps the `data` payload inside the SDK's generic withResponse shape; the compiler
1635
- // cannot relate the Input- and Output-instantiated conditional types
1636
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
1637
- return {
1638
- ...response,
1639
- data
1640
- };
1641
- };
1642
- }
1643
- return apiPromise;
1644
- }
1645
- const TERMINAL_RESPONSE_STATUSES = new Set(['completed', 'failed', 'cancelled', 'incomplete']);
1646
1770
  function isPendingBackgroundResponse(params, response) {
1647
- return params.background === true && !response.usage && !!response.status && !TERMINAL_RESPONSE_STATUSES.has(response.status);
1771
+ return params.background === true && !response.usage && !!response.status && !isTerminalResponse(response);
1648
1772
  }
1649
1773
  class PostHogOpenAI extends openai.OpenAI {
1650
1774
  constructor(config) {
@@ -1832,7 +1956,7 @@ class WrappedCompletions extends Completions {
1832
1956
  model: openAIParams.model ?? modelFromResponse,
1833
1957
  provider: 'openai',
1834
1958
  input: sanitizeOpenAI(openAIParams.messages),
1835
- output: formattedOutput,
1959
+ output: sanitizeOpenAIResponse(formattedOutput),
1836
1960
  latency,
1837
1961
  timeToFirstToken,
1838
1962
  baseURL: this.baseURL,
@@ -1888,7 +2012,7 @@ class WrappedCompletions extends Completions {
1888
2012
  }
1889
2013
  return value;
1890
2014
  });
1891
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2015
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1892
2016
  } else {
1893
2017
  const wrappedPromise = parentPromise.then(async result => {
1894
2018
  if ('choices' in result) {
@@ -1900,7 +2024,7 @@ class WrappedCompletions extends Completions {
1900
2024
  model: openAIParams.model ?? result.model,
1901
2025
  provider: 'openai',
1902
2026
  input: sanitizeOpenAI(openAIParams.messages),
1903
- output: formattedOutput,
2027
+ output: sanitizeOpenAIResponse(formattedOutput),
1904
2028
  latency,
1905
2029
  baseURL: this.baseURL,
1906
2030
  modelParameters: getModelParams(body, result.service_tier),
@@ -1943,7 +2067,7 @@ class WrappedCompletions extends Completions {
1943
2067
  });
1944
2068
  throw error;
1945
2069
  });
1946
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2070
+ return preserveProviderPromise(parentPromise, wrappedPromise);
1947
2071
  }
1948
2072
  }
1949
2073
  }
@@ -1988,6 +2112,7 @@ class WrappedResponses extends Responses {
1988
2112
  webSearchCount: 0
1989
2113
  };
1990
2114
  let rawUsageData;
2115
+ let terminalResponse;
1991
2116
  for await (const chunk of stream1) {
1992
2117
  // Track first token time on content delta events
1993
2118
  if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
@@ -2008,10 +2133,9 @@ class WrappedResponses extends Responses {
2008
2133
  if (chunkWebSearchCount > 0 && chunkWebSearchCount > (usage.webSearchCount ?? 0)) {
2009
2134
  usage.webSearchCount = chunkWebSearchCount;
2010
2135
  }
2011
- }
2012
- if (chunk.type === 'response.completed' && 'response' in chunk && chunk.response?.output && chunk.response.output.length > 0) {
2013
- finalContent = chunk.response.output;
2014
- if (chunk.response.status) {
2136
+ if (isTerminalResponse(chunk.response)) {
2137
+ terminalResponse = chunk.response;
2138
+ finalContent = chunk.response.output ?? [];
2015
2139
  stopReason = chunk.response.status;
2016
2140
  }
2017
2141
  }
@@ -2034,7 +2158,7 @@ class WrappedResponses extends Responses {
2034
2158
  model: openAIParams.model ?? modelFromResponse,
2035
2159
  provider: 'openai',
2036
2160
  input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2037
- output: finalContent,
2161
+ output: sanitizeOpenAIResponse(finalContent),
2038
2162
  latency,
2039
2163
  timeToFirstToken,
2040
2164
  baseURL: this.baseURL,
@@ -2050,7 +2174,11 @@ class WrappedResponses extends Responses {
2050
2174
  },
2051
2175
  stopReason,
2052
2176
  tools: availableTools,
2053
- completionId: completionIdFromResponse
2177
+ completionId: completionIdFromResponse,
2178
+ providerMetadata: buildProviderMetadata({
2179
+ incompleteDetails: terminalResponse?.incomplete_details
2180
+ }),
2181
+ error: getResponseFailure(terminalResponse)
2054
2182
  });
2055
2183
  } catch (error) {
2056
2184
  await captureAiGeneration(this.phClient, {
@@ -2081,7 +2209,7 @@ class WrappedResponses extends Responses {
2081
2209
  }
2082
2210
  return value;
2083
2211
  });
2084
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2212
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2085
2213
  } else {
2086
2214
  const wrappedPromise = parentPromise.then(async result => {
2087
2215
  if ('output' in result) {
@@ -2098,7 +2226,7 @@ class WrappedResponses extends Responses {
2098
2226
  model: openAIParams.model ?? result.model,
2099
2227
  provider: 'openai',
2100
2228
  input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2101
- output: formattedOutput,
2229
+ output: sanitizeOpenAIResponse(formattedOutput),
2102
2230
  latency,
2103
2231
  baseURL: this.baseURL,
2104
2232
  modelParameters: getModelParams(body, result.service_tier),
@@ -2115,8 +2243,10 @@ class WrappedResponses extends Responses {
2115
2243
  tools: availableTools,
2116
2244
  completionId: result.id,
2117
2245
  providerMetadata: buildProviderMetadata({
2118
- requestId: extractRequestId(result)
2119
- })
2246
+ requestId: extractRequestId(result),
2247
+ incompleteDetails: result.incomplete_details
2248
+ }),
2249
+ error: getResponseFailure(result)
2120
2250
  });
2121
2251
  }
2122
2252
  return result;
@@ -2140,7 +2270,7 @@ class WrappedResponses extends Responses {
2140
2270
  });
2141
2271
  throw error;
2142
2272
  });
2143
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2273
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2144
2274
  }
2145
2275
  }
2146
2276
  parse(body, options) {
@@ -2149,64 +2279,57 @@ class WrappedResponses extends Responses {
2149
2279
  posthogParams
2150
2280
  } = extractPosthogParams(body);
2151
2281
  const startTime = Date.now();
2152
- const originalCreate = super.create.bind(this);
2153
- const originalSelfRecord = this;
2154
- const tempCreate = originalSelfRecord['create'];
2155
- originalSelfRecord['create'] = originalCreate;
2156
- try {
2157
- const parentPromise = super.parse(openAIParams, options);
2158
- const wrappedPromise = parentPromise.then(async result => {
2159
- if (isPendingBackgroundResponse(openAIParams, result)) {
2160
- return result;
2161
- }
2162
- const latency = (Date.now() - startTime) / 1000;
2163
- await captureAiGeneration(this.phClient, {
2164
- ...posthogParams,
2165
- model: openAIParams.model ?? result.model,
2166
- provider: 'openai',
2167
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2168
- output: result.output,
2169
- latency,
2170
- baseURL: this.baseURL,
2171
- modelParameters: getModelParams(body, result.service_tier),
2172
- httpStatus: 200,
2173
- usage: {
2174
- inputTokens: result.usage?.input_tokens ?? 0,
2175
- outputTokens: result.usage?.output_tokens ?? 0,
2176
- reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
2177
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
2178
- rawUsage: result.usage
2179
- },
2180
- stopReason: result.status ?? undefined,
2181
- completionId: result.id,
2182
- providerMetadata: buildProviderMetadata({
2183
- requestId: extractRequestId(result)
2184
- })
2185
- });
2282
+ const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
2283
+ const wrappedPromise = parentPromise.then(async result => {
2284
+ if (isPendingBackgroundResponse(openAIParams, result)) {
2186
2285
  return result;
2187
- }, async error => {
2188
- await captureAiGeneration(this.phClient, {
2189
- ...posthogParams,
2190
- model: openAIParams.model,
2191
- provider: 'openai',
2192
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2193
- output: [],
2194
- latency: 0,
2195
- baseURL: this.baseURL,
2196
- modelParameters: getModelParams(body),
2197
- usage: {
2198
- inputTokens: 0,
2199
- outputTokens: 0
2200
- },
2201
- error
2202
- });
2203
- throw error;
2286
+ }
2287
+ const latency = (Date.now() - startTime) / 1000;
2288
+ await captureAiGeneration(this.phClient, {
2289
+ ...posthogParams,
2290
+ model: openAIParams.model ?? result.model,
2291
+ provider: 'openai',
2292
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2293
+ output: sanitizeOpenAIResponse(result.output),
2294
+ latency,
2295
+ baseURL: this.baseURL,
2296
+ modelParameters: getModelParams(body, result.service_tier),
2297
+ httpStatus: 200,
2298
+ usage: {
2299
+ inputTokens: result.usage?.input_tokens ?? 0,
2300
+ outputTokens: result.usage?.output_tokens ?? 0,
2301
+ reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
2302
+ cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
2303
+ rawUsage: result.usage
2304
+ },
2305
+ stopReason: result.status ?? undefined,
2306
+ completionId: result.id,
2307
+ providerMetadata: buildProviderMetadata({
2308
+ requestId: extractRequestId(result),
2309
+ incompleteDetails: result.incomplete_details
2310
+ }),
2311
+ error: getResponseFailure(result)
2204
2312
  });
2205
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2206
- } finally {
2207
- // Restore our wrapped create method
2208
- originalSelfRecord['create'] = tempCreate;
2209
- }
2313
+ return result;
2314
+ }, async error => {
2315
+ await captureAiGeneration(this.phClient, {
2316
+ ...posthogParams,
2317
+ model: openAIParams.model,
2318
+ provider: 'openai',
2319
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2320
+ output: [],
2321
+ latency: 0,
2322
+ baseURL: this.baseURL,
2323
+ modelParameters: getModelParams(body),
2324
+ usage: {
2325
+ inputTokens: 0,
2326
+ outputTokens: 0
2327
+ },
2328
+ error
2329
+ });
2330
+ throw error;
2331
+ });
2332
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2210
2333
  }
2211
2334
  }
2212
2335
  class WrappedEmbeddings extends Embeddings {
@@ -2263,7 +2386,7 @@ class WrappedEmbeddings extends Embeddings {
2263
2386
  });
2264
2387
  throw error;
2265
2388
  });
2266
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2389
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2267
2390
  }
2268
2391
  }
2269
2392
  class WrappedAudio extends Audio {
@@ -2338,7 +2461,7 @@ class WrappedTranscriptions extends Transcriptions {
2338
2461
  model: openAIParams.model,
2339
2462
  provider: 'openai',
2340
2463
  input: openAIParams.prompt,
2341
- output: finalContent,
2464
+ output: sanitizeOpenAIResponse(finalContent),
2342
2465
  latency,
2343
2466
  timeToFirstToken,
2344
2467
  baseURL: this.baseURL,
@@ -2373,7 +2496,7 @@ class WrappedTranscriptions extends Transcriptions {
2373
2496
  }
2374
2497
  return value;
2375
2498
  });
2376
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2499
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2377
2500
  } else {
2378
2501
  const wrappedPromise = parentPromise.then(async result => {
2379
2502
  if (result && typeof result === 'object' && 'text' in result) {
@@ -2383,7 +2506,7 @@ class WrappedTranscriptions extends Transcriptions {
2383
2506
  model: openAIParams.model,
2384
2507
  provider: 'openai',
2385
2508
  input: openAIParams.prompt,
2386
- output: result.text,
2509
+ output: sanitizeOpenAIResponse(result.text),
2387
2510
  latency,
2388
2511
  baseURL: this.baseURL,
2389
2512
  modelParameters: getModelParams(body),
@@ -2414,7 +2537,7 @@ class WrappedTranscriptions extends Transcriptions {
2414
2537
  });
2415
2538
  throw error;
2416
2539
  });
2417
- return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
2540
+ return preserveProviderPromise(parentPromise, wrappedPromise);
2418
2541
  }
2419
2542
  }
2420
2543
  }