@posthog/ai 8.9.3 → 8.10.1
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 +977 -0
- package/dist/adk/index.cjs.map +1 -0
- package/dist/adk/index.d.ts +149 -0
- package/dist/adk/index.mjs +976 -0
- package/dist/adk/index.mjs.map +1 -0
- package/dist/anthropic/index.cjs +927 -1104
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.d.ts +34 -33
- package/dist/anthropic/index.mjs +899 -1095
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +867 -1112
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.d.ts +38 -35
- package/dist/gemini/index.mjs +862 -1107
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +1218 -1539
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +170 -157
- package/dist/index.mjs +1216 -1537
- 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 -2516
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.d.ts +106 -104
- package/dist/openai/index.mjs +1985 -2511
- 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 -1336
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.d.ts +21 -16
- package/dist/vercel/index.mjs +991 -1334
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +23 -12
package/dist/openai/index.mjs
CHANGED
|
@@ -1,2656 +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.1";
|
|
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
|
-
$ai_base_url: options.baseURL ?? '',
|
|
811
|
-
...options.properties,
|
|
812
|
-
$ai_tokens_source: getTokensSource(options.properties),
|
|
813
|
-
...(options.distinctId ? {} : {
|
|
814
|
-
$process_person_profile: false
|
|
815
|
-
}),
|
|
816
|
-
...(options.stopReason ? {
|
|
817
|
-
$ai_stop_reason: options.stopReason
|
|
818
|
-
} : {}),
|
|
819
|
-
...(options.tools ? {
|
|
820
|
-
$ai_tools: options.tools
|
|
821
|
-
} : {}),
|
|
822
|
-
...(options.completionId ? {
|
|
823
|
-
$ai_completion_id: options.completionId
|
|
824
|
-
} : {}),
|
|
825
|
-
...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
|
|
826
|
-
$ai_provider_metadata: options.providerMetadata
|
|
827
|
-
} : {}),
|
|
828
|
-
...errorData,
|
|
829
|
-
...costOverrideData
|
|
830
|
-
};
|
|
831
|
-
const event = {
|
|
832
|
-
distinctId: options.distinctId ?? traceId,
|
|
833
|
-
event: eventType,
|
|
834
|
-
properties,
|
|
835
|
-
groups: options.groups
|
|
836
|
-
};
|
|
837
|
-
if (options.captureImmediate) {
|
|
838
|
-
await captureAiEventImmediate(client, event);
|
|
839
|
-
} else {
|
|
840
|
-
captureAiEvent(client, event);
|
|
841
|
-
}
|
|
842
|
-
} catch (error) {
|
|
843
|
-
// Telemetry failures must never affect the instrumented provider call.
|
|
844
|
-
console.warn('[PostHog AI] Failed to capture generation telemetry:', error);
|
|
845
|
-
}
|
|
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
|
+
}
|
|
846
661
|
};
|
|
847
|
-
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/openai/capture.ts
|
|
848
664
|
/**
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
const INPUT_OR_CACHE_TOKEN_KEYS = [
|
|
858
|
-
|
|
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
|
+
];
|
|
859
678
|
/**
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
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
|
+
*/
|
|
871
690
|
const captureAiGeneration = (client, options) => {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
}
|
|
882
|
-
});
|
|
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
|
+
});
|
|
883
700
|
};
|
|
884
|
-
|
|
701
|
+
//#endregion
|
|
702
|
+
//#region src/openai/utils.ts
|
|
885
703
|
/**
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
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
|
+
*/
|
|
889
707
|
function isResponseTokenChunk(chunk) {
|
|
890
|
-
|
|
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";
|
|
891
709
|
}
|
|
892
|
-
|
|
893
710
|
/**
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
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
|
+
*/
|
|
899
716
|
function extractRequestId(result) {
|
|
900
|
-
|
|
717
|
+
return result?._request_id ?? void 0;
|
|
901
718
|
}
|
|
902
|
-
|
|
903
719
|
/**
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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
|
+
*/
|
|
911
727
|
function extractCacheWriteTokens(details) {
|
|
912
|
-
|
|
728
|
+
return details?.cache_write_tokens ?? 0;
|
|
913
729
|
}
|
|
914
|
-
|
|
915
730
|
/**
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
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
|
+
*/
|
|
922
737
|
function buildProviderMetadata(fields) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
metadata.request_id = fields.requestId;
|
|
929
|
-
}
|
|
930
|
-
if (fields.incompleteDetails != null) {
|
|
931
|
-
metadata.incomplete_details = fields.incompleteDetails;
|
|
932
|
-
}
|
|
933
|
-
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;
|
|
934
743
|
}
|
|
935
|
-
const TERMINAL_RESPONSE_STATUSES = new Set([
|
|
936
|
-
|
|
744
|
+
const TERMINAL_RESPONSE_STATUSES = /* @__PURE__ */ new Set([
|
|
745
|
+
"completed",
|
|
746
|
+
"failed",
|
|
747
|
+
"cancelled",
|
|
748
|
+
"incomplete"
|
|
749
|
+
]);
|
|
937
750
|
/**
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
751
|
+
* Checks whether a Responses API response has reached a status that should
|
|
752
|
+
* produce a final `$ai_generation` event.
|
|
753
|
+
*/
|
|
941
754
|
function isTerminalResponse(response) {
|
|
942
|
-
|
|
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;
|
|
943
768
|
}
|
|
944
|
-
|
|
945
769
|
/**
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
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
|
+
*/
|
|
949
773
|
function getResponseFailure(response) {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
}
|
|
953
|
-
return response.error ? {
|
|
954
|
-
...response.error
|
|
955
|
-
} : 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`);
|
|
956
776
|
}
|
|
957
|
-
|
|
777
|
+
//#endregion
|
|
778
|
+
//#region src/openai/background-responses.ts
|
|
958
779
|
function isPendingBackgroundResponse(params, response) {
|
|
959
|
-
|
|
780
|
+
return params.background === true && !!response.status && !isTerminalResponse(response);
|
|
960
781
|
}
|
|
961
|
-
|
|
962
782
|
/**
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
783
|
+
* Uses provider timestamps so background polling cadence does not inflate
|
|
784
|
+
* generation latency. Non-completed responses do not expose a terminal time.
|
|
785
|
+
*/
|
|
966
786
|
function getBackgroundResponseLatency(response) {
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
}
|
|
970
|
-
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);
|
|
971
789
|
}
|
|
972
|
-
|
|
973
790
|
/**
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
this.contexts.delete(responseID);
|
|
1002
|
-
}
|
|
1003
|
-
return context;
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
|
|
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
|
+
};
|
|
1007
818
|
/**
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
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
|
+
*/
|
|
1013
824
|
function wrapBackgroundResponseStream(stream, responseID, tracker, captureTerminalResponse) {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
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);
|
|
1027
835
|
}
|
|
1028
|
-
|
|
836
|
+
//#endregion
|
|
837
|
+
//#region src/providerPromise.ts
|
|
1029
838
|
function addResponseIds(result, response, requestIdHeader, workspaceIdHeader) {
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
properties._workspace_id = {
|
|
1041
|
-
value: response.headers.get(workspaceIdHeader),
|
|
1042
|
-
enumerable: false
|
|
1043
|
-
};
|
|
1044
|
-
}
|
|
1045
|
-
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);
|
|
1046
849
|
}
|
|
1047
850
|
function getResponsePropsPromise(parentPromise) {
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
}
|
|
1052
|
-
return responsePromise;
|
|
851
|
+
const responsePromise = parentPromise.responsePromise;
|
|
852
|
+
if (!responsePromise || typeof responsePromise.then !== "function") return;
|
|
853
|
+
return responsePromise;
|
|
1053
854
|
}
|
|
1054
855
|
function decorateProviderPromise(wrappedPromise, responsePropsPromise, requestIdHeader, workspaceIdHeader, preserveThenUnwrap) {
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
throw new Error('The provider promise response metadata is unavailable');
|
|
1074
|
-
}
|
|
1075
|
-
const transformedPromise = Promise.all([wrappedPromise, responsePropsPromise]).then(([data, props]) => addResponseIds(transform(data, props), props.response, requestIdHeader, workspaceIdHeader));
|
|
1076
|
-
return decorateProviderPromise(transformedPromise, responsePropsPromise, requestIdHeader, workspaceIdHeader, true);
|
|
1077
|
-
};
|
|
1078
|
-
}
|
|
1079
|
-
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;
|
|
1080
874
|
}
|
|
1081
|
-
|
|
1082
|
-
/**
|
|
1083
|
-
* Keep the provider SDK helpers on a promise whose resolved value is instrumented.
|
|
1084
|
-
* OpenAI's parse helpers compose create calls through `_thenUnwrap`, while both
|
|
1085
|
-
* OpenAI and Anthropic expose the raw response through `asResponse` and
|
|
1086
|
-
* `withResponse`.
|
|
1087
|
-
*/
|
|
1088
|
-
|
|
1089
875
|
function preserveProviderPromise(parentPromise, wrappedPromise, options = {}) {
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
};
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
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;
|
|
1110
892
|
}
|
|
1111
|
-
|
|
1112
893
|
/**
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
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
|
+
*/
|
|
1117
898
|
function callWithOriginalCreate(resource, originalCreate, callback) {
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
delete resourceRecord['create'];
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
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
|
+
}
|
|
1131
909
|
}
|
|
1132
|
-
|
|
910
|
+
//#endregion
|
|
911
|
+
//#region src/stream.ts
|
|
1133
912
|
/**
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
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
|
+
*/
|
|
1139
918
|
function monitoredStreamTee(source, createStream) {
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
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
|
-
reject
|
|
1310
|
-
});
|
|
1311
|
-
pump();
|
|
1312
|
-
});
|
|
1313
|
-
},
|
|
1314
|
-
return: value => cancelSource(value),
|
|
1315
|
-
throw: async error => {
|
|
1316
|
-
if (!sourceIterator.throw) {
|
|
1317
|
-
await cancelSource();
|
|
1318
|
-
throw error;
|
|
1319
|
-
}
|
|
1320
|
-
try {
|
|
1321
|
-
const result = await sourceIterator.throw(error);
|
|
1322
|
-
if (result.done) {
|
|
1323
|
-
terminalResult = result;
|
|
1324
|
-
removeAbortListener();
|
|
1325
|
-
settleCallersTerminal();
|
|
1326
|
-
}
|
|
1327
|
-
if (monitorPending) {
|
|
1328
|
-
monitorPending.resolve(result);
|
|
1329
|
-
monitorPending = undefined;
|
|
1330
|
-
} else {
|
|
1331
|
-
bufferedMonitorResult = result;
|
|
1332
|
-
}
|
|
1333
|
-
return result;
|
|
1334
|
-
} catch (sourceError) {
|
|
1335
|
-
terminalError = sourceError;
|
|
1336
|
-
hasTerminalError = true;
|
|
1337
|
-
removeAbortListener();
|
|
1338
|
-
settleMonitorTerminal();
|
|
1339
|
-
settleCallersTerminal();
|
|
1340
|
-
throw sourceError;
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
}), controller);
|
|
1344
|
-
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)];
|
|
1345
1088
|
}
|
|
1346
|
-
|
|
1089
|
+
//#endregion
|
|
1090
|
+
//#region src/openai/stream-accumulators.ts
|
|
1347
1091
|
/** Pure state accumulator for OpenAI-compatible Chat Completions chunks. */
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
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
|
-
return {
|
|
1429
|
-
output: [{
|
|
1430
|
-
role: 'assistant',
|
|
1431
|
-
content: content.length > 0 ? content : [{
|
|
1432
|
-
type: 'text',
|
|
1433
|
-
text: ''
|
|
1434
|
-
}]
|
|
1435
|
-
}],
|
|
1436
|
-
model: this.model,
|
|
1437
|
-
completionId: this.completionId,
|
|
1438
|
-
systemFingerprint: this.systemFingerprint,
|
|
1439
|
-
serviceTier: this.serviceTier,
|
|
1440
|
-
firstTokenTime: this.firstTokenTime,
|
|
1441
|
-
stopReason: this.stopReason,
|
|
1442
|
-
usage: {
|
|
1443
|
-
...this.usage
|
|
1444
|
-
}
|
|
1445
|
-
};
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
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
|
+
};
|
|
1448
1168
|
/** Pure state accumulator for OpenAI-compatible Responses stream events. */
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
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
|
-
serviceTier: this.serviceTier,
|
|
1494
|
-
firstTokenTime: this.firstTokenTime,
|
|
1495
|
-
stopReason: this.stopReason,
|
|
1496
|
-
usage: {
|
|
1497
|
-
...this.usage
|
|
1498
|
-
},
|
|
1499
|
-
terminalResponse: this.terminalResponse
|
|
1500
|
-
};
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
|
|
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
|
|
1504
1213
|
function captureAiGenerationInBackground(...args) {
|
|
1505
|
-
|
|
1214
|
+
captureAiGeneration(...args).catch(() => void 0);
|
|
1506
1215
|
}
|
|
1507
|
-
|
|
1508
1216
|
/** Preserve immediate delivery while isolating normal telemetry from provider latency/failures. */
|
|
1509
1217
|
async function captureAiGenerationAfterSuccess(...args) {
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
} else {
|
|
1513
|
-
captureAiGenerationInBackground(...args);
|
|
1514
|
-
}
|
|
1218
|
+
if (args[1].captureImmediate) await captureAiGeneration(...args);
|
|
1219
|
+
else captureAiGenerationInBackground(...args);
|
|
1515
1220
|
}
|
|
1516
1221
|
function buildChatUsage(usage, webSearchSource) {
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
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
|
+
};
|
|
1526
1231
|
}
|
|
1527
1232
|
function buildResponsesUsage(usage, webSearchSource) {
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
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
|
+
};
|
|
1537
1242
|
}
|
|
1538
1243
|
function buildChatSuccessOptions(context, result) {
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
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
|
+
};
|
|
1559
1264
|
}
|
|
1560
1265
|
function buildChatErrorOptions(context, error, metadata) {
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
error
|
|
1576
|
-
};
|
|
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
|
+
};
|
|
1577
1280
|
}
|
|
1578
1281
|
function buildSanitizedResponsesInput(context) {
|
|
1579
|
-
|
|
1282
|
+
return formatOpenAIResponsesInput(sanitizeOpenAIResponse(context.params.input, context.client), sanitizeOpenAIResponse(context.params.instructions, context.client));
|
|
1580
1283
|
}
|
|
1581
1284
|
function buildResponsesSuccessOptions(context, result) {
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
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
|
+
};
|
|
1608
1311
|
}
|
|
1609
1312
|
function buildBackgroundResponseOptions(context, response) {
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
includeRequestId: true
|
|
1618
|
-
});
|
|
1313
|
+
return buildResponsesSuccessOptions(context, {
|
|
1314
|
+
response,
|
|
1315
|
+
output: formatResponseOpenAI({ output: response.output }),
|
|
1316
|
+
latency: getBackgroundResponseLatency(response),
|
|
1317
|
+
includeTools: true,
|
|
1318
|
+
includeRequestId: true
|
|
1319
|
+
});
|
|
1619
1320
|
}
|
|
1620
1321
|
function buildResponsesErrorOptions(context, error, metadata) {
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
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
|
+
};
|
|
1634
1335
|
}
|
|
1635
1336
|
function buildEmbeddingSuccessOptions(context, usage, latency) {
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
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
|
+
};
|
|
1652
1353
|
}
|
|
1653
1354
|
function buildEmbeddingErrorOptions(context, error, latency) {
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1669
|
-
class PostHogAzureOpenAI extends AzureOpenAI {
|
|
1670
|
-
constructor(config) {
|
|
1671
|
-
const {
|
|
1672
|
-
posthog,
|
|
1673
|
-
...openAIConfig
|
|
1674
|
-
} = config;
|
|
1675
|
-
super(openAIConfig);
|
|
1676
|
-
this.phClient = posthog;
|
|
1677
|
-
this.chat = new WrappedChat$1(this, this.phClient);
|
|
1678
|
-
this.responses = new WrappedResponses$1(this, this.phClient);
|
|
1679
|
-
this.embeddings = new WrappedEmbeddings$1(this, this.phClient);
|
|
1680
|
-
}
|
|
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
|
+
};
|
|
1681
1368
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
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
|
+
}
|
|
1687
1380
|
};
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
// --- Overload #1: Non-streaming
|
|
1696
|
-
|
|
1697
|
-
// --- Overload #2: Streaming
|
|
1698
|
-
|
|
1699
|
-
// --- Overload #3: Generic base
|
|
1700
|
-
|
|
1701
|
-
// --- Implementation Signature
|
|
1702
|
-
create(body, options) {
|
|
1703
|
-
const {
|
|
1704
|
-
providerParams: openAIParams,
|
|
1705
|
-
posthogParams
|
|
1706
|
-
} = extractPosthogParams(body);
|
|
1707
|
-
const startTime = Date.now();
|
|
1708
|
-
const parentPromise = super.create(openAIParams, options);
|
|
1709
|
-
if (openAIParams.stream) {
|
|
1710
|
-
const wrappedPromise = parentPromise.then(value => {
|
|
1711
|
-
if (Symbol.asyncIterator in value) {
|
|
1712
|
-
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
1713
|
-
(async () => {
|
|
1714
|
-
const accumulator = new OpenAIChatStreamAccumulator();
|
|
1715
|
-
try {
|
|
1716
|
-
for await (const chunk of stream1) {
|
|
1717
|
-
accumulator.consume(chunk);
|
|
1718
|
-
}
|
|
1719
|
-
const accumulated = accumulator.result();
|
|
1720
|
-
await captureAiGeneration(this.phClient, buildChatSuccessOptions({
|
|
1721
|
-
client: this.phClient,
|
|
1722
|
-
provider: 'azure',
|
|
1723
|
-
baseURL: this.baseURL,
|
|
1724
|
-
params: openAIParams,
|
|
1725
|
-
monitoring: posthogParams,
|
|
1726
|
-
modelParametersSource: body
|
|
1727
|
-
}, {
|
|
1728
|
-
...accumulated,
|
|
1729
|
-
latency: (Date.now() - startTime) / 1000,
|
|
1730
|
-
timeToFirstToken: accumulated.firstTokenTime === undefined ? undefined : (accumulated.firstTokenTime - startTime) / 1000
|
|
1731
|
-
}));
|
|
1732
|
-
} catch (error) {
|
|
1733
|
-
const accumulated = accumulator.result();
|
|
1734
|
-
await captureAiGeneration(this.phClient, buildChatErrorOptions({
|
|
1735
|
-
client: this.phClient,
|
|
1736
|
-
provider: 'azure',
|
|
1737
|
-
baseURL: this.baseURL,
|
|
1738
|
-
params: openAIParams,
|
|
1739
|
-
monitoring: posthogParams,
|
|
1740
|
-
modelParametersSource: body
|
|
1741
|
-
}, error, {
|
|
1742
|
-
completionId: accumulated.completionId,
|
|
1743
|
-
systemFingerprint: accumulated.systemFingerprint,
|
|
1744
|
-
usage: accumulated.usage,
|
|
1745
|
-
latency: (Date.now() - startTime) / 1000
|
|
1746
|
-
}));
|
|
1747
|
-
throw error;
|
|
1748
|
-
}
|
|
1749
|
-
})().catch(() => {
|
|
1750
|
-
// Swallow: analytics must never crash the host process. The caller
|
|
1751
|
-
// already receives this error via their own tee of the stream.
|
|
1752
|
-
});
|
|
1753
|
-
|
|
1754
|
-
// Return the other stream to the user
|
|
1755
|
-
return stream2;
|
|
1756
|
-
}
|
|
1757
|
-
return value;
|
|
1758
|
-
});
|
|
1759
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
1760
|
-
} else {
|
|
1761
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
1762
|
-
if ('choices' in result) {
|
|
1763
|
-
await captureAiGenerationAfterSuccess(this.phClient, buildChatSuccessOptions({
|
|
1764
|
-
client: this.phClient,
|
|
1765
|
-
provider: 'azure',
|
|
1766
|
-
baseURL: this.baseURL,
|
|
1767
|
-
params: openAIParams,
|
|
1768
|
-
monitoring: posthogParams,
|
|
1769
|
-
modelParametersSource: body
|
|
1770
|
-
}, {
|
|
1771
|
-
output: formatResponseOpenAI(result),
|
|
1772
|
-
model: result.model,
|
|
1773
|
-
serviceTier: result.service_tier ?? undefined,
|
|
1774
|
-
latency: (Date.now() - startTime) / 1000,
|
|
1775
|
-
usage: buildChatUsage(result.usage, result),
|
|
1776
|
-
stopReason: result.choices[0]?.finish_reason ?? undefined,
|
|
1777
|
-
completionId: result.id,
|
|
1778
|
-
systemFingerprint: result.system_fingerprint,
|
|
1779
|
-
requestId: result._request_id
|
|
1780
|
-
}));
|
|
1781
|
-
}
|
|
1782
|
-
return result;
|
|
1783
|
-
}, async error => {
|
|
1784
|
-
await captureAiGeneration(this.phClient, buildChatErrorOptions({
|
|
1785
|
-
client: this.phClient,
|
|
1786
|
-
provider: 'azure',
|
|
1787
|
-
baseURL: this.baseURL,
|
|
1788
|
-
params: openAIParams,
|
|
1789
|
-
monitoring: posthogParams,
|
|
1790
|
-
modelParametersSource: body
|
|
1791
|
-
}, error, {
|
|
1792
|
-
latency: (Date.now() - startTime) / 1000
|
|
1793
|
-
}));
|
|
1794
|
-
throw error;
|
|
1795
|
-
});
|
|
1796
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1381
|
+
var WrappedChat$1 = class extends AzureOpenAI.Chat {
|
|
1382
|
+
constructor(parentClient, phClient) {
|
|
1383
|
+
super(parentClient);
|
|
1384
|
+
this.completions = new WrappedCompletions$1(parentClient, phClient);
|
|
1385
|
+
}
|
|
1799
1386
|
};
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
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
|
-
const accumulated = accumulator.result();
|
|
1885
|
-
if (openAIParams.background === true && accumulated.completionId && this.backgroundResponses.get(accumulated.completionId)) {
|
|
1886
|
-
throw error;
|
|
1887
|
-
}
|
|
1888
|
-
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1889
|
-
client: this.phClient,
|
|
1890
|
-
provider: 'azure',
|
|
1891
|
-
baseURL: this.baseURL,
|
|
1892
|
-
params: openAIParams,
|
|
1893
|
-
monitoring: posthogParams,
|
|
1894
|
-
modelParametersSource: body
|
|
1895
|
-
}, error, {
|
|
1896
|
-
completionId: accumulated.completionId,
|
|
1897
|
-
usage: accumulated.usage,
|
|
1898
|
-
latency: (Date.now() - startTime) / 1000
|
|
1899
|
-
}));
|
|
1900
|
-
throw error;
|
|
1901
|
-
}
|
|
1902
|
-
})().catch(() => {
|
|
1903
|
-
// Swallow: analytics must never crash the host process. The caller
|
|
1904
|
-
// already receives this error via their own tee of the stream.
|
|
1905
|
-
});
|
|
1906
|
-
return stream2;
|
|
1907
|
-
}
|
|
1908
|
-
return value;
|
|
1909
|
-
});
|
|
1910
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
1911
|
-
} else {
|
|
1912
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
1913
|
-
if ('output' in result) {
|
|
1914
|
-
if (isPendingBackgroundResponse(openAIParams, result)) {
|
|
1915
|
-
this.backgroundResponses.set(result.id, {
|
|
1916
|
-
openAIParams,
|
|
1917
|
-
posthogParams
|
|
1918
|
-
});
|
|
1919
|
-
return result;
|
|
1920
|
-
}
|
|
1921
|
-
await captureAiGenerationAfterSuccess(this.phClient, buildResponsesSuccessOptions({
|
|
1922
|
-
client: this.phClient,
|
|
1923
|
-
provider: 'azure',
|
|
1924
|
-
baseURL: this.baseURL,
|
|
1925
|
-
params: openAIParams,
|
|
1926
|
-
monitoring: posthogParams,
|
|
1927
|
-
modelParametersSource: body
|
|
1928
|
-
}, {
|
|
1929
|
-
response: result,
|
|
1930
|
-
output: formatResponseOpenAI({
|
|
1931
|
-
output: result.output
|
|
1932
|
-
}),
|
|
1933
|
-
latency: (Date.now() - startTime) / 1000,
|
|
1934
|
-
includeTools: true,
|
|
1935
|
-
includeRequestId: true
|
|
1936
|
-
}));
|
|
1937
|
-
}
|
|
1938
|
-
return result;
|
|
1939
|
-
}, async error => {
|
|
1940
|
-
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
1941
|
-
client: this.phClient,
|
|
1942
|
-
provider: 'azure',
|
|
1943
|
-
baseURL: this.baseURL,
|
|
1944
|
-
params: openAIParams,
|
|
1945
|
-
monitoring: posthogParams,
|
|
1946
|
-
modelParametersSource: body
|
|
1947
|
-
}, error, {
|
|
1948
|
-
latency: (Date.now() - startTime) / 1000
|
|
1949
|
-
}));
|
|
1950
|
-
throw error;
|
|
1951
|
-
});
|
|
1952
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
retrieve(responseID, query = {}, options) {
|
|
1956
|
-
const parentPromise = super.retrieve(responseID, query, options);
|
|
1957
|
-
|
|
1958
|
-
// Preserve the upstream promise and stream unchanged for responses that
|
|
1959
|
-
// were not created through this client.
|
|
1960
|
-
if (!this.backgroundResponses.get(responseID)) {
|
|
1961
|
-
return parentPromise;
|
|
1962
|
-
}
|
|
1963
|
-
if (query.stream) {
|
|
1964
|
-
return parentPromise._thenUnwrap(result => {
|
|
1965
|
-
if ('controller' in result) {
|
|
1966
|
-
return wrapBackgroundResponseStream(result, responseID, this.backgroundResponses, (response, context) => this.captureBackgroundResponse(response, context));
|
|
1967
|
-
}
|
|
1968
|
-
return result;
|
|
1969
|
-
});
|
|
1970
|
-
}
|
|
1971
|
-
return parentPromise._thenUnwrap(async result => {
|
|
1972
|
-
if (!('output' in result) || !isTerminalResponse(result)) {
|
|
1973
|
-
return result;
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
|
-
// Removing the context before capture makes concurrent or repeated
|
|
1977
|
-
// terminal polls idempotent.
|
|
1978
|
-
const context = this.backgroundResponses.take(responseID);
|
|
1979
|
-
if (context) {
|
|
1980
|
-
await this.captureBackgroundResponse(result, context).catch(() => undefined);
|
|
1981
|
-
}
|
|
1982
|
-
return result;
|
|
1983
|
-
});
|
|
1984
|
-
}
|
|
1985
|
-
cancel(responseID, options) {
|
|
1986
|
-
const parentPromise = super.cancel(responseID, options);
|
|
1987
|
-
|
|
1988
|
-
// Avoid wrapping calls that do not belong to a background response created
|
|
1989
|
-
// through this client, preserving the upstream APIPromise unchanged.
|
|
1990
|
-
if (!this.backgroundResponses.get(responseID)) {
|
|
1991
|
-
return parentPromise;
|
|
1992
|
-
}
|
|
1993
|
-
return parentPromise._thenUnwrap(async result => {
|
|
1994
|
-
if (!isTerminalResponse(result)) {
|
|
1995
|
-
return result;
|
|
1996
|
-
}
|
|
1997
|
-
const context = this.backgroundResponses.take(responseID);
|
|
1998
|
-
if (context) {
|
|
1999
|
-
await this.captureBackgroundResponse(result, context).catch(() => undefined);
|
|
2000
|
-
}
|
|
2001
|
-
return result;
|
|
2002
|
-
});
|
|
2003
|
-
}
|
|
2004
|
-
parse(body, options) {
|
|
2005
|
-
const {
|
|
2006
|
-
providerParams: openAIParams,
|
|
2007
|
-
posthogParams
|
|
2008
|
-
} = extractPosthogParams(body);
|
|
2009
|
-
const startTime = Date.now();
|
|
2010
|
-
const parentPromise = callWithOriginalCreate(this, super.create.bind(this), () => super.parse(openAIParams, options));
|
|
2011
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
2012
|
-
if (isPendingBackgroundResponse(openAIParams, result)) {
|
|
2013
|
-
this.backgroundResponses.set(result.id, {
|
|
2014
|
-
openAIParams,
|
|
2015
|
-
posthogParams
|
|
2016
|
-
});
|
|
2017
|
-
return result;
|
|
2018
|
-
}
|
|
2019
|
-
await captureAiGeneration(this.phClient, buildResponsesSuccessOptions({
|
|
2020
|
-
client: this.phClient,
|
|
2021
|
-
provider: 'azure',
|
|
2022
|
-
baseURL: this.baseURL,
|
|
2023
|
-
params: openAIParams,
|
|
2024
|
-
monitoring: posthogParams,
|
|
2025
|
-
modelParametersSource: body
|
|
2026
|
-
}, {
|
|
2027
|
-
response: result,
|
|
2028
|
-
output: result.output,
|
|
2029
|
-
latency: (Date.now() - startTime) / 1000,
|
|
2030
|
-
includeRequestId: true
|
|
2031
|
-
}));
|
|
2032
|
-
return result;
|
|
2033
|
-
}, async error => {
|
|
2034
|
-
await captureAiGeneration(this.phClient, buildResponsesErrorOptions({
|
|
2035
|
-
client: this.phClient,
|
|
2036
|
-
provider: 'azure',
|
|
2037
|
-
baseURL: this.baseURL,
|
|
2038
|
-
params: openAIParams,
|
|
2039
|
-
monitoring: posthogParams,
|
|
2040
|
-
modelParametersSource: body
|
|
2041
|
-
}, error, {
|
|
2042
|
-
latency: (Date.now() - startTime) / 1000
|
|
2043
|
-
}));
|
|
2044
|
-
throw error;
|
|
2045
|
-
});
|
|
2046
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
2047
|
-
}
|
|
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
|
+
}
|
|
2048
1471
|
};
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
2085
1656
|
};
|
|
2086
|
-
|
|
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
|
|
2087
1692
|
const Chat = OpenAI.Chat;
|
|
2088
1693
|
const Completions = Chat.Completions;
|
|
2089
1694
|
const Responses = OpenAI.Responses;
|
|
2090
1695
|
const Embeddings = OpenAI.Embeddings;
|
|
2091
1696
|
const Audio = OpenAI.Audio;
|
|
2092
1697
|
const Transcriptions = OpenAI.Audio.Transcriptions;
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
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
|
-
// --- Overload #1: Non-streaming
|
|
2525
|
-
|
|
2526
|
-
// --- Overload #2: Non-streaming
|
|
2527
|
-
|
|
2528
|
-
// --- Overload #3: Non-streaming
|
|
2529
|
-
|
|
2530
|
-
// --- Overload #4: Non-streaming
|
|
2531
|
-
|
|
2532
|
-
// --- Overload #5: Streaming
|
|
2533
|
-
|
|
2534
|
-
// --- Overload #6: Streaming
|
|
2535
|
-
|
|
2536
|
-
// --- Overload #7: Generic base
|
|
2537
|
-
|
|
2538
|
-
// --- Implementation Signature
|
|
2539
|
-
create(body, options) {
|
|
2540
|
-
const {
|
|
2541
|
-
providerParams: openAIParams,
|
|
2542
|
-
posthogParams
|
|
2543
|
-
} = extractPosthogParams(body);
|
|
2544
|
-
const startTime = Date.now();
|
|
2545
|
-
const parentPromise = openAIParams.stream ? super.create(openAIParams, options) : super.create(openAIParams, options);
|
|
2546
|
-
if (openAIParams.stream) {
|
|
2547
|
-
const wrappedPromise = parentPromise.then(value => {
|
|
2548
|
-
if (Symbol.asyncIterator in value) {
|
|
2549
|
-
const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new Stream(iterator, controller));
|
|
2550
|
-
(async () => {
|
|
2551
|
-
let usage = {};
|
|
2552
|
-
try {
|
|
2553
|
-
let finalContent = '';
|
|
2554
|
-
let firstTokenTime;
|
|
2555
|
-
const doneEvent = 'transcript.text.done';
|
|
2556
|
-
for await (const chunk of stream1) {
|
|
2557
|
-
// Track first token on text delta events
|
|
2558
|
-
if (firstTokenTime === undefined && chunk.type === 'transcript.text.delta') {
|
|
2559
|
-
firstTokenTime = Date.now();
|
|
2560
|
-
}
|
|
2561
|
-
if (chunk.type === doneEvent && 'text' in chunk && chunk.text && chunk.text.length > 0) {
|
|
2562
|
-
finalContent = chunk.text;
|
|
2563
|
-
}
|
|
2564
|
-
if ('usage' in chunk && chunk.usage) {
|
|
2565
|
-
usage = {
|
|
2566
|
-
inputTokens: chunk.usage?.type === 'tokens' ? chunk.usage.input_tokens ?? 0 : 0,
|
|
2567
|
-
outputTokens: chunk.usage?.type === 'tokens' ? chunk.usage.output_tokens ?? 0 : 0,
|
|
2568
|
-
rawUsage: chunk.usage
|
|
2569
|
-
};
|
|
2570
|
-
}
|
|
2571
|
-
}
|
|
2572
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
2573
|
-
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
2574
|
-
const availableTools = extractAvailableToolCalls('openai', openAIParams);
|
|
2575
|
-
await captureAiGeneration(this.phClient, {
|
|
2576
|
-
...posthogParams,
|
|
2577
|
-
model: openAIParams.model,
|
|
2578
|
-
provider: 'openai',
|
|
2579
|
-
input: openAIParams.prompt,
|
|
2580
|
-
output: sanitizeOpenAIResponse(finalContent, this.phClient),
|
|
2581
|
-
latency,
|
|
2582
|
-
timeToFirstToken,
|
|
2583
|
-
baseURL: this.baseURL,
|
|
2584
|
-
modelParameters: getModelParams(body),
|
|
2585
|
-
httpStatus: 200,
|
|
2586
|
-
usage,
|
|
2587
|
-
tools: availableTools
|
|
2588
|
-
});
|
|
2589
|
-
} catch (error) {
|
|
2590
|
-
await captureAiGeneration(this.phClient, {
|
|
2591
|
-
...posthogParams,
|
|
2592
|
-
model: openAIParams.model,
|
|
2593
|
-
provider: 'openai',
|
|
2594
|
-
input: openAIParams.prompt,
|
|
2595
|
-
output: [],
|
|
2596
|
-
latency: (Date.now() - startTime) / 1000,
|
|
2597
|
-
baseURL: this.baseURL,
|
|
2598
|
-
modelParameters: getModelParams(body),
|
|
2599
|
-
usage,
|
|
2600
|
-
error
|
|
2601
|
-
});
|
|
2602
|
-
throw error;
|
|
2603
|
-
}
|
|
2604
|
-
})().catch(() => {
|
|
2605
|
-
// Swallow: analytics must never crash the host process. The caller
|
|
2606
|
-
// already receives this error via their own tee of the stream.
|
|
2607
|
-
});
|
|
2608
|
-
return stream2;
|
|
2609
|
-
}
|
|
2610
|
-
return value;
|
|
2611
|
-
});
|
|
2612
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
2613
|
-
} else {
|
|
2614
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
2615
|
-
if (result && typeof result === 'object' && 'text' in result) {
|
|
2616
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
2617
|
-
await captureAiGenerationAfterSuccess(this.phClient, {
|
|
2618
|
-
...posthogParams,
|
|
2619
|
-
model: openAIParams.model,
|
|
2620
|
-
provider: 'openai',
|
|
2621
|
-
input: openAIParams.prompt,
|
|
2622
|
-
output: sanitizeOpenAIResponse(result.text, this.phClient),
|
|
2623
|
-
latency,
|
|
2624
|
-
baseURL: this.baseURL,
|
|
2625
|
-
modelParameters: getModelParams(body),
|
|
2626
|
-
httpStatus: 200,
|
|
2627
|
-
usage: {
|
|
2628
|
-
inputTokens: result.usage?.type === 'tokens' ? result.usage.input_tokens ?? 0 : 0,
|
|
2629
|
-
outputTokens: result.usage?.type === 'tokens' ? result.usage.output_tokens ?? 0 : 0,
|
|
2630
|
-
rawUsage: result.usage
|
|
2631
|
-
}
|
|
2632
|
-
});
|
|
2633
|
-
}
|
|
2634
|
-
return result;
|
|
2635
|
-
}, async error => {
|
|
2636
|
-
await captureAiGeneration(this.phClient, {
|
|
2637
|
-
...posthogParams,
|
|
2638
|
-
model: openAIParams.model,
|
|
2639
|
-
provider: 'openai',
|
|
2640
|
-
input: openAIParams.prompt,
|
|
2641
|
-
output: [],
|
|
2642
|
-
latency: (Date.now() - startTime) / 1000,
|
|
2643
|
-
baseURL: this.baseURL,
|
|
2644
|
-
modelParameters: getModelParams(body),
|
|
2645
|
-
usage: {},
|
|
2646
|
-
error
|
|
2647
|
-
});
|
|
2648
|
-
throw error;
|
|
2649
|
-
});
|
|
2650
|
-
return preserveProviderPromise(parentPromise, wrappedPromise);
|
|
2651
|
-
}
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
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 };
|
|
2654
2129
|
|
|
2655
|
-
|
|
2656
|
-
//# sourceMappingURL=index.mjs.map
|
|
2130
|
+
//# sourceMappingURL=index.mjs.map
|