@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
package/dist/openai/index.mjs
CHANGED
|
@@ -1,2663 +1,2130 @@
|
|
|
1
|
-
import { AzureOpenAI, OpenAI } from
|
|
2
|
-
import { v4 } from
|
|
3
|
-
import { toJsonSafeValue, uuidv7 } from
|
|
4
|
-
import { Stream } from
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const isString = value => {
|
|
9
|
-
return typeof value === 'string';
|
|
1
|
+
import { AzureOpenAI, OpenAI } from "openai";
|
|
2
|
+
import { v4 } from "uuid";
|
|
3
|
+
import { toJsonSafeValue, uuidv7 } from "@posthog/core";
|
|
4
|
+
import { Stream } from "openai/streaming";
|
|
5
|
+
//#region src/typeGuards.ts
|
|
6
|
+
const isString = (value) => {
|
|
7
|
+
return typeof value === "string";
|
|
10
8
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/** @internal */
|
|
15
|
-
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/captureAiEvent.ts
|
|
16
11
|
/** @internal */
|
|
17
12
|
function isFullAiCaptureEnabled(client) {
|
|
18
|
-
|
|
13
|
+
return client?.enableFullAiCapture === true;
|
|
19
14
|
}
|
|
20
|
-
|
|
21
15
|
/** @internal */
|
|
22
16
|
function captureAiEvent(client, event) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
if (isFullAiCaptureEnabled(client) && typeof client.captureAi === "function") {
|
|
18
|
+
client.captureAi(event);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
client.capture(event);
|
|
28
22
|
}
|
|
29
|
-
|
|
30
23
|
/** @internal */
|
|
31
24
|
async function captureAiEventImmediate(client, event) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
if (isFullAiCaptureEnabled(client) && typeof client.captureAiImmediate === "function") {
|
|
26
|
+
await client.captureAiImmediate(event);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
await client.captureImmediate(event);
|
|
37
30
|
}
|
|
38
|
-
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/sanitization/base64_recognizer.ts
|
|
39
33
|
const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
|
|
40
34
|
const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
35
|
+
var Base64Recognizer = class {
|
|
36
|
+
recognize(value, minLength) {
|
|
37
|
+
const dataUrl = DATA_URL_PREFIX_RE.exec(value);
|
|
38
|
+
if (dataUrl) return {
|
|
39
|
+
kind: "data-url",
|
|
40
|
+
mediaType: dataUrl[1]
|
|
41
|
+
};
|
|
42
|
+
if (value.length < minLength) return { kind: "none" };
|
|
43
|
+
const confidencePrefix = value.slice(0, minLength);
|
|
44
|
+
if (BASE64_ALPHABET_RE.test(confidencePrefix)) return { kind: "raw" };
|
|
45
|
+
else return { kind: "none" };
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/sanitization/media_type_context.ts
|
|
50
|
+
const MIME_HINT_KEYS = [
|
|
51
|
+
"mediaType",
|
|
52
|
+
"media_type",
|
|
53
|
+
"mimeType",
|
|
54
|
+
"mime_type"
|
|
55
|
+
];
|
|
56
|
+
const STRONG_CONTEXT_KEYS = /* @__PURE__ */ new Set([
|
|
57
|
+
"data",
|
|
58
|
+
"file_data",
|
|
59
|
+
"fileData",
|
|
60
|
+
"image_url",
|
|
61
|
+
"imageUrl",
|
|
62
|
+
"video_url",
|
|
63
|
+
"videoUrl",
|
|
64
|
+
"audio",
|
|
65
|
+
"audio_data",
|
|
66
|
+
"audioData",
|
|
67
|
+
"inline_data",
|
|
68
|
+
"inlineData",
|
|
69
|
+
"source",
|
|
70
|
+
"result"
|
|
71
|
+
]);
|
|
72
|
+
const STRONG_CONTEXT_TYPES = /* @__PURE__ */ new Set([
|
|
73
|
+
"image",
|
|
74
|
+
"image_url",
|
|
75
|
+
"input_image",
|
|
76
|
+
"audio",
|
|
77
|
+
"input_audio",
|
|
78
|
+
"video",
|
|
79
|
+
"video_url",
|
|
80
|
+
"file",
|
|
81
|
+
"input_file",
|
|
82
|
+
"document",
|
|
83
|
+
"media",
|
|
84
|
+
"file-data"
|
|
85
|
+
]);
|
|
86
|
+
const FILE_FAMILY_TYPES = /* @__PURE__ */ new Set([
|
|
87
|
+
"file",
|
|
88
|
+
"input_file",
|
|
89
|
+
"document",
|
|
90
|
+
"media",
|
|
91
|
+
"file-data"
|
|
92
|
+
]);
|
|
93
|
+
const KNOWN_AUDIO_FORMATS = /* @__PURE__ */ new Set([
|
|
94
|
+
"wav",
|
|
95
|
+
"mp3",
|
|
96
|
+
"ogg",
|
|
97
|
+
"flac",
|
|
98
|
+
"m4a",
|
|
99
|
+
"aac",
|
|
100
|
+
"webm"
|
|
101
|
+
]);
|
|
102
|
+
var MediaTypeContext = class MediaTypeContext {
|
|
103
|
+
static {
|
|
104
|
+
this.EMPTY = new MediaTypeContext(void 0, void 0);
|
|
105
|
+
}
|
|
106
|
+
constructor(parent, key, explicitMediaType) {
|
|
107
|
+
this.parent = parent;
|
|
108
|
+
this.key = key;
|
|
109
|
+
this.explicitMediaType = explicitMediaType;
|
|
110
|
+
}
|
|
111
|
+
inferMediaType() {
|
|
112
|
+
return this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey();
|
|
113
|
+
}
|
|
114
|
+
inferFromSiblingMime() {
|
|
115
|
+
if (this.explicitMediaType) return this.explicitMediaType;
|
|
116
|
+
if (!this.parent) return void 0;
|
|
117
|
+
for (const hint of MIME_HINT_KEYS) {
|
|
118
|
+
const v = this.parent[hint];
|
|
119
|
+
if (typeof v === "string") return v;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
inferFromSiblingFormat() {
|
|
123
|
+
if (!this.parent) return void 0;
|
|
124
|
+
const fmt = this.parent.format;
|
|
125
|
+
if (typeof fmt === "string" && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return `audio/${fmt.toLowerCase()}`;
|
|
126
|
+
}
|
|
127
|
+
inferFromParentType() {
|
|
128
|
+
if (!this.parent) return void 0;
|
|
129
|
+
const t = this.parent.type;
|
|
130
|
+
if (typeof t !== "string") return void 0;
|
|
131
|
+
if (t === "image" || t === "image_url" || t === "input_image") return "image";
|
|
132
|
+
if (t === "audio" || t === "input_audio") return "audio";
|
|
133
|
+
if (t === "video" || t === "video_url") return "video";
|
|
134
|
+
if (FILE_FAMILY_TYPES.has(t)) return "application/octet-stream";
|
|
135
|
+
}
|
|
136
|
+
inferFromKey() {
|
|
137
|
+
if (!this.key) return void 0;
|
|
138
|
+
const key = this.key.toLowerCase();
|
|
139
|
+
if (key.includes("audio")) return "audio";
|
|
140
|
+
if (key.includes("video")) return "video";
|
|
141
|
+
if (key.includes("image")) return "image";
|
|
142
|
+
if (key.includes("file") || key.includes("document")) return "application/octet-stream";
|
|
143
|
+
}
|
|
144
|
+
hasExplicitBinaryMediaType() {
|
|
145
|
+
if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false;
|
|
146
|
+
const mediaType = this.inferFromSiblingMime();
|
|
147
|
+
return mediaType !== void 0 && !mediaType.toLowerCase().startsWith("text/");
|
|
148
|
+
}
|
|
149
|
+
signalsBinary() {
|
|
150
|
+
if (this.explicitMediaType) return true;
|
|
151
|
+
if (this.parent) {
|
|
152
|
+
for (const hint of MIME_HINT_KEYS) if (typeof this.parent[hint] === "string") return true;
|
|
153
|
+
const fmt = this.parent.format;
|
|
154
|
+
if (typeof fmt === "string" && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return true;
|
|
155
|
+
const t = this.parent.type;
|
|
156
|
+
if (typeof t === "string" && STRONG_CONTEXT_TYPES.has(t)) return true;
|
|
157
|
+
}
|
|
158
|
+
if (this.key && STRONG_CONTEXT_KEYS.has(this.key)) return true;
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
//#endregion
|
|
163
|
+
//#region src/sanitization/binary_content_redactor.ts
|
|
136
164
|
const STRONG_CONTEXT_MIN_LENGTH = 64;
|
|
137
165
|
const WEAK_CONTEXT_MIN_LENGTH = 1024;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
return value;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
placeholderFor(mediaType) {
|
|
183
|
-
if (!mediaType) return '[base64 redacted]';
|
|
184
|
-
if (mediaType === 'application/octet-stream') return '[base64 file redacted]';
|
|
185
|
-
return `[base64 ${mediaType} redacted]`;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
166
|
+
var BinaryContentRedactor = class {
|
|
167
|
+
constructor(recognizer = new Base64Recognizer()) {
|
|
168
|
+
this.recognizer = recognizer;
|
|
169
|
+
this.visited = /* @__PURE__ */ new WeakSet();
|
|
170
|
+
}
|
|
171
|
+
redact(value, mediaType) {
|
|
172
|
+
this.visited = /* @__PURE__ */ new WeakSet();
|
|
173
|
+
return this.walk(value, mediaType ? new MediaTypeContext(void 0, void 0, mediaType) : MediaTypeContext.EMPTY);
|
|
174
|
+
}
|
|
175
|
+
walk(value, ctx) {
|
|
176
|
+
if (value === null || value === void 0) return value;
|
|
177
|
+
if (typeof value === "string") return this.redactString(value, ctx);
|
|
178
|
+
if (typeof value !== "object") return value;
|
|
179
|
+
if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return this.placeholderFor(ctx.inferMediaType());
|
|
180
|
+
if (this.visited.has(value)) return null;
|
|
181
|
+
this.visited.add(value);
|
|
182
|
+
if (Array.isArray(value)) return value.map((item) => this.walk(item, ctx));
|
|
183
|
+
const obj = value;
|
|
184
|
+
const out = {};
|
|
185
|
+
for (const k of Object.keys(obj)) out[k] = this.walk(obj[k], new MediaTypeContext(obj, k));
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
redactString(value, ctx) {
|
|
189
|
+
const hasExplicitBinaryMediaType = ctx.hasExplicitBinaryMediaType();
|
|
190
|
+
const recognitionValue = hasExplicitBinaryMediaType ? value.replace(/[\r\n]/g, "") : value;
|
|
191
|
+
const minLength = hasExplicitBinaryMediaType ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH) : ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
|
|
192
|
+
const recognition = this.recognizer.recognize(recognitionValue, minLength);
|
|
193
|
+
switch (recognition.kind) {
|
|
194
|
+
case "data-url": return this.placeholderFor(recognition.mediaType);
|
|
195
|
+
case "raw": return this.placeholderFor(ctx.inferMediaType());
|
|
196
|
+
case "none": return value;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
placeholderFor(mediaType) {
|
|
200
|
+
if (!mediaType) return "[base64 redacted]";
|
|
201
|
+
if (mediaType === "application/octet-stream") return "[base64 file redacted]";
|
|
202
|
+
return `[base64 ${mediaType} redacted]`;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/sanitization.ts
|
|
189
207
|
const redactor = new BinaryContentRedactor();
|
|
190
208
|
const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
|
|
191
209
|
const sanitizeOpenAI = (data, client) => sanitize(data, client);
|
|
192
210
|
const sanitizeOpenAIResponse = (data, client) => sanitize(data, client);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/utils.ts
|
|
213
|
+
const TOKEN_PROPERTY_KEYS = /* @__PURE__ */ new Set([
|
|
214
|
+
"$ai_input_tokens",
|
|
215
|
+
"$ai_output_tokens",
|
|
216
|
+
"$ai_cache_read_input_tokens",
|
|
217
|
+
"$ai_cache_creation_input_tokens",
|
|
218
|
+
"$ai_total_tokens",
|
|
219
|
+
"$ai_reasoning_tokens"
|
|
220
|
+
]);
|
|
196
221
|
/**
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
222
|
+
* Whether the caller supplied their own token counts, which override the ones the SDK
|
|
223
|
+
* derived from the provider response.
|
|
224
|
+
*/
|
|
200
225
|
function hasTokenOverrides(posthogProperties) {
|
|
201
|
-
|
|
226
|
+
return !!posthogProperties && Object.keys(posthogProperties).some((key) => TOKEN_PROPERTY_KEYS.has(key));
|
|
202
227
|
}
|
|
203
228
|
function getTokensSource(posthogProperties) {
|
|
204
|
-
|
|
229
|
+
return hasTokenOverrides(posthogProperties) ? "passthrough" : "sdk";
|
|
205
230
|
}
|
|
206
|
-
const STRING_FORMAT =
|
|
207
|
-
|
|
208
|
-
// Reused across calls to avoid per-invocation allocation; truncate() runs
|
|
209
|
-
// hundreds of times for prompts with many parts.
|
|
231
|
+
const STRING_FORMAT = "utf8";
|
|
210
232
|
new TextEncoder();
|
|
211
|
-
new TextDecoder(STRING_FORMAT, {
|
|
212
|
-
fatal: false
|
|
213
|
-
});
|
|
214
|
-
|
|
233
|
+
new TextDecoder(STRING_FORMAT, { fatal: false });
|
|
215
234
|
/**
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
235
|
+
* Safely converts content to a string, preserving structure for objects/arrays.
|
|
236
|
+
* - If content is already a string, returns it as-is
|
|
237
|
+
* - If content is an object or array, stringifies it with JSON.stringify to preserve structure
|
|
238
|
+
* - Otherwise, converts to string with String()
|
|
239
|
+
*
|
|
240
|
+
* This prevents the "[object Object]" bug when objects are naively converted to strings.
|
|
241
|
+
*
|
|
242
|
+
* @param content - The content to convert to a string
|
|
243
|
+
* @returns A string representation that preserves structure for complex types
|
|
244
|
+
*/
|
|
226
245
|
function toContentString(content) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// Fallback for circular refs, BigInt, or objects with throwing toJSON
|
|
235
|
-
return String(content);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return String(content);
|
|
246
|
+
if (typeof content === "string") return content;
|
|
247
|
+
if (content !== void 0 && content !== null && typeof content === "object") try {
|
|
248
|
+
return JSON.stringify(content);
|
|
249
|
+
} catch {
|
|
250
|
+
return String(content);
|
|
251
|
+
}
|
|
252
|
+
return String(content);
|
|
239
253
|
}
|
|
240
254
|
const getModelParams = (params, responseServiceTier) => {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
+
if (!params) return {};
|
|
256
|
+
const modelParams = {};
|
|
257
|
+
for (const key of [
|
|
258
|
+
"temperature",
|
|
259
|
+
"max_tokens",
|
|
260
|
+
"max_completion_tokens",
|
|
261
|
+
"top_p",
|
|
262
|
+
"frequency_penalty",
|
|
263
|
+
"presence_penalty",
|
|
264
|
+
"n",
|
|
265
|
+
"stop",
|
|
266
|
+
"stream",
|
|
267
|
+
"streaming",
|
|
268
|
+
"language",
|
|
269
|
+
"response_format",
|
|
270
|
+
"timestamp_granularities",
|
|
271
|
+
"service_tier"
|
|
272
|
+
]) if (key in params && params[key] !== void 0) modelParams[key] = params[key];
|
|
273
|
+
if (responseServiceTier != null) modelParams.service_tier = responseServiceTier;
|
|
274
|
+
return modelParams;
|
|
255
275
|
};
|
|
256
|
-
const formatResponseOpenAI = response => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
} else if (item.content) {
|
|
329
|
-
content.push({
|
|
330
|
-
type: 'text',
|
|
331
|
-
text: String(item.content)
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
} else if (item.type === 'function_call') {
|
|
335
|
-
content.push({
|
|
336
|
-
type: 'function',
|
|
337
|
-
id: item.call_id || item.id || '',
|
|
338
|
-
function: {
|
|
339
|
-
name: item.name,
|
|
340
|
-
arguments: item.arguments || {}
|
|
341
|
-
}
|
|
342
|
-
});
|
|
343
|
-
} else if (item.type === 'image_generation_call' && item.result) {
|
|
344
|
-
content.push({
|
|
345
|
-
type: 'image',
|
|
346
|
-
image: item.result
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (content.length > 0) {
|
|
351
|
-
output.push({
|
|
352
|
-
role,
|
|
353
|
-
content
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
return output;
|
|
276
|
+
const formatResponseOpenAI = (response) => {
|
|
277
|
+
const output = [];
|
|
278
|
+
if (response.choices) for (const choice of response.choices) {
|
|
279
|
+
const content = [];
|
|
280
|
+
let role = "assistant";
|
|
281
|
+
if (choice.message) {
|
|
282
|
+
if (choice.message.role) role = choice.message.role;
|
|
283
|
+
if (choice.message.content) content.push({
|
|
284
|
+
type: "text",
|
|
285
|
+
text: choice.message.content
|
|
286
|
+
});
|
|
287
|
+
if (choice.message.tool_calls) for (const toolCall of choice.message.tool_calls) content.push({
|
|
288
|
+
type: "function",
|
|
289
|
+
id: toolCall.id,
|
|
290
|
+
function: {
|
|
291
|
+
name: toolCall.function.name,
|
|
292
|
+
arguments: toolCall.function.arguments
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
if (choice.message.audio) content.push({
|
|
296
|
+
type: "audio",
|
|
297
|
+
...choice.message.audio
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (content.length > 0) output.push({
|
|
301
|
+
role,
|
|
302
|
+
content
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
if (response.output) {
|
|
306
|
+
const content = [];
|
|
307
|
+
let role = "assistant";
|
|
308
|
+
for (const item of response.output) if (item.type === "message") {
|
|
309
|
+
role = item.role;
|
|
310
|
+
if (item.content && Array.isArray(item.content)) {
|
|
311
|
+
for (const contentItem of item.content) if (contentItem.type === "output_text" && contentItem.text) content.push({
|
|
312
|
+
type: "text",
|
|
313
|
+
text: contentItem.text
|
|
314
|
+
});
|
|
315
|
+
else if (contentItem.text) content.push({
|
|
316
|
+
type: "text",
|
|
317
|
+
text: contentItem.text
|
|
318
|
+
});
|
|
319
|
+
else if (contentItem.type === "input_image" && contentItem.image_url) content.push({
|
|
320
|
+
type: "image",
|
|
321
|
+
image: contentItem.image_url
|
|
322
|
+
});
|
|
323
|
+
} else if (item.content) content.push({
|
|
324
|
+
type: "text",
|
|
325
|
+
text: String(item.content)
|
|
326
|
+
});
|
|
327
|
+
} else if (item.type === "function_call") content.push({
|
|
328
|
+
type: "function",
|
|
329
|
+
id: item.call_id || item.id || "",
|
|
330
|
+
function: {
|
|
331
|
+
name: item.name,
|
|
332
|
+
arguments: item.arguments || {}
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
else if (item.type === "image_generation_call" && item.result) content.push({
|
|
336
|
+
type: "image",
|
|
337
|
+
image: item.result
|
|
338
|
+
});
|
|
339
|
+
if (content.length > 0) output.push({
|
|
340
|
+
role,
|
|
341
|
+
content
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
return output;
|
|
358
345
|
};
|
|
359
346
|
const withPrivacyMode = (client, privacyMode, input) => {
|
|
360
|
-
|
|
347
|
+
return client.privacy_mode || privacyMode ? null : input;
|
|
361
348
|
};
|
|
362
|
-
|
|
363
349
|
/**
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
350
|
+
* Calculate web search count from raw API response.
|
|
351
|
+
*
|
|
352
|
+
* Uses a two-tier detection strategy:
|
|
353
|
+
* Priority 1 (Exact Count): Count actual web search calls when available
|
|
354
|
+
* Priority 2 (Binary Detection): Return 1 if web search indicators are present, 0 otherwise
|
|
355
|
+
*
|
|
356
|
+
* @param result - Raw API response from any provider (OpenAI, Perplexity, OpenRouter, Gemini, etc.)
|
|
357
|
+
* @returns Number of web searches performed (exact count or binary 1/0)
|
|
358
|
+
*/
|
|
373
359
|
function calculateWebSearchCount(result) {
|
|
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
|
-
if (typeof content === 'object' && content !== null && 'annotations' in content) {
|
|
418
|
-
const annotations = content.annotations;
|
|
419
|
-
if (Array.isArray(annotations)) {
|
|
420
|
-
const hasUrlCitation = annotations.some(ann => {
|
|
421
|
-
return typeof ann === 'object' && ann !== null && 'type' in ann && ann.type === 'url_citation';
|
|
422
|
-
});
|
|
423
|
-
if (hasUrlCitation) {
|
|
424
|
-
return 1;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// Check for annotations in output[].content[] (OpenAI Responses API)
|
|
433
|
-
if ('output' in result && Array.isArray(result.output)) {
|
|
434
|
-
for (const item of result.output) {
|
|
435
|
-
if (typeof item === 'object' && item !== null && 'content' in item) {
|
|
436
|
-
const content = item.content;
|
|
437
|
-
if (Array.isArray(content)) {
|
|
438
|
-
for (const contentItem of content) {
|
|
439
|
-
if (typeof contentItem === 'object' && contentItem !== null && 'annotations' in contentItem) {
|
|
440
|
-
const annotations = contentItem.annotations;
|
|
441
|
-
if (Array.isArray(annotations)) {
|
|
442
|
-
const hasUrlCitation = annotations.some(ann => {
|
|
443
|
-
return typeof ann === 'object' && ann !== null && 'type' in ann && ann.type === 'url_citation';
|
|
444
|
-
});
|
|
445
|
-
if (hasUrlCitation) {
|
|
446
|
-
return 1;
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Check for grounding_metadata (Gemini)
|
|
457
|
-
if ('candidates' in result && Array.isArray(result.candidates)) {
|
|
458
|
-
for (const candidate of result.candidates) {
|
|
459
|
-
if (typeof candidate === 'object' && candidate !== null && 'grounding_metadata' in candidate && candidate.grounding_metadata) {
|
|
460
|
-
return 1;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return 0;
|
|
360
|
+
if (!result || typeof result !== "object") return 0;
|
|
361
|
+
if ("output" in result && Array.isArray(result.output)) {
|
|
362
|
+
let count = 0;
|
|
363
|
+
for (const item of result.output) if (typeof item === "object" && item !== null && "type" in item && item.type === "web_search_call") count++;
|
|
364
|
+
if (count > 0) return count;
|
|
365
|
+
}
|
|
366
|
+
if ("citations" in result && Array.isArray(result.citations) && result.citations.length > 0) return 1;
|
|
367
|
+
if ("search_results" in result && Array.isArray(result.search_results) && result.search_results.length > 0) return 1;
|
|
368
|
+
if ("usage" in result && typeof result.usage === "object" && result.usage !== null) {
|
|
369
|
+
if ("search_context_size" in result.usage && result.usage.search_context_size) return 1;
|
|
370
|
+
}
|
|
371
|
+
if ("choices" in result && Array.isArray(result.choices)) {
|
|
372
|
+
for (const choice of result.choices) if (typeof choice === "object" && choice !== null) {
|
|
373
|
+
const content = ("message" in choice ? choice.message : null) || ("delta" in choice ? choice.delta : null);
|
|
374
|
+
if (typeof content === "object" && content !== null && "annotations" in content) {
|
|
375
|
+
const annotations = content.annotations;
|
|
376
|
+
if (Array.isArray(annotations)) {
|
|
377
|
+
if (annotations.some((ann) => {
|
|
378
|
+
return typeof ann === "object" && ann !== null && "type" in ann && ann.type === "url_citation";
|
|
379
|
+
})) return 1;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if ("output" in result && Array.isArray(result.output)) {
|
|
385
|
+
for (const item of result.output) if (typeof item === "object" && item !== null && "content" in item) {
|
|
386
|
+
const content = item.content;
|
|
387
|
+
if (Array.isArray(content)) {
|
|
388
|
+
for (const contentItem of content) if (typeof contentItem === "object" && contentItem !== null && "annotations" in contentItem) {
|
|
389
|
+
const annotations = contentItem.annotations;
|
|
390
|
+
if (Array.isArray(annotations)) {
|
|
391
|
+
if (annotations.some((ann) => {
|
|
392
|
+
return typeof ann === "object" && ann !== null && "type" in ann && ann.type === "url_citation";
|
|
393
|
+
})) return 1;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if ("candidates" in result && Array.isArray(result.candidates)) {
|
|
400
|
+
for (const candidate of result.candidates) if (typeof candidate === "object" && candidate !== null && "grounding_metadata" in candidate && candidate.grounding_metadata) return 1;
|
|
401
|
+
}
|
|
402
|
+
return 0;
|
|
465
403
|
}
|
|
466
|
-
|
|
467
404
|
/**
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
405
|
+
* Extract available tool calls from the request parameters.
|
|
406
|
+
* These are the tools provided to the LLM, not the tool calls in the response.
|
|
407
|
+
*/
|
|
471
408
|
const extractAvailableToolCalls = (provider, params) => {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
409
|
+
if (provider === "anthropic") {
|
|
410
|
+
if (params.tools) return params.tools;
|
|
411
|
+
return null;
|
|
412
|
+
} else if (provider === "gemini") {
|
|
413
|
+
if (params.config && params.config.tools) return params.config.tools;
|
|
414
|
+
return null;
|
|
415
|
+
} else if (provider === "openai") {
|
|
416
|
+
if (params.tools) return params.tools;
|
|
417
|
+
return null;
|
|
418
|
+
} else if (provider === "vercel") {
|
|
419
|
+
if (params.tools) return params.tools;
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
return null;
|
|
478
423
|
};
|
|
479
|
-
let AIEvent = /*#__PURE__*/function (AIEvent) {
|
|
480
|
-
AIEvent["Generation"] = "$ai_generation";
|
|
481
|
-
AIEvent["Embedding"] = "$ai_embedding";
|
|
482
|
-
return AIEvent;
|
|
483
|
-
}({});
|
|
484
424
|
function sanitizeValues(obj) {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
return new TextDecoder().decode(new TextEncoder().encode(jsonSafe));
|
|
492
|
-
} else if (Array.isArray(jsonSafe)) {
|
|
493
|
-
return jsonSafe.map(sanitizeValues);
|
|
494
|
-
} else if (jsonSafe && typeof jsonSafe === 'object') {
|
|
495
|
-
return Object.fromEntries(Object.entries(jsonSafe).map(([k, v]) => [k, sanitizeValues(v)]));
|
|
496
|
-
}
|
|
497
|
-
return jsonSafe;
|
|
425
|
+
if (obj === void 0 || obj === null) return obj;
|
|
426
|
+
const jsonSafe = JSON.parse(JSON.stringify(obj));
|
|
427
|
+
if (typeof jsonSafe === "string") return new TextDecoder().decode(new TextEncoder().encode(jsonSafe));
|
|
428
|
+
else if (Array.isArray(jsonSafe)) return jsonSafe.map(sanitizeValues);
|
|
429
|
+
else if (jsonSafe && typeof jsonSafe === "object") return Object.fromEntries(Object.entries(jsonSafe).map(([k, v]) => [k, sanitizeValues(v)]));
|
|
430
|
+
return jsonSafe;
|
|
498
431
|
}
|
|
499
432
|
const POSTHOG_PARAMS_MAP = {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
433
|
+
posthogDistinctId: "distinctId",
|
|
434
|
+
posthogTraceId: "traceId",
|
|
435
|
+
posthogProperties: "properties",
|
|
436
|
+
posthogPrivacyMode: "privacyMode",
|
|
437
|
+
posthogGroups: "groups",
|
|
438
|
+
posthogModelOverride: "modelOverride",
|
|
439
|
+
posthogProviderOverride: "providerOverride",
|
|
440
|
+
posthogCostOverride: "costOverride",
|
|
441
|
+
posthogCaptureImmediate: "captureImmediate"
|
|
509
442
|
};
|
|
510
443
|
function extractPosthogParams(body) {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
return {
|
|
523
|
-
providerParams: providerParams,
|
|
524
|
-
posthogParams: addDefaults(posthogParams)
|
|
525
|
-
};
|
|
444
|
+
const providerParams = {};
|
|
445
|
+
const posthogParams = {};
|
|
446
|
+
for (const [key, value] of Object.entries(body)) if (POSTHOG_PARAMS_MAP[key]) posthogParams[POSTHOG_PARAMS_MAP[key]] = value;
|
|
447
|
+
else if (key.startsWith("posthog")) console.warn(`Unknown Posthog parameter ${key}`);
|
|
448
|
+
else providerParams[key] = value;
|
|
449
|
+
return {
|
|
450
|
+
providerParams,
|
|
451
|
+
posthogParams: addDefaults(posthogParams)
|
|
452
|
+
};
|
|
526
453
|
}
|
|
527
454
|
function addDefaults(params) {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
455
|
+
return {
|
|
456
|
+
...params,
|
|
457
|
+
privacyMode: params.privacyMode ?? false,
|
|
458
|
+
traceId: params.traceId ?? v4()
|
|
459
|
+
};
|
|
533
460
|
}
|
|
534
461
|
function formatOpenAIResponsesInput(input, instructions) {
|
|
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
|
-
} else if (typeof input === 'string') {
|
|
567
|
-
messages.push({
|
|
568
|
-
role: 'user',
|
|
569
|
-
content: input
|
|
570
|
-
});
|
|
571
|
-
} else if (input) {
|
|
572
|
-
messages.push({
|
|
573
|
-
role: 'user',
|
|
574
|
-
content: toContentString(input)
|
|
575
|
-
});
|
|
576
|
-
}
|
|
577
|
-
return messages;
|
|
462
|
+
const messages = [];
|
|
463
|
+
if (instructions) messages.push({
|
|
464
|
+
role: "system",
|
|
465
|
+
content: instructions
|
|
466
|
+
});
|
|
467
|
+
if (Array.isArray(input)) for (const item of input) if (typeof item === "string") messages.push({
|
|
468
|
+
role: "user",
|
|
469
|
+
content: item
|
|
470
|
+
});
|
|
471
|
+
else if (item && typeof item === "object") {
|
|
472
|
+
const obj = item;
|
|
473
|
+
const role = isString(obj.role) ? obj.role : "user";
|
|
474
|
+
const content = obj.content ?? obj.text ?? item;
|
|
475
|
+
messages.push({
|
|
476
|
+
role,
|
|
477
|
+
content: toContentString(content)
|
|
478
|
+
});
|
|
479
|
+
} else messages.push({
|
|
480
|
+
role: "user",
|
|
481
|
+
content: toContentString(item)
|
|
482
|
+
});
|
|
483
|
+
else if (typeof input === "string") messages.push({
|
|
484
|
+
role: "user",
|
|
485
|
+
content: input
|
|
486
|
+
});
|
|
487
|
+
else if (input) messages.push({
|
|
488
|
+
role: "user",
|
|
489
|
+
content: toContentString(input)
|
|
490
|
+
});
|
|
491
|
+
return messages;
|
|
578
492
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region package.json
|
|
495
|
+
var version = "8.10.2";
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/serializeError.ts
|
|
582
498
|
const DEFAULT_MAX_DEPTH = 3;
|
|
583
499
|
const MAX_STACK_LINES = 20;
|
|
584
500
|
function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
out.cause = serializeError(value.cause, depth - 1);
|
|
599
|
-
}
|
|
600
|
-
return out;
|
|
601
|
-
}
|
|
602
|
-
if (Array.isArray(value)) {
|
|
603
|
-
return value.map(item => serializeError(item, depth - 1));
|
|
604
|
-
}
|
|
605
|
-
return value;
|
|
501
|
+
if (depth < 0 || value === null || typeof value !== "object") return value;
|
|
502
|
+
if (value instanceof Error) {
|
|
503
|
+
const out = {
|
|
504
|
+
name: value.name,
|
|
505
|
+
message: value.message,
|
|
506
|
+
stack: truncateStack(value.stack)
|
|
507
|
+
};
|
|
508
|
+
for (const key of Object.keys(value)) out[key] = serializeError(value[key], depth - 1);
|
|
509
|
+
if (value.cause !== void 0) out.cause = serializeError(value.cause, depth - 1);
|
|
510
|
+
return out;
|
|
511
|
+
}
|
|
512
|
+
if (Array.isArray(value)) return value.map((item) => serializeError(item, depth - 1));
|
|
513
|
+
return value;
|
|
606
514
|
}
|
|
607
515
|
function stringifyError(error) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
return JSON.stringify({
|
|
618
|
-
message: String(error)
|
|
619
|
-
});
|
|
620
|
-
}
|
|
516
|
+
try {
|
|
517
|
+
return JSON.stringify(sanitizeValues(serializeError(error)));
|
|
518
|
+
} catch {
|
|
519
|
+
if (error instanceof Error) return JSON.stringify({
|
|
520
|
+
name: error.name,
|
|
521
|
+
message: error.message
|
|
522
|
+
});
|
|
523
|
+
return JSON.stringify({ message: String(error) });
|
|
524
|
+
}
|
|
621
525
|
}
|
|
622
526
|
function truncateStack(stack) {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
if (lines.length <= MAX_STACK_LINES) {
|
|
628
|
-
return stack;
|
|
629
|
-
}
|
|
630
|
-
return [...lines.slice(0, MAX_STACK_LINES), '... (truncated)'].join('\n');
|
|
527
|
+
if (!stack) return stack;
|
|
528
|
+
const lines = stack.split("\n");
|
|
529
|
+
if (lines.length <= MAX_STACK_LINES) return stack;
|
|
530
|
+
return [...lines.slice(0, MAX_STACK_LINES), "... (truncated)"].join("\n");
|
|
631
531
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
return new URL(hasScheme ? baseURL : `https://${baseURL}`).hostname.toLowerCase();
|
|
650
|
-
} catch {
|
|
651
|
-
return undefined;
|
|
652
|
-
}
|
|
532
|
+
//#endregion
|
|
533
|
+
//#region src/gatewayWarning.ts
|
|
534
|
+
const POSTHOG_AI_GATEWAY_HOSTS = [
|
|
535
|
+
"gateway.posthog.com",
|
|
536
|
+
"gateway.us.posthog.com",
|
|
537
|
+
"gateway.eu.posthog.com",
|
|
538
|
+
"ai-gateway.us.posthog.com",
|
|
539
|
+
"ai-gateway.eu.posthog.com"
|
|
540
|
+
];
|
|
541
|
+
const GATEWAY_DOCS_URL = "https://posthog.com/docs/ai-observability";
|
|
542
|
+
const extractHost = (baseURL) => {
|
|
543
|
+
try {
|
|
544
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(baseURL);
|
|
545
|
+
return new URL(hasScheme ? baseURL : `https://${baseURL}`).hostname.toLowerCase();
|
|
546
|
+
} catch {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
653
549
|
};
|
|
654
|
-
const isPostHogAiGatewayUrl = baseURL => {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
const host = extractHost(baseURL);
|
|
659
|
-
return host !== undefined && POSTHOG_AI_GATEWAY_HOSTS.includes(host);
|
|
550
|
+
const isPostHogAiGatewayUrl = (baseURL) => {
|
|
551
|
+
if (!baseURL) return false;
|
|
552
|
+
const host = extractHost(baseURL);
|
|
553
|
+
return host !== void 0 && POSTHOG_AI_GATEWAY_HOSTS.includes(host);
|
|
660
554
|
};
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
const warnIfPostHogAiGateway = baseURL => {
|
|
665
|
-
if (!isPostHogAiGatewayUrl(baseURL)) {
|
|
666
|
-
return;
|
|
667
|
-
}
|
|
668
|
-
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}.`);
|
|
555
|
+
const warnIfPostHogAiGateway = (baseURL) => {
|
|
556
|
+
if (!isPostHogAiGatewayUrl(baseURL)) return;
|
|
557
|
+
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}.`);
|
|
669
558
|
};
|
|
670
|
-
|
|
559
|
+
//#endregion
|
|
560
|
+
//#region src/captureAiGeneration.ts
|
|
671
561
|
/**
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
* same events the SDK wrappers produce.
|
|
685
|
-
*
|
|
686
|
-
* When `error` is set, the event is captured as an error. If the error is an
|
|
687
|
-
* object, it is mutated in place to set `__posthog_previously_captured_error`
|
|
688
|
-
* so callers can re-throw the original error reference safely.
|
|
689
|
-
*/
|
|
562
|
+
* Capture an `$ai_generation` (or `$ai_embedding`) event to PostHog.
|
|
563
|
+
*
|
|
564
|
+
* This is the canonical primitive that every `@posthog/ai` wrapper
|
|
565
|
+
* (`withTracing`, `OpenAI`, `Anthropic`, `GoogleGenAI`, …) funnels through, so
|
|
566
|
+
* external code can use it directly to instrument LLM calls made through
|
|
567
|
+
* arbitrary clients (Cloudflare Workers AI, custom HTTP, etc.) and get the
|
|
568
|
+
* same events the SDK wrappers produce.
|
|
569
|
+
*
|
|
570
|
+
* When `error` is set, the event is captured as an error. If the error is an
|
|
571
|
+
* object, it is mutated in place to set `__posthog_previously_captured_error`
|
|
572
|
+
* so callers can re-throw the original error reference safely.
|
|
573
|
+
*/
|
|
690
574
|
const captureAiGeneration$1 = async (client, options) => {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
...(usage.cacheReportingExclusive !== undefined && !tokensOverridden ? {
|
|
778
|
-
$ai_cache_reporting_exclusive: usage.cacheReportingExclusive
|
|
779
|
-
} : {}),
|
|
780
|
-
...(usage.webSearchCount ? {
|
|
781
|
-
$ai_web_search_count: usage.webSearchCount
|
|
782
|
-
} : {}),
|
|
783
|
-
...(usage.rawUsage ? {
|
|
784
|
-
$ai_usage: usage.rawUsage
|
|
785
|
-
} : {})
|
|
786
|
-
};
|
|
787
|
-
const properties = {
|
|
788
|
-
$ai_lib: 'posthog-ai',
|
|
789
|
-
$ai_lib_version: version,
|
|
790
|
-
$ai_provider: options.providerOverride ?? options.provider,
|
|
791
|
-
$ai_model: options.modelOverride ?? options.model,
|
|
792
|
-
$ai_model_parameters: options.modelParameters ?? {},
|
|
793
|
-
$ai_input: safeInput,
|
|
794
|
-
$ai_output_choices: safeOutput,
|
|
795
|
-
$ai_http_status: httpStatus,
|
|
796
|
-
...(usage.inputTokens !== undefined ? {
|
|
797
|
-
$ai_input_tokens: usage.inputTokens
|
|
798
|
-
} : {}),
|
|
799
|
-
...(usage.outputTokens !== undefined ? {
|
|
800
|
-
$ai_output_tokens: usage.outputTokens
|
|
801
|
-
} : {}),
|
|
802
|
-
...additionalTokenValues,
|
|
803
|
-
...(options.latency !== undefined ? {
|
|
804
|
-
$ai_latency: options.latency
|
|
805
|
-
} : {}),
|
|
806
|
-
...(options.timeToFirstToken !== undefined ? {
|
|
807
|
-
$ai_time_to_first_token: options.timeToFirstToken
|
|
808
|
-
} : {}),
|
|
809
|
-
$ai_trace_id: traceId,
|
|
810
|
-
...(options.baseURL === null ? {} : {
|
|
811
|
-
$ai_base_url: options.baseURL ?? ''
|
|
812
|
-
}),
|
|
813
|
-
...options.properties,
|
|
814
|
-
$ai_tokens_source: getTokensSource(options.properties),
|
|
815
|
-
...(options.distinctId ? {} : {
|
|
816
|
-
$process_person_profile: false
|
|
817
|
-
}),
|
|
818
|
-
...(options.stopReason ? {
|
|
819
|
-
$ai_stop_reason: options.stopReason
|
|
820
|
-
} : {}),
|
|
821
|
-
...(options.tools ? {
|
|
822
|
-
$ai_tools: options.tools
|
|
823
|
-
} : {}),
|
|
824
|
-
...(options.completionId ? {
|
|
825
|
-
$ai_completion_id: options.completionId
|
|
826
|
-
} : {}),
|
|
827
|
-
...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
|
|
828
|
-
$ai_provider_metadata: options.providerMetadata
|
|
829
|
-
} : {}),
|
|
830
|
-
...errorData,
|
|
831
|
-
...costOverrideData
|
|
832
|
-
};
|
|
833
|
-
const event = {
|
|
834
|
-
distinctId: options.distinctId ?? traceId,
|
|
835
|
-
event: eventType,
|
|
836
|
-
properties,
|
|
837
|
-
groups: options.groups
|
|
838
|
-
};
|
|
839
|
-
if (options.captureImmediate) {
|
|
840
|
-
await captureAiEventImmediate(client, event);
|
|
841
|
-
} else {
|
|
842
|
-
captureAiEvent(client, event);
|
|
843
|
-
}
|
|
844
|
-
} catch (error) {
|
|
845
|
-
// Telemetry failures must never affect the instrumented provider call.
|
|
846
|
-
try {
|
|
847
|
-
options.onError?.(error);
|
|
848
|
-
} catch {
|
|
849
|
-
// Error reporting must not affect the instrumented provider call either.
|
|
850
|
-
}
|
|
851
|
-
console.warn('[PostHog AI] Failed to capture generation telemetry:', error);
|
|
852
|
-
}
|
|
575
|
+
try {
|
|
576
|
+
if (!client.capture) return;
|
|
577
|
+
warnIfPostHogAiGateway(options.baseURL);
|
|
578
|
+
const traceId = options.traceId ?? v4();
|
|
579
|
+
const eventType = options.eventType ?? "$ai_generation";
|
|
580
|
+
const privacyMode = options.privacyMode ?? false;
|
|
581
|
+
const usage = options.usage ?? {};
|
|
582
|
+
const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
|
|
583
|
+
const safeInput = shouldRedact ? null : toJsonSafeValue(options.input);
|
|
584
|
+
const safeOutput = shouldRedact ? null : toJsonSafeValue(options.output);
|
|
585
|
+
let httpStatus = options.httpStatus;
|
|
586
|
+
let errorData = {};
|
|
587
|
+
if (options.error) {
|
|
588
|
+
if (httpStatus === void 0) {
|
|
589
|
+
if (typeof options.error === "object" && "status" in options.error && typeof options.error.status === "number") httpStatus = options.error.status;
|
|
590
|
+
else if (typeof options.error === "object" && "statusCode" in options.error && typeof options.error.statusCode === "number") httpStatus = options.error.statusCode;
|
|
591
|
+
else httpStatus = 500;
|
|
592
|
+
}
|
|
593
|
+
let exceptionId;
|
|
594
|
+
if (client.options?.enableExceptionAutocapture) {
|
|
595
|
+
exceptionId = uuidv7();
|
|
596
|
+
client.captureException(options.error, void 0, { $ai_trace_id: traceId }, exceptionId);
|
|
597
|
+
if (typeof options.error === "object") options.error.__posthog_previously_captured_error = true;
|
|
598
|
+
}
|
|
599
|
+
errorData = {
|
|
600
|
+
$ai_is_error: true,
|
|
601
|
+
$ai_error: stringifyError(options.error),
|
|
602
|
+
$exception_event_id: exceptionId
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
httpStatus = httpStatus ?? 200;
|
|
606
|
+
const costOverrideData = {};
|
|
607
|
+
if (options.costOverride) {
|
|
608
|
+
if (usage.inputTokens !== void 0) costOverrideData.$ai_input_cost_usd = (options.costOverride.inputCost ?? 0) * usage.inputTokens;
|
|
609
|
+
if (usage.outputTokens !== void 0) costOverrideData.$ai_output_cost_usd = (options.costOverride.outputCost ?? 0) * usage.outputTokens;
|
|
610
|
+
if (Object.keys(costOverrideData).length > 0) costOverrideData.$ai_total_cost_usd = (costOverrideData.$ai_input_cost_usd ?? 0) + (costOverrideData.$ai_output_cost_usd ?? 0);
|
|
611
|
+
}
|
|
612
|
+
const tokensOverridden = hasTokenOverrides(options.properties);
|
|
613
|
+
const additionalTokenValues = {
|
|
614
|
+
...usage.reasoningTokens ? { $ai_reasoning_tokens: usage.reasoningTokens } : {},
|
|
615
|
+
...usage.cacheReadInputTokens ? { $ai_cache_read_input_tokens: usage.cacheReadInputTokens } : {},
|
|
616
|
+
...usage.cacheCreationInputTokens ? { $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens } : {},
|
|
617
|
+
...usage.cacheReportingExclusive !== void 0 && !tokensOverridden ? { $ai_cache_reporting_exclusive: usage.cacheReportingExclusive } : {},
|
|
618
|
+
...usage.webSearchCount ? { $ai_web_search_count: usage.webSearchCount } : {},
|
|
619
|
+
...usage.rawUsage ? { $ai_usage: usage.rawUsage } : {}
|
|
620
|
+
};
|
|
621
|
+
const properties = {
|
|
622
|
+
$ai_lib: "posthog-ai",
|
|
623
|
+
$ai_lib_version: version,
|
|
624
|
+
$ai_provider: options.providerOverride ?? options.provider,
|
|
625
|
+
$ai_model: options.modelOverride ?? options.model,
|
|
626
|
+
$ai_model_parameters: options.modelParameters ?? {},
|
|
627
|
+
$ai_input: safeInput,
|
|
628
|
+
$ai_output_choices: safeOutput,
|
|
629
|
+
$ai_http_status: httpStatus,
|
|
630
|
+
...usage.inputTokens !== void 0 ? { $ai_input_tokens: usage.inputTokens } : {},
|
|
631
|
+
...usage.outputTokens !== void 0 ? { $ai_output_tokens: usage.outputTokens } : {},
|
|
632
|
+
...additionalTokenValues,
|
|
633
|
+
...options.latency !== void 0 ? { $ai_latency: options.latency } : {},
|
|
634
|
+
...options.timeToFirstToken !== void 0 ? { $ai_time_to_first_token: options.timeToFirstToken } : {},
|
|
635
|
+
$ai_trace_id: traceId,
|
|
636
|
+
...options.baseURL === null ? {} : { $ai_base_url: options.baseURL ?? "" },
|
|
637
|
+
...options.properties,
|
|
638
|
+
$ai_tokens_source: getTokensSource(options.properties),
|
|
639
|
+
...options.distinctId ? {} : { $process_person_profile: false },
|
|
640
|
+
...options.stopReason ? { $ai_stop_reason: options.stopReason } : {},
|
|
641
|
+
...options.tools ? { $ai_tools: options.tools } : {},
|
|
642
|
+
...options.completionId ? { $ai_completion_id: options.completionId } : {},
|
|
643
|
+
...options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? { $ai_provider_metadata: options.providerMetadata } : {},
|
|
644
|
+
...errorData,
|
|
645
|
+
...costOverrideData
|
|
646
|
+
};
|
|
647
|
+
const event = {
|
|
648
|
+
distinctId: options.distinctId ?? traceId,
|
|
649
|
+
event: eventType,
|
|
650
|
+
properties,
|
|
651
|
+
groups: options.groups
|
|
652
|
+
};
|
|
653
|
+
if (options.captureImmediate) await captureAiEventImmediate(client, event);
|
|
654
|
+
else captureAiEvent(client, event);
|
|
655
|
+
} catch (error) {
|
|
656
|
+
try {
|
|
657
|
+
options.onError?.(error);
|
|
658
|
+
} catch {}
|
|
659
|
+
console.warn("[PostHog AI] Failed to capture generation telemetry:", error);
|
|
660
|
+
}
|
|
853
661
|
};
|
|
854
|
-
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/openai/capture.ts
|
|
855
664
|
/**
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
const INPUT_OR_CACHE_TOKEN_KEYS = [
|
|
865
|
-
|
|
665
|
+
* The declared convention only describes the wrapper's own usage numbers, so
|
|
666
|
+
* when the caller passes any of these through posthogProperties the wrapper no
|
|
667
|
+
* longer knows the convention of the reported counts (callers working around
|
|
668
|
+
* the double-billing already pass exclusive ones) and must not declare it.
|
|
669
|
+
* Output/reasoning token overrides don't affect the input/cache relationship,
|
|
670
|
+
* so they don't suppress the declaration. Subset of the input/cache keys in
|
|
671
|
+
* `TOKEN_PROPERTY_KEYS` (../utils.ts) — keep in sync if that taxonomy grows.
|
|
672
|
+
*/
|
|
673
|
+
const INPUT_OR_CACHE_TOKEN_KEYS = [
|
|
674
|
+
"$ai_input_tokens",
|
|
675
|
+
"$ai_cache_read_input_tokens",
|
|
676
|
+
"$ai_cache_creation_input_tokens"
|
|
677
|
+
];
|
|
866
678
|
/**
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
679
|
+
* OpenAI-compatible usage reports `prompt_tokens` INCLUSIVE of cached tokens
|
|
680
|
+
* (`prompt_tokens_details.cached_tokens` is a subset of it), unlike Anthropic's
|
|
681
|
+
* exclusive convention. Ingestion auto-classifies Claude-shaped models as
|
|
682
|
+
* exclusive regardless of provider, so events for Claude served through
|
|
683
|
+
* OpenAI-compatible hosts (e.g. OpenRouter) would get cache reads billed twice.
|
|
684
|
+
* Declaring the convention on every event from this wrapper lets ingestion
|
|
685
|
+
* normalize correctly (see PostHog/posthog#49252); for non-Claude models the
|
|
686
|
+
* flag is a no-op. Callers can still override it via posthogProperties, and
|
|
687
|
+
* when they pass through input or cache token counts themselves the flag stays
|
|
688
|
+
* unset unless they declare it explicitly.
|
|
689
|
+
*/
|
|
878
690
|
const captureAiGeneration = (client, options) => {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
}
|
|
889
|
-
});
|
|
691
|
+
const props = options.properties;
|
|
692
|
+
const callerReportsTokens = props !== void 0 && INPUT_OR_CACHE_TOKEN_KEYS.some((key) => Object.prototype.hasOwnProperty.call(props, key));
|
|
693
|
+
return captureAiGeneration$1(client, {
|
|
694
|
+
...options,
|
|
695
|
+
properties: callerReportsTokens ? props : {
|
|
696
|
+
$ai_cache_reporting_exclusive: false,
|
|
697
|
+
...props
|
|
698
|
+
}
|
|
699
|
+
});
|
|
890
700
|
};
|
|
891
|
-
|
|
701
|
+
//#endregion
|
|
702
|
+
//#region src/openai/utils.ts
|
|
892
703
|
/**
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
704
|
+
* Checks if a ResponseStreamEvent chunk represents the first token/content from the model.
|
|
705
|
+
* This includes various content types like text, reasoning, audio, and refusals.
|
|
706
|
+
*/
|
|
896
707
|
function isResponseTokenChunk(chunk) {
|
|
897
|
-
|
|
708
|
+
return chunk.type === "response.output_item.added" || chunk.type === "response.content_part.added" || chunk.type === "response.output_text.delta" || chunk.type === "response.reasoning_text.delta" || chunk.type === "response.reasoning_summary_text.delta" || chunk.type === "response.audio.delta" || chunk.type === "response.audio.transcript.delta" || chunk.type === "response.refusal.delta";
|
|
898
709
|
}
|
|
899
|
-
|
|
900
710
|
/**
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
711
|
+
* Reads the OpenAI SDK's `_request_id` field from a response object. The SDK
|
|
712
|
+
* attaches the `x-request-id` response header here, but it is not part of the
|
|
713
|
+
* public response types, so it has to be read through a cast. Used to populate
|
|
714
|
+
* `$ai_provider_metadata.request_id`.
|
|
715
|
+
*/
|
|
906
716
|
function extractRequestId(result) {
|
|
907
|
-
|
|
717
|
+
return result?._request_id ?? void 0;
|
|
908
718
|
}
|
|
909
|
-
|
|
910
719
|
/**
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
720
|
+
* Reads `cache_write_tokens` from a usage details object — Chat Completions'
|
|
721
|
+
* `prompt_tokens_details` or the Responses API's `input_tokens_details`, both of
|
|
722
|
+
* which carry the field — and returns 0 when it is absent. A defensive reader
|
|
723
|
+
* (mirroring `extractRequestId`) that tolerates the loosely-typed usage shapes
|
|
724
|
+
* OpenAI-compatible providers return, used to populate
|
|
725
|
+
* `$ai_cache_creation_input_tokens`.
|
|
726
|
+
*/
|
|
918
727
|
function extractCacheWriteTokens(details) {
|
|
919
|
-
|
|
728
|
+
return details?.cache_write_tokens ?? 0;
|
|
920
729
|
}
|
|
921
|
-
|
|
922
730
|
/**
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
731
|
+
* Assembles the `$ai_provider_metadata` blob for OpenAI / Azure OpenAI events.
|
|
732
|
+
* Provider-specific fields (system fingerprint, request id) live here rather
|
|
733
|
+
* than in the shared, provider-agnostic `$ai_*` namespace. Only keys with a
|
|
734
|
+
* meaningful value are included, and `undefined` is returned when there is nothing
|
|
735
|
+
* to report so the property can be omitted from the event entirely.
|
|
736
|
+
*/
|
|
929
737
|
function buildProviderMetadata(fields) {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
metadata.request_id = fields.requestId;
|
|
936
|
-
}
|
|
937
|
-
if (fields.incompleteDetails != null) {
|
|
938
|
-
metadata.incomplete_details = fields.incompleteDetails;
|
|
939
|
-
}
|
|
940
|
-
return Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
738
|
+
const metadata = {};
|
|
739
|
+
if (fields.systemFingerprint) metadata.system_fingerprint = fields.systemFingerprint;
|
|
740
|
+
if (fields.requestId) metadata.request_id = fields.requestId;
|
|
741
|
+
if (fields.incompleteDetails != null) metadata.incomplete_details = fields.incompleteDetails;
|
|
742
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
941
743
|
}
|
|
942
|
-
const TERMINAL_RESPONSE_STATUSES = new Set([
|
|
943
|
-
|
|
744
|
+
const TERMINAL_RESPONSE_STATUSES = /* @__PURE__ */ new Set([
|
|
745
|
+
"completed",
|
|
746
|
+
"failed",
|
|
747
|
+
"cancelled",
|
|
748
|
+
"incomplete"
|
|
749
|
+
]);
|
|
944
750
|
/**
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
751
|
+
* Checks whether a Responses API response has reached a status that should
|
|
752
|
+
* produce a final `$ai_generation` event.
|
|
753
|
+
*/
|
|
948
754
|
function isTerminalResponse(response) {
|
|
949
|
-
|
|
755
|
+
return !!response?.status && TERMINAL_RESPONSE_STATUSES.has(response.status);
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Maps a Responses API outcome to a `$ai_stop_reason`. An incomplete run is
|
|
759
|
+
* named by what cut it short (`incomplete_details.reason`, e.g.
|
|
760
|
+
* `max_output_tokens`); the other terminal statuses stand for themselves.
|
|
761
|
+
* Non-terminal lifecycle statuses (`queued`, `in_progress`) are not stop
|
|
762
|
+
* reasons, so they yield undefined.
|
|
763
|
+
*/
|
|
764
|
+
function responsesStopReason(response) {
|
|
765
|
+
if (!response || !isTerminalResponse(response)) return;
|
|
766
|
+
if (response.status === "incomplete" && response.incomplete_details?.reason) return response.incomplete_details.reason;
|
|
767
|
+
return response.status ?? void 0;
|
|
950
768
|
}
|
|
951
|
-
|
|
952
769
|
/**
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
770
|
+
* Returns an isolated copy of a failed Responses API error for `$ai_error`, or
|
|
771
|
+
* creates a fallback error when the provider omitted failure details.
|
|
772
|
+
*/
|
|
956
773
|
function getResponseFailure(response) {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
}
|
|
960
|
-
return response.error ? {
|
|
961
|
-
...response.error
|
|
962
|
-
} : new Error(`OpenAI response ${response.id} failed without error details`);
|
|
774
|
+
if (response?.status !== "failed") return;
|
|
775
|
+
return response.error ? { ...response.error } : /* @__PURE__ */ new Error(`OpenAI response ${response.id} failed without error details`);
|
|
963
776
|
}
|
|
964
|
-
|
|
777
|
+
//#endregion
|
|
778
|
+
//#region src/openai/background-responses.ts
|
|
965
779
|
function isPendingBackgroundResponse(params, response) {
|
|
966
|
-
|
|
780
|
+
return params.background === true && !!response.status && !isTerminalResponse(response);
|
|
967
781
|
}
|
|
968
|
-
|
|
969
782
|
/**
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
783
|
+
* Uses provider timestamps so background polling cadence does not inflate
|
|
784
|
+
* generation latency. Non-completed responses do not expose a terminal time.
|
|
785
|
+
*/
|
|
973
786
|
function getBackgroundResponseLatency(response) {
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
}
|
|
977
|
-
return Math.max(0, response.completed_at - response.created_at);
|
|
787
|
+
if (typeof response.created_at !== "number" || typeof response.completed_at !== "number") return;
|
|
788
|
+
return Math.max(0, response.completed_at - response.created_at);
|
|
978
789
|
}
|
|
979
|
-
|
|
980
790
|
/**
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
this.contexts.delete(responseID);
|
|
1009
|
-
}
|
|
1010
|
-
return context;
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
791
|
+
* Keeps the original create context available while a background response is
|
|
792
|
+
* polled. Entries are insertion ordered, so the oldest context is discarded
|
|
793
|
+
* when the bound is reached.
|
|
794
|
+
*/
|
|
795
|
+
var BackgroundResponseTracker = class {
|
|
796
|
+
constructor(maxEntries = 1e3) {
|
|
797
|
+
this.maxEntries = maxEntries;
|
|
798
|
+
this.contexts = /* @__PURE__ */ new Map();
|
|
799
|
+
}
|
|
800
|
+
set(responseID, context) {
|
|
801
|
+
this.contexts.delete(responseID);
|
|
802
|
+
this.contexts.set(responseID, context);
|
|
803
|
+
while (this.contexts.size > this.maxEntries) {
|
|
804
|
+
const oldestResponseID = this.contexts.keys().next().value;
|
|
805
|
+
if (oldestResponseID === void 0) break;
|
|
806
|
+
this.contexts.delete(oldestResponseID);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
get(responseID) {
|
|
810
|
+
return this.contexts.get(responseID);
|
|
811
|
+
}
|
|
812
|
+
take(responseID) {
|
|
813
|
+
const context = this.contexts.get(responseID);
|
|
814
|
+
if (context !== void 0) this.contexts.delete(responseID);
|
|
815
|
+
return context;
|
|
816
|
+
}
|
|
817
|
+
};
|
|
1014
818
|
/**
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
819
|
+
* Inspects a streamed background retrieval without consuming it on the
|
|
820
|
+
* caller's behalf. The stored create context is consumed only by a terminal
|
|
821
|
+
* response; an interrupted or nonterminal stream may be followed by another
|
|
822
|
+
* retrieval while the background job continues.
|
|
823
|
+
*/
|
|
1020
824
|
function wrapBackgroundResponseStream(stream, responseID, tracker, captureTerminalResponse) {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
return new Stream(() => inspectStream(), stream.controller);
|
|
825
|
+
async function* inspectStream() {
|
|
826
|
+
for await (const event of stream) {
|
|
827
|
+
if ("response" in event && isTerminalResponse(event.response)) {
|
|
828
|
+
const context = tracker.take(responseID);
|
|
829
|
+
if (context) captureTerminalResponse(event.response, context).catch(() => void 0);
|
|
830
|
+
}
|
|
831
|
+
yield event;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return new Stream(() => inspectStream(), stream.controller);
|
|
1034
835
|
}
|
|
1035
|
-
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region src/providerPromise.ts
|
|
1036
838
|
function addResponseIds(result, response, requestIdHeader, workspaceIdHeader) {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
properties._workspace_id = {
|
|
1048
|
-
value: response.headers.get(workspaceIdHeader),
|
|
1049
|
-
enumerable: false
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
return Object.defineProperties(result, properties);
|
|
839
|
+
if (!result || typeof result !== "object" || Array.isArray(result)) return result;
|
|
840
|
+
const properties = { _request_id: {
|
|
841
|
+
value: response.headers.get(requestIdHeader),
|
|
842
|
+
enumerable: false
|
|
843
|
+
} };
|
|
844
|
+
if (workspaceIdHeader) properties._workspace_id = {
|
|
845
|
+
value: response.headers.get(workspaceIdHeader),
|
|
846
|
+
enumerable: false
|
|
847
|
+
};
|
|
848
|
+
return Object.defineProperties(result, properties);
|
|
1053
849
|
}
|
|
1054
850
|
function getResponsePropsPromise(parentPromise) {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
}
|
|
1059
|
-
return responsePromise;
|
|
851
|
+
const responsePromise = parentPromise.responsePromise;
|
|
852
|
+
if (!responsePromise || typeof responsePromise.then !== "function") return;
|
|
853
|
+
return responsePromise;
|
|
1060
854
|
}
|
|
1061
855
|
function decorateProviderPromise(wrappedPromise, responsePropsPromise, requestIdHeader, workspaceIdHeader, preserveThenUnwrap) {
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
throw new Error('The provider promise response metadata is unavailable');
|
|
1081
|
-
}
|
|
1082
|
-
const transformedPromise = Promise.all([wrappedPromise, responsePropsPromise]).then(([data, props]) => addResponseIds(transform(data, props), props.response, requestIdHeader, workspaceIdHeader));
|
|
1083
|
-
return decorateProviderPromise(transformedPromise, responsePropsPromise, requestIdHeader, workspaceIdHeader, true);
|
|
1084
|
-
};
|
|
1085
|
-
}
|
|
1086
|
-
return providerPromise;
|
|
856
|
+
const providerPromise = wrappedPromise;
|
|
857
|
+
if (responsePropsPromise) {
|
|
858
|
+
providerPromise.asResponse = async () => (await responsePropsPromise).response;
|
|
859
|
+
providerPromise.withResponse = async () => {
|
|
860
|
+
const [props, data] = await Promise.all([responsePropsPromise, wrappedPromise]);
|
|
861
|
+
return {
|
|
862
|
+
response: props.response,
|
|
863
|
+
data,
|
|
864
|
+
request_id: props.response.headers.get(requestIdHeader),
|
|
865
|
+
...workspaceIdHeader ? { workspace_id: props.response.headers.get(workspaceIdHeader) } : {}
|
|
866
|
+
};
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
if (preserveThenUnwrap) providerPromise._thenUnwrap = (transform) => {
|
|
870
|
+
if (!responsePropsPromise) throw new Error("The provider promise response metadata is unavailable");
|
|
871
|
+
return decorateProviderPromise(Promise.all([wrappedPromise, responsePropsPromise]).then(([data, props]) => addResponseIds(transform(data, props), props.response, requestIdHeader, workspaceIdHeader)), responsePropsPromise, requestIdHeader, workspaceIdHeader, true);
|
|
872
|
+
};
|
|
873
|
+
return providerPromise;
|
|
1087
874
|
}
|
|
1088
|
-
|
|
1089
|
-
/**
|
|
1090
|
-
* Keep the provider SDK helpers on a promise whose resolved value is instrumented.
|
|
1091
|
-
* OpenAI's parse helpers compose create calls through `_thenUnwrap`, while both
|
|
1092
|
-
* OpenAI and Anthropic expose the raw response through `asResponse` and
|
|
1093
|
-
* `withResponse`.
|
|
1094
|
-
*/
|
|
1095
|
-
|
|
1096
875
|
function preserveProviderPromise(parentPromise, wrappedPromise, options = {}) {
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
};
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
return providerPromise;
|
|
876
|
+
const responsePropsPromise = getResponsePropsPromise(parentPromise);
|
|
877
|
+
const preserveThenUnwrap = typeof parentPromise._thenUnwrap === "function";
|
|
878
|
+
const providerPromise = decorateProviderPromise(wrappedPromise, responsePropsPromise, options.requestIdHeader ?? "x-request-id", options.workspaceIdHeader, preserveThenUnwrap);
|
|
879
|
+
if (!responsePropsPromise) {
|
|
880
|
+
const asResponse = parentPromise.asResponse?.bind(parentPromise);
|
|
881
|
+
if (asResponse) providerPromise.asResponse = asResponse;
|
|
882
|
+
const withResponse = parentPromise.withResponse?.bind(parentPromise);
|
|
883
|
+
if (withResponse) providerPromise.withResponse = async () => {
|
|
884
|
+
const [response, data] = await Promise.all([withResponse(), wrappedPromise]);
|
|
885
|
+
return {
|
|
886
|
+
...response,
|
|
887
|
+
data
|
|
888
|
+
};
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
return providerPromise;
|
|
1117
892
|
}
|
|
1118
|
-
|
|
1119
893
|
/**
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
894
|
+
* OpenAI's `Responses.parse` dispatches through `this._client.responses.create`.
|
|
895
|
+
* Temporarily use the provider's original `create` implementation so parsing a
|
|
896
|
+
* wrapped response does not capture the same request twice.
|
|
897
|
+
*/
|
|
1124
898
|
function callWithOriginalCreate(resource, originalCreate, callback) {
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
delete resourceRecord['create'];
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
899
|
+
const resourceRecord = resource;
|
|
900
|
+
const hadOwnCreate = Object.prototype.hasOwnProperty.call(resource, "create");
|
|
901
|
+
const wrappedCreate = resourceRecord["create"];
|
|
902
|
+
resourceRecord["create"] = originalCreate;
|
|
903
|
+
try {
|
|
904
|
+
return callback();
|
|
905
|
+
} finally {
|
|
906
|
+
if (hadOwnCreate) resourceRecord["create"] = wrappedCreate;
|
|
907
|
+
else delete resourceRecord["create"];
|
|
908
|
+
}
|
|
1138
909
|
}
|
|
1139
|
-
|
|
910
|
+
//#endregion
|
|
911
|
+
//#region src/stream.ts
|
|
1140
912
|
/**
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
913
|
+
* Splits an SDK stream into a monitoring branch and a caller branch without
|
|
914
|
+
* allowing either branch to read ahead of the other. Unlike the SDKs' `tee()`
|
|
915
|
+
* implementations, this keeps at most one result in flight and makes caller
|
|
916
|
+
* cancellation terminate the monitoring branch and the source iterator.
|
|
917
|
+
*/
|
|
1146
918
|
function monitoredStreamTee(source, createStream) {
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
reject
|
|
1317
|
-
});
|
|
1318
|
-
pump();
|
|
1319
|
-
});
|
|
1320
|
-
},
|
|
1321
|
-
return: value => cancelSource(value),
|
|
1322
|
-
throw: async error => {
|
|
1323
|
-
if (!sourceIterator.throw) {
|
|
1324
|
-
await cancelSource();
|
|
1325
|
-
throw error;
|
|
1326
|
-
}
|
|
1327
|
-
try {
|
|
1328
|
-
const result = await sourceIterator.throw(error);
|
|
1329
|
-
if (result.done) {
|
|
1330
|
-
terminalResult = result;
|
|
1331
|
-
removeAbortListener();
|
|
1332
|
-
settleCallersTerminal();
|
|
1333
|
-
}
|
|
1334
|
-
if (monitorPending) {
|
|
1335
|
-
monitorPending.resolve(result);
|
|
1336
|
-
monitorPending = undefined;
|
|
1337
|
-
} else {
|
|
1338
|
-
bufferedMonitorResult = result;
|
|
1339
|
-
}
|
|
1340
|
-
return result;
|
|
1341
|
-
} catch (sourceError) {
|
|
1342
|
-
terminalError = sourceError;
|
|
1343
|
-
hasTerminalError = true;
|
|
1344
|
-
removeAbortListener();
|
|
1345
|
-
settleMonitorTerminal();
|
|
1346
|
-
settleCallersTerminal();
|
|
1347
|
-
throw sourceError;
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
}), controller);
|
|
1351
|
-
return [monitoringStream, callerStream];
|
|
919
|
+
const controller = source.controller ?? new AbortController();
|
|
920
|
+
const sourceIterator = source[Symbol.asyncIterator]();
|
|
921
|
+
const callerQueue = [];
|
|
922
|
+
let monitorPending;
|
|
923
|
+
let monitorActive = true;
|
|
924
|
+
let operationInFlight = false;
|
|
925
|
+
let terminalResult;
|
|
926
|
+
let bufferedMonitorResult;
|
|
927
|
+
let terminalError;
|
|
928
|
+
let hasTerminalError = false;
|
|
929
|
+
let cancellationPromise;
|
|
930
|
+
let abortListener;
|
|
931
|
+
const removeAbortListener = () => {
|
|
932
|
+
if (abortListener) {
|
|
933
|
+
controller.signal.removeEventListener("abort", abortListener);
|
|
934
|
+
abortListener = void 0;
|
|
935
|
+
}
|
|
936
|
+
};
|
|
937
|
+
const settleMonitorTerminal = () => {
|
|
938
|
+
if (!monitorPending) return;
|
|
939
|
+
const pending = monitorPending;
|
|
940
|
+
monitorPending = void 0;
|
|
941
|
+
if (hasTerminalError) pending.reject(terminalError);
|
|
942
|
+
else if (terminalResult) pending.resolve(terminalResult);
|
|
943
|
+
};
|
|
944
|
+
const settleCallersTerminal = () => {
|
|
945
|
+
while (callerQueue.length > 0) {
|
|
946
|
+
const pending = callerQueue.shift();
|
|
947
|
+
if (hasTerminalError) pending.reject(terminalError);
|
|
948
|
+
else if (terminalResult) pending.resolve(terminalResult);
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
const pump = () => {
|
|
952
|
+
if (operationInFlight || callerQueue.length === 0 || monitorActive && !monitorPending) return;
|
|
953
|
+
const pendingCaller = callerQueue.shift();
|
|
954
|
+
const pendingMonitor = monitorPending;
|
|
955
|
+
monitorPending = void 0;
|
|
956
|
+
operationInFlight = true;
|
|
957
|
+
sourceIterator.next().then((result) => {
|
|
958
|
+
operationInFlight = false;
|
|
959
|
+
if (result.done) {
|
|
960
|
+
terminalResult = result;
|
|
961
|
+
removeAbortListener();
|
|
962
|
+
}
|
|
963
|
+
pendingCaller.resolve(result);
|
|
964
|
+
pendingMonitor?.resolve(result);
|
|
965
|
+
if (result.done) settleCallersTerminal();
|
|
966
|
+
else pump();
|
|
967
|
+
}, (error) => {
|
|
968
|
+
operationInFlight = false;
|
|
969
|
+
terminalError = error;
|
|
970
|
+
hasTerminalError = true;
|
|
971
|
+
removeAbortListener();
|
|
972
|
+
pendingCaller.reject(error);
|
|
973
|
+
pendingMonitor?.reject(error);
|
|
974
|
+
settleCallersTerminal();
|
|
975
|
+
});
|
|
976
|
+
};
|
|
977
|
+
const monitoringStream = { [Symbol.asyncIterator]() {
|
|
978
|
+
return {
|
|
979
|
+
next: () => {
|
|
980
|
+
if (hasTerminalError) return Promise.reject(terminalError);
|
|
981
|
+
if (terminalResult) return Promise.resolve(terminalResult);
|
|
982
|
+
if (bufferedMonitorResult) {
|
|
983
|
+
const result = bufferedMonitorResult;
|
|
984
|
+
bufferedMonitorResult = void 0;
|
|
985
|
+
return Promise.resolve(result);
|
|
986
|
+
}
|
|
987
|
+
return new Promise((resolve, reject) => {
|
|
988
|
+
monitorPending = {
|
|
989
|
+
resolve,
|
|
990
|
+
reject
|
|
991
|
+
};
|
|
992
|
+
pump();
|
|
993
|
+
});
|
|
994
|
+
},
|
|
995
|
+
return: async (value) => {
|
|
996
|
+
monitorActive = false;
|
|
997
|
+
monitorPending = void 0;
|
|
998
|
+
pump();
|
|
999
|
+
return {
|
|
1000
|
+
done: true,
|
|
1001
|
+
value
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
} };
|
|
1006
|
+
const cancelSource = (value) => {
|
|
1007
|
+
if (cancellationPromise) return cancellationPromise;
|
|
1008
|
+
removeAbortListener();
|
|
1009
|
+
if (!controller.signal.aborted) controller.abort();
|
|
1010
|
+
cancellationPromise = (async () => {
|
|
1011
|
+
try {
|
|
1012
|
+
const defaultResult = {
|
|
1013
|
+
done: true,
|
|
1014
|
+
value
|
|
1015
|
+
};
|
|
1016
|
+
const result = sourceIterator.return ? await sourceIterator.return(value) : defaultResult;
|
|
1017
|
+
if (result.done) {
|
|
1018
|
+
terminalResult = result;
|
|
1019
|
+
removeAbortListener();
|
|
1020
|
+
settleMonitorTerminal();
|
|
1021
|
+
settleCallersTerminal();
|
|
1022
|
+
} else if (monitorPending) {
|
|
1023
|
+
monitorPending.resolve(result);
|
|
1024
|
+
monitorPending = void 0;
|
|
1025
|
+
cancellationPromise = void 0;
|
|
1026
|
+
} else {
|
|
1027
|
+
bufferedMonitorResult = result;
|
|
1028
|
+
cancellationPromise = void 0;
|
|
1029
|
+
}
|
|
1030
|
+
return result;
|
|
1031
|
+
} catch (error) {
|
|
1032
|
+
terminalError = error;
|
|
1033
|
+
hasTerminalError = true;
|
|
1034
|
+
removeAbortListener();
|
|
1035
|
+
settleMonitorTerminal();
|
|
1036
|
+
settleCallersTerminal();
|
|
1037
|
+
throw error;
|
|
1038
|
+
}
|
|
1039
|
+
})();
|
|
1040
|
+
cancellationPromise.catch(() => void 0);
|
|
1041
|
+
return cancellationPromise;
|
|
1042
|
+
};
|
|
1043
|
+
abortListener = () => {
|
|
1044
|
+
cancelSource();
|
|
1045
|
+
};
|
|
1046
|
+
if (controller.signal.aborted) abortListener();
|
|
1047
|
+
else controller.signal.addEventListener("abort", abortListener, { once: true });
|
|
1048
|
+
return [monitoringStream, createStream(() => ({
|
|
1049
|
+
next: () => {
|
|
1050
|
+
if (hasTerminalError) return Promise.reject(terminalError);
|
|
1051
|
+
if (terminalResult) return Promise.resolve(terminalResult);
|
|
1052
|
+
return new Promise((resolve, reject) => {
|
|
1053
|
+
callerQueue.push({
|
|
1054
|
+
resolve,
|
|
1055
|
+
reject
|
|
1056
|
+
});
|
|
1057
|
+
pump();
|
|
1058
|
+
});
|
|
1059
|
+
},
|
|
1060
|
+
return: (value) => cancelSource(value),
|
|
1061
|
+
throw: async (error) => {
|
|
1062
|
+
if (!sourceIterator.throw) {
|
|
1063
|
+
await cancelSource();
|
|
1064
|
+
throw error;
|
|
1065
|
+
}
|
|
1066
|
+
try {
|
|
1067
|
+
const result = await sourceIterator.throw(error);
|
|
1068
|
+
if (result.done) {
|
|
1069
|
+
terminalResult = result;
|
|
1070
|
+
removeAbortListener();
|
|
1071
|
+
settleCallersTerminal();
|
|
1072
|
+
}
|
|
1073
|
+
if (monitorPending) {
|
|
1074
|
+
monitorPending.resolve(result);
|
|
1075
|
+
monitorPending = void 0;
|
|
1076
|
+
} else bufferedMonitorResult = result;
|
|
1077
|
+
return result;
|
|
1078
|
+
} catch (sourceError) {
|
|
1079
|
+
terminalError = sourceError;
|
|
1080
|
+
hasTerminalError = true;
|
|
1081
|
+
removeAbortListener();
|
|
1082
|
+
settleMonitorTerminal();
|
|
1083
|
+
settleCallersTerminal();
|
|
1084
|
+
throw sourceError;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}), controller)];
|
|
1352
1088
|
}
|
|
1353
|
-
|
|
1089
|
+
//#endregion
|
|
1090
|
+
//#region src/openai/stream-accumulators.ts
|
|
1354
1091
|
/** Pure state accumulator for OpenAI-compatible Chat Completions chunks. */
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
}
|
|
1432
|
-
});
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
return {
|
|
1436
|
-
output: [{
|
|
1437
|
-
role: 'assistant',
|
|
1438
|
-
content: content.length > 0 ? content : [{
|
|
1439
|
-
type: 'text',
|
|
1440
|
-
text: ''
|
|
1441
|
-
}]
|
|
1442
|
-
}],
|
|
1443
|
-
model: this.model,
|
|
1444
|
-
completionId: this.completionId,
|
|
1445
|
-
systemFingerprint: this.systemFingerprint,
|
|
1446
|
-
serviceTier: this.serviceTier,
|
|
1447
|
-
firstTokenTime: this.firstTokenTime,
|
|
1448
|
-
stopReason: this.stopReason,
|
|
1449
|
-
usage: {
|
|
1450
|
-
...this.usage
|
|
1451
|
-
}
|
|
1452
|
-
};
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1092
|
+
var OpenAIChatStreamAccumulator = class {
|
|
1093
|
+
constructor() {
|
|
1094
|
+
this.accumulatedContent = "";
|
|
1095
|
+
this.usage = { webSearchCount: 0 };
|
|
1096
|
+
this.toolCalls = /* @__PURE__ */ new Map();
|
|
1097
|
+
}
|
|
1098
|
+
consume(chunk, receivedAt = Date.now()) {
|
|
1099
|
+
this.model ||= chunk.model || void 0;
|
|
1100
|
+
this.completionId ||= chunk.id || void 0;
|
|
1101
|
+
this.systemFingerprint ||= chunk.system_fingerprint || void 0;
|
|
1102
|
+
if (chunk.service_tier != null) this.serviceTier = chunk.service_tier;
|
|
1103
|
+
const choice = chunk.choices?.[0];
|
|
1104
|
+
if (choice?.finish_reason) this.stopReason = choice.finish_reason;
|
|
1105
|
+
const webSearchCount = calculateWebSearchCount(chunk);
|
|
1106
|
+
if (webSearchCount > (this.usage.webSearchCount ?? 0)) this.usage.webSearchCount = webSearchCount;
|
|
1107
|
+
if (choice?.delta?.content) {
|
|
1108
|
+
this.firstTokenTime ??= receivedAt;
|
|
1109
|
+
this.accumulatedContent += choice.delta.content;
|
|
1110
|
+
}
|
|
1111
|
+
if (Array.isArray(choice?.delta?.tool_calls)) {
|
|
1112
|
+
this.firstTokenTime ??= receivedAt;
|
|
1113
|
+
for (const toolCall of choice.delta.tool_calls) {
|
|
1114
|
+
if (toolCall.index === void 0) continue;
|
|
1115
|
+
const current = this.toolCalls.get(toolCall.index) ?? {
|
|
1116
|
+
id: "",
|
|
1117
|
+
name: "",
|
|
1118
|
+
arguments: ""
|
|
1119
|
+
};
|
|
1120
|
+
if (toolCall.id) current.id = toolCall.id;
|
|
1121
|
+
if (toolCall.function?.name) current.name = toolCall.function.name;
|
|
1122
|
+
if (toolCall.function?.arguments) current.arguments += toolCall.function.arguments;
|
|
1123
|
+
this.toolCalls.set(toolCall.index, current);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
if (chunk.usage) this.usage = {
|
|
1127
|
+
...this.usage,
|
|
1128
|
+
inputTokens: chunk.usage.prompt_tokens ?? 0,
|
|
1129
|
+
outputTokens: chunk.usage.completion_tokens ?? 0,
|
|
1130
|
+
reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? 0,
|
|
1131
|
+
cacheReadInputTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0,
|
|
1132
|
+
cacheCreationInputTokens: extractCacheWriteTokens(chunk.usage.prompt_tokens_details),
|
|
1133
|
+
rawUsage: chunk.usage
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
result() {
|
|
1137
|
+
const content = [];
|
|
1138
|
+
if (this.accumulatedContent) content.push({
|
|
1139
|
+
type: "text",
|
|
1140
|
+
text: this.accumulatedContent
|
|
1141
|
+
});
|
|
1142
|
+
for (const toolCall of this.toolCalls.values()) if (toolCall.name) content.push({
|
|
1143
|
+
type: "function",
|
|
1144
|
+
id: toolCall.id,
|
|
1145
|
+
function: {
|
|
1146
|
+
name: toolCall.name,
|
|
1147
|
+
arguments: toolCall.arguments
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
return {
|
|
1151
|
+
output: [{
|
|
1152
|
+
role: "assistant",
|
|
1153
|
+
content: content.length > 0 ? content : [{
|
|
1154
|
+
type: "text",
|
|
1155
|
+
text: ""
|
|
1156
|
+
}]
|
|
1157
|
+
}],
|
|
1158
|
+
model: this.model,
|
|
1159
|
+
completionId: this.completionId,
|
|
1160
|
+
systemFingerprint: this.systemFingerprint,
|
|
1161
|
+
serviceTier: this.serviceTier,
|
|
1162
|
+
firstTokenTime: this.firstTokenTime,
|
|
1163
|
+
stopReason: this.stopReason,
|
|
1164
|
+
usage: { ...this.usage }
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1455
1168
|
/** Pure state accumulator for OpenAI-compatible Responses stream events. */
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
serviceTier: this.serviceTier,
|
|
1501
|
-
firstTokenTime: this.firstTokenTime,
|
|
1502
|
-
stopReason: this.stopReason,
|
|
1503
|
-
usage: {
|
|
1504
|
-
...this.usage
|
|
1505
|
-
},
|
|
1506
|
-
terminalResponse: this.terminalResponse
|
|
1507
|
-
};
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
|
|
1169
|
+
var OpenAIResponsesStreamAccumulator = class {
|
|
1170
|
+
constructor() {
|
|
1171
|
+
this.output = [];
|
|
1172
|
+
this.usage = { webSearchCount: 0 };
|
|
1173
|
+
}
|
|
1174
|
+
consume(event, receivedAt = Date.now()) {
|
|
1175
|
+
if (this.firstTokenTime === void 0 && isResponseTokenChunk(event)) this.firstTokenTime = receivedAt;
|
|
1176
|
+
if (!("response" in event) || !event.response) return;
|
|
1177
|
+
const response = event.response;
|
|
1178
|
+
this.model ||= response.model || void 0;
|
|
1179
|
+
this.completionId ||= response.id || void 0;
|
|
1180
|
+
if (response.service_tier != null) this.serviceTier = response.service_tier;
|
|
1181
|
+
const webSearchCount = calculateWebSearchCount(response);
|
|
1182
|
+
if (webSearchCount > (this.usage.webSearchCount ?? 0)) this.usage.webSearchCount = webSearchCount;
|
|
1183
|
+
if (response.usage) this.usage = {
|
|
1184
|
+
...this.usage,
|
|
1185
|
+
inputTokens: response.usage.input_tokens ?? 0,
|
|
1186
|
+
outputTokens: response.usage.output_tokens ?? 0,
|
|
1187
|
+
reasoningTokens: response.usage.output_tokens_details?.reasoning_tokens ?? 0,
|
|
1188
|
+
cacheReadInputTokens: response.usage.input_tokens_details?.cached_tokens ?? 0,
|
|
1189
|
+
cacheCreationInputTokens: extractCacheWriteTokens(response.usage.input_tokens_details),
|
|
1190
|
+
rawUsage: response.usage
|
|
1191
|
+
};
|
|
1192
|
+
if (isTerminalResponse(response)) {
|
|
1193
|
+
this.terminalResponse = response;
|
|
1194
|
+
this.output = response.output ?? [];
|
|
1195
|
+
this.stopReason = responsesStopReason(response);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
result() {
|
|
1199
|
+
return {
|
|
1200
|
+
output: [...this.output],
|
|
1201
|
+
model: this.model,
|
|
1202
|
+
completionId: this.completionId,
|
|
1203
|
+
serviceTier: this.serviceTier,
|
|
1204
|
+
firstTokenTime: this.firstTokenTime,
|
|
1205
|
+
stopReason: this.stopReason,
|
|
1206
|
+
usage: { ...this.usage },
|
|
1207
|
+
terminalResponse: this.terminalResponse
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
};
|
|
1211
|
+
//#endregion
|
|
1212
|
+
//#region src/openai/telemetry.ts
|
|
1511
1213
|
function captureAiGenerationInBackground(...args) {
|
|
1512
|
-
|
|
1214
|
+
captureAiGeneration(...args).catch(() => void 0);
|
|
1513
1215
|
}
|
|
1514
|
-
|
|
1515
1216
|
/** Preserve immediate delivery while isolating normal telemetry from provider latency/failures. */
|
|
1516
1217
|
async function captureAiGenerationAfterSuccess(...args) {
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
} else {
|
|
1520
|
-
captureAiGenerationInBackground(...args);
|
|
1521
|
-
}
|
|
1218
|
+
if (args[1].captureImmediate) await captureAiGeneration(...args);
|
|
1219
|
+
else captureAiGenerationInBackground(...args);
|
|
1522
1220
|
}
|
|
1523
1221
|
function buildChatUsage(usage, webSearchSource) {
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1222
|
+
return {
|
|
1223
|
+
inputTokens: usage?.prompt_tokens ?? 0,
|
|
1224
|
+
outputTokens: usage?.completion_tokens ?? 0,
|
|
1225
|
+
reasoningTokens: usage?.completion_tokens_details?.reasoning_tokens ?? 0,
|
|
1226
|
+
cacheReadInputTokens: usage?.prompt_tokens_details?.cached_tokens ?? 0,
|
|
1227
|
+
cacheCreationInputTokens: extractCacheWriteTokens(usage?.prompt_tokens_details),
|
|
1228
|
+
webSearchCount: calculateWebSearchCount(webSearchSource),
|
|
1229
|
+
rawUsage: usage
|
|
1230
|
+
};
|
|
1533
1231
|
}
|
|
1534
1232
|
function buildResponsesUsage(usage, webSearchSource) {
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1233
|
+
return {
|
|
1234
|
+
inputTokens: usage?.input_tokens ?? 0,
|
|
1235
|
+
outputTokens: usage?.output_tokens ?? 0,
|
|
1236
|
+
reasoningTokens: usage?.output_tokens_details?.reasoning_tokens ?? 0,
|
|
1237
|
+
cacheReadInputTokens: usage?.input_tokens_details?.cached_tokens ?? 0,
|
|
1238
|
+
cacheCreationInputTokens: extractCacheWriteTokens(usage?.input_tokens_details),
|
|
1239
|
+
webSearchCount: calculateWebSearchCount(webSearchSource),
|
|
1240
|
+
rawUsage: usage
|
|
1241
|
+
};
|
|
1544
1242
|
}
|
|
1545
1243
|
function buildChatSuccessOptions(context, result) {
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1244
|
+
return {
|
|
1245
|
+
...context.monitoring,
|
|
1246
|
+
model: context.params.model ?? result.model,
|
|
1247
|
+
provider: context.provider,
|
|
1248
|
+
input: sanitizeOpenAI(context.params.messages, context.client),
|
|
1249
|
+
output: sanitizeOpenAIResponse(result.output, context.client),
|
|
1250
|
+
latency: result.latency,
|
|
1251
|
+
timeToFirstToken: result.timeToFirstToken,
|
|
1252
|
+
baseURL: context.baseURL,
|
|
1253
|
+
modelParameters: getModelParams(context.modelParametersSource, result.serviceTier),
|
|
1254
|
+
httpStatus: 200,
|
|
1255
|
+
usage: result.usage,
|
|
1256
|
+
stopReason: result.stopReason,
|
|
1257
|
+
tools: extractAvailableToolCalls("openai", context.params),
|
|
1258
|
+
completionId: result.completionId,
|
|
1259
|
+
providerMetadata: buildProviderMetadata({
|
|
1260
|
+
systemFingerprint: result.systemFingerprint,
|
|
1261
|
+
requestId: result.requestId
|
|
1262
|
+
})
|
|
1263
|
+
};
|
|
1566
1264
|
}
|
|
1567
1265
|
function buildChatErrorOptions(context, error, metadata) {
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
error
|
|
1583
|
-
};
|
|
1266
|
+
return {
|
|
1267
|
+
...context.monitoring,
|
|
1268
|
+
model: context.params.model,
|
|
1269
|
+
provider: context.provider,
|
|
1270
|
+
input: sanitizeOpenAI(context.params.messages, context.client),
|
|
1271
|
+
output: [],
|
|
1272
|
+
latency: metadata.latency,
|
|
1273
|
+
baseURL: context.baseURL,
|
|
1274
|
+
modelParameters: getModelParams(context.modelParametersSource),
|
|
1275
|
+
usage: metadata.usage ?? {},
|
|
1276
|
+
completionId: metadata.completionId,
|
|
1277
|
+
providerMetadata: buildProviderMetadata({ systemFingerprint: metadata.systemFingerprint }),
|
|
1278
|
+
error
|
|
1279
|
+
};
|
|
1584
1280
|
}
|
|
1585
1281
|
function buildSanitizedResponsesInput(context) {
|
|
1586
|
-
|
|
1282
|
+
return formatOpenAIResponsesInput(sanitizeOpenAIResponse(context.params.input, context.client), sanitizeOpenAIResponse(context.params.instructions, context.client));
|
|
1587
1283
|
}
|
|
1588
1284
|
function buildResponsesSuccessOptions(context, result) {
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1285
|
+
const response = result.response;
|
|
1286
|
+
return {
|
|
1287
|
+
...context.monitoring,
|
|
1288
|
+
model: context.params.model ?? response.model,
|
|
1289
|
+
provider: context.provider,
|
|
1290
|
+
input: buildSanitizedResponsesInput(context),
|
|
1291
|
+
output: sanitizeOpenAIResponse(result.output, context.client),
|
|
1292
|
+
latency: result.latency,
|
|
1293
|
+
timeToFirstToken: result.timeToFirstToken,
|
|
1294
|
+
baseURL: context.baseURL,
|
|
1295
|
+
modelParameters: getModelParams(context.modelParametersSource, response.service_tier),
|
|
1296
|
+
httpStatus: 200,
|
|
1297
|
+
usage: result.usage ?? buildResponsesUsage(response.usage, response),
|
|
1298
|
+
stopReason: responsesStopReason(response),
|
|
1299
|
+
tools: result.includeTools ? extractAvailableToolCalls("openai", context.params) : void 0,
|
|
1300
|
+
completionId: response.id,
|
|
1301
|
+
providerMetadata: buildProviderMetadata({
|
|
1302
|
+
requestId: result.includeRequestId ? extractRequestId(response) : void 0,
|
|
1303
|
+
incompleteDetails: response.incomplete_details
|
|
1304
|
+
}),
|
|
1305
|
+
error: getResponseFailure({
|
|
1306
|
+
id: response.id,
|
|
1307
|
+
status: response.status,
|
|
1308
|
+
error: response.error ?? null
|
|
1309
|
+
})
|
|
1310
|
+
};
|
|
1615
1311
|
}
|
|
1616
1312
|
function buildBackgroundResponseOptions(context, response) {
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
includeRequestId: true
|
|
1625
|
-
});
|
|
1313
|
+
return buildResponsesSuccessOptions(context, {
|
|
1314
|
+
response,
|
|
1315
|
+
output: formatResponseOpenAI({ output: response.output }),
|
|
1316
|
+
latency: getBackgroundResponseLatency(response),
|
|
1317
|
+
includeTools: true,
|
|
1318
|
+
includeRequestId: true
|
|
1319
|
+
});
|
|
1626
1320
|
}
|
|
1627
1321
|
function buildResponsesErrorOptions(context, error, metadata) {
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1322
|
+
return {
|
|
1323
|
+
...context.monitoring,
|
|
1324
|
+
model: context.params.model,
|
|
1325
|
+
provider: context.provider,
|
|
1326
|
+
input: buildSanitizedResponsesInput(context),
|
|
1327
|
+
output: [],
|
|
1328
|
+
latency: metadata.latency,
|
|
1329
|
+
baseURL: context.baseURL,
|
|
1330
|
+
modelParameters: getModelParams(context.modelParametersSource),
|
|
1331
|
+
usage: metadata.usage ?? {},
|
|
1332
|
+
completionId: metadata.completionId,
|
|
1333
|
+
error
|
|
1334
|
+
};
|
|
1641
1335
|
}
|
|
1642
1336
|
function buildEmbeddingSuccessOptions(context, usage, latency) {
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1337
|
+
return {
|
|
1338
|
+
eventType: "$ai_embedding",
|
|
1339
|
+
...context.monitoring,
|
|
1340
|
+
model: context.params.model,
|
|
1341
|
+
provider: context.provider,
|
|
1342
|
+
input: withPrivacyMode(context.client, context.monitoring.privacyMode, context.params.input),
|
|
1343
|
+
output: null,
|
|
1344
|
+
latency,
|
|
1345
|
+
baseURL: context.baseURL,
|
|
1346
|
+
modelParameters: getModelParams(context.modelParametersSource),
|
|
1347
|
+
httpStatus: 200,
|
|
1348
|
+
usage: {
|
|
1349
|
+
inputTokens: usage?.prompt_tokens ?? 0,
|
|
1350
|
+
rawUsage: usage
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1659
1353
|
}
|
|
1660
1354
|
function buildEmbeddingErrorOptions(context, error, latency) {
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
}
|
|
1675
|
-
|
|
1676
|
-
class PostHogAzureOpenAI extends AzureOpenAI {
|
|
1677
|
-
constructor(config) {
|
|
1678
|
-
const {
|
|
1679
|
-
posthog,
|
|
1680
|
-
...openAIConfig
|
|
1681
|
-
} = config;
|
|
1682
|
-
super(openAIConfig);
|
|
1683
|
-
this.phClient = posthog;
|
|
1684
|
-
this.chat = new WrappedChat$1(this, this.phClient);
|
|
1685
|
-
this.responses = new WrappedResponses$1(this, this.phClient);
|
|
1686
|
-
this.embeddings = new WrappedEmbeddings$1(this, this.phClient);
|
|
1687
|
-
}
|
|
1355
|
+
return {
|
|
1356
|
+
eventType: "$ai_embedding",
|
|
1357
|
+
...context.monitoring,
|
|
1358
|
+
model: context.params.model,
|
|
1359
|
+
provider: context.provider,
|
|
1360
|
+
input: withPrivacyMode(context.client, context.monitoring.privacyMode, context.params.input),
|
|
1361
|
+
output: null,
|
|
1362
|
+
latency,
|
|
1363
|
+
baseURL: context.baseURL,
|
|
1364
|
+
modelParameters: getModelParams(context.modelParametersSource),
|
|
1365
|
+
usage: {},
|
|
1366
|
+
error
|
|
1367
|
+
};
|
|
1688
1368
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1369
|
+
//#endregion
|
|
1370
|
+
//#region src/openai/azure.ts
|
|
1371
|
+
var PostHogAzureOpenAI = class extends AzureOpenAI {
|
|
1372
|
+
constructor(config) {
|
|
1373
|
+
const { posthog, ...openAIConfig } = config;
|
|
1374
|
+
super(openAIConfig);
|
|
1375
|
+
this.phClient = posthog;
|
|
1376
|
+
this.chat = new WrappedChat$1(this, this.phClient);
|
|
1377
|
+
this.responses = new WrappedResponses$1(this, this.phClient);
|
|
1378
|
+
this.embeddings = new WrappedEmbeddings$1(this, this.phClient);
|
|
1379
|
+
}
|
|
1694
1380
|
};
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
}
|
|
1701
|
-
|
|
1702
|
-
// --- Overload #1: Non-streaming
|
|
1703
|
-
|
|
1704
|
-
// --- Overload #2: Streaming
|
|
1705
|
-
|
|
1706
|
-
// --- Overload #3: Generic base
|
|
1707
|
-
|
|
1708
|
-
// --- Implementation Signature
|
|
1709
|
-
create(body, options) {
|
|
1710
|
-
const {
|
|
1711
|
-
providerParams: openAIParams,
|
|
1712
|
-
posthogParams
|
|
1713
|
-
} = extractPosthogParams(body);
|
|
1714
|
-
const startTime = Date.now();
|
|
1715
|
-
const parentPromise = super.create(openAIParams, options);
|
|
1716
|
-
if (openAIParams.stream) {
|
|
1717
|
-
const wrappedPromise = parentPromise.then(value => {
|
|
1718
|
-
if (Symbol.asyncIterator in value) {
|
|
1719
|
-
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
1720
|
-
(async () => {
|
|
1721
|
-
const accumulator = new OpenAIChatStreamAccumulator();
|
|
1722
|
-
try {
|
|
1723
|
-
for await (const chunk of stream1) {
|
|
1724
|
-
accumulator.consume(chunk);
|
|
1725
|
-
}
|
|
1726
|
-
const accumulated = accumulator.result();
|
|
1727
|
-
await captureAiGeneration(this.phClient, buildChatSuccessOptions({
|
|
1728
|
-
client: this.phClient,
|
|
1729
|
-
provider: 'azure',
|
|
1730
|
-
baseURL: this.baseURL,
|
|
1731
|
-
params: openAIParams,
|
|
1732
|
-
monitoring: posthogParams,
|
|
1733
|
-
modelParametersSource: body
|
|
1734
|
-
}, {
|
|
1735
|
-
...accumulated,
|
|
1736
|
-
latency: (Date.now() - startTime) / 1000,
|
|
1737
|
-
timeToFirstToken: accumulated.firstTokenTime === undefined ? undefined : (accumulated.firstTokenTime - startTime) / 1000
|
|
1738
|
-
}));
|
|
1739
|
-
} catch (error) {
|
|
1740
|
-
const accumulated = accumulator.result();
|
|
1741
|
-
await captureAiGeneration(this.phClient, buildChatErrorOptions({
|
|
1742
|
-
client: this.phClient,
|
|
1743
|
-
provider: 'azure',
|
|
1744
|
-
baseURL: this.baseURL,
|
|
1745
|
-
params: openAIParams,
|
|
1746
|
-
monitoring: posthogParams,
|
|
1747
|
-
modelParametersSource: body
|
|
1748
|
-
}, error, {
|
|
1749
|
-
completionId: accumulated.completionId,
|
|
1750
|
-
systemFingerprint: accumulated.systemFingerprint,
|
|
1751
|
-
usage: accumulated.usage,
|
|
1752
|
-
latency: (Date.now() - startTime) / 1000
|
|
1753
|
-
}));
|
|
1754
|
-
throw error;
|
|
1755
|
-
}
|
|
1756
|
-
})().catch(() => {
|
|
1757
|
-
// Swallow: analytics must never crash the host process. The caller
|
|
1758
|
-
// already receives this error via their own tee of the stream.
|
|
1759
|
-
});
|
|
1760
|
-
|
|
1761
|
-
// Return the other stream to the user
|
|
1762
|
-
return stream2;
|
|
1763
|
-
}
|
|
1764
|
-
return value;
|
|
1765
|
-
});
|
|
1766
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
1767
|
-
} else {
|
|
1768
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
1769
|
-
if ('choices' in result) {
|
|
1770
|
-
await captureAiGenerationAfterSuccess(this.phClient, buildChatSuccessOptions({
|
|
1771
|
-
client: this.phClient,
|
|
1772
|
-
provider: 'azure',
|
|
1773
|
-
baseURL: this.baseURL,
|
|
1774
|
-
params: openAIParams,
|
|
1775
|
-
monitoring: posthogParams,
|
|
1776
|
-
modelParametersSource: body
|
|
1777
|
-
}, {
|
|
1778
|
-
output: formatResponseOpenAI(result),
|
|
1779
|
-
model: result.model,
|
|
1780
|
-
serviceTier: result.service_tier ?? undefined,
|
|
1781
|
-
latency: (Date.now() - startTime) / 1000,
|
|
1782
|
-
usage: buildChatUsage(result.usage, result),
|
|
1783
|
-
stopReason: result.choices[0]?.finish_reason ?? undefined,
|
|
1784
|
-
completionId: result.id,
|
|
1785
|
-
systemFingerprint: result.system_fingerprint,
|
|
1786
|
-
requestId: result._request_id
|
|
1787
|
-
}));
|
|
1788
|
-
}
|
|
1789
|
-
return result;
|
|
1790
|
-
}, async error => {
|
|
1791
|
-
await captureAiGeneration(this.phClient, buildChatErrorOptions({
|
|
1792
|
-
client: this.phClient,
|
|
1793
|
-
provider: 'azure',
|
|
1794
|
-
baseURL: this.baseURL,
|
|
1795
|
-
params: openAIParams,
|
|
1796
|
-
monitoring: posthogParams,
|
|
1797
|
-
modelParametersSource: body
|
|
1798
|
-
}, error, {
|
|
1799
|
-
latency: (Date.now() - startTime) / 1000
|
|
1800
|
-
}));
|
|
1801
|
-
throw error;
|
|
1802
|
-
});
|
|
1803
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1381
|
+
var WrappedChat$1 = class extends AzureOpenAI.Chat {
|
|
1382
|
+
constructor(parentClient, phClient) {
|
|
1383
|
+
super(parentClient);
|
|
1384
|
+
this.completions = new WrappedCompletions$1(parentClient, phClient);
|
|
1385
|
+
}
|
|
1806
1386
|
};
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
const accumulated = accumulator.result();
|
|
1892
|
-
if (openAIParams.background === true && accumulated.completionId && this.backgroundResponses.get(accumulated.completionId)) {
|
|
1893
|
-
throw error;
|
|
1894
|
-
}
|
|
1895
|
-
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1896
|
-
client: this.phClient,
|
|
1897
|
-
provider: 'azure',
|
|
1898
|
-
baseURL: this.baseURL,
|
|
1899
|
-
params: openAIParams,
|
|
1900
|
-
monitoring: posthogParams,
|
|
1901
|
-
modelParametersSource: body
|
|
1902
|
-
}, error, {
|
|
1903
|
-
completionId: accumulated.completionId,
|
|
1904
|
-
usage: accumulated.usage,
|
|
1905
|
-
latency: (Date.now() - startTime) / 1000
|
|
1906
|
-
}));
|
|
1907
|
-
throw error;
|
|
1908
|
-
}
|
|
1909
|
-
})().catch(() => {
|
|
1910
|
-
// Swallow: analytics must never crash the host process. The caller
|
|
1911
|
-
// already receives this error via their own tee of the stream.
|
|
1912
|
-
});
|
|
1913
|
-
return stream2;
|
|
1914
|
-
}
|
|
1915
|
-
return value;
|
|
1916
|
-
});
|
|
1917
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
1918
|
-
} else {
|
|
1919
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
1920
|
-
if ('output' in result) {
|
|
1921
|
-
if (isPendingBackgroundResponse(openAIParams, result)) {
|
|
1922
|
-
this.backgroundResponses.set(result.id, {
|
|
1923
|
-
openAIParams,
|
|
1924
|
-
posthogParams
|
|
1925
|
-
});
|
|
1926
|
-
return result;
|
|
1927
|
-
}
|
|
1928
|
-
await captureAiGenerationAfterSuccess(this.phClient, buildResponsesSuccessOptions({
|
|
1929
|
-
client: this.phClient,
|
|
1930
|
-
provider: 'azure',
|
|
1931
|
-
baseURL: this.baseURL,
|
|
1932
|
-
params: openAIParams,
|
|
1933
|
-
monitoring: posthogParams,
|
|
1934
|
-
modelParametersSource: body
|
|
1935
|
-
}, {
|
|
1936
|
-
response: result,
|
|
1937
|
-
output: formatResponseOpenAI({
|
|
1938
|
-
output: result.output
|
|
1939
|
-
}),
|
|
1940
|
-
latency: (Date.now() - startTime) / 1000,
|
|
1941
|
-
includeTools: true,
|
|
1942
|
-
includeRequestId: true
|
|
1943
|
-
}));
|
|
1944
|
-
}
|
|
1945
|
-
return result;
|
|
1946
|
-
}, async error => {
|
|
1947
|
-
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1948
|
-
client: this.phClient,
|
|
1949
|
-
provider: 'azure',
|
|
1950
|
-
baseURL: this.baseURL,
|
|
1951
|
-
params: openAIParams,
|
|
1952
|
-
monitoring: posthogParams,
|
|
1953
|
-
modelParametersSource: body
|
|
1954
|
-
}, error, {
|
|
1955
|
-
latency: (Date.now() - startTime) / 1000
|
|
1956
|
-
}));
|
|
1957
|
-
throw error;
|
|
1958
|
-
});
|
|
1959
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
1960
|
-
}
|
|
1961
|
-
}
|
|
1962
|
-
retrieve(responseID, query = {}, options) {
|
|
1963
|
-
const parentPromise = super.retrieve(responseID, query, options);
|
|
1964
|
-
|
|
1965
|
-
// Preserve the upstream promise and stream unchanged for responses that
|
|
1966
|
-
// were not created through this client.
|
|
1967
|
-
if (!this.backgroundResponses.get(responseID)) {
|
|
1968
|
-
return parentPromise;
|
|
1969
|
-
}
|
|
1970
|
-
if (query.stream) {
|
|
1971
|
-
return parentPromise._thenUnwrap(result => {
|
|
1972
|
-
if ('controller' in result) {
|
|
1973
|
-
return wrapBackgroundResponseStream(result, responseID, this.backgroundResponses, (response, context) => this.captureBackgroundResponse(response, context));
|
|
1974
|
-
}
|
|
1975
|
-
return result;
|
|
1976
|
-
});
|
|
1977
|
-
}
|
|
1978
|
-
return parentPromise._thenUnwrap(async result => {
|
|
1979
|
-
if (!('output' in result) || !isTerminalResponse(result)) {
|
|
1980
|
-
return result;
|
|
1981
|
-
}
|
|
1982
|
-
|
|
1983
|
-
// Removing the context before capture makes concurrent or repeated
|
|
1984
|
-
// terminal polls idempotent.
|
|
1985
|
-
const context = this.backgroundResponses.take(responseID);
|
|
1986
|
-
if (context) {
|
|
1987
|
-
await this.captureBackgroundResponse(result, context).catch(() => undefined);
|
|
1988
|
-
}
|
|
1989
|
-
return result;
|
|
1990
|
-
});
|
|
1991
|
-
}
|
|
1992
|
-
cancel(responseID, options) {
|
|
1993
|
-
const parentPromise = super.cancel(responseID, options);
|
|
1994
|
-
|
|
1995
|
-
// Avoid wrapping calls that do not belong to a background response created
|
|
1996
|
-
// through this client, preserving the upstream APIPromise unchanged.
|
|
1997
|
-
if (!this.backgroundResponses.get(responseID)) {
|
|
1998
|
-
return parentPromise;
|
|
1999
|
-
}
|
|
2000
|
-
return parentPromise._thenUnwrap(async result => {
|
|
2001
|
-
if (!isTerminalResponse(result)) {
|
|
2002
|
-
return result;
|
|
2003
|
-
}
|
|
2004
|
-
const context = this.backgroundResponses.take(responseID);
|
|
2005
|
-
if (context) {
|
|
2006
|
-
await this.captureBackgroundResponse(result, context).catch(() => undefined);
|
|
2007
|
-
}
|
|
2008
|
-
return result;
|
|
2009
|
-
});
|
|
2010
|
-
}
|
|
2011
|
-
parse(body, options) {
|
|
2012
|
-
const {
|
|
2013
|
-
providerParams: openAIParams,
|
|
2014
|
-
posthogParams
|
|
2015
|
-
} = extractPosthogParams(body);
|
|
2016
|
-
const startTime = Date.now();
|
|
2017
|
-
const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
|
|
2018
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
2019
|
-
if (isPendingBackgroundResponse(openAIParams, result)) {
|
|
2020
|
-
this.backgroundResponses.set(result.id, {
|
|
2021
|
-
openAIParams,
|
|
2022
|
-
posthogParams
|
|
2023
|
-
});
|
|
2024
|
-
return result;
|
|
2025
|
-
}
|
|
2026
|
-
await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
|
|
2027
|
-
client: this.phClient,
|
|
2028
|
-
provider: 'azure',
|
|
2029
|
-
baseURL: this.baseURL,
|
|
2030
|
-
params: openAIParams,
|
|
2031
|
-
monitoring: posthogParams,
|
|
2032
|
-
modelParametersSource: body
|
|
2033
|
-
}, {
|
|
2034
|
-
response: result,
|
|
2035
|
-
output: result.output,
|
|
2036
|
-
latency: (Date.now() - startTime) / 1000,
|
|
2037
|
-
includeRequestId: true
|
|
2038
|
-
}));
|
|
2039
|
-
return result;
|
|
2040
|
-
}, async error => {
|
|
2041
|
-
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
2042
|
-
client: this.phClient,
|
|
2043
|
-
provider: 'azure',
|
|
2044
|
-
baseURL: this.baseURL,
|
|
2045
|
-
params: openAIParams,
|
|
2046
|
-
monitoring: posthogParams,
|
|
2047
|
-
modelParametersSource: body
|
|
2048
|
-
}, error, {
|
|
2049
|
-
latency: (Date.now() - startTime) / 1000
|
|
2050
|
-
}));
|
|
2051
|
-
throw error;
|
|
2052
|
-
});
|
|
2053
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
2054
|
-
}
|
|
1387
|
+
var WrappedCompletions$1 = class extends AzureOpenAI.Chat.Completions {
|
|
1388
|
+
constructor(client, phClient) {
|
|
1389
|
+
super(client);
|
|
1390
|
+
this.phClient = phClient;
|
|
1391
|
+
this.baseURL = client.baseURL;
|
|
1392
|
+
}
|
|
1393
|
+
create(body, options) {
|
|
1394
|
+
const { providerParams: openAIParams, posthogParams } = extractPosthogParams(body);
|
|
1395
|
+
const startTime = Date.now();
|
|
1396
|
+
const parentPromise = super.create(openAIParams, options);
|
|
1397
|
+
if (openAIParams.stream) return preserveProviderPromise(parentPromise, parentPromise.then((value) => {
|
|
1398
|
+
if (Symbol.asyncIterator in value) {
|
|
1399
|
+
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
1400
|
+
(async () => {
|
|
1401
|
+
const accumulator = new OpenAIChatStreamAccumulator();
|
|
1402
|
+
try {
|
|
1403
|
+
for await (const chunk of stream1) accumulator.consume(chunk);
|
|
1404
|
+
const accumulated = accumulator.result();
|
|
1405
|
+
await captureAiGeneration(this.phClient, buildChatSuccessOptions({
|
|
1406
|
+
client: this.phClient,
|
|
1407
|
+
provider: "azure",
|
|
1408
|
+
baseURL: this.baseURL,
|
|
1409
|
+
params: openAIParams,
|
|
1410
|
+
monitoring: posthogParams,
|
|
1411
|
+
modelParametersSource: body
|
|
1412
|
+
}, {
|
|
1413
|
+
...accumulated,
|
|
1414
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1415
|
+
timeToFirstToken: accumulated.firstTokenTime === void 0 ? void 0 : (accumulated.firstTokenTime - startTime) / 1e3
|
|
1416
|
+
}));
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
const accumulated = accumulator.result();
|
|
1419
|
+
await captureAiGeneration(this.phClient, buildChatErrorOptions({
|
|
1420
|
+
client: this.phClient,
|
|
1421
|
+
provider: "azure",
|
|
1422
|
+
baseURL: this.baseURL,
|
|
1423
|
+
params: openAIParams,
|
|
1424
|
+
monitoring: posthogParams,
|
|
1425
|
+
modelParametersSource: body
|
|
1426
|
+
}, error, {
|
|
1427
|
+
completionId: accumulated.completionId,
|
|
1428
|
+
systemFingerprint: accumulated.systemFingerprint,
|
|
1429
|
+
usage: accumulated.usage,
|
|
1430
|
+
latency: (Date.now() - startTime) / 1e3
|
|
1431
|
+
}));
|
|
1432
|
+
throw error;
|
|
1433
|
+
}
|
|
1434
|
+
})().catch(() => {});
|
|
1435
|
+
return stream2;
|
|
1436
|
+
}
|
|
1437
|
+
return value;
|
|
1438
|
+
}));
|
|
1439
|
+
else return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
|
|
1440
|
+
if ("choices" in result) await captureAiGenerationAfterSuccess(this.phClient, buildChatSuccessOptions({
|
|
1441
|
+
client: this.phClient,
|
|
1442
|
+
provider: "azure",
|
|
1443
|
+
baseURL: this.baseURL,
|
|
1444
|
+
params: openAIParams,
|
|
1445
|
+
monitoring: posthogParams,
|
|
1446
|
+
modelParametersSource: body
|
|
1447
|
+
}, {
|
|
1448
|
+
output: formatResponseOpenAI(result),
|
|
1449
|
+
model: result.model,
|
|
1450
|
+
serviceTier: result.service_tier ?? void 0,
|
|
1451
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1452
|
+
usage: buildChatUsage(result.usage, result),
|
|
1453
|
+
stopReason: result.choices[0]?.finish_reason ?? void 0,
|
|
1454
|
+
completionId: result.id,
|
|
1455
|
+
systemFingerprint: result.system_fingerprint,
|
|
1456
|
+
requestId: result._request_id
|
|
1457
|
+
}));
|
|
1458
|
+
return result;
|
|
1459
|
+
}, async (error) => {
|
|
1460
|
+
await captureAiGeneration(this.phClient, buildChatErrorOptions({
|
|
1461
|
+
client: this.phClient,
|
|
1462
|
+
provider: "azure",
|
|
1463
|
+
baseURL: this.baseURL,
|
|
1464
|
+
params: openAIParams,
|
|
1465
|
+
monitoring: posthogParams,
|
|
1466
|
+
modelParametersSource: body
|
|
1467
|
+
}, error, { latency: (Date.now() - startTime) / 1e3 }));
|
|
1468
|
+
throw error;
|
|
1469
|
+
}));
|
|
1470
|
+
}
|
|
2055
1471
|
};
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
1472
|
+
var WrappedResponses$1 = class extends AzureOpenAI.Responses {
|
|
1473
|
+
constructor(client, phClient) {
|
|
1474
|
+
super(client);
|
|
1475
|
+
this.backgroundResponses = new BackgroundResponseTracker();
|
|
1476
|
+
this.phClient = phClient;
|
|
1477
|
+
this.baseURL = client.baseURL;
|
|
1478
|
+
}
|
|
1479
|
+
async captureBackgroundResponse(result, context) {
|
|
1480
|
+
const { openAIParams, posthogParams } = context;
|
|
1481
|
+
await captureAiGenerationAfterSuccess(this.phClient, buildBackgroundResponseOptions({
|
|
1482
|
+
client: this.phClient,
|
|
1483
|
+
provider: "azure",
|
|
1484
|
+
baseURL: this.baseURL,
|
|
1485
|
+
params: openAIParams,
|
|
1486
|
+
monitoring: posthogParams,
|
|
1487
|
+
modelParametersSource: openAIParams
|
|
1488
|
+
}, result));
|
|
1489
|
+
}
|
|
1490
|
+
create(body, options) {
|
|
1491
|
+
const { providerParams: openAIParams, posthogParams } = extractPosthogParams(body);
|
|
1492
|
+
const startTime = Date.now();
|
|
1493
|
+
const parentPromise = super.create(openAIParams, options);
|
|
1494
|
+
if (openAIParams.stream) return preserveProviderPromise(parentPromise, parentPromise.then((value) => {
|
|
1495
|
+
if (Symbol.asyncIterator in value) {
|
|
1496
|
+
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
1497
|
+
(async () => {
|
|
1498
|
+
const accumulator = new OpenAIResponsesStreamAccumulator();
|
|
1499
|
+
try {
|
|
1500
|
+
for await (const chunk of stream1) {
|
|
1501
|
+
accumulator.consume(chunk);
|
|
1502
|
+
if (openAIParams.background === true && "response" in chunk && chunk.response && !this.backgroundResponses.get(chunk.response.id)) this.backgroundResponses.set(chunk.response.id, {
|
|
1503
|
+
openAIParams,
|
|
1504
|
+
posthogParams
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
const accumulated = accumulator.result();
|
|
1508
|
+
if (openAIParams.background === true) {
|
|
1509
|
+
if (accumulated.terminalResponse) {
|
|
1510
|
+
const context = this.backgroundResponses.take(accumulated.terminalResponse.id);
|
|
1511
|
+
if (context) await this.captureBackgroundResponse(accumulated.terminalResponse, context).catch(() => void 0);
|
|
1512
|
+
}
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
const response = accumulated.terminalResponse ?? {
|
|
1516
|
+
id: accumulated.completionId ?? "",
|
|
1517
|
+
model: accumulated.model ?? openAIParams.model,
|
|
1518
|
+
service_tier: accumulated.serviceTier
|
|
1519
|
+
};
|
|
1520
|
+
await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
|
|
1521
|
+
client: this.phClient,
|
|
1522
|
+
provider: "azure",
|
|
1523
|
+
baseURL: this.baseURL,
|
|
1524
|
+
params: openAIParams,
|
|
1525
|
+
monitoring: posthogParams,
|
|
1526
|
+
modelParametersSource: body
|
|
1527
|
+
}, {
|
|
1528
|
+
response,
|
|
1529
|
+
output: accumulated.output,
|
|
1530
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1531
|
+
timeToFirstToken: accumulated.firstTokenTime === void 0 ? void 0 : (accumulated.firstTokenTime - startTime) / 1e3,
|
|
1532
|
+
usage: accumulated.usage,
|
|
1533
|
+
includeTools: true
|
|
1534
|
+
}));
|
|
1535
|
+
} catch (error) {
|
|
1536
|
+
const accumulated = accumulator.result();
|
|
1537
|
+
if (openAIParams.background === true && accumulated.completionId && this.backgroundResponses.get(accumulated.completionId)) throw error;
|
|
1538
|
+
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1539
|
+
client: this.phClient,
|
|
1540
|
+
provider: "azure",
|
|
1541
|
+
baseURL: this.baseURL,
|
|
1542
|
+
params: openAIParams,
|
|
1543
|
+
monitoring: posthogParams,
|
|
1544
|
+
modelParametersSource: body
|
|
1545
|
+
}, error, {
|
|
1546
|
+
completionId: accumulated.completionId,
|
|
1547
|
+
usage: accumulated.usage,
|
|
1548
|
+
latency: (Date.now() - startTime) / 1e3
|
|
1549
|
+
}));
|
|
1550
|
+
throw error;
|
|
1551
|
+
}
|
|
1552
|
+
})().catch(() => {});
|
|
1553
|
+
return stream2;
|
|
1554
|
+
}
|
|
1555
|
+
return value;
|
|
1556
|
+
}));
|
|
1557
|
+
else return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
|
|
1558
|
+
if ("output" in result) {
|
|
1559
|
+
if (isPendingBackgroundResponse(openAIParams, result)) {
|
|
1560
|
+
this.backgroundResponses.set(result.id, {
|
|
1561
|
+
openAIParams,
|
|
1562
|
+
posthogParams
|
|
1563
|
+
});
|
|
1564
|
+
return result;
|
|
1565
|
+
}
|
|
1566
|
+
await captureAiGenerationAfterSuccess(this.phClient, buildResponsesSuccessOptions({
|
|
1567
|
+
client: this.phClient,
|
|
1568
|
+
provider: "azure",
|
|
1569
|
+
baseURL: this.baseURL,
|
|
1570
|
+
params: openAIParams,
|
|
1571
|
+
monitoring: posthogParams,
|
|
1572
|
+
modelParametersSource: body
|
|
1573
|
+
}, {
|
|
1574
|
+
response: result,
|
|
1575
|
+
output: formatResponseOpenAI({ output: result.output }),
|
|
1576
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1577
|
+
includeTools: true,
|
|
1578
|
+
includeRequestId: true
|
|
1579
|
+
}));
|
|
1580
|
+
}
|
|
1581
|
+
return result;
|
|
1582
|
+
}, async (error) => {
|
|
1583
|
+
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1584
|
+
client: this.phClient,
|
|
1585
|
+
provider: "azure",
|
|
1586
|
+
baseURL: this.baseURL,
|
|
1587
|
+
params: openAIParams,
|
|
1588
|
+
monitoring: posthogParams,
|
|
1589
|
+
modelParametersSource: body
|
|
1590
|
+
}, error, { latency: (Date.now() - startTime) / 1e3 }));
|
|
1591
|
+
throw error;
|
|
1592
|
+
}));
|
|
1593
|
+
}
|
|
1594
|
+
retrieve(responseID, query = {}, options) {
|
|
1595
|
+
const parentPromise = super.retrieve(responseID, query, options);
|
|
1596
|
+
if (!this.backgroundResponses.get(responseID)) return parentPromise;
|
|
1597
|
+
if (query.stream) return parentPromise._thenUnwrap((result) => {
|
|
1598
|
+
if ("controller" in result) return wrapBackgroundResponseStream(result, responseID, this.backgroundResponses, (response, context) => this.captureBackgroundResponse(response, context));
|
|
1599
|
+
return result;
|
|
1600
|
+
});
|
|
1601
|
+
return parentPromise._thenUnwrap(async (result) => {
|
|
1602
|
+
if (!("output" in result) || !isTerminalResponse(result)) return result;
|
|
1603
|
+
const context = this.backgroundResponses.take(responseID);
|
|
1604
|
+
if (context) await this.captureBackgroundResponse(result, context).catch(() => void 0);
|
|
1605
|
+
return result;
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
cancel(responseID, options) {
|
|
1609
|
+
const parentPromise = super.cancel(responseID, options);
|
|
1610
|
+
if (!this.backgroundResponses.get(responseID)) return parentPromise;
|
|
1611
|
+
return parentPromise._thenUnwrap(async (result) => {
|
|
1612
|
+
if (!isTerminalResponse(result)) return result;
|
|
1613
|
+
const context = this.backgroundResponses.take(responseID);
|
|
1614
|
+
if (context) await this.captureBackgroundResponse(result, context).catch(() => void 0);
|
|
1615
|
+
return result;
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
parse(body, options) {
|
|
1619
|
+
const { providerParams: openAIParams, posthogParams } = extractPosthogParams(body);
|
|
1620
|
+
const startTime = Date.now();
|
|
1621
|
+
const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
|
|
1622
|
+
return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
|
|
1623
|
+
if (isPendingBackgroundResponse(openAIParams, result)) {
|
|
1624
|
+
this.backgroundResponses.set(result.id, {
|
|
1625
|
+
openAIParams,
|
|
1626
|
+
posthogParams
|
|
1627
|
+
});
|
|
1628
|
+
return result;
|
|
1629
|
+
}
|
|
1630
|
+
await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
|
|
1631
|
+
client: this.phClient,
|
|
1632
|
+
provider: "azure",
|
|
1633
|
+
baseURL: this.baseURL,
|
|
1634
|
+
params: openAIParams,
|
|
1635
|
+
monitoring: posthogParams,
|
|
1636
|
+
modelParametersSource: body
|
|
1637
|
+
}, {
|
|
1638
|
+
response: result,
|
|
1639
|
+
output: result.output,
|
|
1640
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1641
|
+
includeRequestId: true
|
|
1642
|
+
}));
|
|
1643
|
+
return result;
|
|
1644
|
+
}, async (error) => {
|
|
1645
|
+
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1646
|
+
client: this.phClient,
|
|
1647
|
+
provider: "azure",
|
|
1648
|
+
baseURL: this.baseURL,
|
|
1649
|
+
params: openAIParams,
|
|
1650
|
+
monitoring: posthogParams,
|
|
1651
|
+
modelParametersSource: body
|
|
1652
|
+
}, error, { latency: (Date.now() - startTime) / 1e3 }));
|
|
1653
|
+
throw error;
|
|
1654
|
+
}));
|
|
1655
|
+
}
|
|
2092
1656
|
};
|
|
2093
|
-
|
|
1657
|
+
var WrappedEmbeddings$1 = class extends AzureOpenAI.Embeddings {
|
|
1658
|
+
constructor(client, phClient) {
|
|
1659
|
+
super(client);
|
|
1660
|
+
this.phClient = phClient;
|
|
1661
|
+
this.baseURL = client.baseURL;
|
|
1662
|
+
}
|
|
1663
|
+
create(body, options) {
|
|
1664
|
+
const { providerParams: openAIParams, posthogParams } = extractPosthogParams(body);
|
|
1665
|
+
const startTime = Date.now();
|
|
1666
|
+
const parentPromise = super.create(openAIParams, options);
|
|
1667
|
+
return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
|
|
1668
|
+
await captureAiGeneration(this.phClient, buildEmbeddingSuccessOptions({
|
|
1669
|
+
client: this.phClient,
|
|
1670
|
+
provider: "azure",
|
|
1671
|
+
baseURL: this.baseURL,
|
|
1672
|
+
params: openAIParams,
|
|
1673
|
+
monitoring: posthogParams,
|
|
1674
|
+
modelParametersSource: body
|
|
1675
|
+
}, result.usage, (Date.now() - startTime) / 1e3));
|
|
1676
|
+
return result;
|
|
1677
|
+
}, async (error) => {
|
|
1678
|
+
await captureAiGeneration(this.phClient, buildEmbeddingErrorOptions({
|
|
1679
|
+
client: this.phClient,
|
|
1680
|
+
provider: "azure",
|
|
1681
|
+
baseURL: this.baseURL,
|
|
1682
|
+
params: openAIParams,
|
|
1683
|
+
monitoring: posthogParams,
|
|
1684
|
+
modelParametersSource: body
|
|
1685
|
+
}, error, (Date.now() - startTime) / 1e3));
|
|
1686
|
+
throw error;
|
|
1687
|
+
}));
|
|
1688
|
+
}
|
|
1689
|
+
};
|
|
1690
|
+
//#endregion
|
|
1691
|
+
//#region src/openai/index.ts
|
|
2094
1692
|
const Chat = OpenAI.Chat;
|
|
2095
1693
|
const Completions = Chat.Completions;
|
|
2096
1694
|
const Responses = OpenAI.Responses;
|
|
2097
1695
|
const Embeddings = OpenAI.Embeddings;
|
|
2098
1696
|
const Audio = OpenAI.Audio;
|
|
2099
1697
|
const Transcriptions = OpenAI.Audio.Transcriptions;
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
// --- Overload #1: Non-streaming
|
|
2532
|
-
|
|
2533
|
-
// --- Overload #2: Non-streaming
|
|
2534
|
-
|
|
2535
|
-
// --- Overload #3: Non-streaming
|
|
2536
|
-
|
|
2537
|
-
// --- Overload #4: Non-streaming
|
|
2538
|
-
|
|
2539
|
-
// --- Overload #5: Streaming
|
|
2540
|
-
|
|
2541
|
-
// --- Overload #6: Streaming
|
|
2542
|
-
|
|
2543
|
-
// --- Overload #7: Generic base
|
|
2544
|
-
|
|
2545
|
-
// --- Implementation Signature
|
|
2546
|
-
create(body, options) {
|
|
2547
|
-
const {
|
|
2548
|
-
providerParams: openAIParams,
|
|
2549
|
-
posthogParams
|
|
2550
|
-
} = extractPosthogParams(body);
|
|
2551
|
-
const startTime = Date.now();
|
|
2552
|
-
const parentPromise = openAIParams.stream ? super.create(openAIParams, options) : super.create(openAIParams, options);
|
|
2553
|
-
if (openAIParams.stream) {
|
|
2554
|
-
const wrappedPromise = parentPromise.then(value => {
|
|
2555
|
-
if (Symbol.asyncIterator in value) {
|
|
2556
|
-
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
2557
|
-
(async () => {
|
|
2558
|
-
let usage = {};
|
|
2559
|
-
try {
|
|
2560
|
-
let finalContent = '';
|
|
2561
|
-
let firstTokenTime;
|
|
2562
|
-
const doneEvent = 'transcript.text.done';
|
|
2563
|
-
for await (const chunk of stream1) {
|
|
2564
|
-
// Track first token on text delta events
|
|
2565
|
-
if (firstTokenTime === undefined && chunk.type === 'transcript.text.delta') {
|
|
2566
|
-
firstTokenTime = Date.now();
|
|
2567
|
-
}
|
|
2568
|
-
if (chunk.type === doneEvent && 'text' in chunk && chunk.text && chunk.text.length > 0) {
|
|
2569
|
-
finalContent = chunk.text;
|
|
2570
|
-
}
|
|
2571
|
-
if ('usage' in chunk && chunk.usage) {
|
|
2572
|
-
usage = {
|
|
2573
|
-
inputTokens: chunk.usage?.type === 'tokens' ? chunk.usage.input_tokens ?? 0 : 0,
|
|
2574
|
-
outputTokens: chunk.usage?.type === 'tokens' ? chunk.usage.output_tokens ?? 0 : 0,
|
|
2575
|
-
rawUsage: chunk.usage
|
|
2576
|
-
};
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
2580
|
-
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
2581
|
-
const availableTools = extractAvailableToolCalls('openai', openAIParams);
|
|
2582
|
-
await captureAiGeneration(this.phClient, {
|
|
2583
|
-
...posthogParams,
|
|
2584
|
-
model: openAIParams.model,
|
|
2585
|
-
provider: 'openai',
|
|
2586
|
-
input: openAIParams.prompt,
|
|
2587
|
-
output: sanitizeOpenAIResponse(finalContent, this.phClient),
|
|
2588
|
-
latency,
|
|
2589
|
-
timeToFirstToken,
|
|
2590
|
-
baseURL: this.baseURL,
|
|
2591
|
-
modelParameters: getModelParams(body),
|
|
2592
|
-
httpStatus: 200,
|
|
2593
|
-
usage,
|
|
2594
|
-
tools: availableTools
|
|
2595
|
-
});
|
|
2596
|
-
} catch (error) {
|
|
2597
|
-
await captureAiGeneration(this.phClient, {
|
|
2598
|
-
...posthogParams,
|
|
2599
|
-
model: openAIParams.model,
|
|
2600
|
-
provider: 'openai',
|
|
2601
|
-
input: openAIParams.prompt,
|
|
2602
|
-
output: [],
|
|
2603
|
-
latency: (Date.now() - startTime) / 1000,
|
|
2604
|
-
baseURL: this.baseURL,
|
|
2605
|
-
modelParameters: getModelParams(body),
|
|
2606
|
-
usage,
|
|
2607
|
-
error
|
|
2608
|
-
});
|
|
2609
|
-
throw error;
|
|
2610
|
-
}
|
|
2611
|
-
})().catch(() => {
|
|
2612
|
-
// Swallow: analytics must never crash the host process. The caller
|
|
2613
|
-
// already receives this error via their own tee of the stream.
|
|
2614
|
-
});
|
|
2615
|
-
return stream2;
|
|
2616
|
-
}
|
|
2617
|
-
return value;
|
|
2618
|
-
});
|
|
2619
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
2620
|
-
} else {
|
|
2621
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
2622
|
-
if (result && typeof result === 'object' && 'text' in result) {
|
|
2623
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
2624
|
-
await captureAiGenerationAfterSuccess(this.phClient, {
|
|
2625
|
-
...posthogParams,
|
|
2626
|
-
model: openAIParams.model,
|
|
2627
|
-
provider: 'openai',
|
|
2628
|
-
input: openAIParams.prompt,
|
|
2629
|
-
output: sanitizeOpenAIResponse(result.text, this.phClient),
|
|
2630
|
-
latency,
|
|
2631
|
-
baseURL: this.baseURL,
|
|
2632
|
-
modelParameters: getModelParams(body),
|
|
2633
|
-
httpStatus: 200,
|
|
2634
|
-
usage: {
|
|
2635
|
-
inputTokens: result.usage?.type === 'tokens' ? result.usage.input_tokens ?? 0 : 0,
|
|
2636
|
-
outputTokens: result.usage?.type === 'tokens' ? result.usage.output_tokens ?? 0 : 0,
|
|
2637
|
-
rawUsage: result.usage
|
|
2638
|
-
}
|
|
2639
|
-
});
|
|
2640
|
-
}
|
|
2641
|
-
return result;
|
|
2642
|
-
}, async error => {
|
|
2643
|
-
await captureAiGeneration(this.phClient, {
|
|
2644
|
-
...posthogParams,
|
|
2645
|
-
model: openAIParams.model,
|
|
2646
|
-
provider: 'openai',
|
|
2647
|
-
input: openAIParams.prompt,
|
|
2648
|
-
output: [],
|
|
2649
|
-
latency: (Date.now() - startTime) / 1000,
|
|
2650
|
-
baseURL: this.baseURL,
|
|
2651
|
-
modelParameters: getModelParams(body),
|
|
2652
|
-
usage: {},
|
|
2653
|
-
error
|
|
2654
|
-
});
|
|
2655
|
-
throw error;
|
|
2656
|
-
});
|
|
2657
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
2658
|
-
}
|
|
2659
|
-
}
|
|
2660
|
-
}
|
|
1698
|
+
var PostHogOpenAI = class extends OpenAI {
|
|
1699
|
+
constructor(config) {
|
|
1700
|
+
const { posthog, ...openAIConfig } = config;
|
|
1701
|
+
super(openAIConfig);
|
|
1702
|
+
this.phClient = posthog;
|
|
1703
|
+
this.chat = new WrappedChat(this, this.phClient);
|
|
1704
|
+
this.responses = new WrappedResponses(this, this.phClient);
|
|
1705
|
+
this.embeddings = new WrappedEmbeddings(this, this.phClient);
|
|
1706
|
+
this.audio = new WrappedAudio(this, this.phClient);
|
|
1707
|
+
}
|
|
1708
|
+
};
|
|
1709
|
+
var WrappedChat = class extends Chat {
|
|
1710
|
+
constructor(parentClient, phClient) {
|
|
1711
|
+
super(parentClient);
|
|
1712
|
+
this.completions = new WrappedCompletions(parentClient, phClient);
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
var WrappedCompletions = class extends Completions {
|
|
1716
|
+
constructor(client, phClient) {
|
|
1717
|
+
super(client);
|
|
1718
|
+
this.phClient = phClient;
|
|
1719
|
+
this.baseURL = client.baseURL;
|
|
1720
|
+
}
|
|
1721
|
+
create(body, options) {
|
|
1722
|
+
const { providerParams: openAIParams, posthogParams } = extractPosthogParams(body);
|
|
1723
|
+
const startTime = Date.now();
|
|
1724
|
+
const parentPromise = super.create(openAIParams, options);
|
|
1725
|
+
if (openAIParams.stream) return preserveProviderPromise(parentPromise, parentPromise.then((value) => {
|
|
1726
|
+
if (Symbol.asyncIterator in value) {
|
|
1727
|
+
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
1728
|
+
(async () => {
|
|
1729
|
+
const accumulator = new OpenAIChatStreamAccumulator();
|
|
1730
|
+
try {
|
|
1731
|
+
for await (const chunk of stream1) accumulator.consume(chunk);
|
|
1732
|
+
const accumulated = accumulator.result();
|
|
1733
|
+
await captureAiGeneration(this.phClient, buildChatSuccessOptions({
|
|
1734
|
+
client: this.phClient,
|
|
1735
|
+
provider: "openai",
|
|
1736
|
+
baseURL: this.baseURL,
|
|
1737
|
+
params: openAIParams,
|
|
1738
|
+
monitoring: posthogParams,
|
|
1739
|
+
modelParametersSource: body
|
|
1740
|
+
}, {
|
|
1741
|
+
...accumulated,
|
|
1742
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1743
|
+
timeToFirstToken: accumulated.firstTokenTime === void 0 ? void 0 : (accumulated.firstTokenTime - startTime) / 1e3
|
|
1744
|
+
}));
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
const accumulated = accumulator.result();
|
|
1747
|
+
await captureAiGeneration(this.phClient, buildChatErrorOptions({
|
|
1748
|
+
client: this.phClient,
|
|
1749
|
+
provider: "openai",
|
|
1750
|
+
baseURL: this.baseURL,
|
|
1751
|
+
params: openAIParams,
|
|
1752
|
+
monitoring: posthogParams,
|
|
1753
|
+
modelParametersSource: body
|
|
1754
|
+
}, error, {
|
|
1755
|
+
completionId: accumulated.completionId,
|
|
1756
|
+
systemFingerprint: accumulated.systemFingerprint,
|
|
1757
|
+
usage: accumulated.usage,
|
|
1758
|
+
latency: (Date.now() - startTime) / 1e3
|
|
1759
|
+
}));
|
|
1760
|
+
throw error;
|
|
1761
|
+
}
|
|
1762
|
+
})().catch(() => {});
|
|
1763
|
+
return stream2;
|
|
1764
|
+
}
|
|
1765
|
+
return value;
|
|
1766
|
+
}));
|
|
1767
|
+
else return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
|
|
1768
|
+
if ("choices" in result) await captureAiGenerationAfterSuccess(this.phClient, buildChatSuccessOptions({
|
|
1769
|
+
client: this.phClient,
|
|
1770
|
+
provider: "openai",
|
|
1771
|
+
baseURL: this.baseURL,
|
|
1772
|
+
params: openAIParams,
|
|
1773
|
+
monitoring: posthogParams,
|
|
1774
|
+
modelParametersSource: body
|
|
1775
|
+
}, {
|
|
1776
|
+
output: formatResponseOpenAI(result),
|
|
1777
|
+
model: result.model,
|
|
1778
|
+
serviceTier: result.service_tier ?? void 0,
|
|
1779
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1780
|
+
usage: buildChatUsage(result.usage, result),
|
|
1781
|
+
stopReason: result.choices[0]?.finish_reason ?? void 0,
|
|
1782
|
+
completionId: result.id,
|
|
1783
|
+
systemFingerprint: result.system_fingerprint,
|
|
1784
|
+
requestId: extractRequestId(result)
|
|
1785
|
+
}));
|
|
1786
|
+
return result;
|
|
1787
|
+
}, async (error) => {
|
|
1788
|
+
await captureAiGeneration(this.phClient, buildChatErrorOptions({
|
|
1789
|
+
client: this.phClient,
|
|
1790
|
+
provider: "openai",
|
|
1791
|
+
baseURL: this.baseURL,
|
|
1792
|
+
params: openAIParams,
|
|
1793
|
+
monitoring: posthogParams,
|
|
1794
|
+
modelParametersSource: body
|
|
1795
|
+
}, error, { latency: (Date.now() - startTime) / 1e3 }));
|
|
1796
|
+
throw error;
|
|
1797
|
+
}));
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
var WrappedResponses = class extends Responses {
|
|
1801
|
+
constructor(client, phClient) {
|
|
1802
|
+
super(client);
|
|
1803
|
+
this.backgroundResponses = new BackgroundResponseTracker();
|
|
1804
|
+
this.phClient = phClient;
|
|
1805
|
+
this.baseURL = client.baseURL;
|
|
1806
|
+
}
|
|
1807
|
+
async captureBackgroundResponse(result, context) {
|
|
1808
|
+
const { openAIParams, posthogParams } = context;
|
|
1809
|
+
await captureAiGenerationAfterSuccess(this.phClient, buildBackgroundResponseOptions({
|
|
1810
|
+
client: this.phClient,
|
|
1811
|
+
provider: "openai",
|
|
1812
|
+
baseURL: this.baseURL,
|
|
1813
|
+
params: openAIParams,
|
|
1814
|
+
monitoring: posthogParams,
|
|
1815
|
+
modelParametersSource: openAIParams
|
|
1816
|
+
}, result));
|
|
1817
|
+
}
|
|
1818
|
+
create(body, options) {
|
|
1819
|
+
const { providerParams: openAIParams, posthogParams } = extractPosthogParams(body);
|
|
1820
|
+
const startTime = Date.now();
|
|
1821
|
+
const parentPromise = super.create(openAIParams, options);
|
|
1822
|
+
if (openAIParams.stream) return preserveProviderPromise(parentPromise, parentPromise.then((value) => {
|
|
1823
|
+
if (Symbol.asyncIterator in value) {
|
|
1824
|
+
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
1825
|
+
(async () => {
|
|
1826
|
+
const accumulator = new OpenAIResponsesStreamAccumulator();
|
|
1827
|
+
try {
|
|
1828
|
+
for await (const chunk of stream1) {
|
|
1829
|
+
accumulator.consume(chunk);
|
|
1830
|
+
if (openAIParams.background === true && "response" in chunk && chunk.response && !this.backgroundResponses.get(chunk.response.id)) this.backgroundResponses.set(chunk.response.id, {
|
|
1831
|
+
openAIParams,
|
|
1832
|
+
posthogParams
|
|
1833
|
+
});
|
|
1834
|
+
}
|
|
1835
|
+
const accumulated = accumulator.result();
|
|
1836
|
+
if (openAIParams.background === true) {
|
|
1837
|
+
if (accumulated.terminalResponse) {
|
|
1838
|
+
const context = this.backgroundResponses.take(accumulated.terminalResponse.id);
|
|
1839
|
+
if (context) await this.captureBackgroundResponse(accumulated.terminalResponse, context).catch(() => void 0);
|
|
1840
|
+
}
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
const response = accumulated.terminalResponse ?? {
|
|
1844
|
+
id: accumulated.completionId ?? "",
|
|
1845
|
+
model: accumulated.model ?? openAIParams.model,
|
|
1846
|
+
service_tier: accumulated.serviceTier
|
|
1847
|
+
};
|
|
1848
|
+
await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
|
|
1849
|
+
client: this.phClient,
|
|
1850
|
+
provider: "openai",
|
|
1851
|
+
baseURL: this.baseURL,
|
|
1852
|
+
params: openAIParams,
|
|
1853
|
+
monitoring: posthogParams,
|
|
1854
|
+
modelParametersSource: body
|
|
1855
|
+
}, {
|
|
1856
|
+
response,
|
|
1857
|
+
output: accumulated.output,
|
|
1858
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1859
|
+
timeToFirstToken: accumulated.firstTokenTime === void 0 ? void 0 : (accumulated.firstTokenTime - startTime) / 1e3,
|
|
1860
|
+
usage: accumulated.usage,
|
|
1861
|
+
includeTools: true
|
|
1862
|
+
}));
|
|
1863
|
+
} catch (error) {
|
|
1864
|
+
const accumulated = accumulator.result();
|
|
1865
|
+
if (openAIParams.background === true && accumulated.completionId && this.backgroundResponses.get(accumulated.completionId)) throw error;
|
|
1866
|
+
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1867
|
+
client: this.phClient,
|
|
1868
|
+
provider: "openai",
|
|
1869
|
+
baseURL: this.baseURL,
|
|
1870
|
+
params: openAIParams,
|
|
1871
|
+
monitoring: posthogParams,
|
|
1872
|
+
modelParametersSource: body
|
|
1873
|
+
}, error, {
|
|
1874
|
+
completionId: accumulated.completionId,
|
|
1875
|
+
usage: accumulated.usage,
|
|
1876
|
+
latency: (Date.now() - startTime) / 1e3
|
|
1877
|
+
}));
|
|
1878
|
+
throw error;
|
|
1879
|
+
}
|
|
1880
|
+
})().catch(() => {});
|
|
1881
|
+
return stream2;
|
|
1882
|
+
}
|
|
1883
|
+
return value;
|
|
1884
|
+
}));
|
|
1885
|
+
else return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
|
|
1886
|
+
if ("output" in result) {
|
|
1887
|
+
if (isPendingBackgroundResponse(openAIParams, result)) {
|
|
1888
|
+
this.backgroundResponses.set(result.id, {
|
|
1889
|
+
openAIParams,
|
|
1890
|
+
posthogParams
|
|
1891
|
+
});
|
|
1892
|
+
return result;
|
|
1893
|
+
}
|
|
1894
|
+
await captureAiGenerationAfterSuccess(this.phClient, buildResponsesSuccessOptions({
|
|
1895
|
+
client: this.phClient,
|
|
1896
|
+
provider: "openai",
|
|
1897
|
+
baseURL: this.baseURL,
|
|
1898
|
+
params: openAIParams,
|
|
1899
|
+
monitoring: posthogParams,
|
|
1900
|
+
modelParametersSource: body
|
|
1901
|
+
}, {
|
|
1902
|
+
response: result,
|
|
1903
|
+
output: formatResponseOpenAI({ output: result.output }),
|
|
1904
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1905
|
+
includeTools: true,
|
|
1906
|
+
includeRequestId: true
|
|
1907
|
+
}));
|
|
1908
|
+
}
|
|
1909
|
+
return result;
|
|
1910
|
+
}, async (error) => {
|
|
1911
|
+
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1912
|
+
client: this.phClient,
|
|
1913
|
+
provider: "openai",
|
|
1914
|
+
baseURL: this.baseURL,
|
|
1915
|
+
params: openAIParams,
|
|
1916
|
+
monitoring: posthogParams,
|
|
1917
|
+
modelParametersSource: body
|
|
1918
|
+
}, error, { latency: (Date.now() - startTime) / 1e3 }));
|
|
1919
|
+
throw error;
|
|
1920
|
+
}));
|
|
1921
|
+
}
|
|
1922
|
+
retrieve(responseID, query = {}, options) {
|
|
1923
|
+
const parentPromise = super.retrieve(responseID, query, options);
|
|
1924
|
+
if (!this.backgroundResponses.get(responseID)) return parentPromise;
|
|
1925
|
+
if (query.stream) return parentPromise._thenUnwrap((result) => {
|
|
1926
|
+
if ("controller" in result) return wrapBackgroundResponseStream(result, responseID, this.backgroundResponses, (response, context) => this.captureBackgroundResponse(response, context));
|
|
1927
|
+
return result;
|
|
1928
|
+
});
|
|
1929
|
+
return parentPromise._thenUnwrap(async (result) => {
|
|
1930
|
+
if (!("output" in result) || !isTerminalResponse(result)) return result;
|
|
1931
|
+
const context = this.backgroundResponses.take(responseID);
|
|
1932
|
+
if (context) await this.captureBackgroundResponse(result, context).catch(() => void 0);
|
|
1933
|
+
return result;
|
|
1934
|
+
});
|
|
1935
|
+
}
|
|
1936
|
+
cancel(responseID, options) {
|
|
1937
|
+
const parentPromise = super.cancel(responseID, options);
|
|
1938
|
+
if (!this.backgroundResponses.get(responseID)) return parentPromise;
|
|
1939
|
+
return parentPromise._thenUnwrap(async (result) => {
|
|
1940
|
+
if (!isTerminalResponse(result)) return result;
|
|
1941
|
+
const context = this.backgroundResponses.take(responseID);
|
|
1942
|
+
if (context) await this.captureBackgroundResponse(result, context).catch(() => void 0);
|
|
1943
|
+
return result;
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
parse(body, options) {
|
|
1947
|
+
const { providerParams: openAIParams, posthogParams } = extractPosthogParams(body);
|
|
1948
|
+
const startTime = Date.now();
|
|
1949
|
+
const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
|
|
1950
|
+
return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
|
|
1951
|
+
if (isPendingBackgroundResponse(openAIParams, result)) {
|
|
1952
|
+
this.backgroundResponses.set(result.id, {
|
|
1953
|
+
openAIParams,
|
|
1954
|
+
posthogParams
|
|
1955
|
+
});
|
|
1956
|
+
return result;
|
|
1957
|
+
}
|
|
1958
|
+
await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
|
|
1959
|
+
client: this.phClient,
|
|
1960
|
+
provider: "openai",
|
|
1961
|
+
baseURL: this.baseURL,
|
|
1962
|
+
params: openAIParams,
|
|
1963
|
+
monitoring: posthogParams,
|
|
1964
|
+
modelParametersSource: body
|
|
1965
|
+
}, {
|
|
1966
|
+
response: result,
|
|
1967
|
+
output: result.output,
|
|
1968
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
1969
|
+
includeRequestId: true
|
|
1970
|
+
}));
|
|
1971
|
+
return result;
|
|
1972
|
+
}, async (error) => {
|
|
1973
|
+
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1974
|
+
client: this.phClient,
|
|
1975
|
+
provider: "openai",
|
|
1976
|
+
baseURL: this.baseURL,
|
|
1977
|
+
params: openAIParams,
|
|
1978
|
+
monitoring: posthogParams,
|
|
1979
|
+
modelParametersSource: body
|
|
1980
|
+
}, error, { latency: (Date.now() - startTime) / 1e3 }));
|
|
1981
|
+
throw error;
|
|
1982
|
+
}));
|
|
1983
|
+
}
|
|
1984
|
+
};
|
|
1985
|
+
var WrappedEmbeddings = class extends Embeddings {
|
|
1986
|
+
constructor(client, phClient) {
|
|
1987
|
+
super(client);
|
|
1988
|
+
this.phClient = phClient;
|
|
1989
|
+
this.baseURL = client.baseURL;
|
|
1990
|
+
}
|
|
1991
|
+
create(body, options) {
|
|
1992
|
+
const { providerParams: openAIParams, posthogParams } = extractPosthogParams(body);
|
|
1993
|
+
const startTime = Date.now();
|
|
1994
|
+
const parentPromise = super.create(openAIParams, options);
|
|
1995
|
+
return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
|
|
1996
|
+
await captureAiGeneration(this.phClient, buildEmbeddingSuccessOptions({
|
|
1997
|
+
client: this.phClient,
|
|
1998
|
+
provider: "openai",
|
|
1999
|
+
baseURL: this.baseURL,
|
|
2000
|
+
params: openAIParams,
|
|
2001
|
+
monitoring: posthogParams,
|
|
2002
|
+
modelParametersSource: body
|
|
2003
|
+
}, result.usage, (Date.now() - startTime) / 1e3));
|
|
2004
|
+
return result;
|
|
2005
|
+
}, async (error) => {
|
|
2006
|
+
await captureAiGeneration(this.phClient, buildEmbeddingErrorOptions({
|
|
2007
|
+
client: this.phClient,
|
|
2008
|
+
provider: "openai",
|
|
2009
|
+
baseURL: this.baseURL,
|
|
2010
|
+
params: openAIParams,
|
|
2011
|
+
monitoring: posthogParams,
|
|
2012
|
+
modelParametersSource: body
|
|
2013
|
+
}, error, (Date.now() - startTime) / 1e3));
|
|
2014
|
+
throw error;
|
|
2015
|
+
}));
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
var WrappedAudio = class extends Audio {
|
|
2019
|
+
constructor(parentClient, phClient) {
|
|
2020
|
+
super(parentClient);
|
|
2021
|
+
this.transcriptions = new WrappedTranscriptions(parentClient, phClient);
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
var WrappedTranscriptions = class extends Transcriptions {
|
|
2025
|
+
constructor(client, phClient) {
|
|
2026
|
+
super(client);
|
|
2027
|
+
this.phClient = phClient;
|
|
2028
|
+
this.baseURL = client.baseURL;
|
|
2029
|
+
}
|
|
2030
|
+
create(body, options) {
|
|
2031
|
+
const { providerParams: openAIParams, posthogParams } = extractPosthogParams(body);
|
|
2032
|
+
const startTime = Date.now();
|
|
2033
|
+
const parentPromise = openAIParams.stream ? super.create(openAIParams, options) : super.create(openAIParams, options);
|
|
2034
|
+
if (openAIParams.stream) return preserveProviderPromise(parentPromise, parentPromise.then((value) => {
|
|
2035
|
+
if (Symbol.asyncIterator in value) {
|
|
2036
|
+
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
2037
|
+
(async () => {
|
|
2038
|
+
let usage = {};
|
|
2039
|
+
try {
|
|
2040
|
+
let finalContent = "";
|
|
2041
|
+
let firstTokenTime;
|
|
2042
|
+
const doneEvent = "transcript.text.done";
|
|
2043
|
+
for await (const chunk of stream1) {
|
|
2044
|
+
if (firstTokenTime === void 0 && chunk.type === "transcript.text.delta") firstTokenTime = Date.now();
|
|
2045
|
+
if (chunk.type === doneEvent && "text" in chunk && chunk.text && chunk.text.length > 0) finalContent = chunk.text;
|
|
2046
|
+
if ("usage" in chunk && chunk.usage) usage = {
|
|
2047
|
+
inputTokens: chunk.usage?.type === "tokens" ? chunk.usage.input_tokens ?? 0 : 0,
|
|
2048
|
+
outputTokens: chunk.usage?.type === "tokens" ? chunk.usage.output_tokens ?? 0 : 0,
|
|
2049
|
+
rawUsage: chunk.usage
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
const latency = (Date.now() - startTime) / 1e3;
|
|
2053
|
+
const timeToFirstToken = firstTokenTime !== void 0 ? (firstTokenTime - startTime) / 1e3 : void 0;
|
|
2054
|
+
const availableTools = extractAvailableToolCalls("openai", openAIParams);
|
|
2055
|
+
await captureAiGeneration(this.phClient, {
|
|
2056
|
+
...posthogParams,
|
|
2057
|
+
model: openAIParams.model,
|
|
2058
|
+
provider: "openai",
|
|
2059
|
+
input: openAIParams.prompt,
|
|
2060
|
+
output: sanitizeOpenAIResponse(finalContent, this.phClient),
|
|
2061
|
+
latency,
|
|
2062
|
+
timeToFirstToken,
|
|
2063
|
+
baseURL: this.baseURL,
|
|
2064
|
+
modelParameters: getModelParams(body),
|
|
2065
|
+
httpStatus: 200,
|
|
2066
|
+
usage,
|
|
2067
|
+
tools: availableTools
|
|
2068
|
+
});
|
|
2069
|
+
} catch (error) {
|
|
2070
|
+
await captureAiGeneration(this.phClient, {
|
|
2071
|
+
...posthogParams,
|
|
2072
|
+
model: openAIParams.model,
|
|
2073
|
+
provider: "openai",
|
|
2074
|
+
input: openAIParams.prompt,
|
|
2075
|
+
output: [],
|
|
2076
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
2077
|
+
baseURL: this.baseURL,
|
|
2078
|
+
modelParameters: getModelParams(body),
|
|
2079
|
+
usage,
|
|
2080
|
+
error
|
|
2081
|
+
});
|
|
2082
|
+
throw error;
|
|
2083
|
+
}
|
|
2084
|
+
})().catch(() => {});
|
|
2085
|
+
return stream2;
|
|
2086
|
+
}
|
|
2087
|
+
return value;
|
|
2088
|
+
}));
|
|
2089
|
+
else return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
|
|
2090
|
+
if (result && typeof result === "object" && "text" in result) {
|
|
2091
|
+
const latency = (Date.now() - startTime) / 1e3;
|
|
2092
|
+
await captureAiGenerationAfterSuccess(this.phClient, {
|
|
2093
|
+
...posthogParams,
|
|
2094
|
+
model: openAIParams.model,
|
|
2095
|
+
provider: "openai",
|
|
2096
|
+
input: openAIParams.prompt,
|
|
2097
|
+
output: sanitizeOpenAIResponse(result.text, this.phClient),
|
|
2098
|
+
latency,
|
|
2099
|
+
baseURL: this.baseURL,
|
|
2100
|
+
modelParameters: getModelParams(body),
|
|
2101
|
+
httpStatus: 200,
|
|
2102
|
+
usage: {
|
|
2103
|
+
inputTokens: result.usage?.type === "tokens" ? result.usage.input_tokens ?? 0 : 0,
|
|
2104
|
+
outputTokens: result.usage?.type === "tokens" ? result.usage.output_tokens ?? 0 : 0,
|
|
2105
|
+
rawUsage: result.usage
|
|
2106
|
+
}
|
|
2107
|
+
});
|
|
2108
|
+
}
|
|
2109
|
+
return result;
|
|
2110
|
+
}, async (error) => {
|
|
2111
|
+
await captureAiGeneration(this.phClient, {
|
|
2112
|
+
...posthogParams,
|
|
2113
|
+
model: openAIParams.model,
|
|
2114
|
+
provider: "openai",
|
|
2115
|
+
input: openAIParams.prompt,
|
|
2116
|
+
output: [],
|
|
2117
|
+
latency: (Date.now() - startTime) / 1e3,
|
|
2118
|
+
baseURL: this.baseURL,
|
|
2119
|
+
modelParameters: getModelParams(body),
|
|
2120
|
+
usage: {},
|
|
2121
|
+
error
|
|
2122
|
+
});
|
|
2123
|
+
throw error;
|
|
2124
|
+
}));
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
//#endregion
|
|
2128
|
+
export { PostHogAzureOpenAI as AzureOpenAI, PostHogOpenAI as OpenAI, PostHogOpenAI, PostHogOpenAI as default, WrappedAudio, WrappedChat, WrappedCompletions, WrappedEmbeddings, WrappedResponses, WrappedTranscriptions };
|
|
2661
2129
|
|
|
2662
|
-
|
|
2663
|
-
//# sourceMappingURL=index.mjs.map
|
|
2130
|
+
//# sourceMappingURL=index.mjs.map
|