@posthog/ai 7.17.4 → 7.18.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 +142 -61
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +142 -61
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +142 -85
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.mjs +142 -85
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +142 -210
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +142 -210
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +135 -126
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.mjs +135 -126
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/openai/index.cjs +136 -134
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +136 -134
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openai-agents/index.cjs +65 -1
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.mjs +65 -1
- package/dist/openai-agents/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +141 -44
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +141 -44
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/openai/index.mjs
CHANGED
|
@@ -7,153 +7,155 @@ import { uuidv7 } from '@posthog/core';
|
|
|
7
7
|
const isString = value => {
|
|
8
8
|
return typeof value === 'string';
|
|
9
9
|
};
|
|
10
|
-
const isObject = value => {
|
|
11
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const REDACTED_IMAGE_PLACEHOLDER = '[base64 image redacted]';
|
|
15
|
-
|
|
16
|
-
// ============================================
|
|
17
|
-
// Multimodal Feature Toggle
|
|
18
|
-
// ============================================
|
|
19
|
-
|
|
20
|
-
const isMultimodalEnabled = () => {
|
|
21
|
-
const val = process.env._INTERNAL_LLMA_MULTIMODAL || '';
|
|
22
|
-
return val.toLowerCase() === 'true' || val === '1' || val.toLowerCase() === 'yes';
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// ============================================
|
|
26
|
-
// Base64 Detection Helpers
|
|
27
|
-
// ============================================
|
|
28
10
|
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
11
|
+
const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
|
|
12
|
+
const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
|
|
13
|
+
class Base64Recognizer {
|
|
14
|
+
recognize(value, minLength) {
|
|
15
|
+
const dataUrl = DATA_URL_PREFIX_RE.exec(value);
|
|
16
|
+
if (dataUrl) return {
|
|
17
|
+
kind: 'data-url',
|
|
18
|
+
mediaType: dataUrl[1]
|
|
19
|
+
};
|
|
20
|
+
if (value.length < minLength) return {
|
|
21
|
+
kind: 'none'
|
|
22
|
+
};
|
|
23
|
+
const confidencePrefix = value.slice(0, minLength);
|
|
24
|
+
if (BASE64_ALPHABET_RE.test(confidencePrefix)) {
|
|
25
|
+
return {
|
|
26
|
+
kind: 'raw'
|
|
27
|
+
};
|
|
28
|
+
} else {
|
|
29
|
+
return {
|
|
30
|
+
kind: 'none'
|
|
31
|
+
};
|
|
32
|
+
}
|
|
45
33
|
}
|
|
34
|
+
}
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return REDACTED_IMAGE_PLACEHOLDER;
|
|
36
|
+
const MIME_HINT_KEYS = ['mediaType', 'media_type', 'mimeType', 'mime_type'];
|
|
37
|
+
const STRONG_CONTEXT_KEYS = new Set(['data', 'file_data', 'fileData', 'image_url', 'imageUrl', 'video_url', 'videoUrl', 'audio', 'audio_data', 'audioData', 'inline_data', 'inlineData', 'source', 'result']);
|
|
38
|
+
const STRONG_CONTEXT_TYPES = new Set(['image', 'image_url', 'input_image', 'audio', 'input_audio', 'video', 'video_url', 'file', 'input_file', 'document', 'media', 'file-data']);
|
|
39
|
+
const FILE_FAMILY_TYPES = new Set(['file', 'input_file', 'document', 'media', 'file-data']);
|
|
40
|
+
const KNOWN_AUDIO_FORMATS = new Set(['wav', 'mp3', 'ogg', 'flac', 'm4a', 'aac', 'webm']);
|
|
41
|
+
class MediaTypeContext {
|
|
42
|
+
static EMPTY = new MediaTypeContext(undefined, undefined);
|
|
43
|
+
constructor(parent, key) {
|
|
44
|
+
this.parent = parent;
|
|
45
|
+
this.key = key;
|
|
58
46
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (isRawBase64(str)) {
|
|
62
|
-
return REDACTED_IMAGE_PLACEHOLDER;
|
|
47
|
+
inferMediaType() {
|
|
48
|
+
return this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey();
|
|
63
49
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// ============================================
|
|
70
|
-
|
|
71
|
-
const processMessages = (messages, transformContent) => {
|
|
72
|
-
if (!messages) return messages;
|
|
73
|
-
const processContent = content => {
|
|
74
|
-
if (typeof content === 'string') return content;
|
|
75
|
-
if (!content) return content;
|
|
76
|
-
if (Array.isArray(content)) {
|
|
77
|
-
return content.map(transformContent);
|
|
50
|
+
inferFromSiblingMime() {
|
|
51
|
+
if (!this.parent) return undefined;
|
|
52
|
+
for (const hint of MIME_HINT_KEYS) {
|
|
53
|
+
const v = this.parent[hint];
|
|
54
|
+
if (typeof v === 'string') return v;
|
|
78
55
|
}
|
|
79
|
-
|
|
80
|
-
// Handle single object content
|
|
81
|
-
return transformContent(content);
|
|
82
|
-
};
|
|
83
|
-
const processMessage = msg => {
|
|
84
|
-
if (!isObject(msg) || !('content' in msg)) return msg;
|
|
85
|
-
return {
|
|
86
|
-
...msg,
|
|
87
|
-
content: processContent(msg.content)
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
// Handle both arrays and single messages
|
|
92
|
-
if (Array.isArray(messages)) {
|
|
93
|
-
return messages.map(processMessage);
|
|
56
|
+
return undefined;
|
|
94
57
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const sanitizeOpenAIImage = item => {
|
|
103
|
-
if (!isObject(item)) return item;
|
|
104
|
-
|
|
105
|
-
// Handle image_url format
|
|
106
|
-
if (item.type === 'image_url' && 'image_url' in item && isObject(item.image_url) && 'url' in item.image_url) {
|
|
107
|
-
return {
|
|
108
|
-
...item,
|
|
109
|
-
image_url: {
|
|
110
|
-
...item.image_url,
|
|
111
|
-
url: redactBase64DataUrl(item.image_url.url)
|
|
112
|
-
}
|
|
113
|
-
};
|
|
58
|
+
inferFromSiblingFormat() {
|
|
59
|
+
if (!this.parent) return undefined;
|
|
60
|
+
const fmt = this.parent.format;
|
|
61
|
+
if (typeof fmt === 'string' && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) {
|
|
62
|
+
return `audio/${fmt.toLowerCase()}`;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
114
65
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
66
|
+
inferFromParentType() {
|
|
67
|
+
if (!this.parent) return undefined;
|
|
68
|
+
const t = this.parent.type;
|
|
69
|
+
if (typeof t !== 'string') return undefined;
|
|
70
|
+
if (t === 'image' || t === 'image_url' || t === 'input_image') return 'image';
|
|
71
|
+
if (t === 'audio' || t === 'input_audio') return 'audio';
|
|
72
|
+
if (t === 'video' || t === 'video_url') return 'video';
|
|
73
|
+
if (FILE_FAMILY_TYPES.has(t)) return 'application/octet-stream';
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
inferFromKey() {
|
|
77
|
+
if (!this.key) return undefined;
|
|
78
|
+
const key = this.key.toLowerCase();
|
|
79
|
+
if (key.includes('audio')) return 'audio';
|
|
80
|
+
if (key.includes('video')) return 'video';
|
|
81
|
+
if (key.includes('image')) return 'image';
|
|
82
|
+
if (key.includes('file') || key.includes('document')) return 'application/octet-stream';
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
signalsBinary() {
|
|
86
|
+
if (this.parent) {
|
|
87
|
+
for (const hint of MIME_HINT_KEYS) {
|
|
88
|
+
if (typeof this.parent[hint] === 'string') return true;
|
|
123
89
|
}
|
|
124
|
-
|
|
90
|
+
const fmt = this.parent.format;
|
|
91
|
+
if (typeof fmt === 'string' && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return true;
|
|
92
|
+
const t = this.parent.type;
|
|
93
|
+
if (typeof t === 'string' && STRONG_CONTEXT_TYPES.has(t)) return true;
|
|
94
|
+
}
|
|
95
|
+
if (this.key && STRONG_CONTEXT_KEYS.has(this.key)) return true;
|
|
96
|
+
return false;
|
|
125
97
|
}
|
|
98
|
+
}
|
|
126
99
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
};
|
|
100
|
+
const STRONG_CONTEXT_MIN_LENGTH = 64;
|
|
101
|
+
const WEAK_CONTEXT_MIN_LENGTH = 1024;
|
|
102
|
+
class BinaryContentRedactor {
|
|
103
|
+
visited = new WeakSet();
|
|
104
|
+
constructor(recognizer = new Base64Recognizer()) {
|
|
105
|
+
this.recognizer = recognizer;
|
|
134
106
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// Handle input_image format
|
|
141
|
-
if (item.type === 'input_image' && 'image_url' in item) {
|
|
142
|
-
return {
|
|
143
|
-
...item,
|
|
144
|
-
image_url: redactBase64DataUrl(item.image_url)
|
|
145
|
-
};
|
|
107
|
+
redact(value) {
|
|
108
|
+
if (this.isMultimodalEnabled()) return value;
|
|
109
|
+
this.visited = new WeakSet();
|
|
110
|
+
return this.walk(value, MediaTypeContext.EMPTY);
|
|
146
111
|
}
|
|
147
|
-
|
|
148
|
-
|
|
112
|
+
walk(value, ctx) {
|
|
113
|
+
if (value === null || value === undefined) return value;
|
|
114
|
+
if (typeof value === 'string') return this.redactString(value, ctx);
|
|
115
|
+
if (typeof value !== 'object') return value;
|
|
116
|
+
|
|
117
|
+
// Buffer extends Uint8Array, so this branch catches both.
|
|
118
|
+
if (typeof Uint8Array !== 'undefined' && value instanceof Uint8Array) {
|
|
119
|
+
return this.placeholderFor(ctx.inferMediaType());
|
|
120
|
+
}
|
|
121
|
+
if (this.visited.has(value)) return null;
|
|
122
|
+
this.visited.add(value);
|
|
123
|
+
if (Array.isArray(value)) {
|
|
124
|
+
return value.map(item => this.walk(item, ctx));
|
|
125
|
+
}
|
|
126
|
+
const obj = value;
|
|
127
|
+
const out = {};
|
|
128
|
+
for (const k of Object.keys(obj)) {
|
|
129
|
+
out[k] = this.walk(obj[k], new MediaTypeContext(obj, k));
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
redactString(value, ctx) {
|
|
134
|
+
const minLength = ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
|
|
135
|
+
const recognition = this.recognizer.recognize(value, minLength);
|
|
136
|
+
switch (recognition.kind) {
|
|
137
|
+
case 'data-url':
|
|
138
|
+
return this.placeholderFor(recognition.mediaType);
|
|
139
|
+
case 'raw':
|
|
140
|
+
return this.placeholderFor(ctx.inferMediaType());
|
|
141
|
+
case 'none':
|
|
142
|
+
return value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
placeholderFor(mediaType) {
|
|
146
|
+
if (!mediaType) return '[base64 redacted]';
|
|
147
|
+
if (mediaType === 'application/octet-stream') return '[base64 file redacted]';
|
|
148
|
+
return `[base64 ${mediaType} redacted]`;
|
|
149
|
+
}
|
|
150
|
+
isMultimodalEnabled() {
|
|
151
|
+
const val = process.env._INTERNAL_LLMA_MULTIMODAL || '';
|
|
152
|
+
return val.toLowerCase() === 'true' || val === '1' || val.toLowerCase() === 'yes';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
149
155
|
|
|
150
|
-
|
|
151
|
-
const sanitizeOpenAI = data =>
|
|
152
|
-
|
|
153
|
-
};
|
|
154
|
-
const sanitizeOpenAIResponse = data => {
|
|
155
|
-
return processMessages(data, sanitizeOpenAIResponseImage);
|
|
156
|
-
};
|
|
156
|
+
const redactor = new BinaryContentRedactor();
|
|
157
|
+
const sanitizeOpenAI = data => redactor.redact(data);
|
|
158
|
+
const sanitizeOpenAIResponse = data => redactor.redact(data);
|
|
157
159
|
|
|
158
160
|
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']);
|
|
159
161
|
function getTokensSource(posthogProperties) {
|
|
@@ -528,7 +530,7 @@ function formatOpenAIResponsesInput(input, instructions) {
|
|
|
528
530
|
return messages;
|
|
529
531
|
}
|
|
530
532
|
|
|
531
|
-
var version = "7.
|
|
533
|
+
var version = "7.18.0";
|
|
532
534
|
|
|
533
535
|
/**
|
|
534
536
|
* Options for `captureAiGeneration`. Mirrors the `$ai_generation` event shape
|