@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.
@@ -11,6 +11,33 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
11
 
12
12
  var AnthropicOriginal__default = /*#__PURE__*/_interopDefault(AnthropicOriginal);
13
13
 
14
+ /** @internal */
15
+
16
+ /** @internal */
17
+
18
+ /** @internal */
19
+ function isFullAiCaptureEnabled(client) {
20
+ return client?.enableFullAiCapture === true;
21
+ }
22
+
23
+ /** @internal */
24
+ function captureAiEvent(client, event) {
25
+ if (isFullAiCaptureEnabled(client) && typeof client.captureAi === 'function') {
26
+ client.captureAi(event);
27
+ return;
28
+ }
29
+ client.capture(event);
30
+ }
31
+
32
+ /** @internal */
33
+ async function captureAiEventImmediate(client, event) {
34
+ if (isFullAiCaptureEnabled(client) && typeof client.captureAiImmediate === 'function') {
35
+ await client.captureAiImmediate(event);
36
+ return;
37
+ }
38
+ await client.captureImmediate(event);
39
+ }
40
+
14
41
  const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
15
42
  const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
16
43
  class Base64Recognizer {
@@ -116,7 +143,6 @@ class BinaryContentRedactor {
116
143
  this.recognizer = recognizer;
117
144
  }
118
145
  redact(value, mediaType) {
119
- if (this.isMultimodalEnabled()) return value;
120
146
  this.visited = new WeakSet();
121
147
  return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY);
122
148
  }
@@ -160,21 +186,23 @@ class BinaryContentRedactor {
160
186
  if (mediaType === 'application/octet-stream') return '[base64 file redacted]';
161
187
  return `[base64 ${mediaType} redacted]`;
162
188
  }
163
- isMultimodalEnabled() {
164
- const val = process.env._INTERNAL_LLMA_MULTIMODAL || '';
165
- return val.toLowerCase() === 'true' || val === '1' || val.toLowerCase() === 'yes';
166
- }
167
189
  }
168
190
 
169
191
  const redactor = new BinaryContentRedactor();
170
- const sanitizeAnthropic = data => redactor.redact(data);
192
+ const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
193
+ const sanitizeAnthropic = (data, client) => sanitize(data, client);
171
194
 
172
195
  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']);
196
+
197
+ /**
198
+ * Whether the caller supplied their own token counts, which override the ones the SDK
199
+ * derived from the provider response.
200
+ */
201
+ function hasTokenOverrides(posthogProperties) {
202
+ return !!posthogProperties && Object.keys(posthogProperties).some(key => TOKEN_PROPERTY_KEYS.has(key));
203
+ }
173
204
  function getTokensSource(posthogProperties) {
174
- if (posthogProperties && Object.keys(posthogProperties).some(key => TOKEN_PROPERTY_KEYS.has(key))) {
175
- return 'passthrough';
176
- }
177
- return 'sdk';
205
+ return hasTokenOverrides(posthogProperties) ? 'passthrough' : 'sdk';
178
206
  }
179
207
  const STRING_FORMAT = 'utf8';
180
208
 
@@ -310,7 +338,7 @@ function addDefaults(params) {
310
338
  };
311
339
  }
312
340
 
313
- var version = "8.7.0";
341
+ var version = "8.8.0";
314
342
 
315
343
  const DEFAULT_MAX_DEPTH = 3;
316
344
  const MAX_STACK_LINES = 20;
@@ -477,6 +505,10 @@ const captureAiGeneration = async (client, options) => {
477
505
  $ai_total_cost_usd: inputCostUSD + outputCostUSD
478
506
  };
479
507
  }
508
+
509
+ // The caller's own token counts override the SDK-derived ones further down, via the
510
+ // `options.properties` spread.
511
+ const tokensOverridden = hasTokenOverrides(options.properties);
480
512
  const additionalTokenValues = {
481
513
  ...(usage.reasoningTokens ? {
482
514
  $ai_reasoning_tokens: usage.reasoningTokens
@@ -487,6 +519,18 @@ const captureAiGeneration = async (client, options) => {
487
519
  ...(usage.cacheCreationInputTokens ? {
488
520
  $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
489
521
  } : {}),
522
+ // Checked against undefined rather than truthiness, because false is the meaningful
523
+ // value here and a truthiness guard would drop it.
524
+ //
525
+ // Dropped entirely when the caller overrides the token counts: the flag describes how
526
+ // the SDK-derived counts relate to each other, so against passthrough counts it can be
527
+ // wrong in the expensive direction. Declaring inclusive over counts that are actually
528
+ // exclusive makes ingestion subtract the cache pool that was never in the input. A
529
+ // caller who knows their own accounting model can still pass
530
+ // `$ai_cache_reporting_exclusive` themselves, and that value wins.
531
+ ...(usage.cacheReportingExclusive !== undefined && !tokensOverridden ? {
532
+ $ai_cache_reporting_exclusive: usage.cacheReportingExclusive
533
+ } : {}),
490
534
  ...(usage.webSearchCount ? {
491
535
  $ai_web_search_count: usage.webSearchCount
492
536
  } : {}),
@@ -543,9 +587,9 @@ const captureAiGeneration = async (client, options) => {
543
587
  groups: options.groups
544
588
  };
545
589
  if (options.captureImmediate) {
546
- await client.captureImmediate(event);
590
+ await captureAiEventImmediate(client, event);
547
591
  } else {
548
- client.capture(event);
592
+ captureAiEvent(client, event);
549
593
  }
550
594
  } catch (error) {
551
595
  // Telemetry failures must never affect the instrumented provider call.
@@ -1006,7 +1050,7 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
1006
1050
  ...posthogParams,
1007
1051
  model: anthropicParams.model,
1008
1052
  provider: 'anthropic',
1009
- input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams, 'anthropic')),
1053
+ input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams, 'anthropic'), this.phClient),
1010
1054
  output: formattedOutput,
1011
1055
  latency,
1012
1056
  timeToFirstToken,
@@ -1022,7 +1066,7 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
1022
1066
  ...posthogParams,
1023
1067
  model: anthropicParams.model,
1024
1068
  provider: 'anthropic',
1025
- input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams)),
1069
+ input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams), this.phClient),
1026
1070
  output: [],
1027
1071
  latency: 0,
1028
1072
  baseURL: this.baseURL,
@@ -1058,7 +1102,7 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
1058
1102
  ...posthogParams,
1059
1103
  model: anthropicParams.model,
1060
1104
  provider: 'anthropic',
1061
- input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams)),
1105
+ input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams), this.phClient),
1062
1106
  output: formatResponseAnthropic(result),
1063
1107
  latency,
1064
1108
  baseURL: this.baseURL,
@@ -1082,7 +1126,7 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
1082
1126
  ...posthogParams,
1083
1127
  model: anthropicParams.model,
1084
1128
  provider: 'anthropic',
1085
- input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams)),
1129
+ input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams), this.phClient),
1086
1130
  output: [],
1087
1131
  latency: 0,
1088
1132
  baseURL: this.baseURL,