@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.
@@ -13,6 +13,33 @@ const isString = value => {
13
13
  return typeof value === 'string';
14
14
  };
15
15
 
16
+ /** @internal */
17
+
18
+ /** @internal */
19
+
20
+ /** @internal */
21
+ function isFullAiCaptureEnabled(client) {
22
+ return client?.enableFullAiCapture === true;
23
+ }
24
+
25
+ /** @internal */
26
+ function captureAiEvent(client, event) {
27
+ if (isFullAiCaptureEnabled(client) && typeof client.captureAi === 'function') {
28
+ client.captureAi(event);
29
+ return;
30
+ }
31
+ client.capture(event);
32
+ }
33
+
34
+ /** @internal */
35
+ async function captureAiEventImmediate(client, event) {
36
+ if (isFullAiCaptureEnabled(client) && typeof client.captureAiImmediate === 'function') {
37
+ await client.captureAiImmediate(event);
38
+ return;
39
+ }
40
+ await client.captureImmediate(event);
41
+ }
42
+
16
43
  const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
17
44
  const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
18
45
  class Base64Recognizer {
@@ -118,7 +145,6 @@ class BinaryContentRedactor {
118
145
  this.recognizer = recognizer;
119
146
  }
120
147
  redact(value, mediaType) {
121
- if (this.isMultimodalEnabled()) return value;
122
148
  this.visited = new WeakSet();
123
149
  return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY);
124
150
  }
@@ -162,22 +188,24 @@ class BinaryContentRedactor {
162
188
  if (mediaType === 'application/octet-stream') return '[base64 file redacted]';
163
189
  return `[base64 ${mediaType} redacted]`;
164
190
  }
165
- isMultimodalEnabled() {
166
- const val = process.env._INTERNAL_LLMA_MULTIMODAL || '';
167
- return val.toLowerCase() === 'true' || val === '1' || val.toLowerCase() === 'yes';
168
- }
169
191
  }
170
192
 
171
193
  const redactor = new BinaryContentRedactor();
172
- const sanitizeOpenAI = data => redactor.redact(data);
173
- const sanitizeOpenAIResponse = data => redactor.redact(data);
194
+ const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
195
+ const sanitizeOpenAI = (data, client) => sanitize(data, client);
196
+ const sanitizeOpenAIResponse = (data, client) => sanitize(data, client);
174
197
 
175
198
  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']);
199
+
200
+ /**
201
+ * Whether the caller supplied their own token counts, which override the ones the SDK
202
+ * derived from the provider response.
203
+ */
204
+ function hasTokenOverrides(posthogProperties) {
205
+ return !!posthogProperties && Object.keys(posthogProperties).some(key => TOKEN_PROPERTY_KEYS.has(key));
206
+ }
176
207
  function getTokensSource(posthogProperties) {
177
- if (posthogProperties && Object.keys(posthogProperties).some(key => TOKEN_PROPERTY_KEYS.has(key))) {
178
- return 'passthrough';
179
- }
180
- return 'sdk';
208
+ return hasTokenOverrides(posthogProperties) ? 'passthrough' : 'sdk';
181
209
  }
182
210
  const STRING_FORMAT = 'utf8';
183
211
 
@@ -548,7 +576,7 @@ function formatOpenAIResponsesInput(input, instructions) {
548
576
  return messages;
549
577
  }
550
578
 
551
- var version = "8.7.0";
579
+ var version = "8.8.0";
552
580
 
553
581
  const DEFAULT_MAX_DEPTH = 3;
554
582
  const MAX_STACK_LINES = 20;
@@ -715,6 +743,10 @@ const captureAiGeneration$1 = async (client, options) => {
715
743
  $ai_total_cost_usd: inputCostUSD + outputCostUSD
716
744
  };
717
745
  }
746
+
747
+ // The caller's own token counts override the SDK-derived ones further down, via the
748
+ // `options.properties` spread.
749
+ const tokensOverridden = hasTokenOverrides(options.properties);
718
750
  const additionalTokenValues = {
719
751
  ...(usage.reasoningTokens ? {
720
752
  $ai_reasoning_tokens: usage.reasoningTokens
@@ -725,6 +757,18 @@ const captureAiGeneration$1 = async (client, options) => {
725
757
  ...(usage.cacheCreationInputTokens ? {
726
758
  $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
727
759
  } : {}),
760
+ // Checked against undefined rather than truthiness, because false is the meaningful
761
+ // value here and a truthiness guard would drop it.
762
+ //
763
+ // Dropped entirely when the caller overrides the token counts: the flag describes how
764
+ // the SDK-derived counts relate to each other, so against passthrough counts it can be
765
+ // wrong in the expensive direction. Declaring inclusive over counts that are actually
766
+ // exclusive makes ingestion subtract the cache pool that was never in the input. A
767
+ // caller who knows their own accounting model can still pass
768
+ // `$ai_cache_reporting_exclusive` themselves, and that value wins.
769
+ ...(usage.cacheReportingExclusive !== undefined && !tokensOverridden ? {
770
+ $ai_cache_reporting_exclusive: usage.cacheReportingExclusive
771
+ } : {}),
728
772
  ...(usage.webSearchCount ? {
729
773
  $ai_web_search_count: usage.webSearchCount
730
774
  } : {}),
@@ -781,9 +825,9 @@ const captureAiGeneration$1 = async (client, options) => {
781
825
  groups: options.groups
782
826
  };
783
827
  if (options.captureImmediate) {
784
- await client.captureImmediate(event);
828
+ await captureAiEventImmediate(client, event);
785
829
  } else {
786
- client.capture(event);
830
+ captureAiEvent(client, event);
787
831
  }
788
832
  } catch (error) {
789
833
  // Telemetry failures must never affect the instrumented provider call.
@@ -1450,8 +1494,8 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1450
1494
  ...posthogParams,
1451
1495
  model: openAIParams.model ?? modelFromResponse,
1452
1496
  provider: 'azure',
1453
- input: sanitizeOpenAI(openAIParams.messages),
1454
- output: sanitizeOpenAIResponse(formattedOutput),
1497
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1498
+ output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
1455
1499
  latency,
1456
1500
  timeToFirstToken,
1457
1501
  baseURL: this.baseURL,
@@ -1468,7 +1512,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1468
1512
  ...posthogParams,
1469
1513
  model: openAIParams.model,
1470
1514
  provider: 'azure',
1471
- input: sanitizeOpenAI(openAIParams.messages),
1515
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1472
1516
  output: [],
1473
1517
  latency: 0,
1474
1518
  baseURL: this.baseURL,
@@ -1507,8 +1551,8 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1507
1551
  ...posthogParams,
1508
1552
  model: openAIParams.model ?? result.model,
1509
1553
  provider: 'azure',
1510
- input: sanitizeOpenAI(openAIParams.messages),
1511
- output: sanitizeOpenAIResponse(formatResponseOpenAI(result)),
1554
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1555
+ output: sanitizeOpenAIResponse(formatResponseOpenAI(result), this.phClient),
1512
1556
  latency,
1513
1557
  baseURL: this.baseURL,
1514
1558
  modelParameters: getModelParams(body, result.service_tier),
@@ -1534,7 +1578,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.C
1534
1578
  ...posthogParams,
1535
1579
  model: openAIParams.model,
1536
1580
  provider: 'azure',
1537
- input: sanitizeOpenAI(openAIParams.messages),
1581
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
1538
1582
  output: [],
1539
1583
  latency: 0,
1540
1584
  baseURL: this.baseURL,
@@ -1676,8 +1720,8 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1676
1720
  ...posthogParams,
1677
1721
  model: openAIParams.model ?? modelFromResponse,
1678
1722
  provider: 'azure',
1679
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1680
- output: sanitizeOpenAIResponse(finalContent),
1723
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1724
+ output: sanitizeOpenAIResponse(finalContent, this.phClient),
1681
1725
  latency,
1682
1726
  timeToFirstToken,
1683
1727
  baseURL: this.baseURL,
@@ -1699,7 +1743,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1699
1743
  ...posthogParams,
1700
1744
  model: openAIParams.model,
1701
1745
  provider: 'azure',
1702
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1746
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1703
1747
  output: [],
1704
1748
  latency: 0,
1705
1749
  baseURL: this.baseURL,
@@ -1739,8 +1783,8 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1739
1783
  ...posthogParams,
1740
1784
  model: openAIParams.model ?? result.model,
1741
1785
  provider: 'azure',
1742
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1743
- output: sanitizeOpenAIResponse(result.output),
1786
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1787
+ output: sanitizeOpenAIResponse(result.output, this.phClient),
1744
1788
  latency,
1745
1789
  baseURL: this.baseURL,
1746
1790
  modelParameters: getModelParams(body, result.service_tier),
@@ -1769,7 +1813,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1769
1813
  ...posthogParams,
1770
1814
  model: openAIParams.model,
1771
1815
  provider: 'azure',
1772
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1816
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1773
1817
  output: [],
1774
1818
  latency: 0,
1775
1819
  baseURL: this.baseURL,
@@ -1855,8 +1899,8 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1855
1899
  ...posthogParams,
1856
1900
  model: openAIParams.model ?? result.model,
1857
1901
  provider: 'azure',
1858
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1859
- output: sanitizeOpenAIResponse(result.output),
1902
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1903
+ output: sanitizeOpenAIResponse(result.output, this.phClient),
1860
1904
  latency,
1861
1905
  baseURL: this.baseURL,
1862
1906
  modelParameters: getModelParams(body, result.service_tier),
@@ -1883,7 +1927,7 @@ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Respo
1883
1927
  ...posthogParams,
1884
1928
  model: openAIParams.model,
1885
1929
  provider: 'azure',
1886
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
1930
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
1887
1931
  output: [],
1888
1932
  latency: 0,
1889
1933
  baseURL: this.baseURL,
@@ -2159,8 +2203,8 @@ class WrappedCompletions extends Completions {
2159
2203
  ...posthogParams,
2160
2204
  model: openAIParams.model ?? modelFromResponse,
2161
2205
  provider: 'openai',
2162
- input: sanitizeOpenAI(openAIParams.messages),
2163
- output: sanitizeOpenAIResponse(formattedOutput),
2206
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2207
+ output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
2164
2208
  latency,
2165
2209
  timeToFirstToken,
2166
2210
  baseURL: this.baseURL,
@@ -2187,7 +2231,7 @@ class WrappedCompletions extends Completions {
2187
2231
  ...posthogParams,
2188
2232
  model: openAIParams.model,
2189
2233
  provider: 'openai',
2190
- input: sanitizeOpenAI(openAIParams.messages),
2234
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2191
2235
  output: [],
2192
2236
  latency: 0,
2193
2237
  baseURL: this.baseURL,
@@ -2228,8 +2272,8 @@ class WrappedCompletions extends Completions {
2228
2272
  ...posthogParams,
2229
2273
  model: openAIParams.model ?? result.model,
2230
2274
  provider: 'openai',
2231
- input: sanitizeOpenAI(openAIParams.messages),
2232
- output: sanitizeOpenAIResponse(formattedOutput),
2275
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2276
+ output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
2233
2277
  latency,
2234
2278
  baseURL: this.baseURL,
2235
2279
  modelParameters: getModelParams(body, result.service_tier),
@@ -2259,7 +2303,7 @@ class WrappedCompletions extends Completions {
2259
2303
  ...posthogParams,
2260
2304
  model: openAIParams.model,
2261
2305
  provider: 'openai',
2262
- input: sanitizeOpenAI(openAIParams.messages),
2306
+ input: sanitizeOpenAI(openAIParams.messages, this.phClient),
2263
2307
  output: [],
2264
2308
  latency: 0,
2265
2309
  baseURL: this.baseURL,
@@ -2293,7 +2337,7 @@ class WrappedResponses extends Responses {
2293
2337
  ...posthogParams,
2294
2338
  model: openAIParams.model ?? result.model,
2295
2339
  provider: 'openai',
2296
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2340
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2297
2341
  output: formatResponseOpenAI({
2298
2342
  output: result.output
2299
2343
  }),
@@ -2416,8 +2460,8 @@ class WrappedResponses extends Responses {
2416
2460
  ...posthogParams,
2417
2461
  model: openAIParams.model ?? modelFromResponse,
2418
2462
  provider: 'openai',
2419
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2420
- output: sanitizeOpenAIResponse(finalContent),
2463
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2464
+ output: sanitizeOpenAIResponse(finalContent, this.phClient),
2421
2465
  latency,
2422
2466
  timeToFirstToken,
2423
2467
  baseURL: this.baseURL,
@@ -2448,7 +2492,7 @@ class WrappedResponses extends Responses {
2448
2492
  ...posthogParams,
2449
2493
  model: openAIParams.model,
2450
2494
  provider: 'openai',
2451
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2495
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2452
2496
  output: [],
2453
2497
  latency: 0,
2454
2498
  baseURL: this.baseURL,
@@ -2492,8 +2536,8 @@ class WrappedResponses extends Responses {
2492
2536
  ...posthogParams,
2493
2537
  model: openAIParams.model ?? result.model,
2494
2538
  provider: 'openai',
2495
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2496
- output: sanitizeOpenAIResponse(formattedOutput),
2539
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2540
+ output: sanitizeOpenAIResponse(formattedOutput, this.phClient),
2497
2541
  latency,
2498
2542
  baseURL: this.baseURL,
2499
2543
  modelParameters: getModelParams(body, result.service_tier),
@@ -2524,7 +2568,7 @@ class WrappedResponses extends Responses {
2524
2568
  ...posthogParams,
2525
2569
  model: openAIParams.model,
2526
2570
  provider: 'openai',
2527
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2571
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2528
2572
  output: [],
2529
2573
  latency: 0,
2530
2574
  baseURL: this.baseURL,
@@ -2610,8 +2654,8 @@ class WrappedResponses extends Responses {
2610
2654
  ...posthogParams,
2611
2655
  model: openAIParams.model ?? result.model,
2612
2656
  provider: 'openai',
2613
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2614
- output: sanitizeOpenAIResponse(result.output),
2657
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2658
+ output: sanitizeOpenAIResponse(result.output, this.phClient),
2615
2659
  latency,
2616
2660
  baseURL: this.baseURL,
2617
2661
  modelParameters: getModelParams(body, result.service_tier),
@@ -2638,7 +2682,7 @@ class WrappedResponses extends Responses {
2638
2682
  ...posthogParams,
2639
2683
  model: openAIParams.model,
2640
2684
  provider: 'openai',
2641
- input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
2685
+ input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input, this.phClient), openAIParams.instructions),
2642
2686
  output: [],
2643
2687
  latency: 0,
2644
2688
  baseURL: this.baseURL,
@@ -2783,7 +2827,7 @@ class WrappedTranscriptions extends Transcriptions {
2783
2827
  model: openAIParams.model,
2784
2828
  provider: 'openai',
2785
2829
  input: openAIParams.prompt,
2786
- output: sanitizeOpenAIResponse(finalContent),
2830
+ output: sanitizeOpenAIResponse(finalContent, this.phClient),
2787
2831
  latency,
2788
2832
  timeToFirstToken,
2789
2833
  baseURL: this.baseURL,
@@ -2828,7 +2872,7 @@ class WrappedTranscriptions extends Transcriptions {
2828
2872
  model: openAIParams.model,
2829
2873
  provider: 'openai',
2830
2874
  input: openAIParams.prompt,
2831
- output: sanitizeOpenAIResponse(result.text),
2875
+ output: sanitizeOpenAIResponse(result.text, this.phClient),
2832
2876
  latency,
2833
2877
  baseURL: this.baseURL,
2834
2878
  modelParameters: getModelParams(body),