@posthog/ai 7.5.4 → 7.6.1
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.
- package/dist/anthropic/index.cjs +71 -63
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +71 -63
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +106 -54
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.mjs +106 -54
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +308 -223
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +308 -223
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +161 -136
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.mjs +161 -136
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/openai/index.cjs +163 -133
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +163 -133
- package/dist/openai/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +82 -57
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +82 -57
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +7 -7
package/dist/vercel/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { v4 } from 'uuid';
|
|
|
2
2
|
import { Buffer } from 'buffer';
|
|
3
3
|
import { uuidv7 } from '@posthog/core';
|
|
4
4
|
|
|
5
|
-
var version = "7.
|
|
5
|
+
var version = "7.6.1";
|
|
6
6
|
|
|
7
7
|
// Type guards for safer type checking
|
|
8
8
|
|
|
@@ -10,6 +10,59 @@ const isString = value => {
|
|
|
10
10
|
return typeof value === 'string';
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
const REDACTED_IMAGE_PLACEHOLDER = '[base64 image redacted]';
|
|
14
|
+
|
|
15
|
+
// ============================================
|
|
16
|
+
// Multimodal Feature Toggle
|
|
17
|
+
// ============================================
|
|
18
|
+
|
|
19
|
+
const isMultimodalEnabled = () => {
|
|
20
|
+
const val = process.env._INTERNAL_LLMA_MULTIMODAL || '';
|
|
21
|
+
return val.toLowerCase() === 'true' || val === '1' || val.toLowerCase() === 'yes';
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// ============================================
|
|
25
|
+
// Base64 Detection Helpers
|
|
26
|
+
// ============================================
|
|
27
|
+
|
|
28
|
+
const isBase64DataUrl = str => {
|
|
29
|
+
return /^data:([^;]+);base64,/.test(str);
|
|
30
|
+
};
|
|
31
|
+
const isValidUrl = str => {
|
|
32
|
+
try {
|
|
33
|
+
new URL(str);
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
// Not an absolute URL, check if it's a relative URL or path
|
|
37
|
+
return str.startsWith('/') || str.startsWith('./') || str.startsWith('../');
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const isRawBase64 = str => {
|
|
41
|
+
// Skip if it's a valid URL or path
|
|
42
|
+
if (isValidUrl(str)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Check if it's a valid base64 string
|
|
47
|
+
// Base64 images are typically at least a few hundred chars, but we'll be conservative
|
|
48
|
+
return str.length > 20 && /^[A-Za-z0-9+/]+=*$/.test(str);
|
|
49
|
+
};
|
|
50
|
+
function redactBase64DataUrl(str) {
|
|
51
|
+
if (isMultimodalEnabled()) return str;
|
|
52
|
+
if (!isString(str)) return str;
|
|
53
|
+
|
|
54
|
+
// Check for data URL format
|
|
55
|
+
if (isBase64DataUrl(str)) {
|
|
56
|
+
return REDACTED_IMAGE_PLACEHOLDER;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check for raw base64 (Vercel sends raw base64 for inline images)
|
|
60
|
+
if (isRawBase64(str)) {
|
|
61
|
+
return REDACTED_IMAGE_PLACEHOLDER;
|
|
62
|
+
}
|
|
63
|
+
return str;
|
|
64
|
+
}
|
|
65
|
+
|
|
13
66
|
// limit large outputs by truncating to 200kb (approx 200k bytes)
|
|
14
67
|
const MAX_OUTPUT_SIZE = 200000;
|
|
15
68
|
const STRING_FORMAT = 'utf8';
|
|
@@ -314,6 +367,9 @@ const sendEventToPosthog = async ({
|
|
|
314
367
|
} : {}),
|
|
315
368
|
...(usage.webSearchCount ? {
|
|
316
369
|
$ai_web_search_count: usage.webSearchCount
|
|
370
|
+
} : {}),
|
|
371
|
+
...(usage.rawUsage ? {
|
|
372
|
+
$ai_usage: usage.rawUsage
|
|
317
373
|
} : {})
|
|
318
374
|
};
|
|
319
375
|
const properties = {
|
|
@@ -358,59 +414,6 @@ const sendEventToPosthog = async ({
|
|
|
358
414
|
return Promise.resolve();
|
|
359
415
|
};
|
|
360
416
|
|
|
361
|
-
const REDACTED_IMAGE_PLACEHOLDER = '[base64 image redacted]';
|
|
362
|
-
|
|
363
|
-
// ============================================
|
|
364
|
-
// Multimodal Feature Toggle
|
|
365
|
-
// ============================================
|
|
366
|
-
|
|
367
|
-
const isMultimodalEnabled = () => {
|
|
368
|
-
const val = process.env._INTERNAL_LLMA_MULTIMODAL || '';
|
|
369
|
-
return val.toLowerCase() === 'true' || val === '1' || val.toLowerCase() === 'yes';
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// ============================================
|
|
373
|
-
// Base64 Detection Helpers
|
|
374
|
-
// ============================================
|
|
375
|
-
|
|
376
|
-
const isBase64DataUrl = str => {
|
|
377
|
-
return /^data:([^;]+);base64,/.test(str);
|
|
378
|
-
};
|
|
379
|
-
const isValidUrl = str => {
|
|
380
|
-
try {
|
|
381
|
-
new URL(str);
|
|
382
|
-
return true;
|
|
383
|
-
} catch {
|
|
384
|
-
// Not an absolute URL, check if it's a relative URL or path
|
|
385
|
-
return str.startsWith('/') || str.startsWith('./') || str.startsWith('../');
|
|
386
|
-
}
|
|
387
|
-
};
|
|
388
|
-
const isRawBase64 = str => {
|
|
389
|
-
// Skip if it's a valid URL or path
|
|
390
|
-
if (isValidUrl(str)) {
|
|
391
|
-
return false;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// Check if it's a valid base64 string
|
|
395
|
-
// Base64 images are typically at least a few hundred chars, but we'll be conservative
|
|
396
|
-
return str.length > 20 && /^[A-Za-z0-9+/]+=*$/.test(str);
|
|
397
|
-
};
|
|
398
|
-
function redactBase64DataUrl(str) {
|
|
399
|
-
if (isMultimodalEnabled()) return str;
|
|
400
|
-
if (!isString(str)) return str;
|
|
401
|
-
|
|
402
|
-
// Check for data URL format
|
|
403
|
-
if (isBase64DataUrl(str)) {
|
|
404
|
-
return REDACTED_IMAGE_PLACEHOLDER;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Check for raw base64 (Vercel sends raw base64 for inline images)
|
|
408
|
-
if (isRawBase64(str)) {
|
|
409
|
-
return REDACTED_IMAGE_PLACEHOLDER;
|
|
410
|
-
}
|
|
411
|
-
return str;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
417
|
// Union types for dual version support
|
|
415
418
|
|
|
416
419
|
// Type guards
|
|
@@ -738,13 +741,31 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
738
741
|
|
|
739
742
|
// V2 usage has simple numbers, V3 has objects with .total - normalize both
|
|
740
743
|
const usageObj = result.usage;
|
|
744
|
+
|
|
745
|
+
// Extract raw response for providers that include detailed usage metadata
|
|
746
|
+
// For Gemini, candidatesTokensDetails is in result.response.body.usageMetadata
|
|
747
|
+
const rawUsageData = {
|
|
748
|
+
usage: result.usage,
|
|
749
|
+
providerMetadata
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
// Include response body usageMetadata if it contains detailed token breakdown (e.g., candidatesTokensDetails)
|
|
753
|
+
if (result.response && typeof result.response === 'object') {
|
|
754
|
+
const responseBody = result.response.body;
|
|
755
|
+
if (responseBody && typeof responseBody === 'object' && 'usageMetadata' in responseBody) {
|
|
756
|
+
rawUsageData.rawResponse = {
|
|
757
|
+
usageMetadata: responseBody.usageMetadata
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
}
|
|
741
761
|
const usage = {
|
|
742
762
|
inputTokens: extractTokenCount(result.usage.inputTokens),
|
|
743
763
|
outputTokens: extractTokenCount(result.usage.outputTokens),
|
|
744
764
|
reasoningTokens: extractReasoningTokens(usageObj),
|
|
745
765
|
cacheReadInputTokens: extractCacheReadTokens(usageObj),
|
|
746
766
|
webSearchCount,
|
|
747
|
-
...additionalTokenValues
|
|
767
|
+
...additionalTokenValues,
|
|
768
|
+
rawUsage: rawUsageData
|
|
748
769
|
};
|
|
749
770
|
adjustAnthropicV3CacheTokens(model, provider, usage);
|
|
750
771
|
await sendEventToPosthog({
|
|
@@ -904,10 +925,14 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
904
925
|
}] : [];
|
|
905
926
|
const webSearchCount = extractWebSearchCount(providerMetadata, usage);
|
|
906
927
|
|
|
907
|
-
// Update usage with web search count
|
|
928
|
+
// Update usage with web search count and raw metadata
|
|
908
929
|
const finalUsage = {
|
|
909
930
|
...usage,
|
|
910
|
-
webSearchCount
|
|
931
|
+
webSearchCount,
|
|
932
|
+
rawUsage: {
|
|
933
|
+
usage,
|
|
934
|
+
providerMetadata
|
|
935
|
+
}
|
|
911
936
|
};
|
|
912
937
|
adjustAnthropicV3CacheTokens(model, provider, finalUsage);
|
|
913
938
|
await sendEventToPosthog({
|