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