@posthog/ai 8.10.0 → 8.10.2
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/adk/index.cjs +908 -1198
- package/dist/adk/index.cjs.map +1 -1
- package/dist/adk/index.d.ts +109 -109
- package/dist/adk/index.mjs +907 -1196
- package/dist/adk/index.mjs.map +1 -1
- package/dist/anthropic/index.cjs +927 -1111
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.d.ts +34 -33
- package/dist/anthropic/index.mjs +899 -1102
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +863 -1108
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.d.ts +38 -35
- package/dist/gemini/index.mjs +858 -1103
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +1218 -1546
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +170 -163
- package/dist/index.mjs +1216 -1544
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +851 -1029
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.d.ts +75 -75
- package/dist/langchain/index.mjs +850 -1027
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/langchain/middleware/index.cjs +1016 -1225
- package/dist/langchain/middleware/index.cjs.map +1 -1
- package/dist/langchain/middleware/index.d.ts +29 -25
- package/dist/langchain/middleware/index.mjs +1015 -1223
- package/dist/langchain/middleware/index.mjs.map +1 -1
- package/dist/openai/index.cjs +1990 -2523
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.d.ts +106 -104
- package/dist/openai/index.mjs +1985 -2518
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openai-agents/index.cjs +745 -827
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.d.ts +48 -47
- package/dist/openai-agents/index.mjs +744 -825
- package/dist/openai-agents/index.mjs.map +1 -1
- package/dist/otel/index.cjs +427 -486
- package/dist/otel/index.cjs.map +1 -1
- package/dist/otel/index.d.ts +36 -35
- package/dist/otel/index.mjs +426 -484
- package/dist/otel/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +992 -1343
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.d.ts +21 -16
- package/dist/vercel/index.mjs +991 -1341
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +15 -14
|
@@ -1,862 +1,781 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
/** @internal */
|
|
4
|
-
|
|
5
|
-
/** @internal */
|
|
6
|
-
|
|
1
|
+
import "uuid";
|
|
2
|
+
//#region src/captureAiEvent.ts
|
|
7
3
|
/** @internal */
|
|
8
4
|
function isFullAiCaptureEnabled(client) {
|
|
9
|
-
|
|
5
|
+
return client?.enableFullAiCapture === true;
|
|
10
6
|
}
|
|
11
|
-
|
|
12
7
|
/** @internal */
|
|
13
8
|
function captureAiEvent(client, event) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
if (isFullAiCaptureEnabled(client) && typeof client.captureAi === "function") {
|
|
10
|
+
client.captureAi(event);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
client.capture(event);
|
|
19
14
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/sanitization/base64_recognizer.ts
|
|
17
|
+
const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
|
|
18
|
+
const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
|
|
19
|
+
var Base64Recognizer = class {
|
|
20
|
+
recognize(value, minLength) {
|
|
21
|
+
const dataUrl = DATA_URL_PREFIX_RE.exec(value);
|
|
22
|
+
if (dataUrl) return {
|
|
23
|
+
kind: "data-url",
|
|
24
|
+
mediaType: dataUrl[1]
|
|
25
|
+
};
|
|
26
|
+
if (value.length < minLength) return { kind: "none" };
|
|
27
|
+
const confidencePrefix = value.slice(0, minLength);
|
|
28
|
+
if (BASE64_ALPHABET_RE.test(confidencePrefix)) return { kind: "raw" };
|
|
29
|
+
else return { kind: "none" };
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/sanitization/media_type_context.ts
|
|
34
|
+
const MIME_HINT_KEYS = [
|
|
35
|
+
"mediaType",
|
|
36
|
+
"media_type",
|
|
37
|
+
"mimeType",
|
|
38
|
+
"mime_type"
|
|
39
|
+
];
|
|
40
|
+
const STRONG_CONTEXT_KEYS = /* @__PURE__ */ new Set([
|
|
41
|
+
"data",
|
|
42
|
+
"file_data",
|
|
43
|
+
"fileData",
|
|
44
|
+
"image_url",
|
|
45
|
+
"imageUrl",
|
|
46
|
+
"video_url",
|
|
47
|
+
"videoUrl",
|
|
48
|
+
"audio",
|
|
49
|
+
"audio_data",
|
|
50
|
+
"audioData",
|
|
51
|
+
"inline_data",
|
|
52
|
+
"inlineData",
|
|
53
|
+
"source",
|
|
54
|
+
"result"
|
|
55
|
+
]);
|
|
56
|
+
const STRONG_CONTEXT_TYPES = /* @__PURE__ */ new Set([
|
|
57
|
+
"image",
|
|
58
|
+
"image_url",
|
|
59
|
+
"input_image",
|
|
60
|
+
"audio",
|
|
61
|
+
"input_audio",
|
|
62
|
+
"video",
|
|
63
|
+
"video_url",
|
|
64
|
+
"file",
|
|
65
|
+
"input_file",
|
|
66
|
+
"document",
|
|
67
|
+
"media",
|
|
68
|
+
"file-data"
|
|
69
|
+
]);
|
|
70
|
+
const FILE_FAMILY_TYPES = /* @__PURE__ */ new Set([
|
|
71
|
+
"file",
|
|
72
|
+
"input_file",
|
|
73
|
+
"document",
|
|
74
|
+
"media",
|
|
75
|
+
"file-data"
|
|
76
|
+
]);
|
|
77
|
+
const KNOWN_AUDIO_FORMATS = /* @__PURE__ */ new Set([
|
|
78
|
+
"wav",
|
|
79
|
+
"mp3",
|
|
80
|
+
"ogg",
|
|
81
|
+
"flac",
|
|
82
|
+
"m4a",
|
|
83
|
+
"aac",
|
|
84
|
+
"webm"
|
|
85
|
+
]);
|
|
86
|
+
var MediaTypeContext = class MediaTypeContext {
|
|
87
|
+
static {
|
|
88
|
+
this.EMPTY = new MediaTypeContext(void 0, void 0);
|
|
89
|
+
}
|
|
90
|
+
constructor(parent, key, explicitMediaType) {
|
|
91
|
+
this.parent = parent;
|
|
92
|
+
this.key = key;
|
|
93
|
+
this.explicitMediaType = explicitMediaType;
|
|
94
|
+
}
|
|
95
|
+
inferMediaType() {
|
|
96
|
+
return this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey();
|
|
97
|
+
}
|
|
98
|
+
inferFromSiblingMime() {
|
|
99
|
+
if (this.explicitMediaType) return this.explicitMediaType;
|
|
100
|
+
if (!this.parent) return void 0;
|
|
101
|
+
for (const hint of MIME_HINT_KEYS) {
|
|
102
|
+
const v = this.parent[hint];
|
|
103
|
+
if (typeof v === "string") return v;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
inferFromSiblingFormat() {
|
|
107
|
+
if (!this.parent) return void 0;
|
|
108
|
+
const fmt = this.parent.format;
|
|
109
|
+
if (typeof fmt === "string" && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return `audio/${fmt.toLowerCase()}`;
|
|
110
|
+
}
|
|
111
|
+
inferFromParentType() {
|
|
112
|
+
if (!this.parent) return void 0;
|
|
113
|
+
const t = this.parent.type;
|
|
114
|
+
if (typeof t !== "string") return void 0;
|
|
115
|
+
if (t === "image" || t === "image_url" || t === "input_image") return "image";
|
|
116
|
+
if (t === "audio" || t === "input_audio") return "audio";
|
|
117
|
+
if (t === "video" || t === "video_url") return "video";
|
|
118
|
+
if (FILE_FAMILY_TYPES.has(t)) return "application/octet-stream";
|
|
119
|
+
}
|
|
120
|
+
inferFromKey() {
|
|
121
|
+
if (!this.key) return void 0;
|
|
122
|
+
const key = this.key.toLowerCase();
|
|
123
|
+
if (key.includes("audio")) return "audio";
|
|
124
|
+
if (key.includes("video")) return "video";
|
|
125
|
+
if (key.includes("image")) return "image";
|
|
126
|
+
if (key.includes("file") || key.includes("document")) return "application/octet-stream";
|
|
127
|
+
}
|
|
128
|
+
hasExplicitBinaryMediaType() {
|
|
129
|
+
if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false;
|
|
130
|
+
const mediaType = this.inferFromSiblingMime();
|
|
131
|
+
return mediaType !== void 0 && !mediaType.toLowerCase().startsWith("text/");
|
|
132
|
+
}
|
|
133
|
+
signalsBinary() {
|
|
134
|
+
if (this.explicitMediaType) return true;
|
|
135
|
+
if (this.parent) {
|
|
136
|
+
for (const hint of MIME_HINT_KEYS) if (typeof this.parent[hint] === "string") return true;
|
|
137
|
+
const fmt = this.parent.format;
|
|
138
|
+
if (typeof fmt === "string" && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return true;
|
|
139
|
+
const t = this.parent.type;
|
|
140
|
+
if (typeof t === "string" && STRONG_CONTEXT_TYPES.has(t)) return true;
|
|
141
|
+
}
|
|
142
|
+
if (this.key && STRONG_CONTEXT_KEYS.has(this.key)) return true;
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/sanitization/binary_content_redactor.ts
|
|
148
|
+
const STRONG_CONTEXT_MIN_LENGTH = 64;
|
|
149
|
+
const WEAK_CONTEXT_MIN_LENGTH = 1024;
|
|
150
|
+
var BinaryContentRedactor = class {
|
|
151
|
+
constructor(recognizer = new Base64Recognizer()) {
|
|
152
|
+
this.recognizer = recognizer;
|
|
153
|
+
this.visited = /* @__PURE__ */ new WeakSet();
|
|
154
|
+
}
|
|
155
|
+
redact(value, mediaType) {
|
|
156
|
+
this.visited = /* @__PURE__ */ new WeakSet();
|
|
157
|
+
return this.walk(value, mediaType ? new MediaTypeContext(void 0, void 0, mediaType) : MediaTypeContext.EMPTY);
|
|
158
|
+
}
|
|
159
|
+
walk(value, ctx) {
|
|
160
|
+
if (value === null || value === void 0) return value;
|
|
161
|
+
if (typeof value === "string") return this.redactString(value, ctx);
|
|
162
|
+
if (typeof value !== "object") return value;
|
|
163
|
+
if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return this.placeholderFor(ctx.inferMediaType());
|
|
164
|
+
if (this.visited.has(value)) return null;
|
|
165
|
+
this.visited.add(value);
|
|
166
|
+
if (Array.isArray(value)) return value.map((item) => this.walk(item, ctx));
|
|
167
|
+
const obj = value;
|
|
168
|
+
const out = {};
|
|
169
|
+
for (const k of Object.keys(obj)) out[k] = this.walk(obj[k], new MediaTypeContext(obj, k));
|
|
170
|
+
return out;
|
|
171
|
+
}
|
|
172
|
+
redactString(value, ctx) {
|
|
173
|
+
const hasExplicitBinaryMediaType = ctx.hasExplicitBinaryMediaType();
|
|
174
|
+
const recognitionValue = hasExplicitBinaryMediaType ? value.replace(/[\r\n]/g, "") : value;
|
|
175
|
+
const minLength = hasExplicitBinaryMediaType ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH) : ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
|
|
176
|
+
const recognition = this.recognizer.recognize(recognitionValue, minLength);
|
|
177
|
+
switch (recognition.kind) {
|
|
178
|
+
case "data-url": return this.placeholderFor(recognition.mediaType);
|
|
179
|
+
case "raw": return this.placeholderFor(ctx.inferMediaType());
|
|
180
|
+
case "none": return value;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
placeholderFor(mediaType) {
|
|
184
|
+
if (!mediaType) return "[base64 redacted]";
|
|
185
|
+
if (mediaType === "application/octet-stream") return "[base64 file redacted]";
|
|
186
|
+
return `[base64 ${mediaType} redacted]`;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
new BinaryContentRedactor();
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/utils.ts
|
|
192
|
+
const MAX_OUTPUT_SIZE = 2e5;
|
|
193
|
+
const STRING_FORMAT = "utf8";
|
|
99
194
|
const sharedTextEncoder = new TextEncoder();
|
|
100
|
-
const sharedTextDecoder = new TextDecoder(STRING_FORMAT, {
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
const utf8ByteLength = str => sharedTextEncoder.encode(str).byteLength;
|
|
104
|
-
|
|
195
|
+
const sharedTextDecoder = new TextDecoder(STRING_FORMAT, { fatal: false });
|
|
196
|
+
const utf8ByteLength = (str) => sharedTextEncoder.encode(str).byteLength;
|
|
105
197
|
/**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
198
|
+
* Safely converts content to a string, preserving structure for objects/arrays.
|
|
199
|
+
* - If content is already a string, returns it as-is
|
|
200
|
+
* - If content is an object or array, stringifies it with JSON.stringify to preserve structure
|
|
201
|
+
* - Otherwise, converts to string with String()
|
|
202
|
+
*
|
|
203
|
+
* This prevents the "[object Object]" bug when objects are naively converted to strings.
|
|
204
|
+
*
|
|
205
|
+
* @param content - The content to convert to a string
|
|
206
|
+
* @returns A string representation that preserves structure for complex types
|
|
207
|
+
*/
|
|
116
208
|
function toContentString(content) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// Fallback for circular refs, BigInt, or objects with throwing toJSON
|
|
125
|
-
return String(content);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return String(content);
|
|
209
|
+
if (typeof content === "string") return content;
|
|
210
|
+
if (content !== void 0 && content !== null && typeof content === "object") try {
|
|
211
|
+
return JSON.stringify(content);
|
|
212
|
+
} catch {
|
|
213
|
+
return String(content);
|
|
214
|
+
}
|
|
215
|
+
return String(content);
|
|
129
216
|
}
|
|
130
217
|
const withPrivacyMode = (client, privacyMode, input) => {
|
|
131
|
-
|
|
218
|
+
return client.privacy_mode || privacyMode ? null : input;
|
|
132
219
|
};
|
|
133
220
|
function toSafeString(input) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
} catch {
|
|
143
|
-
console.warn('Failed to stringify input', input);
|
|
144
|
-
return '';
|
|
145
|
-
}
|
|
221
|
+
if (input === void 0 || input === null) return "";
|
|
222
|
+
if (typeof input === "string") return input;
|
|
223
|
+
try {
|
|
224
|
+
return JSON.stringify(input);
|
|
225
|
+
} catch {
|
|
226
|
+
console.warn("Failed to stringify input", input);
|
|
227
|
+
return "";
|
|
228
|
+
}
|
|
146
229
|
}
|
|
147
230
|
const truncate = (input, client) => {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const buffer = sharedTextEncoder.encode(str);
|
|
158
|
-
if (buffer.length <= MAX_OUTPUT_SIZE) {
|
|
159
|
-
// Ensure STRING_FORMAT is respected
|
|
160
|
-
return sharedTextDecoder.decode(buffer);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Truncate the buffer and ensure a valid string is returned.
|
|
164
|
-
// fatal: false means we get U+FFFD at the end if truncation broke the encoding.
|
|
165
|
-
const truncatedBuffer = buffer.slice(0, MAX_OUTPUT_SIZE);
|
|
166
|
-
let truncatedStr = sharedTextDecoder.decode(truncatedBuffer);
|
|
167
|
-
if (truncatedStr.endsWith('\uFFFD')) {
|
|
168
|
-
truncatedStr = truncatedStr.slice(0, -1);
|
|
169
|
-
}
|
|
170
|
-
return `${truncatedStr}... [truncated]`;
|
|
231
|
+
const str = toSafeString(input);
|
|
232
|
+
if (str === "") return "";
|
|
233
|
+
if (isFullAiCaptureEnabled(client)) return str;
|
|
234
|
+
const buffer = sharedTextEncoder.encode(str);
|
|
235
|
+
if (buffer.length <= 2e5) return sharedTextDecoder.decode(buffer);
|
|
236
|
+
const truncatedBuffer = buffer.slice(0, MAX_OUTPUT_SIZE);
|
|
237
|
+
let truncatedStr = sharedTextDecoder.decode(truncatedBuffer);
|
|
238
|
+
if (truncatedStr.endsWith("�")) truncatedStr = truncatedStr.slice(0, -1);
|
|
239
|
+
return `${truncatedStr}... [truncated]`;
|
|
171
240
|
};
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
} catch {
|
|
193
|
-
return undefined;
|
|
194
|
-
}
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region package.json
|
|
243
|
+
var version = "8.10.2";
|
|
244
|
+
//#endregion
|
|
245
|
+
//#region src/gatewayWarning.ts
|
|
246
|
+
const POSTHOG_AI_GATEWAY_HOSTS = [
|
|
247
|
+
"gateway.posthog.com",
|
|
248
|
+
"gateway.us.posthog.com",
|
|
249
|
+
"gateway.eu.posthog.com",
|
|
250
|
+
"ai-gateway.us.posthog.com",
|
|
251
|
+
"ai-gateway.eu.posthog.com"
|
|
252
|
+
];
|
|
253
|
+
const GATEWAY_DOCS_URL = "https://posthog.com/docs/ai-observability";
|
|
254
|
+
const extractHost = (baseURL) => {
|
|
255
|
+
try {
|
|
256
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(baseURL);
|
|
257
|
+
return new URL(hasScheme ? baseURL : `https://${baseURL}`).hostname.toLowerCase();
|
|
258
|
+
} catch {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
195
261
|
};
|
|
196
|
-
const isPostHogAiGatewayUrl = baseURL => {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const host = extractHost(baseURL);
|
|
201
|
-
return host !== undefined && POSTHOG_AI_GATEWAY_HOSTS.includes(host);
|
|
262
|
+
const isPostHogAiGatewayUrl = (baseURL) => {
|
|
263
|
+
if (!baseURL) return false;
|
|
264
|
+
const host = extractHost(baseURL);
|
|
265
|
+
return host !== void 0 && POSTHOG_AI_GATEWAY_HOSTS.includes(host);
|
|
202
266
|
};
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const warnIfPostHogAiGateway = baseURL => {
|
|
207
|
-
if (!isPostHogAiGatewayUrl(baseURL)) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
console.warn('[PostHog] The PostHog AI wrapper is pointed at the PostHog AI Gateway. ' + 'Both capture $ai_generation, so every call is double-counted and double-billed. ' + `Use one or the other — see ${GATEWAY_DOCS_URL}.`);
|
|
267
|
+
const warnIfPostHogAiGateway = (baseURL) => {
|
|
268
|
+
if (!isPostHogAiGatewayUrl(baseURL)) return;
|
|
269
|
+
console.warn(`[PostHog] The PostHog AI wrapper is pointed at the PostHog AI Gateway. Both capture \$ai_generation, so every call is double-counted and double-billed. Use one or the other — see ${GATEWAY_DOCS_URL}.`);
|
|
211
270
|
};
|
|
212
|
-
|
|
271
|
+
//#endregion
|
|
272
|
+
//#region src/openai-agents/processor.ts
|
|
213
273
|
/**
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
274
|
+
* Normalize OpenAI Responses API input items to include a `role` field.
|
|
275
|
+
* Items like `function_call` and `function_call_result` don't have a role,
|
|
276
|
+
* causing PostHog's trace viewer to default them to "user".
|
|
277
|
+
*/
|
|
218
278
|
function normalizeInputRoles(input) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
role: 'tool'
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
return item;
|
|
238
|
-
});
|
|
279
|
+
if (!Array.isArray(input)) return input;
|
|
280
|
+
return input.map((item) => {
|
|
281
|
+
if (item && typeof item === "object" && !("role" in item) && "type" in item) {
|
|
282
|
+
if (item.type === "function_call") return {
|
|
283
|
+
...item,
|
|
284
|
+
role: "assistant"
|
|
285
|
+
};
|
|
286
|
+
if (item.type === "function_call_result") return {
|
|
287
|
+
...item,
|
|
288
|
+
role: "tool"
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
return item;
|
|
292
|
+
});
|
|
239
293
|
}
|
|
240
294
|
function safeContentString(value) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
295
|
+
try {
|
|
296
|
+
return toContentString(value);
|
|
297
|
+
} catch {
|
|
298
|
+
return Object.prototype.toString.call(value);
|
|
299
|
+
}
|
|
246
300
|
}
|
|
247
301
|
function ensureSerializable(obj) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
} catch {
|
|
255
|
-
return safeContentString(obj);
|
|
256
|
-
}
|
|
302
|
+
if (obj === null || obj === void 0) return obj;
|
|
303
|
+
try {
|
|
304
|
+
return JSON.stringify(obj) === void 0 ? safeContentString(obj) : obj;
|
|
305
|
+
} catch {
|
|
306
|
+
return safeContentString(obj);
|
|
307
|
+
}
|
|
257
308
|
}
|
|
258
309
|
function stringifyForSizeCheck(value) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return JSON.stringify(value) ?? safeContentString(value);
|
|
267
|
-
} catch {
|
|
268
|
-
return safeContentString(value);
|
|
269
|
-
}
|
|
310
|
+
if (value === null || value === void 0) return null;
|
|
311
|
+
if (typeof value === "string") return value;
|
|
312
|
+
try {
|
|
313
|
+
return JSON.stringify(value) ?? safeContentString(value);
|
|
314
|
+
} catch {
|
|
315
|
+
return safeContentString(value);
|
|
316
|
+
}
|
|
270
317
|
}
|
|
271
318
|
function exceedsMaxOutputSize(serializedValue) {
|
|
272
|
-
|
|
319
|
+
return serializedValue === null ? false : utf8ByteLength(serializedValue) > MAX_OUTPUT_SIZE;
|
|
273
320
|
}
|
|
274
321
|
function parseIsoTimestamp(isoStr) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const ts = new Date(isoStr).getTime();
|
|
279
|
-
return Number.isFinite(ts) ? ts / 1000 : null;
|
|
322
|
+
if (typeof isoStr !== "string" || isoStr.trim() === "") return null;
|
|
323
|
+
const ts = new Date(isoStr).getTime();
|
|
324
|
+
return Number.isFinite(ts) ? ts / 1e3 : null;
|
|
280
325
|
}
|
|
281
326
|
/**
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
}
|
|
689
|
-
_handleFunctionSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
690
|
-
const properties = {
|
|
691
|
-
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
692
|
-
$ai_span_name: spanData.name,
|
|
693
|
-
$ai_span_type: 'tool',
|
|
694
|
-
$ai_input_state: this._prepareCapturedValue(spanData.input),
|
|
695
|
-
$ai_output_state: this._prepareCapturedValue(spanData.output)
|
|
696
|
-
};
|
|
697
|
-
if (spanData.mcp_data) {
|
|
698
|
-
properties.$ai_mcp_data = this._prepareCapturedValue(spanData.mcp_data);
|
|
699
|
-
}
|
|
700
|
-
this._captureEvent('$ai_span', properties, distinctId);
|
|
701
|
-
}
|
|
702
|
-
_handleAgentSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
703
|
-
const properties = {
|
|
704
|
-
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
705
|
-
$ai_span_name: spanData.name,
|
|
706
|
-
$ai_span_type: 'agent'
|
|
707
|
-
};
|
|
708
|
-
if (spanData.handoffs) {
|
|
709
|
-
properties.$ai_agent_handoffs = spanData.handoffs;
|
|
710
|
-
}
|
|
711
|
-
if (spanData.tools) {
|
|
712
|
-
properties.$ai_agent_tools = spanData.tools;
|
|
713
|
-
}
|
|
714
|
-
if (spanData.output_type) {
|
|
715
|
-
properties.$ai_agent_output_type = spanData.output_type;
|
|
716
|
-
}
|
|
717
|
-
this._captureEvent('$ai_span', properties, distinctId);
|
|
718
|
-
}
|
|
719
|
-
_handleHandoffSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
720
|
-
const properties = {
|
|
721
|
-
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
722
|
-
$ai_span_name: `${spanData.from_agent} -> ${spanData.to_agent}`,
|
|
723
|
-
$ai_span_type: 'handoff',
|
|
724
|
-
$ai_handoff_from_agent: spanData.from_agent,
|
|
725
|
-
$ai_handoff_to_agent: spanData.to_agent
|
|
726
|
-
};
|
|
727
|
-
this._captureEvent('$ai_span', properties, distinctId);
|
|
728
|
-
}
|
|
729
|
-
_handleGuardrailSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
730
|
-
const properties = {
|
|
731
|
-
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
732
|
-
$ai_span_name: spanData.name,
|
|
733
|
-
$ai_span_type: 'guardrail',
|
|
734
|
-
$ai_guardrail_triggered: spanData.triggered
|
|
735
|
-
};
|
|
736
|
-
this._captureEvent('$ai_span', properties, distinctId);
|
|
737
|
-
}
|
|
738
|
-
_handleCustomSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
739
|
-
const properties = {
|
|
740
|
-
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
741
|
-
$ai_span_name: spanData.name,
|
|
742
|
-
$ai_span_type: 'custom',
|
|
743
|
-
$ai_custom_data: this._prepareCapturedValue(spanData.data)
|
|
744
|
-
};
|
|
745
|
-
this._captureEvent('$ai_span', properties, distinctId);
|
|
746
|
-
}
|
|
747
|
-
_handleAudioSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
748
|
-
const spanType = spanData.type;
|
|
749
|
-
const properties = {
|
|
750
|
-
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
751
|
-
$ai_span_name: spanType,
|
|
752
|
-
$ai_span_type: spanType
|
|
753
|
-
};
|
|
754
|
-
|
|
755
|
-
// Add model info if available
|
|
756
|
-
if ('model' in spanData && spanData.model) {
|
|
757
|
-
properties.$ai_model = spanData.model;
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
// Add model config if available
|
|
761
|
-
if ('model_config' in spanData && spanData.model_config) {
|
|
762
|
-
properties.$ai_model_config = this._prepareCapturedValue(spanData.model_config);
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
// Add audio format info
|
|
766
|
-
if (spanData.type === 'transcription') {
|
|
767
|
-
const transcription = spanData;
|
|
768
|
-
if (transcription.input?.format) {
|
|
769
|
-
properties.$ai_audio_input_format = transcription.input.format;
|
|
770
|
-
}
|
|
771
|
-
// Transcription output is text
|
|
772
|
-
if (transcription.output) {
|
|
773
|
-
properties.$ai_output_state = this._prepareCapturedValue(transcription.output);
|
|
774
|
-
}
|
|
775
|
-
} else if (spanData.type === 'speech') {
|
|
776
|
-
const speech = spanData;
|
|
777
|
-
if (speech.output?.format) {
|
|
778
|
-
properties.$ai_audio_output_format = speech.output.format;
|
|
779
|
-
}
|
|
780
|
-
// Text input for TTS
|
|
781
|
-
if (speech.input) {
|
|
782
|
-
properties.$ai_input = this._prepareCapturedValue(speech.input);
|
|
783
|
-
}
|
|
784
|
-
} else if (spanData.type === 'speech_group') {
|
|
785
|
-
const speechGroup = spanData;
|
|
786
|
-
if (speechGroup.input) {
|
|
787
|
-
properties.$ai_input = this._prepareCapturedValue(speechGroup.input);
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
this._captureEvent('$ai_span', properties, distinctId);
|
|
791
|
-
}
|
|
792
|
-
_handleMcpSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
793
|
-
const properties = {
|
|
794
|
-
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
795
|
-
$ai_span_name: `mcp:${spanData.server}`,
|
|
796
|
-
$ai_span_type: 'mcp_tools',
|
|
797
|
-
$ai_mcp_server: spanData.server,
|
|
798
|
-
$ai_mcp_tools: this._prepareCapturedValue(spanData.result)
|
|
799
|
-
};
|
|
800
|
-
this._captureEvent('$ai_span', properties, distinctId);
|
|
801
|
-
}
|
|
802
|
-
_handleGenericSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
803
|
-
const spanType = spanData.type || 'unknown';
|
|
804
|
-
const properties = {
|
|
805
|
-
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
806
|
-
$ai_span_name: spanType,
|
|
807
|
-
$ai_span_type: spanType
|
|
808
|
-
};
|
|
809
|
-
this._captureEvent('$ai_span', properties, distinctId);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
327
|
+
* A tracing processor that sends OpenAI Agents SDK traces to PostHog.
|
|
328
|
+
*
|
|
329
|
+
* Implements the TracingProcessor interface from the OpenAI Agents SDK
|
|
330
|
+
* and maps agent traces, spans, and generations to PostHog's LLM analytics events.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```typescript
|
|
334
|
+
* import { PostHogTracingProcessor } from '@posthog/ai/openai-agents'
|
|
335
|
+
* import { addTraceProcessor } from '@openai/agents'
|
|
336
|
+
*
|
|
337
|
+
* const processor = new PostHogTracingProcessor({
|
|
338
|
+
* client: posthog,
|
|
339
|
+
* distinctId: 'user@example.com',
|
|
340
|
+
* })
|
|
341
|
+
* addTraceProcessor(processor)
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
var PostHogTracingProcessor = class {
|
|
345
|
+
constructor(options) {
|
|
346
|
+
this._spanStartTimes = /* @__PURE__ */ new Map();
|
|
347
|
+
this._traceMetadata = /* @__PURE__ */ new Map();
|
|
348
|
+
this._maxTrackedEntries = 1e4;
|
|
349
|
+
this._client = options.client;
|
|
350
|
+
this._distinctId = options.distinctId;
|
|
351
|
+
this._privacyMode = options.privacyMode ?? false;
|
|
352
|
+
this._groups = options.groups ?? {};
|
|
353
|
+
this._properties = options.properties ?? {};
|
|
354
|
+
this._onError = options.onError;
|
|
355
|
+
}
|
|
356
|
+
_getDistinctId(trace) {
|
|
357
|
+
if (typeof this._distinctId === "function") {
|
|
358
|
+
if (trace) {
|
|
359
|
+
const result = this._distinctId(trace);
|
|
360
|
+
if (result) return String(result);
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
} else if (this._distinctId) return String(this._distinctId);
|
|
364
|
+
}
|
|
365
|
+
_withPrivacyMode(value) {
|
|
366
|
+
return withPrivacyMode(this._client, this._privacyMode, value);
|
|
367
|
+
}
|
|
368
|
+
_prepareCapturedValue(value) {
|
|
369
|
+
const serializableValue = ensureSerializable(value);
|
|
370
|
+
const serializedValue = stringifyForSizeCheck(serializableValue);
|
|
371
|
+
const boundedValue = isFullAiCaptureEnabled(this._client) || !exceedsMaxOutputSize(serializedValue) ? serializableValue : truncate(serializedValue, this._client);
|
|
372
|
+
return this._withPrivacyMode(boundedValue);
|
|
373
|
+
}
|
|
374
|
+
_evictStaleEntries() {
|
|
375
|
+
if (this._spanStartTimes.size > this._maxTrackedEntries) {
|
|
376
|
+
const entries = [...this._spanStartTimes.entries()].sort((a, b) => a[1] - b[1]);
|
|
377
|
+
const toRemove = entries.slice(0, Math.floor(entries.length / 2));
|
|
378
|
+
for (const [key] of toRemove) this._spanStartTimes.delete(key);
|
|
379
|
+
}
|
|
380
|
+
if (this._traceMetadata.size > this._maxTrackedEntries) {
|
|
381
|
+
const keys = [...this._traceMetadata.keys()];
|
|
382
|
+
const toRemove = keys.slice(0, Math.floor(keys.length / 2));
|
|
383
|
+
for (const key of toRemove) this._traceMetadata.delete(key);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
_handleError(error, context) {
|
|
387
|
+
try {
|
|
388
|
+
this._onError?.(error, context);
|
|
389
|
+
} catch (handlerError) {}
|
|
390
|
+
}
|
|
391
|
+
_captureEvent(event, properties, distinctId) {
|
|
392
|
+
try {
|
|
393
|
+
if (!this._client?.capture) return;
|
|
394
|
+
const finalProperties = {
|
|
395
|
+
...this._properties,
|
|
396
|
+
...properties
|
|
397
|
+
};
|
|
398
|
+
const eventMessage = {
|
|
399
|
+
distinctId: distinctId || "unknown",
|
|
400
|
+
event,
|
|
401
|
+
properties: finalProperties,
|
|
402
|
+
groups: Object.keys(this._groups).length > 0 ? this._groups : void 0
|
|
403
|
+
};
|
|
404
|
+
captureAiEvent(this._client, eventMessage);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
this._handleError(error, "capture");
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
_baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties) {
|
|
410
|
+
const properties = {
|
|
411
|
+
$ai_lib: "posthog-ai",
|
|
412
|
+
$ai_lib_version: version,
|
|
413
|
+
$ai_trace_id: traceId,
|
|
414
|
+
$ai_span_id: spanId,
|
|
415
|
+
$ai_parent_id: parentId,
|
|
416
|
+
$ai_provider: "openai",
|
|
417
|
+
$ai_framework: "openai-agents",
|
|
418
|
+
$ai_latency: latency,
|
|
419
|
+
...errorProperties
|
|
420
|
+
};
|
|
421
|
+
if (groupId) {
|
|
422
|
+
properties.$ai_session_id = groupId;
|
|
423
|
+
properties.$ai_group_id = groupId;
|
|
424
|
+
}
|
|
425
|
+
return properties;
|
|
426
|
+
}
|
|
427
|
+
_getErrorProperties(error) {
|
|
428
|
+
if (!error) return {};
|
|
429
|
+
const errorMessage = error.message || String(error);
|
|
430
|
+
let errorType = "unknown";
|
|
431
|
+
if (errorMessage.includes("ModelBehaviorError")) errorType = "model_behavior_error";
|
|
432
|
+
else if (errorMessage.includes("UserError")) errorType = "user_error";
|
|
433
|
+
else if (errorMessage.includes("InputGuardrailTripwireTriggered")) errorType = "input_guardrail_triggered";
|
|
434
|
+
else if (errorMessage.includes("OutputGuardrailTripwireTriggered")) errorType = "output_guardrail_triggered";
|
|
435
|
+
else if (errorMessage.includes("MaxTurnsExceeded")) errorType = "max_turns_exceeded";
|
|
436
|
+
return {
|
|
437
|
+
$ai_is_error: true,
|
|
438
|
+
$ai_error: errorMessage,
|
|
439
|
+
$ai_error_type: errorType
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
async onTraceStart(trace) {
|
|
443
|
+
try {
|
|
444
|
+
this._evictStaleEntries();
|
|
445
|
+
const traceId = trace.traceId;
|
|
446
|
+
const traceName = trace.name;
|
|
447
|
+
const groupId = trace.groupId ?? null;
|
|
448
|
+
const metadata = trace.metadata;
|
|
449
|
+
const distinctId = this._getDistinctId(trace);
|
|
450
|
+
this._traceMetadata.set(traceId, {
|
|
451
|
+
name: traceName,
|
|
452
|
+
groupId,
|
|
453
|
+
metadata,
|
|
454
|
+
distinctId,
|
|
455
|
+
startTime: Date.now() / 1e3
|
|
456
|
+
});
|
|
457
|
+
} catch (error) {
|
|
458
|
+
this._handleError(error, "onTraceStart");
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
async onTraceEnd(trace) {
|
|
462
|
+
try {
|
|
463
|
+
const traceId = trace.traceId;
|
|
464
|
+
const traceInfo = this._traceMetadata.get(traceId);
|
|
465
|
+
this._traceMetadata.delete(traceId);
|
|
466
|
+
const traceName = traceInfo?.name ?? trace.name;
|
|
467
|
+
const groupId = traceInfo?.groupId ?? trace.groupId ?? null;
|
|
468
|
+
const metadata = traceInfo?.metadata ?? trace.metadata;
|
|
469
|
+
const distinctId = traceInfo?.distinctId ?? this._getDistinctId(trace);
|
|
470
|
+
const startTime = traceInfo?.startTime;
|
|
471
|
+
const latency = startTime != null ? Date.now() / 1e3 - startTime : void 0;
|
|
472
|
+
const properties = {
|
|
473
|
+
$ai_lib: "posthog-ai",
|
|
474
|
+
$ai_lib_version: version,
|
|
475
|
+
$ai_trace_id: traceId,
|
|
476
|
+
$ai_trace_name: traceName,
|
|
477
|
+
$ai_provider: "openai",
|
|
478
|
+
$ai_framework: "openai-agents"
|
|
479
|
+
};
|
|
480
|
+
if (latency != null) properties.$ai_latency = latency;
|
|
481
|
+
if (groupId) {
|
|
482
|
+
properties.$ai_session_id = groupId;
|
|
483
|
+
properties.$ai_group_id = groupId;
|
|
484
|
+
}
|
|
485
|
+
if (metadata && Object.keys(metadata).length > 0) properties.$ai_trace_metadata = this._prepareCapturedValue(metadata);
|
|
486
|
+
if (distinctId == null) properties.$process_person_profile = false;
|
|
487
|
+
this._captureEvent("$ai_trace", properties, distinctId ?? traceId);
|
|
488
|
+
} catch (error) {
|
|
489
|
+
this._handleError(error, "onTraceEnd");
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async onSpanStart(span) {
|
|
493
|
+
try {
|
|
494
|
+
this._evictStaleEntries();
|
|
495
|
+
this._spanStartTimes.set(span.spanId, Date.now() / 1e3);
|
|
496
|
+
} catch (error) {
|
|
497
|
+
this._handleError(error, "onSpanStart");
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
async onSpanEnd(span) {
|
|
501
|
+
try {
|
|
502
|
+
const spanId = span.spanId;
|
|
503
|
+
const traceId = span.traceId;
|
|
504
|
+
const parentId = span.parentId;
|
|
505
|
+
const spanData = span.spanData;
|
|
506
|
+
const startTime = this._spanStartTimes.get(spanId);
|
|
507
|
+
this._spanStartTimes.delete(spanId);
|
|
508
|
+
let latency;
|
|
509
|
+
if (startTime != null) latency = Date.now() / 1e3 - startTime;
|
|
510
|
+
else {
|
|
511
|
+
const started = parseIsoTimestamp(span.startedAt);
|
|
512
|
+
const ended = parseIsoTimestamp(span.endedAt);
|
|
513
|
+
latency = started != null && ended != null ? ended - started : 0;
|
|
514
|
+
}
|
|
515
|
+
const traceInfo = this._traceMetadata.get(traceId);
|
|
516
|
+
const userDistinctId = traceInfo?.distinctId ?? this._getDistinctId(null);
|
|
517
|
+
const groupId = traceInfo?.groupId ?? null;
|
|
518
|
+
const errorProperties = this._getErrorProperties(span.error);
|
|
519
|
+
if (userDistinctId == null) errorProperties.$process_person_profile = false;
|
|
520
|
+
const distinctId = userDistinctId ?? traceId;
|
|
521
|
+
switch (spanData.type) {
|
|
522
|
+
case "generation":
|
|
523
|
+
this._handleGenerationSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
524
|
+
break;
|
|
525
|
+
case "response":
|
|
526
|
+
this._handleResponseSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
527
|
+
break;
|
|
528
|
+
case "function":
|
|
529
|
+
this._handleFunctionSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
530
|
+
break;
|
|
531
|
+
case "agent":
|
|
532
|
+
this._handleAgentSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
533
|
+
break;
|
|
534
|
+
case "handoff":
|
|
535
|
+
this._handleHandoffSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
536
|
+
break;
|
|
537
|
+
case "guardrail":
|
|
538
|
+
this._handleGuardrailSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
539
|
+
break;
|
|
540
|
+
case "custom":
|
|
541
|
+
this._handleCustomSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
542
|
+
break;
|
|
543
|
+
case "transcription":
|
|
544
|
+
case "speech":
|
|
545
|
+
case "speech_group":
|
|
546
|
+
this._handleAudioSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
547
|
+
break;
|
|
548
|
+
case "mcp_tools":
|
|
549
|
+
this._handleMcpSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
550
|
+
break;
|
|
551
|
+
default: this._handleGenericSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties);
|
|
552
|
+
}
|
|
553
|
+
} catch (error) {
|
|
554
|
+
this._handleError(error, "onSpanEnd");
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
async shutdown() {
|
|
558
|
+
try {
|
|
559
|
+
this._spanStartTimes.clear();
|
|
560
|
+
this._traceMetadata.clear();
|
|
561
|
+
if (typeof this._client?.flush === "function") await this._client.flush();
|
|
562
|
+
} catch (error) {
|
|
563
|
+
this._handleError(error, "shutdown");
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
async forceFlush() {
|
|
567
|
+
try {
|
|
568
|
+
if (typeof this._client?.flush === "function") await this._client.flush();
|
|
569
|
+
} catch (error) {
|
|
570
|
+
this._handleError(error, "forceFlush");
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
_handleGenerationSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
574
|
+
const rawResponse = spanData.output?.[0];
|
|
575
|
+
const rawResponseUsage = rawResponse?.usage;
|
|
576
|
+
const usage = spanData.usage ?? rawResponseUsage ?? {};
|
|
577
|
+
const usesRawResponseUsage = spanData.usage === void 0 && rawResponseUsage !== void 0;
|
|
578
|
+
const model = spanData.model ?? rawResponse?.model;
|
|
579
|
+
const inputTokens = usage.input_tokens || usage.prompt_tokens || 0;
|
|
580
|
+
const outputTokens = usage.output_tokens || usage.completion_tokens || 0;
|
|
581
|
+
const modelConfig = spanData.model_config ?? {};
|
|
582
|
+
const modelParams = {};
|
|
583
|
+
for (const param of [
|
|
584
|
+
"temperature",
|
|
585
|
+
"max_tokens",
|
|
586
|
+
"top_p",
|
|
587
|
+
"frequency_penalty",
|
|
588
|
+
"presence_penalty"
|
|
589
|
+
]) if (param in modelConfig) modelParams[param] = modelConfig[param];
|
|
590
|
+
if (typeof modelConfig.base_url === "string") warnIfPostHogAiGateway(modelConfig.base_url);
|
|
591
|
+
const properties = {
|
|
592
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
593
|
+
$ai_model: model,
|
|
594
|
+
$ai_base_url: typeof modelConfig.base_url === "string" ? modelConfig.base_url : "",
|
|
595
|
+
$ai_model_parameters: Object.keys(modelParams).length > 0 ? modelParams : null,
|
|
596
|
+
$ai_input: this._prepareCapturedValue(normalizeInputRoles(spanData.input)),
|
|
597
|
+
$ai_output_choices: this._prepareCapturedValue(spanData.output),
|
|
598
|
+
$ai_input_tokens: inputTokens,
|
|
599
|
+
$ai_output_tokens: outputTokens,
|
|
600
|
+
$ai_total_tokens: inputTokens + outputTokens
|
|
601
|
+
};
|
|
602
|
+
if (usesRawResponseUsage) properties.$ai_cache_reporting_exclusive = false;
|
|
603
|
+
const promptTokenDetails = usage.prompt_tokens_details;
|
|
604
|
+
const completionTokenDetails = usage.completion_tokens_details;
|
|
605
|
+
if (completionTokenDetails?.reasoning_tokens) properties.$ai_reasoning_tokens = completionTokenDetails.reasoning_tokens;
|
|
606
|
+
if (promptTokenDetails?.cached_tokens) properties.$ai_cache_read_input_tokens = promptTokenDetails.cached_tokens;
|
|
607
|
+
if (usage.details) {
|
|
608
|
+
const details = usage.details;
|
|
609
|
+
if (details.reasoning_tokens) properties.$ai_reasoning_tokens = details.reasoning_tokens;
|
|
610
|
+
if (details.cache_read_input_tokens) properties.$ai_cache_read_input_tokens = details.cache_read_input_tokens;
|
|
611
|
+
if (details.cache_creation_input_tokens) properties.$ai_cache_creation_input_tokens = details.cache_creation_input_tokens;
|
|
612
|
+
}
|
|
613
|
+
if (usage.reasoning_tokens) properties.$ai_reasoning_tokens = usage.reasoning_tokens;
|
|
614
|
+
if (usage.cache_read_input_tokens) properties.$ai_cache_read_input_tokens = usage.cache_read_input_tokens;
|
|
615
|
+
if (usage.cache_creation_input_tokens) properties.$ai_cache_creation_input_tokens = usage.cache_creation_input_tokens;
|
|
616
|
+
this._captureEvent("$ai_generation", properties, distinctId);
|
|
617
|
+
}
|
|
618
|
+
_handleResponseSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
619
|
+
const responseSpanData = spanData;
|
|
620
|
+
const response = responseSpanData._response;
|
|
621
|
+
const responseId = spanData.response_id ?? response?.id;
|
|
622
|
+
const usage = response?.usage ?? {};
|
|
623
|
+
const inputTokens = usage?.input_tokens ?? 0;
|
|
624
|
+
const outputTokens = usage?.output_tokens ?? 0;
|
|
625
|
+
const model = response?.model;
|
|
626
|
+
const properties = {
|
|
627
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
628
|
+
$ai_model: model,
|
|
629
|
+
$ai_response_id: responseId,
|
|
630
|
+
$ai_input: this._prepareCapturedValue(normalizeInputRoles(responseSpanData._input)),
|
|
631
|
+
$ai_input_tokens: inputTokens,
|
|
632
|
+
$ai_output_tokens: outputTokens,
|
|
633
|
+
$ai_total_tokens: inputTokens + outputTokens
|
|
634
|
+
};
|
|
635
|
+
if (response?.output) properties.$ai_output_choices = this._prepareCapturedValue(response.output);
|
|
636
|
+
this._captureEvent("$ai_generation", properties, distinctId);
|
|
637
|
+
}
|
|
638
|
+
_handleFunctionSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
639
|
+
const properties = {
|
|
640
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
641
|
+
$ai_span_name: spanData.name,
|
|
642
|
+
$ai_span_type: "tool",
|
|
643
|
+
$ai_input_state: this._prepareCapturedValue(spanData.input),
|
|
644
|
+
$ai_output_state: this._prepareCapturedValue(spanData.output)
|
|
645
|
+
};
|
|
646
|
+
if (spanData.mcp_data) properties.$ai_mcp_data = this._prepareCapturedValue(spanData.mcp_data);
|
|
647
|
+
this._captureEvent("$ai_span", properties, distinctId);
|
|
648
|
+
}
|
|
649
|
+
_handleAgentSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
650
|
+
const properties = {
|
|
651
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
652
|
+
$ai_span_name: spanData.name,
|
|
653
|
+
$ai_span_type: "agent"
|
|
654
|
+
};
|
|
655
|
+
if (spanData.handoffs) properties.$ai_agent_handoffs = spanData.handoffs;
|
|
656
|
+
if (spanData.tools) properties.$ai_agent_tools = spanData.tools;
|
|
657
|
+
if (spanData.output_type) properties.$ai_agent_output_type = spanData.output_type;
|
|
658
|
+
this._captureEvent("$ai_span", properties, distinctId);
|
|
659
|
+
}
|
|
660
|
+
_handleHandoffSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
661
|
+
const properties = {
|
|
662
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
663
|
+
$ai_span_name: `${spanData.from_agent} -> ${spanData.to_agent}`,
|
|
664
|
+
$ai_span_type: "handoff",
|
|
665
|
+
$ai_handoff_from_agent: spanData.from_agent,
|
|
666
|
+
$ai_handoff_to_agent: spanData.to_agent
|
|
667
|
+
};
|
|
668
|
+
this._captureEvent("$ai_span", properties, distinctId);
|
|
669
|
+
}
|
|
670
|
+
_handleGuardrailSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
671
|
+
const properties = {
|
|
672
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
673
|
+
$ai_span_name: spanData.name,
|
|
674
|
+
$ai_span_type: "guardrail",
|
|
675
|
+
$ai_guardrail_triggered: spanData.triggered
|
|
676
|
+
};
|
|
677
|
+
this._captureEvent("$ai_span", properties, distinctId);
|
|
678
|
+
}
|
|
679
|
+
_handleCustomSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
680
|
+
const properties = {
|
|
681
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
682
|
+
$ai_span_name: spanData.name,
|
|
683
|
+
$ai_span_type: "custom",
|
|
684
|
+
$ai_custom_data: this._prepareCapturedValue(spanData.data)
|
|
685
|
+
};
|
|
686
|
+
this._captureEvent("$ai_span", properties, distinctId);
|
|
687
|
+
}
|
|
688
|
+
_handleAudioSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
689
|
+
const spanType = spanData.type;
|
|
690
|
+
const properties = {
|
|
691
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
692
|
+
$ai_span_name: spanType,
|
|
693
|
+
$ai_span_type: spanType
|
|
694
|
+
};
|
|
695
|
+
if ("model" in spanData && spanData.model) properties.$ai_model = spanData.model;
|
|
696
|
+
if ("model_config" in spanData && spanData.model_config) properties.$ai_model_config = this._prepareCapturedValue(spanData.model_config);
|
|
697
|
+
if (spanData.type === "transcription") {
|
|
698
|
+
const transcription = spanData;
|
|
699
|
+
if (transcription.input?.format) properties.$ai_audio_input_format = transcription.input.format;
|
|
700
|
+
if (transcription.output) properties.$ai_output_state = this._prepareCapturedValue(transcription.output);
|
|
701
|
+
} else if (spanData.type === "speech") {
|
|
702
|
+
const speech = spanData;
|
|
703
|
+
if (speech.output?.format) properties.$ai_audio_output_format = speech.output.format;
|
|
704
|
+
if (speech.input) properties.$ai_input = this._prepareCapturedValue(speech.input);
|
|
705
|
+
} else if (spanData.type === "speech_group") {
|
|
706
|
+
const speechGroup = spanData;
|
|
707
|
+
if (speechGroup.input) properties.$ai_input = this._prepareCapturedValue(speechGroup.input);
|
|
708
|
+
}
|
|
709
|
+
this._captureEvent("$ai_span", properties, distinctId);
|
|
710
|
+
}
|
|
711
|
+
_handleMcpSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
712
|
+
const properties = {
|
|
713
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
714
|
+
$ai_span_name: `mcp:${spanData.server}`,
|
|
715
|
+
$ai_span_type: "mcp_tools",
|
|
716
|
+
$ai_mcp_server: spanData.server,
|
|
717
|
+
$ai_mcp_tools: this._prepareCapturedValue(spanData.result)
|
|
718
|
+
};
|
|
719
|
+
this._captureEvent("$ai_span", properties, distinctId);
|
|
720
|
+
}
|
|
721
|
+
_handleGenericSpan(spanData, traceId, spanId, parentId, latency, distinctId, groupId, errorProperties) {
|
|
722
|
+
const spanType = spanData.type || "unknown";
|
|
723
|
+
const properties = {
|
|
724
|
+
...this._baseProperties(traceId, spanId, parentId, latency, groupId, errorProperties),
|
|
725
|
+
$ai_span_name: spanType,
|
|
726
|
+
$ai_span_type: spanType
|
|
727
|
+
};
|
|
728
|
+
this._captureEvent("$ai_span", properties, distinctId);
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
//#endregion
|
|
732
|
+
//#region src/openai-agents/index.ts
|
|
813
733
|
/**
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
734
|
+
* One-liner to instrument OpenAI Agents SDK with PostHog tracing.
|
|
735
|
+
*
|
|
736
|
+
* This registers a PostHogTracingProcessor with the OpenAI Agents SDK,
|
|
737
|
+
* automatically capturing traces, spans, and LLM generations.
|
|
738
|
+
*
|
|
739
|
+
* @param options - Configuration options
|
|
740
|
+
* @returns The registered processor instance
|
|
741
|
+
*
|
|
742
|
+
* @example
|
|
743
|
+
* ```typescript
|
|
744
|
+
* import { instrument } from '@posthog/ai/openai-agents'
|
|
745
|
+
* import PostHog from 'posthog-node'
|
|
746
|
+
*
|
|
747
|
+
* const phClient = new PostHog('<API_KEY>')
|
|
748
|
+
*
|
|
749
|
+
* // Simple setup — await before running agents
|
|
750
|
+
* await instrument({ client: phClient, distinctId: 'user@example.com' })
|
|
751
|
+
*
|
|
752
|
+
* // With dynamic distinct ID
|
|
753
|
+
* await instrument({
|
|
754
|
+
* client: phClient,
|
|
755
|
+
* distinctId: (trace) => trace.metadata?.userId,
|
|
756
|
+
* privacyMode: true,
|
|
757
|
+
* properties: { environment: 'production' },
|
|
758
|
+
* })
|
|
759
|
+
*
|
|
760
|
+
* // Now run agents as normal - traces automatically sent to PostHog
|
|
761
|
+
* import { Agent, run } from '@openai/agents'
|
|
762
|
+
* const agent = new Agent({ name: 'Assistant', instructions: 'You are helpful.' })
|
|
763
|
+
* const result = await run(agent, 'Hello!')
|
|
764
|
+
* ```
|
|
765
|
+
*/
|
|
846
766
|
async function instrument(options) {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
addTraceProcessor(processor);
|
|
858
|
-
return processor;
|
|
767
|
+
const { addTraceProcessor } = await import("@openai/agents");
|
|
768
|
+
const processor = new PostHogTracingProcessor({
|
|
769
|
+
client: options.client,
|
|
770
|
+
distinctId: options.distinctId,
|
|
771
|
+
privacyMode: options.privacyMode,
|
|
772
|
+
groups: options.groups,
|
|
773
|
+
properties: options.properties
|
|
774
|
+
});
|
|
775
|
+
addTraceProcessor(processor);
|
|
776
|
+
return processor;
|
|
859
777
|
}
|
|
860
|
-
|
|
778
|
+
//#endregion
|
|
861
779
|
export { PostHogTracingProcessor, instrument };
|
|
862
|
-
|
|
780
|
+
|
|
781
|
+
//# sourceMappingURL=index.mjs.map
|