@posthog/ai 8.7.1 → 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.
- package/dist/anthropic/index.cjs +36 -13
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +36 -13
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +36 -13
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.mjs +36 -13
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +60 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +60 -35
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +27 -13
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.mjs +27 -13
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/openai/index.cjs +66 -43
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +66 -43
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openai-agents/index.cjs +25 -4
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.mjs +25 -4
- package/dist/openai-agents/index.mjs.map +1 -1
- package/dist/otel/index.cjs +1 -5
- package/dist/otel/index.cjs.map +1 -1
- package/dist/otel/index.mjs +1 -5
- package/dist/otel/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +67 -35
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +67 -35
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/vercel/index.mjs
CHANGED
|
@@ -10,6 +10,33 @@ const isObject = value => {
|
|
|
10
10
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
/** @internal */
|
|
14
|
+
|
|
15
|
+
/** @internal */
|
|
16
|
+
|
|
17
|
+
/** @internal */
|
|
18
|
+
function isFullAiCaptureEnabled(client) {
|
|
19
|
+
return client?.enableFullAiCapture === true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** @internal */
|
|
23
|
+
function captureAiEvent(client, event) {
|
|
24
|
+
if (isFullAiCaptureEnabled(client) && typeof client.captureAi === 'function') {
|
|
25
|
+
client.captureAi(event);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
client.capture(event);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @internal */
|
|
32
|
+
async function captureAiEventImmediate(client, event) {
|
|
33
|
+
if (isFullAiCaptureEnabled(client) && typeof client.captureAiImmediate === 'function') {
|
|
34
|
+
await client.captureAiImmediate(event);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
await client.captureImmediate(event);
|
|
38
|
+
}
|
|
39
|
+
|
|
13
40
|
const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
|
|
14
41
|
const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
|
|
15
42
|
class Base64Recognizer {
|
|
@@ -115,7 +142,6 @@ class BinaryContentRedactor {
|
|
|
115
142
|
this.recognizer = recognizer;
|
|
116
143
|
}
|
|
117
144
|
redact(value, mediaType) {
|
|
118
|
-
if (this.isMultimodalEnabled()) return value;
|
|
119
145
|
this.visited = new WeakSet();
|
|
120
146
|
return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY);
|
|
121
147
|
}
|
|
@@ -159,17 +185,14 @@ class BinaryContentRedactor {
|
|
|
159
185
|
if (mediaType === 'application/octet-stream') return '[base64 file redacted]';
|
|
160
186
|
return `[base64 ${mediaType} redacted]`;
|
|
161
187
|
}
|
|
162
|
-
isMultimodalEnabled() {
|
|
163
|
-
const val = process.env._INTERNAL_LLMA_MULTIMODAL || '';
|
|
164
|
-
return val.toLowerCase() === 'true' || val === '1' || val.toLowerCase() === 'yes';
|
|
165
|
-
}
|
|
166
188
|
}
|
|
167
189
|
|
|
168
190
|
const redactor = new BinaryContentRedactor();
|
|
169
191
|
function redactBase64DataUrl(str, mediaType) {
|
|
170
192
|
return redactor.redact(str, mediaType);
|
|
171
193
|
}
|
|
172
|
-
const
|
|
194
|
+
const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
|
|
195
|
+
const sanitizeVercel = (data, client) => sanitize(data, client);
|
|
173
196
|
|
|
174
197
|
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']);
|
|
175
198
|
|
|
@@ -251,11 +274,14 @@ function toSafeString(input) {
|
|
|
251
274
|
return '';
|
|
252
275
|
}
|
|
253
276
|
}
|
|
254
|
-
const truncate = input => {
|
|
277
|
+
const truncate = (input, client) => {
|
|
255
278
|
const str = toSafeString(input);
|
|
256
279
|
if (str === '') {
|
|
257
280
|
return '';
|
|
258
281
|
}
|
|
282
|
+
if (isFullAiCaptureEnabled(client)) {
|
|
283
|
+
return str;
|
|
284
|
+
}
|
|
259
285
|
|
|
260
286
|
// Check if we need to truncate and ensure STRING_FORMAT is respected
|
|
261
287
|
const buffer = sharedTextEncoder.encode(str);
|
|
@@ -411,7 +437,7 @@ function sanitizeValues(obj) {
|
|
|
411
437
|
return jsonSafe;
|
|
412
438
|
}
|
|
413
439
|
|
|
414
|
-
var version = "8.
|
|
440
|
+
var version = "8.8.0";
|
|
415
441
|
|
|
416
442
|
const DEFAULT_MAX_DEPTH = 3;
|
|
417
443
|
const MAX_STACK_LINES = 20;
|
|
@@ -660,9 +686,9 @@ const captureAiGeneration = async (client, options) => {
|
|
|
660
686
|
groups: options.groups
|
|
661
687
|
};
|
|
662
688
|
if (options.captureImmediate) {
|
|
663
|
-
await client
|
|
689
|
+
await captureAiEventImmediate(client, event);
|
|
664
690
|
} else {
|
|
665
|
-
client
|
|
691
|
+
captureAiEvent(client, event);
|
|
666
692
|
}
|
|
667
693
|
} catch (error) {
|
|
668
694
|
// Telemetry failures must never affect the instrumented provider call.
|
|
@@ -685,12 +711,12 @@ function getSpecificationVersion(model) {
|
|
|
685
711
|
|
|
686
712
|
// Content types for the output array
|
|
687
713
|
|
|
688
|
-
const redactFileData = (data, mediaType) => {
|
|
714
|
+
const redactFileData = (data, mediaType, client) => {
|
|
689
715
|
if (data instanceof URL) {
|
|
690
|
-
return redactBase64DataUrl(data.toString(), data.protocol === 'data:' ? mediaType : undefined);
|
|
716
|
+
return isFullAiCaptureEnabled(client) ? data.toString() : redactBase64DataUrl(data.toString(), data.protocol === 'data:' ? mediaType : undefined);
|
|
691
717
|
}
|
|
692
718
|
if (isString(data)) {
|
|
693
|
-
return redactBase64DataUrl(data, mediaType);
|
|
719
|
+
return isFullAiCaptureEnabled(client) ? data : redactBase64DataUrl(data, mediaType);
|
|
694
720
|
}
|
|
695
721
|
return undefined;
|
|
696
722
|
};
|
|
@@ -705,7 +731,7 @@ const mapVercelParams = params => {
|
|
|
705
731
|
stream: params.stream
|
|
706
732
|
};
|
|
707
733
|
};
|
|
708
|
-
const mapVercelPrompt = messages => {
|
|
734
|
+
const mapVercelPrompt = (messages, client) => {
|
|
709
735
|
// Map and truncate individual content
|
|
710
736
|
const inputs = messages.map(message => {
|
|
711
737
|
let content;
|
|
@@ -714,7 +740,7 @@ const mapVercelPrompt = messages => {
|
|
|
714
740
|
if (message.role === 'system') {
|
|
715
741
|
content = [{
|
|
716
742
|
type: 'text',
|
|
717
|
-
text: truncate(toContentString(message.content))
|
|
743
|
+
text: truncate(toContentString(message.content), client)
|
|
718
744
|
}];
|
|
719
745
|
} else {
|
|
720
746
|
// Handle other roles which have array content
|
|
@@ -723,11 +749,11 @@ const mapVercelPrompt = messages => {
|
|
|
723
749
|
if (c.type === 'text') {
|
|
724
750
|
return {
|
|
725
751
|
type: 'text',
|
|
726
|
-
text: truncate(c.text)
|
|
752
|
+
text: truncate(c.text, client)
|
|
727
753
|
};
|
|
728
754
|
} else if (c.type === 'file') {
|
|
729
755
|
// Redact base64 data URLs and raw base64 to prevent oversized events
|
|
730
|
-
const fileData = redactFileData(c.data, c.mediaType) ?? 'raw files not supported';
|
|
756
|
+
const fileData = redactFileData(c.data, c.mediaType, client) ?? 'raw files not supported';
|
|
731
757
|
return {
|
|
732
758
|
type: 'file',
|
|
733
759
|
file: fileData,
|
|
@@ -736,7 +762,7 @@ const mapVercelPrompt = messages => {
|
|
|
736
762
|
} else if (c.type === 'reasoning') {
|
|
737
763
|
return {
|
|
738
764
|
type: 'reasoning',
|
|
739
|
-
text: truncate(c.text)
|
|
765
|
+
text: truncate(c.text, client)
|
|
740
766
|
};
|
|
741
767
|
} else if (c.type === 'tool-call') {
|
|
742
768
|
return {
|
|
@@ -750,7 +776,7 @@ const mapVercelPrompt = messages => {
|
|
|
750
776
|
type: 'tool-result',
|
|
751
777
|
toolCallId: c.toolCallId,
|
|
752
778
|
toolName: c.toolName,
|
|
753
|
-
output: sanitizeVercel(c.output),
|
|
779
|
+
output: sanitizeVercel(c.output, client),
|
|
754
780
|
isError: c.isError
|
|
755
781
|
};
|
|
756
782
|
}
|
|
@@ -763,7 +789,7 @@ const mapVercelPrompt = messages => {
|
|
|
763
789
|
// Fallback for non-array content
|
|
764
790
|
content = [{
|
|
765
791
|
type: 'text',
|
|
766
|
-
text: truncate(toContentString(message.content))
|
|
792
|
+
text: truncate(toContentString(message.content), client)
|
|
767
793
|
}];
|
|
768
794
|
}
|
|
769
795
|
}
|
|
@@ -772,6 +798,12 @@ const mapVercelPrompt = messages => {
|
|
|
772
798
|
content
|
|
773
799
|
};
|
|
774
800
|
});
|
|
801
|
+
|
|
802
|
+
// Full AI capture means no truncation of any kind; the aggregate trim below exists
|
|
803
|
+
// only to keep the default-mode payload under MAX_OUTPUT_SIZE.
|
|
804
|
+
if (isFullAiCaptureEnabled(client)) {
|
|
805
|
+
return inputs;
|
|
806
|
+
}
|
|
775
807
|
try {
|
|
776
808
|
// Trim the inputs array until its serialized JSON size fits within MAX_OUTPUT_SIZE.
|
|
777
809
|
// Pre-compute each message's byte size once so we can shift by accumulated budget
|
|
@@ -808,12 +840,12 @@ const mapVercelPrompt = messages => {
|
|
|
808
840
|
}
|
|
809
841
|
return inputs;
|
|
810
842
|
};
|
|
811
|
-
const mapVercelOutput = result => {
|
|
843
|
+
const mapVercelOutput = (result, client) => {
|
|
812
844
|
const content = result.map(item => {
|
|
813
845
|
if (item.type === 'text') {
|
|
814
846
|
return {
|
|
815
847
|
type: 'text',
|
|
816
|
-
text: truncate(item.text)
|
|
848
|
+
text: truncate(item.text, client)
|
|
817
849
|
};
|
|
818
850
|
}
|
|
819
851
|
if (item.type === 'tool-call') {
|
|
@@ -831,15 +863,15 @@ const mapVercelOutput = result => {
|
|
|
831
863
|
if (item.type === 'reasoning') {
|
|
832
864
|
return {
|
|
833
865
|
type: 'reasoning',
|
|
834
|
-
text: truncate(item.text)
|
|
866
|
+
text: truncate(item.text, client)
|
|
835
867
|
};
|
|
836
868
|
}
|
|
837
869
|
if (item.type === 'file') {
|
|
838
870
|
// Handle files similar to input mapping - avoid large base64 data
|
|
839
|
-
let fileData = redactFileData(item.data, item.mediaType) ?? `[binary ${item.mediaType} file]`;
|
|
871
|
+
let fileData = redactFileData(item.data, item.mediaType, client) ?? `[binary ${item.mediaType} file]`;
|
|
840
872
|
|
|
841
|
-
//
|
|
842
|
-
if (typeof item.data === 'string' && fileData === item.data && item.data.length > 1000) {
|
|
873
|
+
// Skipped under full AI capture: media stays untouched, so no placeholder swap either.
|
|
874
|
+
if (!isFullAiCaptureEnabled(client) && typeof item.data === 'string' && fileData === item.data && item.data.length > 1000) {
|
|
843
875
|
fileData = `[${item.mediaType} file - ${item.data.length} bytes]`;
|
|
844
876
|
}
|
|
845
877
|
return {
|
|
@@ -861,7 +893,7 @@ const mapVercelOutput = result => {
|
|
|
861
893
|
// Fallback for unknown types - try to extract text if possible
|
|
862
894
|
return {
|
|
863
895
|
type: 'text',
|
|
864
|
-
text: truncate(JSON.stringify(item))
|
|
896
|
+
text: truncate(JSON.stringify(item), client)
|
|
865
897
|
};
|
|
866
898
|
});
|
|
867
899
|
if (content.length > 0) {
|
|
@@ -874,7 +906,7 @@ const mapVercelOutput = result => {
|
|
|
874
906
|
try {
|
|
875
907
|
const jsonOutput = JSON.stringify(result);
|
|
876
908
|
return [{
|
|
877
|
-
content: truncate(jsonOutput),
|
|
909
|
+
content: truncate(jsonOutput, client),
|
|
878
910
|
role: 'assistant'
|
|
879
911
|
}];
|
|
880
912
|
} catch {
|
|
@@ -1070,7 +1102,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1070
1102
|
const modelId = mergedOptions.posthogModelOverride ?? (result.response?.modelId ? result.response.modelId : model.modelId);
|
|
1071
1103
|
const provider = mergedOptions.posthogProviderOverride ?? extractProvider(model);
|
|
1072
1104
|
// result.content is undefined when the model returns only tool calls with no text output
|
|
1073
|
-
const content = mapVercelOutput(result.content ?? []);
|
|
1105
|
+
const content = mapVercelOutput(result.content ?? [], phClient);
|
|
1074
1106
|
const latency = (Date.now() - startTime) / 1000;
|
|
1075
1107
|
const providerMetadata = result.providerMetadata;
|
|
1076
1108
|
const additionalTokenValues = extractAdditionalTokenValues(providerMetadata, result.usage);
|
|
@@ -1113,7 +1145,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1113
1145
|
...baseOptions,
|
|
1114
1146
|
model: modelId,
|
|
1115
1147
|
provider: provider,
|
|
1116
|
-
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
|
|
1148
|
+
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt, phClient),
|
|
1117
1149
|
output: content,
|
|
1118
1150
|
latency,
|
|
1119
1151
|
baseURL,
|
|
@@ -1130,7 +1162,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1130
1162
|
...baseOptions,
|
|
1131
1163
|
model: modelId,
|
|
1132
1164
|
provider: model.provider,
|
|
1133
|
-
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
|
|
1165
|
+
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt, phClient),
|
|
1134
1166
|
output: [],
|
|
1135
1167
|
latency: 0,
|
|
1136
1168
|
baseURL,
|
|
@@ -1264,13 +1296,13 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1264
1296
|
if (reasoningText) {
|
|
1265
1297
|
content.push({
|
|
1266
1298
|
type: 'reasoning',
|
|
1267
|
-
text: truncate(reasoningText)
|
|
1299
|
+
text: truncate(reasoningText, phClient)
|
|
1268
1300
|
});
|
|
1269
1301
|
}
|
|
1270
1302
|
if (generatedText) {
|
|
1271
1303
|
content.push({
|
|
1272
1304
|
type: 'text',
|
|
1273
|
-
text: truncate(generatedText)
|
|
1305
|
+
text: truncate(generatedText, phClient)
|
|
1274
1306
|
});
|
|
1275
1307
|
}
|
|
1276
1308
|
for (const toolCall of toolCallsInProgress.values()) {
|
|
@@ -1305,7 +1337,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1305
1337
|
...baseOptions,
|
|
1306
1338
|
model: modelId,
|
|
1307
1339
|
provider: provider,
|
|
1308
|
-
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
|
|
1340
|
+
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt, phClient),
|
|
1309
1341
|
output,
|
|
1310
1342
|
latency,
|
|
1311
1343
|
timeToFirstToken,
|
|
@@ -1361,7 +1393,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1361
1393
|
...baseOptions,
|
|
1362
1394
|
model: modelId,
|
|
1363
1395
|
provider: provider,
|
|
1364
|
-
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
|
|
1396
|
+
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt, phClient),
|
|
1365
1397
|
output: [],
|
|
1366
1398
|
latency: 0,
|
|
1367
1399
|
baseURL,
|