@posthog/ai 8.9.3 → 8.10.1

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