@posthog/ai 8.10.0 → 8.10.2

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