@posthog/ai 8.6.5 → 8.6.7

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.
@@ -1,7 +1,7 @@
1
1
  import OpenAIOrignal, { AzureOpenAI, APIPromise, OpenAI, ClientOptions } from 'openai';
2
2
  import { PostHog } from 'posthog-node';
3
3
  import { Stream } from 'openai/streaming';
4
- import { ParsedResponse } from 'openai/resources/responses/responses';
4
+ import { ResponseRetrieveParamsNonStreaming, ResponseRetrieveParamsStreaming, ResponseRetrieveParamsBase, ParsedResponse } from 'openai/resources/responses/responses';
5
5
  import { ResponseCreateParamsWithTools, ExtractParsedContentFromParams } from 'openai/lib/ResponsesParser';
6
6
  import { AzureClientOptions } from 'openai/azure';
7
7
 
@@ -62,10 +62,16 @@ declare class WrappedCompletions$1 extends AzureOpenAI.Chat.Completions {
62
62
  declare class WrappedResponses$1 extends AzureOpenAI.Responses {
63
63
  private readonly phClient;
64
64
  private readonly baseURL;
65
+ private readonly backgroundResponses;
65
66
  constructor(client: AzureOpenAI, phClient: PostHog);
67
+ private captureBackgroundResponse;
66
68
  create(body: ResponsesCreateParamsNonStreaming$1 & MonitoringParams, options?: RequestOptions$1): APIPromise<OpenAIOrignal.Responses.Response>;
67
69
  create(body: ResponsesCreateParamsStreaming$1 & MonitoringParams, options?: RequestOptions$1): APIPromise<Stream<OpenAIOrignal.Responses.ResponseStreamEvent>>;
68
70
  create(body: ResponsesCreateParamsBase$1 & MonitoringParams, options?: RequestOptions$1): APIPromise<OpenAIOrignal.Responses.Response | Stream<OpenAIOrignal.Responses.ResponseStreamEvent>>;
71
+ retrieve(responseID: string, query?: ResponseRetrieveParamsNonStreaming, options?: RequestOptions$1): APIPromise<OpenAIOrignal.Responses.Response>;
72
+ retrieve(responseID: string, query: ResponseRetrieveParamsStreaming, options?: RequestOptions$1): APIPromise<Stream<OpenAIOrignal.Responses.ResponseStreamEvent>>;
73
+ retrieve(responseID: string, query?: ResponseRetrieveParamsBase, options?: RequestOptions$1): APIPromise<OpenAIOrignal.Responses.Response | Stream<OpenAIOrignal.Responses.ResponseStreamEvent>>;
74
+ cancel(responseID: string, options?: RequestOptions$1): APIPromise<OpenAIOrignal.Responses.Response>;
69
75
  parse<Params extends ResponseCreateParamsWithTools, ParsedT = ExtractParsedContentFromParams<Params>>(body: Params & MonitoringParams, options?: RequestOptions$1): APIPromise<ParsedResponse<ParsedT>>;
70
76
  }
71
77
  declare class WrappedEmbeddings$1 extends AzureOpenAI.Embeddings {
@@ -120,10 +126,16 @@ declare class WrappedCompletions extends Completions {
120
126
  declare class WrappedResponses extends Responses {
121
127
  private readonly phClient;
122
128
  private readonly baseURL;
129
+ private readonly backgroundResponses;
123
130
  constructor(client: OpenAI, phClient: PostHog);
131
+ private captureBackgroundResponse;
124
132
  create(body: ResponsesCreateParamsNonStreaming & MonitoringParams, options?: RequestOptions): APIPromise<OpenAI.Responses.Response>;
125
133
  create(body: ResponsesCreateParamsStreaming & MonitoringParams, options?: RequestOptions): APIPromise<Stream<OpenAI.Responses.ResponseStreamEvent>>;
126
134
  create(body: ResponsesCreateParamsBase & MonitoringParams, options?: RequestOptions): APIPromise<OpenAI.Responses.Response | Stream<OpenAI.Responses.ResponseStreamEvent>>;
135
+ retrieve(responseID: string, query?: ResponseRetrieveParamsNonStreaming, options?: RequestOptions): APIPromise<OpenAI.Responses.Response>;
136
+ retrieve(responseID: string, query: ResponseRetrieveParamsStreaming, options?: RequestOptions): APIPromise<Stream<OpenAI.Responses.ResponseStreamEvent>>;
137
+ retrieve(responseID: string, query?: ResponseRetrieveParamsBase, options?: RequestOptions): APIPromise<OpenAI.Responses.Response | Stream<OpenAI.Responses.ResponseStreamEvent>>;
138
+ cancel(responseID: string, options?: RequestOptions): APIPromise<OpenAI.Responses.Response>;
127
139
  parse<Params extends ResponseCreateParamsWithTools, ParsedT = ExtractParsedContentFromParams<Params>>(body: Params & MonitoringParams, options?: RequestOptions): APIPromise<ParsedResponse<ParsedT>>;
128
140
  }
129
141
  declare class WrappedEmbeddings extends Embeddings {
@@ -544,7 +544,7 @@ function formatOpenAIResponsesInput(input, instructions) {
544
544
  return messages;
545
545
  }
546
546
 
547
- var version = "8.6.5";
547
+ var version = "8.6.7";
548
548
 
549
549
  const DEFAULT_MAX_DEPTH = 3;
550
550
  const MAX_STACK_LINES = 20;
@@ -742,7 +742,9 @@ const captureAiGeneration$1 = async (client, options) => {
742
742
  $ai_output_tokens: usage.outputTokens
743
743
  } : {}),
744
744
  ...additionalTokenValues,
745
- $ai_latency: options.latency ?? 0,
745
+ ...(options.latency !== undefined ? {
746
+ $ai_latency: options.latency
747
+ } : {}),
746
748
  ...(options.timeToFirstToken !== undefined ? {
747
749
  $ai_time_to_first_token: options.timeToFirstToken
748
750
  } : {}),
@@ -883,6 +885,77 @@ function getResponseFailure(response) {
883
885
  } : new Error(`OpenAI response ${response.id} failed without error details`);
884
886
  }
885
887
 
888
+ function isPendingBackgroundResponse(params, response) {
889
+ return params.background === true && !!response.status && !isTerminalResponse(response);
890
+ }
891
+
892
+ /**
893
+ * Uses provider timestamps so background polling cadence does not inflate
894
+ * generation latency. Non-completed responses do not expose a terminal time.
895
+ */
896
+ function getBackgroundResponseLatency(response) {
897
+ if (typeof response.created_at !== 'number' || typeof response.completed_at !== 'number') {
898
+ return undefined;
899
+ }
900
+ return Math.max(0, response.completed_at - response.created_at);
901
+ }
902
+
903
+ /**
904
+ * Keeps the original create context available while a background response is
905
+ * polled. Entries are insertion ordered, so the oldest context is discarded
906
+ * when the bound is reached.
907
+ */
908
+ class BackgroundResponseTracker {
909
+ contexts = new Map();
910
+ constructor(maxEntries = 1000) {
911
+ this.maxEntries = maxEntries;
912
+ }
913
+ set(responseID, context) {
914
+ // Refresh an existing response's insertion order.
915
+ this.contexts.delete(responseID);
916
+ this.contexts.set(responseID, context);
917
+ while (this.contexts.size > this.maxEntries) {
918
+ const oldestResponseID = this.contexts.keys().next().value;
919
+ if (oldestResponseID === undefined) {
920
+ break;
921
+ }
922
+ this.contexts.delete(oldestResponseID);
923
+ }
924
+ }
925
+ get(responseID) {
926
+ return this.contexts.get(responseID);
927
+ }
928
+ take(responseID) {
929
+ const context = this.contexts.get(responseID);
930
+ if (context !== undefined) {
931
+ this.contexts.delete(responseID);
932
+ }
933
+ return context;
934
+ }
935
+ }
936
+
937
+ /**
938
+ * Inspects a streamed background retrieval without consuming it on the
939
+ * caller's behalf. The stored create context is consumed only by a terminal
940
+ * response; an interrupted or nonterminal stream may be followed by another
941
+ * retrieval while the background job continues.
942
+ */
943
+ function wrapBackgroundResponseStream(stream, responseID, tracker, captureTerminalResponse) {
944
+ async function* inspectStream() {
945
+ for await (const event of stream) {
946
+ if ('response' in event && isTerminalResponse(event.response)) {
947
+ const context = tracker.take(responseID);
948
+ if (context) {
949
+ // Monitoring must not delay or disrupt delivery of the provider stream.
950
+ void captureTerminalResponse(event.response, context).catch(() => undefined);
951
+ }
952
+ }
953
+ yield event;
954
+ }
955
+ }
956
+ return new Stream(() => inspectStream(), stream.controller);
957
+ }
958
+
886
959
  function addRequestId(result, response, requestIdHeader) {
887
960
  if (!result || typeof result !== 'object' || Array.isArray(result)) {
888
961
  return result;
@@ -1462,11 +1535,43 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1462
1535
  }
1463
1536
  };
1464
1537
  let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1538
+ backgroundResponses = new BackgroundResponseTracker();
1465
1539
  constructor(client, phClient) {
1466
1540
  super(client);
1467
1541
  this.phClient = phClient;
1468
1542
  this.baseURL = client.baseURL;
1469
1543
  }
1544
+ async captureBackgroundResponse(result, context) {
1545
+ const {
1546
+ openAIParams,
1547
+ posthogParams
1548
+ } = context;
1549
+ await captureAiGeneration(this.phClient, {
1550
+ ...posthogParams,
1551
+ model: openAIParams.model ?? result.model,
1552
+ provider: 'azure',
1553
+ input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1554
+ output: result.output,
1555
+ latency: getBackgroundResponseLatency(result),
1556
+ baseURL: this.baseURL,
1557
+ modelParameters: getModelParams(openAIParams, result.service_tier),
1558
+ httpStatus: 200,
1559
+ usage: {
1560
+ inputTokens: result.usage?.input_tokens ?? 0,
1561
+ outputTokens: result.usage?.output_tokens ?? 0,
1562
+ reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1563
+ cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
1564
+ rawUsage: result.usage
1565
+ },
1566
+ stopReason: result.status ?? undefined,
1567
+ completionId: result.id,
1568
+ providerMetadata: buildProviderMetadata({
1569
+ requestId: extractRequestId(result),
1570
+ incompleteDetails: result.incomplete_details
1571
+ }),
1572
+ error: getResponseFailure(result)
1573
+ });
1574
+ }
1470
1575
 
1471
1576
  // --- Overload #1: Non-streaming
1472
1577
 
@@ -1513,6 +1618,12 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1513
1618
  if (!completionIdFromResponse && chunk.response.id) {
1514
1619
  completionIdFromResponse = chunk.response.id;
1515
1620
  }
1621
+ if (openAIParams.background === true && !this.backgroundResponses.get(chunk.response.id)) {
1622
+ this.backgroundResponses.set(chunk.response.id, {
1623
+ openAIParams,
1624
+ posthogParams
1625
+ });
1626
+ }
1516
1627
  if (chunk.response.service_tier != null) {
1517
1628
  serviceTierFromResponse = chunk.response.service_tier;
1518
1629
  }
@@ -1530,6 +1641,15 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1530
1641
  };
1531
1642
  }
1532
1643
  }
1644
+ if (openAIParams.background === true) {
1645
+ if (terminalResponse) {
1646
+ const context = this.backgroundResponses.take(terminalResponse.id);
1647
+ if (context) {
1648
+ await this.captureBackgroundResponse(terminalResponse, context).catch(() => undefined);
1649
+ }
1650
+ }
1651
+ return;
1652
+ }
1533
1653
  const latency = (Date.now() - startTime) / 1000;
1534
1654
  const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1535
1655
  await captureAiGeneration(this.phClient, {
@@ -1552,6 +1672,9 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1552
1672
  error: getResponseFailure(terminalResponse)
1553
1673
  });
1554
1674
  } catch (error) {
1675
+ if (openAIParams.background === true && completionIdFromResponse && this.backgroundResponses.get(completionIdFromResponse)) {
1676
+ throw error;
1677
+ }
1555
1678
  await captureAiGeneration(this.phClient, {
1556
1679
  ...posthogParams,
1557
1680
  model: openAIParams.model,
@@ -1584,6 +1707,13 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1584
1707
  } else {
1585
1708
  const wrappedPromise = parentPromise.then(async result => {
1586
1709
  if ('output' in result) {
1710
+ if (isPendingBackgroundResponse(openAIParams, result)) {
1711
+ this.backgroundResponses.set(result.id, {
1712
+ openAIParams,
1713
+ posthogParams
1714
+ });
1715
+ return result;
1716
+ }
1587
1717
  const latency = (Date.now() - startTime) / 1000;
1588
1718
  await captureAiGeneration(this.phClient, {
1589
1719
  ...posthogParams,
@@ -1599,7 +1729,8 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1599
1729
  inputTokens: result.usage?.input_tokens ?? 0,
1600
1730
  outputTokens: result.usage?.output_tokens ?? 0,
1601
1731
  reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1602
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
1732
+ cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
1733
+ rawUsage: result.usage
1603
1734
  },
1604
1735
  stopReason: result.status ?? undefined,
1605
1736
  completionId: result.id,
@@ -1634,6 +1765,55 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1634
1765
  return preserveProviderPromise(parentPromise, wrappedPromise);
1635
1766
  }
1636
1767
  }
1768
+ retrieve(responseID, query = {}, options) {
1769
+ const parentPromise = super.retrieve(responseID, query, options);
1770
+
1771
+ // Preserve the upstream promise and stream unchanged for responses that
1772
+ // were not created through this client.
1773
+ if (!this.backgroundResponses.get(responseID)) {
1774
+ return parentPromise;
1775
+ }
1776
+ if (query.stream) {
1777
+ return parentPromise._thenUnwrap(result => {
1778
+ if ('controller' in result) {
1779
+ return wrapBackgroundResponseStream(result, responseID, this.backgroundResponses, (response, context) => this.captureBackgroundResponse(response, context));
1780
+ }
1781
+ return result;
1782
+ });
1783
+ }
1784
+ return parentPromise._thenUnwrap(async result => {
1785
+ if (!('output' in result) || !isTerminalResponse(result)) {
1786
+ return result;
1787
+ }
1788
+
1789
+ // Removing the context before capture makes concurrent or repeated
1790
+ // terminal polls idempotent.
1791
+ const context = this.backgroundResponses.take(responseID);
1792
+ if (context) {
1793
+ await this.captureBackgroundResponse(result, context).catch(() => undefined);
1794
+ }
1795
+ return result;
1796
+ });
1797
+ }
1798
+ cancel(responseID, options) {
1799
+ const parentPromise = super.cancel(responseID, options);
1800
+
1801
+ // Avoid wrapping calls that do not belong to a background response created
1802
+ // through this client, preserving the upstream APIPromise unchanged.
1803
+ if (!this.backgroundResponses.get(responseID)) {
1804
+ return parentPromise;
1805
+ }
1806
+ return parentPromise._thenUnwrap(async result => {
1807
+ if (!isTerminalResponse(result)) {
1808
+ return result;
1809
+ }
1810
+ const context = this.backgroundResponses.take(responseID);
1811
+ if (context) {
1812
+ await this.captureBackgroundResponse(result, context).catch(() => undefined);
1813
+ }
1814
+ return result;
1815
+ });
1816
+ }
1637
1817
  parse(body, options) {
1638
1818
  const {
1639
1819
  providerParams: openAIParams,
@@ -1642,6 +1822,13 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1642
1822
  const startTime = Date.now();
1643
1823
  const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
1644
1824
  const wrappedPromise = parentPromise.then(async result => {
1825
+ if (isPendingBackgroundResponse(openAIParams, result)) {
1826
+ this.backgroundResponses.set(result.id, {
1827
+ openAIParams,
1828
+ posthogParams
1829
+ });
1830
+ return result;
1831
+ }
1645
1832
  const latency = (Date.now() - startTime) / 1000;
1646
1833
  await captureAiGeneration(this.phClient, {
1647
1834
  ...posthogParams,
@@ -1657,7 +1844,8 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1657
1844
  inputTokens: result.usage?.input_tokens ?? 0,
1658
1845
  outputTokens: result.usage?.output_tokens ?? 0,
1659
1846
  reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1660
- cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
1847
+ cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
1848
+ rawUsage: result.usage
1661
1849
  },
1662
1850
  stopReason: result.status ?? undefined,
1663
1851
  completionId: result.id,
@@ -1763,9 +1951,6 @@ async function captureAiGenerationAfterSuccess(...args) {
1763
1951
  captureAiGenerationInBackground(...args);
1764
1952
  }
1765
1953
  }
1766
- function isPendingBackgroundResponse(params, response) {
1767
- return params.background === true && !response.usage && !!response.status && !isTerminalResponse(response);
1768
- }
1769
1954
  class PostHogOpenAI extends OpenAI {
1770
1955
  constructor(config) {
1771
1956
  const {
@@ -2068,11 +2253,47 @@ class WrappedCompletions extends Completions {
2068
2253
  }
2069
2254
  }
2070
2255
  class WrappedResponses extends Responses {
2256
+ backgroundResponses = new BackgroundResponseTracker();
2071
2257
  constructor(client, phClient) {
2072
2258
  super(client);
2073
2259
  this.phClient = phClient;
2074
2260
  this.baseURL = client.baseURL;
2075
2261
  }
2262
+ async captureBackgroundResponse(result, context) {
2263
+ const {
2264
+ openAIParams,
2265
+ posthogParams
2266
+ } = context;
2267
+ await captureAiGenerationAfterSuccess(this.phClient, {
2268
+ ...posthogParams,
2269
+ model: openAIParams.model ?? result.model,
2270
+ provider: 'openai',
2271
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2272
+ output: formatResponseOpenAI({
2273
+ output: result.output
2274
+ }),
2275
+ latency: getBackgroundResponseLatency(result),
2276
+ baseURL: this.baseURL,
2277
+ modelParameters: getModelParams(openAIParams, result.service_tier),
2278
+ httpStatus: 200,
2279
+ usage: {
2280
+ inputTokens: result.usage?.input_tokens ?? 0,
2281
+ outputTokens: result.usage?.output_tokens ?? 0,
2282
+ reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
2283
+ cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
2284
+ webSearchCount: calculateWebSearchCount(result),
2285
+ rawUsage: result.usage
2286
+ },
2287
+ stopReason: result.status ?? undefined,
2288
+ tools: extractAvailableToolCalls('openai', openAIParams),
2289
+ completionId: result.id,
2290
+ providerMetadata: buildProviderMetadata({
2291
+ requestId: extractRequestId(result),
2292
+ incompleteDetails: result.incomplete_details
2293
+ }),
2294
+ error: getResponseFailure(result)
2295
+ });
2296
+ }
2076
2297
 
2077
2298
  // --- Overload #1: Non-streaming
2078
2299
 
@@ -2122,6 +2343,12 @@ class WrappedResponses extends Responses {
2122
2343
  if (!completionIdFromResponse && chunk.response.id) {
2123
2344
  completionIdFromResponse = chunk.response.id;
2124
2345
  }
2346
+ if (openAIParams.background === true && !this.backgroundResponses.get(chunk.response.id)) {
2347
+ this.backgroundResponses.set(chunk.response.id, {
2348
+ openAIParams,
2349
+ posthogParams
2350
+ });
2351
+ }
2125
2352
  if (chunk.response.service_tier != null) {
2126
2353
  serviceTierFromResponse = chunk.response.service_tier;
2127
2354
  }
@@ -2146,6 +2373,15 @@ class WrappedResponses extends Responses {
2146
2373
  };
2147
2374
  }
2148
2375
  }
2376
+ if (openAIParams.background === true) {
2377
+ if (terminalResponse) {
2378
+ const context = this.backgroundResponses.take(terminalResponse.id);
2379
+ if (context) {
2380
+ await this.captureBackgroundResponse(terminalResponse, context).catch(() => undefined);
2381
+ }
2382
+ }
2383
+ return;
2384
+ }
2149
2385
  const latency = (Date.now() - startTime) / 1000;
2150
2386
  const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
2151
2387
  const availableTools = extractAvailableToolCalls('openai', openAIParams);
@@ -2177,6 +2413,9 @@ class WrappedResponses extends Responses {
2177
2413
  error: getResponseFailure(terminalResponse)
2178
2414
  });
2179
2415
  } catch (error) {
2416
+ if (openAIParams.background === true && completionIdFromResponse && this.backgroundResponses.get(completionIdFromResponse)) {
2417
+ throw error;
2418
+ }
2180
2419
  await captureAiGeneration(this.phClient, {
2181
2420
  ...posthogParams,
2182
2421
  model: openAIParams.model,
@@ -2210,6 +2449,10 @@ class WrappedResponses extends Responses {
2210
2449
  const wrappedPromise = parentPromise.then(async result => {
2211
2450
  if ('output' in result) {
2212
2451
  if (isPendingBackgroundResponse(openAIParams, result)) {
2452
+ this.backgroundResponses.set(result.id, {
2453
+ openAIParams,
2454
+ posthogParams
2455
+ });
2213
2456
  return result;
2214
2457
  }
2215
2458
  const latency = (Date.now() - startTime) / 1000;
@@ -2269,6 +2512,55 @@ class WrappedResponses extends Responses {
2269
2512
  return preserveProviderPromise(parentPromise, wrappedPromise);
2270
2513
  }
2271
2514
  }
2515
+ retrieve(responseID, query = {}, options) {
2516
+ const parentPromise = super.retrieve(responseID, query, options);
2517
+
2518
+ // Preserve the upstream promise and stream unchanged for responses that
2519
+ // were not created through this client.
2520
+ if (!this.backgroundResponses.get(responseID)) {
2521
+ return parentPromise;
2522
+ }
2523
+ if (query.stream) {
2524
+ return parentPromise._thenUnwrap(result => {
2525
+ if ('controller' in result) {
2526
+ return wrapBackgroundResponseStream(result, responseID, this.backgroundResponses, (response, context) => this.captureBackgroundResponse(response, context));
2527
+ }
2528
+ return result;
2529
+ });
2530
+ }
2531
+ return parentPromise._thenUnwrap(async result => {
2532
+ if (!('output' in result) || !isTerminalResponse(result)) {
2533
+ return result;
2534
+ }
2535
+
2536
+ // Removing the context before capture makes concurrent or repeated
2537
+ // terminal polls idempotent.
2538
+ const context = this.backgroundResponses.take(responseID);
2539
+ if (context) {
2540
+ await this.captureBackgroundResponse(result, context).catch(() => undefined);
2541
+ }
2542
+ return result;
2543
+ });
2544
+ }
2545
+ cancel(responseID, options) {
2546
+ const parentPromise = super.cancel(responseID, options);
2547
+
2548
+ // Avoid wrapping calls that do not belong to a background response created
2549
+ // through this client, preserving the upstream APIPromise unchanged.
2550
+ if (!this.backgroundResponses.get(responseID)) {
2551
+ return parentPromise;
2552
+ }
2553
+ return parentPromise._thenUnwrap(async result => {
2554
+ if (!isTerminalResponse(result)) {
2555
+ return result;
2556
+ }
2557
+ const context = this.backgroundResponses.take(responseID);
2558
+ if (context) {
2559
+ await this.captureBackgroundResponse(result, context).catch(() => undefined);
2560
+ }
2561
+ return result;
2562
+ });
2563
+ }
2272
2564
  parse(body, options) {
2273
2565
  const {
2274
2566
  providerParams: openAIParams,
@@ -2278,6 +2570,10 @@ class WrappedResponses extends Responses {
2278
2570
  const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
2279
2571
  const wrappedPromise = parentPromise.then(async result => {
2280
2572
  if (isPendingBackgroundResponse(openAIParams, result)) {
2573
+ this.backgroundResponses.set(result.id, {
2574
+ openAIParams,
2575
+ posthogParams
2576
+ });
2281
2577
  return result;
2282
2578
  }
2283
2579
  const latency = (Date.now() - startTime) / 1000;