@posthog/ai 8.7.0 → 8.8.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.
@@ -9,6 +9,33 @@ const isString = value => {
9
9
  return typeof value === 'string';
10
10
  };
11
11
 
12
+ /** @internal */
13
+
14
+ /** @internal */
15
+
16
+ /** @internal */
17
+ function isFullAiCaptureEnabled(client) {
18
+ return client?.enableFullAiCapture === true;
19
+ }
20
+
21
+ /** @internal */
22
+ function captureAiEvent(client, event) {
23
+ if (isFullAiCaptureEnabled(client) && typeof client.captureAi === 'function') {
24
+ client.captureAi(event);
25
+ return;
26
+ }
27
+ client.capture(event);
28
+ }
29
+
30
+ /** @internal */
31
+ async function captureAiEventImmediate(client, event) {
32
+ if (isFullAiCaptureEnabled(client) && typeof client.captureAiImmediate === 'function') {
33
+ await client.captureAiImmediate(event);
34
+ return;
35
+ }
36
+ await client.captureImmediate(event);
37
+ }
38
+
12
39
  const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
13
40
  const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
14
41
  class Base64Recognizer {
@@ -114,7 +141,6 @@ class BinaryContentRedactor {
114
141
  this.recognizer = recognizer;
115
142
  }
116
143
  redact(value, mediaType) {
117
- if (this.isMultimodalEnabled()) return value;
118
144
  this.visited = new WeakSet();
119
145
  return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY);
120
146
  }
@@ -158,22 +184,24 @@ class BinaryContentRedactor {
158
184
  if (mediaType === 'application/octet-stream') return '[base64 file redacted]';
159
185
  return `[base64 ${mediaType} redacted]`;
160
186
  }
161
- isMultimodalEnabled() {
162
- const val = process.env._INTERNAL_LLMA_MULTIMODAL || '';
163
- return val.toLowerCase() === 'true' || val === '1' || val.toLowerCase() === 'yes';
164
- }
165
187
  }
166
188
 
167
189
  const redactor = new BinaryContentRedactor();
168
- const sanitizeOpenAI = data => redactor.redact(data);
169
- const sanitizeOpenAIResponse = data => redactor.redact(data);
190
+ const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
191
+ const sanitizeOpenAI = (data, client) => sanitize(data, client);
192
+ const sanitizeOpenAIResponse = (data, client) => sanitize(data, client);
170
193
 
171
194
  const TOKEN_PROPERTY_KEYS = new Set(['$ai_input_tokens', '$ai_output_tokens', '$ai_cache_read_input_tokens', '$ai_cache_creation_input_tokens', '$ai_total_tokens', '$ai_reasoning_tokens']);
195
+
196
+ /**
197
+ * Whether the caller supplied their own token counts, which override the ones the SDK
198
+ * derived from the provider response.
199
+ */
200
+ function hasTokenOverrides(posthogProperties) {
201
+ return !!posthogProperties && Object.keys(posthogProperties).some(key => TOKEN_PROPERTY_KEYS.has(key));
202
+ }
172
203
  function getTokensSource(posthogProperties) {
173
- if (posthogProperties && Object.keys(posthogProperties).some(key => TOKEN_PROPERTY_KEYS.has(key))) {
174
- return 'passthrough';
175
- }
176
- return 'sdk';
204
+ return hasTokenOverrides(posthogProperties) ? 'passthrough' : 'sdk';
177
205
  }
178
206
  const STRING_FORMAT = 'utf8';
179
207
 
@@ -544,7 +572,7 @@ function formatOpenAIResponsesInput(input, instructions) {
544
572
  return messages;
545
573
  }
546
574
 
547
- var version = "8.7.0";
575
+ var version = "8.8.0";
548
576
 
549
577
  const DEFAULT_MAX_DEPTH = 3;
550
578
  const MAX_STACK_LINES = 20;
@@ -711,6 +739,10 @@ const captureAiGeneration$1 = async (client, options) => {
711
739
  $ai_total_cost_usd: inputCostUSD + outputCostUSD
712
740
  };
713
741
  }
742
+
743
+ // The caller's own token counts override the SDK-derived ones further down, via the
744
+ // `options.properties` spread.
745
+ const tokensOverridden = hasTokenOverrides(options.properties);
714
746
  const additionalTokenValues = {
715
747
  ...(usage.reasoningTokens ? {
716
748
  $ai_reasoning_tokens: usage.reasoningTokens
@@ -721,6 +753,18 @@ const captureAiGeneration$1 = async (client, options) => {
721
753
  ...(usage.cacheCreationInputTokens ? {
722
754
  $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
723
755
  } : {}),
756
+ // Checked against undefined rather than truthiness, because false is the meaningful
757
+ // value here and a truthiness guard would drop it.
758
+ //
759
+ // Dropped entirely when the caller overrides the token counts: the flag describes how
760
+ // the SDK-derived counts relate to each other, so against passthrough counts it can be
761
+ // wrong in the expensive direction. Declaring inclusive over counts that are actually
762
+ // exclusive makes ingestion subtract the cache pool that was never in the input. A
763
+ // caller who knows their own accounting model can still pass
764
+ // `$ai_cache_reporting_exclusive` themselves, and that value wins.
765
+ ...(usage.cacheReportingExclusive !== undefined && !tokensOverridden ? {
766
+ $ai_cache_reporting_exclusive: usage.cacheReportingExclusive
767
+ } : {}),
724
768
  ...(usage.webSearchCount ? {
725
769
  $ai_web_search_count: usage.webSearchCount
726
770
  } : {}),
@@ -777,9 +821,9 @@ const captureAiGeneration$1 = async (client, options) => {
777
821
  groups: options.groups
778
822
  };
779
823
  if (options.captureImmediate) {
780
- await client.captureImmediate(event);
824
+ await captureAiEventImmediate(client, event);
781
825
  } else {
782
- client.capture(event);
826
+ captureAiEvent(client, event);
783
827
  }
784
828
  } catch (error) {
785
829
  // Telemetry failures must never affect the instrumented provider call.
@@ -1446,8 +1490,8 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1446
1490
  ...posthogParams,
1447
1491
  model: openAIParams.model ?? modelFromResponse,
1448
1492
  provider: 'azure',
1449
- input: sanitizeOpenAI(openAIParams.messages),
1450
- output: sanitizeOpenAIResponse(formattedOutput),
1493
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1494
+ output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
1451
1495
  latency,
1452
1496
  timeToFirstToken,
1453
1497
  baseURL: this.baseURL,
@@ -1464,7 +1508,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1464
1508
  ...posthogParams,
1465
1509
  model: openAIParams.model,
1466
1510
  provider: 'azure',
1467
- input: sanitizeOpenAI(openAIParams.messages),
1511
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1468
1512
  output: [],
1469
1513
  latency: 0,
1470
1514
  baseURL: this.baseURL,
@@ -1503,8 +1547,8 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1503
1547
  ...posthogParams,
1504
1548
  model: openAIParams.model ?? result.model,
1505
1549
  provider: 'azure',
1506
- input: sanitizeOpenAI(openAIParams.messages),
1507
- output: sanitizeOpenAIResponse(formatResponseOpenAI(result)),
1550
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1551
+ output: sanitizeOpenAIResponse(formatResponseOpenAI(result), this.phClient),
1508
1552
  latency,
1509
1553
  baseURL: this.baseURL,
1510
1554
  modelParameters: getModelParams(body, result.service_tier),
@@ -1530,7 +1574,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends AzureOpenAI.Chat.Com
1530
1574
  ...posthogParams,
1531
1575
  model: openAIParams.model,
1532
1576
  provider: 'azure',
1533
- input: sanitizeOpenAI(openAIParams.messages),
1577
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1534
1578
  output: [],
1535
1579
  latency: 0,
1536
1580
  baseURL: this.baseURL,
@@ -1672,8 +1716,8 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1672
1716
  ...posthogParams,
1673
1717
  model: openAIParams.model ?? modelFromResponse,
1674
1718
  provider: 'azure',
1675
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1676
- output: sanitizeOpenAIResponse(finalContent),
1719
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1720
+ output: sanitizeOpenAIResponse(finalContent, this.phClient),
1677
1721
  latency,
1678
1722
  timeToFirstToken,
1679
1723
  baseURL: this.baseURL,
@@ -1695,7 +1739,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1695
1739
  ...posthogParams,
1696
1740
  model: openAIParams.model,
1697
1741
  provider: 'azure',
1698
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1742
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1699
1743
  output: [],
1700
1744
  latency: 0,
1701
1745
  baseURL: this.baseURL,
@@ -1735,8 +1779,8 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1735
1779
  ...posthogParams,
1736
1780
  model: openAIParams.model ?? result.model,
1737
1781
  provider: 'azure',
1738
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1739
- output: sanitizeOpenAIResponse(result.output),
1782
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1783
+ output: sanitizeOpenAIResponse(result.output, this.phClient),
1740
1784
  latency,
1741
1785
  baseURL: this.baseURL,
1742
1786
  modelParameters: getModelParams(body, result.service_tier),
@@ -1765,7 +1809,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1765
1809
  ...posthogParams,
1766
1810
  model: openAIParams.model,
1767
1811
  provider: 'azure',
1768
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1812
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1769
1813
  output: [],
1770
1814
  latency: 0,
1771
1815
  baseURL: this.baseURL,
@@ -1851,8 +1895,8 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1851
1895
  ...posthogParams,
1852
1896
  model: openAIParams.model ?? result.model,
1853
1897
  provider: 'azure',
1854
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1855
- output: sanitizeOpenAIResponse(result.output),
1898
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1899
+ output: sanitizeOpenAIResponse(result.output, this.phClient),
1856
1900
  latency,
1857
1901
  baseURL: this.baseURL,
1858
1902
  modelParameters: getModelParams(body, result.service_tier),
@@ -1879,7 +1923,7 @@ let WrappedResponses$1 = class WrappedResponses extends AzureOpenAI.Responses {
1879
1923
  ...posthogParams,
1880
1924
  model: openAIParams.model,
1881
1925
  provider: 'azure',
1882
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1926
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1883
1927
  output: [],
1884
1928
  latency: 0,
1885
1929
  baseURL: this.baseURL,
@@ -2155,8 +2199,8 @@ class WrappedCompletions extends Completions {
2155
2199
  ...posthogParams,
2156
2200
  model: openAIParams.model ?? modelFromResponse,
2157
2201
  provider: 'openai',
2158
- input: sanitizeOpenAI(openAIParams.messages),
2159
- output: sanitizeOpenAIResponse(formattedOutput),
2202
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2203
+ output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
2160
2204
  latency,
2161
2205
  timeToFirstToken,
2162
2206
  baseURL: this.baseURL,
@@ -2183,7 +2227,7 @@ class WrappedCompletions extends Completions {
2183
2227
  ...posthogParams,
2184
2228
  model: openAIParams.model,
2185
2229
  provider: 'openai',
2186
- input: sanitizeOpenAI(openAIParams.messages),
2230
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2187
2231
  output: [],
2188
2232
  latency: 0,
2189
2233
  baseURL: this.baseURL,
@@ -2224,8 +2268,8 @@ class WrappedCompletions extends Completions {
2224
2268
  ...posthogParams,
2225
2269
  model: openAIParams.model ?? result.model,
2226
2270
  provider: 'openai',
2227
- input: sanitizeOpenAI(openAIParams.messages),
2228
- output: sanitizeOpenAIResponse(formattedOutput),
2271
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2272
+ output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
2229
2273
  latency,
2230
2274
  baseURL: this.baseURL,
2231
2275
  modelParameters: getModelParams(body, result.service_tier),
@@ -2255,7 +2299,7 @@ class WrappedCompletions extends Completions {
2255
2299
  ...posthogParams,
2256
2300
  model: openAIParams.model,
2257
2301
  provider: 'openai',
2258
- input: sanitizeOpenAI(openAIParams.messages),
2302
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2259
2303
  output: [],
2260
2304
  latency: 0,
2261
2305
  baseURL: this.baseURL,
@@ -2289,7 +2333,7 @@ class WrappedResponses extends Responses {
2289
2333
  ...posthogParams,
2290
2334
  model: openAIParams.model ?? result.model,
2291
2335
  provider: 'openai',
2292
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2336
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2293
2337
  output: formatResponseOpenAI({
2294
2338
  output: result.output
2295
2339
  }),
@@ -2412,8 +2456,8 @@ class WrappedResponses extends Responses {
2412
2456
  ...posthogParams,
2413
2457
  model: openAIParams.model ?? modelFromResponse,
2414
2458
  provider: 'openai',
2415
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2416
- output: sanitizeOpenAIResponse(finalContent),
2459
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2460
+ output: sanitizeOpenAIResponse(finalContent, this.phClient),
2417
2461
  latency,
2418
2462
  timeToFirstToken,
2419
2463
  baseURL: this.baseURL,
@@ -2444,7 +2488,7 @@ class WrappedResponses extends Responses {
2444
2488
  ...posthogParams,
2445
2489
  model: openAIParams.model,
2446
2490
  provider: 'openai',
2447
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2491
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2448
2492
  output: [],
2449
2493
  latency: 0,
2450
2494
  baseURL: this.baseURL,
@@ -2488,8 +2532,8 @@ class WrappedResponses extends Responses {
2488
2532
  ...posthogParams,
2489
2533
  model: openAIParams.model ?? result.model,
2490
2534
  provider: 'openai',
2491
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2492
- output: sanitizeOpenAIResponse(formattedOutput),
2535
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2536
+ output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
2493
2537
  latency,
2494
2538
  baseURL: this.baseURL,
2495
2539
  modelParameters: getModelParams(body, result.service_tier),
@@ -2520,7 +2564,7 @@ class WrappedResponses extends Responses {
2520
2564
  ...posthogParams,
2521
2565
  model: openAIParams.model,
2522
2566
  provider: 'openai',
2523
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2567
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2524
2568
  output: [],
2525
2569
  latency: 0,
2526
2570
  baseURL: this.baseURL,
@@ -2606,8 +2650,8 @@ class WrappedResponses extends Responses {
2606
2650
  ...posthogParams,
2607
2651
  model: openAIParams.model ?? result.model,
2608
2652
  provider: 'openai',
2609
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2610
- output: sanitizeOpenAIResponse(result.output),
2653
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2654
+ output: sanitizeOpenAIResponse(result.output, this.phClient),
2611
2655
  latency,
2612
2656
  baseURL: this.baseURL,
2613
2657
  modelParameters: getModelParams(body, result.service_tier),
@@ -2634,7 +2678,7 @@ class WrappedResponses extends Responses {
2634
2678
  ...posthogParams,
2635
2679
  model: openAIParams.model,
2636
2680
  provider: 'openai',
2637
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2681
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2638
2682
  output: [],
2639
2683
  latency: 0,
2640
2684
  baseURL: this.baseURL,
@@ -2779,7 +2823,7 @@ class WrappedTranscriptions extends Transcriptions {
2779
2823
  model: openAIParams.model,
2780
2824
  provider: 'openai',
2781
2825
  input: openAIParams.prompt,
2782
- output: sanitizeOpenAIResponse(finalContent),
2826
+ output: sanitizeOpenAIResponse(finalContent, this.phClient),
2783
2827
  latency,
2784
2828
  timeToFirstToken,
2785
2829
  baseURL: this.baseURL,
@@ -2824,7 +2868,7 @@ class WrappedTranscriptions extends Transcriptions {
2824
2868
  model: openAIParams.model,
2825
2869
  provider: 'openai',
2826
2870
  input: openAIParams.prompt,
2827
- output: sanitizeOpenAIResponse(result.text),
2871
+ output: sanitizeOpenAIResponse(result.text, this.phClient),
2828
2872
  latency,
2829
2873
  baseURL: this.baseURL,
2830
2874
  modelParameters: getModelParams(body),