@posthog/ai 8.10.0 → 8.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/adk/index.cjs +908 -1198
  2. package/dist/adk/index.cjs.map +1 -1
  3. package/dist/adk/index.d.ts +109 -109
  4. package/dist/adk/index.mjs +907 -1196
  5. package/dist/adk/index.mjs.map +1 -1
  6. package/dist/anthropic/index.cjs +927 -1111
  7. package/dist/anthropic/index.cjs.map +1 -1
  8. package/dist/anthropic/index.d.ts +34 -33
  9. package/dist/anthropic/index.mjs +899 -1102
  10. package/dist/anthropic/index.mjs.map +1 -1
  11. package/dist/gemini/index.cjs +863 -1108
  12. package/dist/gemini/index.cjs.map +1 -1
  13. package/dist/gemini/index.d.ts +38 -35
  14. package/dist/gemini/index.mjs +858 -1103
  15. package/dist/gemini/index.mjs.map +1 -1
  16. package/dist/index.cjs +1218 -1546
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.ts +170 -163
  19. package/dist/index.mjs +1216 -1544
  20. package/dist/index.mjs.map +1 -1
  21. package/dist/langchain/index.cjs +851 -1029
  22. package/dist/langchain/index.cjs.map +1 -1
  23. package/dist/langchain/index.d.ts +75 -75
  24. package/dist/langchain/index.mjs +850 -1027
  25. package/dist/langchain/index.mjs.map +1 -1
  26. package/dist/langchain/middleware/index.cjs +1016 -1225
  27. package/dist/langchain/middleware/index.cjs.map +1 -1
  28. package/dist/langchain/middleware/index.d.ts +29 -25
  29. package/dist/langchain/middleware/index.mjs +1015 -1223
  30. package/dist/langchain/middleware/index.mjs.map +1 -1
  31. package/dist/openai/index.cjs +1990 -2523
  32. package/dist/openai/index.cjs.map +1 -1
  33. package/dist/openai/index.d.ts +106 -104
  34. package/dist/openai/index.mjs +1985 -2518
  35. package/dist/openai/index.mjs.map +1 -1
  36. package/dist/openai-agents/index.cjs +745 -827
  37. package/dist/openai-agents/index.cjs.map +1 -1
  38. package/dist/openai-agents/index.d.ts +48 -47
  39. package/dist/openai-agents/index.mjs +744 -825
  40. package/dist/openai-agents/index.mjs.map +1 -1
  41. package/dist/otel/index.cjs +427 -486
  42. package/dist/otel/index.cjs.map +1 -1
  43. package/dist/otel/index.d.ts +36 -35
  44. package/dist/otel/index.mjs +426 -484
  45. package/dist/otel/index.mjs.map +1 -1
  46. package/dist/vercel/index.cjs +992 -1343
  47. package/dist/vercel/index.cjs.map +1 -1
  48. package/dist/vercel/index.d.ts +21 -16
  49. package/dist/vercel/index.mjs +991 -1341
  50. package/dist/vercel/index.mjs.map +1 -1
  51. package/package.json +15 -14
@@ -1,1179 +1,995 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var AnthropicOriginal = require('@anthropic-ai/sdk');
6
- var uuid = require('uuid');
7
- var core = require('@posthog/core');
8
- var streaming = require('@anthropic-ai/sdk/streaming');
9
-
10
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
-
12
- var AnthropicOriginal__default = /*#__PURE__*/_interopDefault(AnthropicOriginal);
13
-
14
- /** @internal */
15
-
16
- /** @internal */
17
-
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ //#region \0rolldown/runtime.js
6
+ var __create = Object.create;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
14
+ key = keys[i];
15
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: ((k) => from[k]).bind(null, key),
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+ //#endregion
27
+ let _anthropic_ai_sdk = require("@anthropic-ai/sdk");
28
+ _anthropic_ai_sdk = __toESM(_anthropic_ai_sdk);
29
+ let uuid = require("uuid");
30
+ let _posthog_core = require("@posthog/core");
31
+ let _anthropic_ai_sdk_streaming = require("@anthropic-ai/sdk/streaming");
32
+ //#region src/captureAiEvent.ts
18
33
  /** @internal */
19
34
  function isFullAiCaptureEnabled(client) {
20
- return client?.enableFullAiCapture === true;
35
+ return client?.enableFullAiCapture === true;
21
36
  }
22
-
23
37
  /** @internal */
24
38
  function captureAiEvent(client, event) {
25
- if (isFullAiCaptureEnabled(client) && typeof client.captureAi === 'function') {
26
- client.captureAi(event);
27
- return;
28
- }
29
- client.capture(event);
39
+ if (isFullAiCaptureEnabled(client) && typeof client.captureAi === "function") {
40
+ client.captureAi(event);
41
+ return;
42
+ }
43
+ client.capture(event);
30
44
  }
31
-
32
45
  /** @internal */
33
46
  async function captureAiEventImmediate(client, event) {
34
- if (isFullAiCaptureEnabled(client) && typeof client.captureAiImmediate === 'function') {
35
- await client.captureAiImmediate(event);
36
- return;
37
- }
38
- await client.captureImmediate(event);
47
+ if (isFullAiCaptureEnabled(client) && typeof client.captureAiImmediate === "function") {
48
+ await client.captureAiImmediate(event);
49
+ return;
50
+ }
51
+ await client.captureImmediate(event);
39
52
  }
40
-
53
+ //#endregion
54
+ //#region src/sanitization/base64_recognizer.ts
41
55
  const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
42
56
  const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
43
- class Base64Recognizer {
44
- recognize(value, minLength) {
45
- const dataUrl = DATA_URL_PREFIX_RE.exec(value);
46
- if (dataUrl) return {
47
- kind: 'data-url',
48
- mediaType: dataUrl[1]
49
- };
50
- if (value.length < minLength) return {
51
- kind: 'none'
52
- };
53
- const confidencePrefix = value.slice(0, minLength);
54
- if (BASE64_ALPHABET_RE.test(confidencePrefix)) {
55
- return {
56
- kind: 'raw'
57
- };
58
- } else {
59
- return {
60
- kind: 'none'
61
- };
62
- }
63
- }
64
- }
65
-
66
- const MIME_HINT_KEYS = ['mediaType', 'media_type', 'mimeType', 'mime_type'];
67
- 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']);
68
- const STRONG_CONTEXT_TYPES = new Set(['image', 'image_url', 'input_image', 'audio', 'input_audio', 'video', 'video_url', 'file', 'input_file', 'document', 'media', 'file-data']);
69
- const FILE_FAMILY_TYPES = new Set(['file', 'input_file', 'document', 'media', 'file-data']);
70
- const KNOWN_AUDIO_FORMATS = new Set(['wav', 'mp3', 'ogg', 'flac', 'm4a', 'aac', 'webm']);
71
- class MediaTypeContext {
72
- static EMPTY = new MediaTypeContext(undefined, undefined);
73
- constructor(parent, key, explicitMediaType) {
74
- this.parent = parent;
75
- this.key = key;
76
- this.explicitMediaType = explicitMediaType;
77
- }
78
- inferMediaType() {
79
- return this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey();
80
- }
81
- inferFromSiblingMime() {
82
- if (this.explicitMediaType) return this.explicitMediaType;
83
- if (!this.parent) return undefined;
84
- for (const hint of MIME_HINT_KEYS) {
85
- const v = this.parent[hint];
86
- if (typeof v === 'string') return v;
87
- }
88
- return undefined;
89
- }
90
- inferFromSiblingFormat() {
91
- if (!this.parent) return undefined;
92
- const fmt = this.parent.format;
93
- if (typeof fmt === 'string' && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) {
94
- return `audio/${fmt.toLowerCase()}`;
95
- }
96
- return undefined;
97
- }
98
- inferFromParentType() {
99
- if (!this.parent) return undefined;
100
- const t = this.parent.type;
101
- if (typeof t !== 'string') return undefined;
102
- if (t === 'image' || t === 'image_url' || t === 'input_image') return 'image';
103
- if (t === 'audio' || t === 'input_audio') return 'audio';
104
- if (t === 'video' || t === 'video_url') return 'video';
105
- if (FILE_FAMILY_TYPES.has(t)) return 'application/octet-stream';
106
- return undefined;
107
- }
108
- inferFromKey() {
109
- if (!this.key) return undefined;
110
- const key = this.key.toLowerCase();
111
- if (key.includes('audio')) return 'audio';
112
- if (key.includes('video')) return 'video';
113
- if (key.includes('image')) return 'image';
114
- if (key.includes('file') || key.includes('document')) return 'application/octet-stream';
115
- return undefined;
116
- }
117
- hasExplicitBinaryMediaType() {
118
- if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false;
119
- const mediaType = this.inferFromSiblingMime();
120
- return mediaType !== undefined && !mediaType.toLowerCase().startsWith('text/');
121
- }
122
- signalsBinary() {
123
- if (this.explicitMediaType) return true;
124
- if (this.parent) {
125
- for (const hint of MIME_HINT_KEYS) {
126
- if (typeof this.parent[hint] === 'string') return true;
127
- }
128
- const fmt = this.parent.format;
129
- if (typeof fmt === 'string' && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return true;
130
- const t = this.parent.type;
131
- if (typeof t === 'string' && STRONG_CONTEXT_TYPES.has(t)) return true;
132
- }
133
- if (this.key && STRONG_CONTEXT_KEYS.has(this.key)) return true;
134
- return false;
135
- }
136
- }
137
-
57
+ var Base64Recognizer = class {
58
+ recognize(value, minLength) {
59
+ const dataUrl = DATA_URL_PREFIX_RE.exec(value);
60
+ if (dataUrl) return {
61
+ kind: "data-url",
62
+ mediaType: dataUrl[1]
63
+ };
64
+ if (value.length < minLength) return { kind: "none" };
65
+ const confidencePrefix = value.slice(0, minLength);
66
+ if (BASE64_ALPHABET_RE.test(confidencePrefix)) return { kind: "raw" };
67
+ else return { kind: "none" };
68
+ }
69
+ };
70
+ //#endregion
71
+ //#region src/sanitization/media_type_context.ts
72
+ const MIME_HINT_KEYS = [
73
+ "mediaType",
74
+ "media_type",
75
+ "mimeType",
76
+ "mime_type"
77
+ ];
78
+ const STRONG_CONTEXT_KEYS = /* @__PURE__ */ new Set([
79
+ "data",
80
+ "file_data",
81
+ "fileData",
82
+ "image_url",
83
+ "imageUrl",
84
+ "video_url",
85
+ "videoUrl",
86
+ "audio",
87
+ "audio_data",
88
+ "audioData",
89
+ "inline_data",
90
+ "inlineData",
91
+ "source",
92
+ "result"
93
+ ]);
94
+ const STRONG_CONTEXT_TYPES = /* @__PURE__ */ new Set([
95
+ "image",
96
+ "image_url",
97
+ "input_image",
98
+ "audio",
99
+ "input_audio",
100
+ "video",
101
+ "video_url",
102
+ "file",
103
+ "input_file",
104
+ "document",
105
+ "media",
106
+ "file-data"
107
+ ]);
108
+ const FILE_FAMILY_TYPES = /* @__PURE__ */ new Set([
109
+ "file",
110
+ "input_file",
111
+ "document",
112
+ "media",
113
+ "file-data"
114
+ ]);
115
+ const KNOWN_AUDIO_FORMATS = /* @__PURE__ */ new Set([
116
+ "wav",
117
+ "mp3",
118
+ "ogg",
119
+ "flac",
120
+ "m4a",
121
+ "aac",
122
+ "webm"
123
+ ]);
124
+ var MediaTypeContext = class MediaTypeContext {
125
+ static {
126
+ this.EMPTY = new MediaTypeContext(void 0, void 0);
127
+ }
128
+ constructor(parent, key, explicitMediaType) {
129
+ this.parent = parent;
130
+ this.key = key;
131
+ this.explicitMediaType = explicitMediaType;
132
+ }
133
+ inferMediaType() {
134
+ return this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey();
135
+ }
136
+ inferFromSiblingMime() {
137
+ if (this.explicitMediaType) return this.explicitMediaType;
138
+ if (!this.parent) return void 0;
139
+ for (const hint of MIME_HINT_KEYS) {
140
+ const v = this.parent[hint];
141
+ if (typeof v === "string") return v;
142
+ }
143
+ }
144
+ inferFromSiblingFormat() {
145
+ if (!this.parent) return void 0;
146
+ const fmt = this.parent.format;
147
+ if (typeof fmt === "string" && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return `audio/${fmt.toLowerCase()}`;
148
+ }
149
+ inferFromParentType() {
150
+ if (!this.parent) return void 0;
151
+ const t = this.parent.type;
152
+ if (typeof t !== "string") return void 0;
153
+ if (t === "image" || t === "image_url" || t === "input_image") return "image";
154
+ if (t === "audio" || t === "input_audio") return "audio";
155
+ if (t === "video" || t === "video_url") return "video";
156
+ if (FILE_FAMILY_TYPES.has(t)) return "application/octet-stream";
157
+ }
158
+ inferFromKey() {
159
+ if (!this.key) return void 0;
160
+ const key = this.key.toLowerCase();
161
+ if (key.includes("audio")) return "audio";
162
+ if (key.includes("video")) return "video";
163
+ if (key.includes("image")) return "image";
164
+ if (key.includes("file") || key.includes("document")) return "application/octet-stream";
165
+ }
166
+ hasExplicitBinaryMediaType() {
167
+ if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false;
168
+ const mediaType = this.inferFromSiblingMime();
169
+ return mediaType !== void 0 && !mediaType.toLowerCase().startsWith("text/");
170
+ }
171
+ signalsBinary() {
172
+ if (this.explicitMediaType) return true;
173
+ if (this.parent) {
174
+ for (const hint of MIME_HINT_KEYS) if (typeof this.parent[hint] === "string") return true;
175
+ const fmt = this.parent.format;
176
+ if (typeof fmt === "string" && KNOWN_AUDIO_FORMATS.has(fmt.toLowerCase())) return true;
177
+ const t = this.parent.type;
178
+ if (typeof t === "string" && STRONG_CONTEXT_TYPES.has(t)) return true;
179
+ }
180
+ if (this.key && STRONG_CONTEXT_KEYS.has(this.key)) return true;
181
+ return false;
182
+ }
183
+ };
184
+ //#endregion
185
+ //#region src/sanitization/binary_content_redactor.ts
138
186
  const STRONG_CONTEXT_MIN_LENGTH = 64;
139
187
  const WEAK_CONTEXT_MIN_LENGTH = 1024;
140
- class BinaryContentRedactor {
141
- visited = new WeakSet();
142
- constructor(recognizer = new Base64Recognizer()) {
143
- this.recognizer = recognizer;
144
- }
145
- redact(value, mediaType) {
146
- this.visited = new WeakSet();
147
- return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY);
148
- }
149
- walk(value, ctx) {
150
- if (value === null || value === undefined) return value;
151
- if (typeof value === 'string') return this.redactString(value, ctx);
152
- if (typeof value !== 'object') return value;
153
-
154
- // Buffer extends Uint8Array, so this branch catches both.
155
- if (typeof Uint8Array !== 'undefined' && value instanceof Uint8Array) {
156
- return this.placeholderFor(ctx.inferMediaType());
157
- }
158
- if (this.visited.has(value)) return null;
159
- this.visited.add(value);
160
- if (Array.isArray(value)) {
161
- return value.map(item => this.walk(item, ctx));
162
- }
163
- const obj = value;
164
- const out = {};
165
- for (const k of Object.keys(obj)) {
166
- out[k] = this.walk(obj[k], new MediaTypeContext(obj, k));
167
- }
168
- return out;
169
- }
170
- redactString(value, ctx) {
171
- const hasExplicitBinaryMediaType = ctx.hasExplicitBinaryMediaType();
172
- const recognitionValue = hasExplicitBinaryMediaType ? value.replace(/[\r\n]/g, '') : value;
173
- const minLength = hasExplicitBinaryMediaType ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH) : ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
174
- const recognition = this.recognizer.recognize(recognitionValue, minLength);
175
- switch (recognition.kind) {
176
- case 'data-url':
177
- return this.placeholderFor(recognition.mediaType);
178
- case 'raw':
179
- return this.placeholderFor(ctx.inferMediaType());
180
- case 'none':
181
- return value;
182
- }
183
- }
184
- placeholderFor(mediaType) {
185
- if (!mediaType) return '[base64 redacted]';
186
- if (mediaType === 'application/octet-stream') return '[base64 file redacted]';
187
- return `[base64 ${mediaType} redacted]`;
188
- }
189
- }
190
-
188
+ var BinaryContentRedactor = class {
189
+ constructor(recognizer = new Base64Recognizer()) {
190
+ this.recognizer = recognizer;
191
+ this.visited = /* @__PURE__ */ new WeakSet();
192
+ }
193
+ redact(value, mediaType) {
194
+ this.visited = /* @__PURE__ */ new WeakSet();
195
+ return this.walk(value, mediaType ? new MediaTypeContext(void 0, void 0, mediaType) : MediaTypeContext.EMPTY);
196
+ }
197
+ walk(value, ctx) {
198
+ if (value === null || value === void 0) return value;
199
+ if (typeof value === "string") return this.redactString(value, ctx);
200
+ if (typeof value !== "object") return value;
201
+ if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return this.placeholderFor(ctx.inferMediaType());
202
+ if (this.visited.has(value)) return null;
203
+ this.visited.add(value);
204
+ if (Array.isArray(value)) return value.map((item) => this.walk(item, ctx));
205
+ const obj = value;
206
+ const out = {};
207
+ for (const k of Object.keys(obj)) out[k] = this.walk(obj[k], new MediaTypeContext(obj, k));
208
+ return out;
209
+ }
210
+ redactString(value, ctx) {
211
+ const hasExplicitBinaryMediaType = ctx.hasExplicitBinaryMediaType();
212
+ const recognitionValue = hasExplicitBinaryMediaType ? value.replace(/[\r\n]/g, "") : value;
213
+ const minLength = hasExplicitBinaryMediaType ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH) : ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
214
+ const recognition = this.recognizer.recognize(recognitionValue, minLength);
215
+ switch (recognition.kind) {
216
+ case "data-url": return this.placeholderFor(recognition.mediaType);
217
+ case "raw": return this.placeholderFor(ctx.inferMediaType());
218
+ case "none": return value;
219
+ }
220
+ }
221
+ placeholderFor(mediaType) {
222
+ if (!mediaType) return "[base64 redacted]";
223
+ if (mediaType === "application/octet-stream") return "[base64 file redacted]";
224
+ return `[base64 ${mediaType} redacted]`;
225
+ }
226
+ };
227
+ //#endregion
228
+ //#region src/sanitization.ts
191
229
  const redactor = new BinaryContentRedactor();
192
230
  const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
193
231
  const sanitizeAnthropic = (data, client) => sanitize(data, client);
194
-
195
- 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']);
196
-
232
+ //#endregion
233
+ //#region src/utils.ts
234
+ const TOKEN_PROPERTY_KEYS = /* @__PURE__ */ new Set([
235
+ "$ai_input_tokens",
236
+ "$ai_output_tokens",
237
+ "$ai_cache_read_input_tokens",
238
+ "$ai_cache_creation_input_tokens",
239
+ "$ai_total_tokens",
240
+ "$ai_reasoning_tokens"
241
+ ]);
197
242
  /**
198
- * Whether the caller supplied their own token counts, which override the ones the SDK
199
- * derived from the provider response.
200
- */
243
+ * Whether the caller supplied their own token counts, which override the ones the SDK
244
+ * derived from the provider response.
245
+ */
201
246
  function hasTokenOverrides(posthogProperties) {
202
- return !!posthogProperties && Object.keys(posthogProperties).some(key => TOKEN_PROPERTY_KEYS.has(key));
247
+ return !!posthogProperties && Object.keys(posthogProperties).some((key) => TOKEN_PROPERTY_KEYS.has(key));
203
248
  }
204
249
  function getTokensSource(posthogProperties) {
205
- return hasTokenOverrides(posthogProperties) ? 'passthrough' : 'sdk';
250
+ return hasTokenOverrides(posthogProperties) ? "passthrough" : "sdk";
206
251
  }
207
- const STRING_FORMAT = 'utf8';
208
-
209
- // Reused across calls to avoid per-invocation allocation; truncate() runs
210
- // hundreds of times for prompts with many parts.
252
+ const STRING_FORMAT = "utf8";
211
253
  new TextEncoder();
212
- new TextDecoder(STRING_FORMAT, {
213
- fatal: false
214
- });
254
+ new TextDecoder(STRING_FORMAT, { fatal: false });
215
255
  const getModelParams = (params, responseServiceTier) => {
216
- if (!params) {
217
- return {};
218
- }
219
- const modelParams = {};
220
- 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'];
221
- for (const key of paramKeys) {
222
- if (key in params && params[key] !== undefined) {
223
- modelParams[key] = params[key];
224
- }
225
- }
226
- return modelParams;
256
+ if (!params) return {};
257
+ const modelParams = {};
258
+ for (const key of [
259
+ "temperature",
260
+ "max_tokens",
261
+ "max_completion_tokens",
262
+ "top_p",
263
+ "frequency_penalty",
264
+ "presence_penalty",
265
+ "n",
266
+ "stop",
267
+ "stream",
268
+ "streaming",
269
+ "language",
270
+ "response_format",
271
+ "timestamp_granularities",
272
+ "service_tier"
273
+ ]) if (key in params && params[key] !== void 0) modelParams[key] = params[key];
274
+ if (responseServiceTier != null) modelParams.service_tier = responseServiceTier;
275
+ return modelParams;
227
276
  };
228
- const formatResponseAnthropic = response => {
229
- const output = [];
230
- const content = [];
231
- for (const choice of response.content ?? []) {
232
- if (choice?.type === 'text' && choice?.text) {
233
- content.push({
234
- type: 'text',
235
- text: choice.text
236
- });
237
- } else if (choice?.type === 'tool_use' && choice?.name && choice?.id) {
238
- content.push({
239
- type: 'function',
240
- id: choice.id,
241
- function: {
242
- name: choice.name,
243
- arguments: choice.input || {}
244
- }
245
- });
246
- }
247
- }
248
- if (content.length > 0) {
249
- output.push({
250
- role: 'assistant',
251
- content
252
- });
253
- }
254
- return output;
277
+ const formatResponseAnthropic = (response) => {
278
+ const output = [];
279
+ const content = [];
280
+ for (const choice of response.content ?? []) if (choice?.type === "text" && choice?.text) content.push({
281
+ type: "text",
282
+ text: choice.text
283
+ });
284
+ else if (choice?.type === "tool_use" && choice?.name && choice?.id) content.push({
285
+ type: "function",
286
+ id: choice.id,
287
+ function: {
288
+ name: choice.name,
289
+ arguments: choice.input || {}
290
+ }
291
+ });
292
+ if (content.length > 0) output.push({
293
+ role: "assistant",
294
+ content
295
+ });
296
+ return output;
255
297
  };
256
298
  const mergeSystemPrompt = (params, provider) => {
257
- {
258
- const messages = params.messages || [];
259
- if (!params.system) {
260
- return messages;
261
- }
262
- const systemMessage = params.system;
263
- return [{
264
- role: 'system',
265
- content: systemMessage
266
- }, ...messages];
267
- }
299
+ if (provider == "anthropic") {
300
+ const messages = params.messages || [];
301
+ if (!params.system) return messages;
302
+ return [{
303
+ role: "system",
304
+ content: params.system
305
+ }, ...messages];
306
+ }
307
+ return params.messages;
268
308
  };
269
309
  const withPrivacyMode = (client, privacyMode, input) => {
270
- return client.privacy_mode || privacyMode ? null : input;
310
+ return client.privacy_mode || privacyMode ? null : input;
271
311
  };
272
-
273
312
  /**
274
- * Extract available tool calls from the request parameters.
275
- * These are the tools provided to the LLM, not the tool calls in the response.
276
- */
313
+ * Extract available tool calls from the request parameters.
314
+ * These are the tools provided to the LLM, not the tool calls in the response.
315
+ */
277
316
  const extractAvailableToolCalls = (provider, params) => {
278
- {
279
- if (params.tools) {
280
- return params.tools;
281
- }
282
- return null;
283
- }
317
+ if (provider === "anthropic") {
318
+ if (params.tools) return params.tools;
319
+ return null;
320
+ } else if (provider === "gemini") {
321
+ if (params.config && params.config.tools) return params.config.tools;
322
+ return null;
323
+ } else if (provider === "openai") {
324
+ if (params.tools) return params.tools;
325
+ return null;
326
+ } else if (provider === "vercel") {
327
+ if (params.tools) return params.tools;
328
+ return null;
329
+ }
330
+ return null;
284
331
  };
285
- let AIEvent = /*#__PURE__*/function (AIEvent) {
286
- AIEvent["Generation"] = "$ai_generation";
287
- AIEvent["Embedding"] = "$ai_embedding";
288
- return AIEvent;
289
- }({});
290
332
  function sanitizeValues(obj) {
291
- if (obj === undefined || obj === null) {
292
- return obj;
293
- }
294
- const jsonSafe = JSON.parse(JSON.stringify(obj));
295
- if (typeof jsonSafe === 'string') {
296
- // Sanitize lone surrogates by round-tripping through UTF-8
297
- return new TextDecoder().decode(new TextEncoder().encode(jsonSafe));
298
- } else if (Array.isArray(jsonSafe)) {
299
- return jsonSafe.map(sanitizeValues);
300
- } else if (jsonSafe && typeof jsonSafe === 'object') {
301
- return Object.fromEntries(Object.entries(jsonSafe).map(([k, v]) => [k, sanitizeValues(v)]));
302
- }
303
- return jsonSafe;
333
+ if (obj === void 0 || obj === null) return obj;
334
+ const jsonSafe = JSON.parse(JSON.stringify(obj));
335
+ if (typeof jsonSafe === "string") return new TextDecoder().decode(new TextEncoder().encode(jsonSafe));
336
+ else if (Array.isArray(jsonSafe)) return jsonSafe.map(sanitizeValues);
337
+ else if (jsonSafe && typeof jsonSafe === "object") return Object.fromEntries(Object.entries(jsonSafe).map(([k, v]) => [k, sanitizeValues(v)]));
338
+ return jsonSafe;
304
339
  }
305
340
  const POSTHOG_PARAMS_MAP = {
306
- posthogDistinctId: 'distinctId',
307
- posthogTraceId: 'traceId',
308
- posthogProperties: 'properties',
309
- posthogPrivacyMode: 'privacyMode',
310
- posthogGroups: 'groups',
311
- posthogModelOverride: 'modelOverride',
312
- posthogProviderOverride: 'providerOverride',
313
- posthogCostOverride: 'costOverride',
314
- posthogCaptureImmediate: 'captureImmediate'
341
+ posthogDistinctId: "distinctId",
342
+ posthogTraceId: "traceId",
343
+ posthogProperties: "properties",
344
+ posthogPrivacyMode: "privacyMode",
345
+ posthogGroups: "groups",
346
+ posthogModelOverride: "modelOverride",
347
+ posthogProviderOverride: "providerOverride",
348
+ posthogCostOverride: "costOverride",
349
+ posthogCaptureImmediate: "captureImmediate"
315
350
  };
316
351
  function extractPosthogParams(body) {
317
- const providerParams = {};
318
- const posthogParams = {};
319
- for (const [key, value] of Object.entries(body)) {
320
- if (POSTHOG_PARAMS_MAP[key]) {
321
- posthogParams[POSTHOG_PARAMS_MAP[key]] = value;
322
- } else if (key.startsWith('posthog')) {
323
- console.warn(`Unknown Posthog parameter ${key}`);
324
- } else {
325
- providerParams[key] = value;
326
- }
327
- }
328
- return {
329
- providerParams: providerParams,
330
- posthogParams: addDefaults(posthogParams)
331
- };
352
+ const providerParams = {};
353
+ const posthogParams = {};
354
+ for (const [key, value] of Object.entries(body)) if (POSTHOG_PARAMS_MAP[key]) posthogParams[POSTHOG_PARAMS_MAP[key]] = value;
355
+ else if (key.startsWith("posthog")) console.warn(`Unknown Posthog parameter ${key}`);
356
+ else providerParams[key] = value;
357
+ return {
358
+ providerParams,
359
+ posthogParams: addDefaults(posthogParams)
360
+ };
332
361
  }
333
362
  function addDefaults(params) {
334
- return {
335
- ...params,
336
- privacyMode: params.privacyMode ?? false,
337
- traceId: params.traceId ?? uuid.v4()
338
- };
363
+ return {
364
+ ...params,
365
+ privacyMode: params.privacyMode ?? false,
366
+ traceId: params.traceId ?? (0, uuid.v4)()
367
+ };
339
368
  }
340
-
341
- var version = "8.10.0";
342
-
369
+ //#endregion
370
+ //#region package.json
371
+ var version = "8.10.2";
372
+ //#endregion
373
+ //#region src/serializeError.ts
343
374
  const DEFAULT_MAX_DEPTH = 3;
344
375
  const MAX_STACK_LINES = 20;
345
376
  function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
346
- if (depth < 0 || value === null || typeof value !== 'object') {
347
- return value;
348
- }
349
- if (value instanceof Error) {
350
- const out = {
351
- name: value.name,
352
- message: value.message,
353
- stack: truncateStack(value.stack)
354
- };
355
- for (const key of Object.keys(value)) {
356
- out[key] = serializeError(value[key], depth - 1);
357
- }
358
- if (value.cause !== undefined) {
359
- out.cause = serializeError(value.cause, depth - 1);
360
- }
361
- return out;
362
- }
363
- if (Array.isArray(value)) {
364
- return value.map(item => serializeError(item, depth - 1));
365
- }
366
- return value;
377
+ if (depth < 0 || value === null || typeof value !== "object") return value;
378
+ if (value instanceof Error) {
379
+ const out = {
380
+ name: value.name,
381
+ message: value.message,
382
+ stack: truncateStack(value.stack)
383
+ };
384
+ for (const key of Object.keys(value)) out[key] = serializeError(value[key], depth - 1);
385
+ if (value.cause !== void 0) out.cause = serializeError(value.cause, depth - 1);
386
+ return out;
387
+ }
388
+ if (Array.isArray(value)) return value.map((item) => serializeError(item, depth - 1));
389
+ return value;
367
390
  }
368
391
  function stringifyError(error) {
369
- try {
370
- return JSON.stringify(sanitizeValues(serializeError(error)));
371
- } catch {
372
- if (error instanceof Error) {
373
- return JSON.stringify({
374
- name: error.name,
375
- message: error.message
376
- });
377
- }
378
- return JSON.stringify({
379
- message: String(error)
380
- });
381
- }
392
+ try {
393
+ return JSON.stringify(sanitizeValues(serializeError(error)));
394
+ } catch {
395
+ if (error instanceof Error) return JSON.stringify({
396
+ name: error.name,
397
+ message: error.message
398
+ });
399
+ return JSON.stringify({ message: String(error) });
400
+ }
382
401
  }
383
402
  function truncateStack(stack) {
384
- if (!stack) {
385
- return stack;
386
- }
387
- const lines = stack.split('\n');
388
- if (lines.length <= MAX_STACK_LINES) {
389
- return stack;
390
- }
391
- return [...lines.slice(0, MAX_STACK_LINES), '... (truncated)'].join('\n');
403
+ if (!stack) return stack;
404
+ const lines = stack.split("\n");
405
+ if (lines.length <= MAX_STACK_LINES) return stack;
406
+ return [...lines.slice(0, MAX_STACK_LINES), "... (truncated)"].join("\n");
392
407
  }
393
-
394
- // Warn when a wrapper's base_url points at the PostHog AI Gateway: the gateway
395
- // emits its own $ai_generation, so each call would be captured (and, for billable
396
- // products, billed) twice. We only warn — the wrapper's event carries data the
397
- // gateway never sees (groups, custom properties, trace hierarchy).
398
-
399
- // Keep in sync with the gateway's deployed hosts (see services/llm-gateway in the
400
- // main repo). gateway.us.posthog.com is live today; the rest are listed ahead of
401
- // any traffic moving to them.
402
- 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'];
403
-
404
- // Swap for the dedicated AI Gateway page once it ships.
405
- const GATEWAY_DOCS_URL = 'https://posthog.com/docs/ai-observability';
406
- const extractHost = baseURL => {
407
- try {
408
- // Tolerate bare hosts that omit a scheme, e.g. "gateway.us.posthog.com/v1".
409
- const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(baseURL);
410
- return new URL(hasScheme ? baseURL : `https://${baseURL}`).hostname.toLowerCase();
411
- } catch {
412
- return undefined;
413
- }
408
+ //#endregion
409
+ //#region src/gatewayWarning.ts
410
+ const POSTHOG_AI_GATEWAY_HOSTS = [
411
+ "gateway.posthog.com",
412
+ "gateway.us.posthog.com",
413
+ "gateway.eu.posthog.com",
414
+ "ai-gateway.us.posthog.com",
415
+ "ai-gateway.eu.posthog.com"
416
+ ];
417
+ const GATEWAY_DOCS_URL = "https://posthog.com/docs/ai-observability";
418
+ const extractHost = (baseURL) => {
419
+ try {
420
+ const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(baseURL);
421
+ return new URL(hasScheme ? baseURL : `https://${baseURL}`).hostname.toLowerCase();
422
+ } catch {
423
+ return;
424
+ }
414
425
  };
415
- const isPostHogAiGatewayUrl = baseURL => {
416
- if (!baseURL) {
417
- return false;
418
- }
419
- const host = extractHost(baseURL);
420
- return host !== undefined && POSTHOG_AI_GATEWAY_HOSTS.includes(host);
426
+ const isPostHogAiGatewayUrl = (baseURL) => {
427
+ if (!baseURL) return false;
428
+ const host = extractHost(baseURL);
429
+ return host !== void 0 && POSTHOG_AI_GATEWAY_HOSTS.includes(host);
421
430
  };
422
-
423
- // Warns on every gateway call by design: the misconfiguration is impossible to
424
- // miss that way, and a doubled bill is worse than noisy logs.
425
- const warnIfPostHogAiGateway = baseURL => {
426
- if (!isPostHogAiGatewayUrl(baseURL)) {
427
- return;
428
- }
429
- 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}.`);
431
+ const warnIfPostHogAiGateway = (baseURL) => {
432
+ if (!isPostHogAiGatewayUrl(baseURL)) return;
433
+ 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}.`);
430
434
  };
431
-
432
- /**
433
- * Options for `captureAiGeneration`. Mirrors the `$ai_generation` event shape
434
- * directly so that any caller — first-party SDK wrappers and external code
435
- * alike — produces an identical event.
436
- */
437
-
435
+ //#endregion
436
+ //#region src/captureAiGeneration.ts
438
437
  /**
439
- * Capture an `$ai_generation` (or `$ai_embedding`) event to PostHog.
440
- *
441
- * This is the canonical primitive that every `@posthog/ai` wrapper
442
- * (`withTracing`, `OpenAI`, `Anthropic`, `GoogleGenAI`, …) funnels through, so
443
- * external code can use it directly to instrument LLM calls made through
444
- * arbitrary clients (Cloudflare Workers AI, custom HTTP, etc.) and get the
445
- * same events the SDK wrappers produce.
446
- *
447
- * When `error` is set, the event is captured as an error. If the error is an
448
- * object, it is mutated in place to set `__posthog_previously_captured_error`
449
- * so callers can re-throw the original error reference safely.
450
- */
438
+ * Capture an `$ai_generation` (or `$ai_embedding`) event to PostHog.
439
+ *
440
+ * This is the canonical primitive that every `@posthog/ai` wrapper
441
+ * (`withTracing`, `OpenAI`, `Anthropic`, `GoogleGenAI`, …) funnels through, so
442
+ * external code can use it directly to instrument LLM calls made through
443
+ * arbitrary clients (Cloudflare Workers AI, custom HTTP, etc.) and get the
444
+ * same events the SDK wrappers produce.
445
+ *
446
+ * When `error` is set, the event is captured as an error. If the error is an
447
+ * object, it is mutated in place to set `__posthog_previously_captured_error`
448
+ * so callers can re-throw the original error reference safely.
449
+ */
451
450
  const captureAiGeneration = async (client, options) => {
452
- try {
453
- if (!client.capture) {
454
- return;
455
- }
456
- warnIfPostHogAiGateway(options.baseURL);
457
- const traceId = options.traceId ?? uuid.v4();
458
- const eventType = options.eventType ?? AIEvent.Generation;
459
- const privacyMode = options.privacyMode ?? false;
460
- const usage = options.usage ?? {};
461
-
462
- // Check privacy before reading or traversing input/output. Besides avoiding
463
- // needless work, this ensures hostile getters/proxies cannot observe a value
464
- // that the caller explicitly requested us to redact.
465
- const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
466
- const safeInput = shouldRedact ? null : core.toJsonSafeValue(options.input);
467
- const safeOutput = shouldRedact ? null : core.toJsonSafeValue(options.output);
468
- let httpStatus = options.httpStatus;
469
- let errorData = {};
470
- if (options.error) {
471
- if (httpStatus === undefined) {
472
- if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
473
- httpStatus = options.error.status;
474
- } else if (typeof options.error === 'object' && 'statusCode' in options.error && typeof options.error.statusCode === 'number') {
475
- httpStatus = options.error.statusCode;
476
- } else {
477
- httpStatus = 500;
478
- }
479
- }
480
- let exceptionId;
481
- if (client.options?.enableExceptionAutocapture) {
482
- exceptionId = core.uuidv7();
483
- client.captureException(options.error, undefined, {
484
- $ai_trace_id: traceId
485
- }, exceptionId);
486
- if (typeof options.error === 'object') {
487
- ;
488
- options.error.__posthog_previously_captured_error = true;
489
- }
490
- }
491
- errorData = {
492
- $ai_is_error: true,
493
- $ai_error: stringifyError(options.error),
494
- $exception_event_id: exceptionId
495
- };
496
- }
497
- httpStatus = httpStatus ?? 200;
498
-
499
- // A configured price applies only to a count the provider reported, so a call with no
500
- // reported usage sends no cost instead of asserting $0. $ai_total_cost_usd sums the sides
501
- // that were priced, which makes it the cost of the known side alone when the other side
502
- // went unreported: a lower bound on the true total, not an assertion of it.
503
- const costOverrideData = {};
504
- if (options.costOverride) {
505
- if (usage.inputTokens !== undefined) {
506
- costOverrideData.$ai_input_cost_usd = (options.costOverride.inputCost ?? 0) * usage.inputTokens;
507
- }
508
- if (usage.outputTokens !== undefined) {
509
- costOverrideData.$ai_output_cost_usd = (options.costOverride.outputCost ?? 0) * usage.outputTokens;
510
- }
511
- if (Object.keys(costOverrideData).length > 0) {
512
- costOverrideData.$ai_total_cost_usd = (costOverrideData.$ai_input_cost_usd ?? 0) + (costOverrideData.$ai_output_cost_usd ?? 0);
513
- }
514
- }
515
-
516
- // The caller's own token counts override the SDK-derived ones further down, via the
517
- // `options.properties` spread.
518
- const tokensOverridden = hasTokenOverrides(options.properties);
519
- const additionalTokenValues = {
520
- ...(usage.reasoningTokens ? {
521
- $ai_reasoning_tokens: usage.reasoningTokens
522
- } : {}),
523
- ...(usage.cacheReadInputTokens ? {
524
- $ai_cache_read_input_tokens: usage.cacheReadInputTokens
525
- } : {}),
526
- ...(usage.cacheCreationInputTokens ? {
527
- $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
528
- } : {}),
529
- // Checked against undefined rather than truthiness, because false is the meaningful
530
- // value here and a truthiness guard would drop it.
531
- //
532
- // Dropped entirely when the caller overrides the token counts: the flag describes how
533
- // the SDK-derived counts relate to each other, so against passthrough counts it can be
534
- // wrong in the expensive direction. Declaring inclusive over counts that are actually
535
- // exclusive makes ingestion subtract the cache pool that was never in the input. A
536
- // caller who knows their own accounting model can still pass
537
- // `$ai_cache_reporting_exclusive` themselves, and that value wins.
538
- ...(usage.cacheReportingExclusive !== undefined && !tokensOverridden ? {
539
- $ai_cache_reporting_exclusive: usage.cacheReportingExclusive
540
- } : {}),
541
- ...(usage.webSearchCount ? {
542
- $ai_web_search_count: usage.webSearchCount
543
- } : {}),
544
- ...(usage.rawUsage ? {
545
- $ai_usage: usage.rawUsage
546
- } : {})
547
- };
548
- const properties = {
549
- $ai_lib: 'posthog-ai',
550
- $ai_lib_version: version,
551
- $ai_provider: options.providerOverride ?? options.provider,
552
- $ai_model: options.modelOverride ?? options.model,
553
- $ai_model_parameters: options.modelParameters ?? {},
554
- $ai_input: safeInput,
555
- $ai_output_choices: safeOutput,
556
- $ai_http_status: httpStatus,
557
- ...(usage.inputTokens !== undefined ? {
558
- $ai_input_tokens: usage.inputTokens
559
- } : {}),
560
- ...(usage.outputTokens !== undefined ? {
561
- $ai_output_tokens: usage.outputTokens
562
- } : {}),
563
- ...additionalTokenValues,
564
- ...(options.latency !== undefined ? {
565
- $ai_latency: options.latency
566
- } : {}),
567
- ...(options.timeToFirstToken !== undefined ? {
568
- $ai_time_to_first_token: options.timeToFirstToken
569
- } : {}),
570
- $ai_trace_id: traceId,
571
- ...(options.baseURL === null ? {} : {
572
- $ai_base_url: options.baseURL ?? ''
573
- }),
574
- ...options.properties,
575
- $ai_tokens_source: getTokensSource(options.properties),
576
- ...(options.distinctId ? {} : {
577
- $process_person_profile: false
578
- }),
579
- ...(options.stopReason ? {
580
- $ai_stop_reason: options.stopReason
581
- } : {}),
582
- ...(options.tools ? {
583
- $ai_tools: options.tools
584
- } : {}),
585
- ...(options.completionId ? {
586
- $ai_completion_id: options.completionId
587
- } : {}),
588
- ...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
589
- $ai_provider_metadata: options.providerMetadata
590
- } : {}),
591
- ...errorData,
592
- ...costOverrideData
593
- };
594
- const event = {
595
- distinctId: options.distinctId ?? traceId,
596
- event: eventType,
597
- properties,
598
- groups: options.groups
599
- };
600
- if (options.captureImmediate) {
601
- await captureAiEventImmediate(client, event);
602
- } else {
603
- captureAiEvent(client, event);
604
- }
605
- } catch (error) {
606
- // Telemetry failures must never affect the instrumented provider call.
607
- try {
608
- options.onError?.(error);
609
- } catch {
610
- // Error reporting must not affect the instrumented provider call either.
611
- }
612
- console.warn('[PostHog AI] Failed to capture generation telemetry:', error);
613
- }
451
+ try {
452
+ if (!client.capture) return;
453
+ warnIfPostHogAiGateway(options.baseURL);
454
+ const traceId = options.traceId ?? (0, uuid.v4)();
455
+ const eventType = options.eventType ?? "$ai_generation";
456
+ const privacyMode = options.privacyMode ?? false;
457
+ const usage = options.usage ?? {};
458
+ const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
459
+ const safeInput = shouldRedact ? null : (0, _posthog_core.toJsonSafeValue)(options.input);
460
+ const safeOutput = shouldRedact ? null : (0, _posthog_core.toJsonSafeValue)(options.output);
461
+ let httpStatus = options.httpStatus;
462
+ let errorData = {};
463
+ if (options.error) {
464
+ if (httpStatus === void 0) {
465
+ if (typeof options.error === "object" && "status" in options.error && typeof options.error.status === "number") httpStatus = options.error.status;
466
+ else if (typeof options.error === "object" && "statusCode" in options.error && typeof options.error.statusCode === "number") httpStatus = options.error.statusCode;
467
+ else httpStatus = 500;
468
+ }
469
+ let exceptionId;
470
+ if (client.options?.enableExceptionAutocapture) {
471
+ exceptionId = (0, _posthog_core.uuidv7)();
472
+ client.captureException(options.error, void 0, { $ai_trace_id: traceId }, exceptionId);
473
+ if (typeof options.error === "object") options.error.__posthog_previously_captured_error = true;
474
+ }
475
+ errorData = {
476
+ $ai_is_error: true,
477
+ $ai_error: stringifyError(options.error),
478
+ $exception_event_id: exceptionId
479
+ };
480
+ }
481
+ httpStatus = httpStatus ?? 200;
482
+ const costOverrideData = {};
483
+ if (options.costOverride) {
484
+ if (usage.inputTokens !== void 0) costOverrideData.$ai_input_cost_usd = (options.costOverride.inputCost ?? 0) * usage.inputTokens;
485
+ if (usage.outputTokens !== void 0) costOverrideData.$ai_output_cost_usd = (options.costOverride.outputCost ?? 0) * usage.outputTokens;
486
+ if (Object.keys(costOverrideData).length > 0) costOverrideData.$ai_total_cost_usd = (costOverrideData.$ai_input_cost_usd ?? 0) + (costOverrideData.$ai_output_cost_usd ?? 0);
487
+ }
488
+ const tokensOverridden = hasTokenOverrides(options.properties);
489
+ const additionalTokenValues = {
490
+ ...usage.reasoningTokens ? { $ai_reasoning_tokens: usage.reasoningTokens } : {},
491
+ ...usage.cacheReadInputTokens ? { $ai_cache_read_input_tokens: usage.cacheReadInputTokens } : {},
492
+ ...usage.cacheCreationInputTokens ? { $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens } : {},
493
+ ...usage.cacheReportingExclusive !== void 0 && !tokensOverridden ? { $ai_cache_reporting_exclusive: usage.cacheReportingExclusive } : {},
494
+ ...usage.webSearchCount ? { $ai_web_search_count: usage.webSearchCount } : {},
495
+ ...usage.rawUsage ? { $ai_usage: usage.rawUsage } : {}
496
+ };
497
+ const properties = {
498
+ $ai_lib: "posthog-ai",
499
+ $ai_lib_version: version,
500
+ $ai_provider: options.providerOverride ?? options.provider,
501
+ $ai_model: options.modelOverride ?? options.model,
502
+ $ai_model_parameters: options.modelParameters ?? {},
503
+ $ai_input: safeInput,
504
+ $ai_output_choices: safeOutput,
505
+ $ai_http_status: httpStatus,
506
+ ...usage.inputTokens !== void 0 ? { $ai_input_tokens: usage.inputTokens } : {},
507
+ ...usage.outputTokens !== void 0 ? { $ai_output_tokens: usage.outputTokens } : {},
508
+ ...additionalTokenValues,
509
+ ...options.latency !== void 0 ? { $ai_latency: options.latency } : {},
510
+ ...options.timeToFirstToken !== void 0 ? { $ai_time_to_first_token: options.timeToFirstToken } : {},
511
+ $ai_trace_id: traceId,
512
+ ...options.baseURL === null ? {} : { $ai_base_url: options.baseURL ?? "" },
513
+ ...options.properties,
514
+ $ai_tokens_source: getTokensSource(options.properties),
515
+ ...options.distinctId ? {} : { $process_person_profile: false },
516
+ ...options.stopReason ? { $ai_stop_reason: options.stopReason } : {},
517
+ ...options.tools ? { $ai_tools: options.tools } : {},
518
+ ...options.completionId ? { $ai_completion_id: options.completionId } : {},
519
+ ...options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? { $ai_provider_metadata: options.providerMetadata } : {},
520
+ ...errorData,
521
+ ...costOverrideData
522
+ };
523
+ const event = {
524
+ distinctId: options.distinctId ?? traceId,
525
+ event: eventType,
526
+ properties,
527
+ groups: options.groups
528
+ };
529
+ if (options.captureImmediate) await captureAiEventImmediate(client, event);
530
+ else captureAiEvent(client, event);
531
+ } catch (error) {
532
+ try {
533
+ options.onError?.(error);
534
+ } catch {}
535
+ console.warn("[PostHog AI] Failed to capture generation telemetry:", error);
536
+ }
614
537
  };
615
-
538
+ //#endregion
539
+ //#region src/providerPromise.ts
616
540
  function addResponseIds(result, response, requestIdHeader, workspaceIdHeader) {
617
- if (!result || typeof result !== 'object' || Array.isArray(result)) {
618
- return result;
619
- }
620
- const properties = {
621
- _request_id: {
622
- value: response.headers.get(requestIdHeader),
623
- enumerable: false
624
- }
625
- };
626
- if (workspaceIdHeader) {
627
- properties._workspace_id = {
628
- value: response.headers.get(workspaceIdHeader),
629
- enumerable: false
630
- };
631
- }
632
- return Object.defineProperties(result, properties);
541
+ if (!result || typeof result !== "object" || Array.isArray(result)) return result;
542
+ const properties = { _request_id: {
543
+ value: response.headers.get(requestIdHeader),
544
+ enumerable: false
545
+ } };
546
+ if (workspaceIdHeader) properties._workspace_id = {
547
+ value: response.headers.get(workspaceIdHeader),
548
+ enumerable: false
549
+ };
550
+ return Object.defineProperties(result, properties);
633
551
  }
634
552
  function getResponsePropsPromise(parentPromise) {
635
- const responsePromise = parentPromise.responsePromise;
636
- if (!responsePromise || typeof responsePromise.then !== 'function') {
637
- return undefined;
638
- }
639
- return responsePromise;
553
+ const responsePromise = parentPromise.responsePromise;
554
+ if (!responsePromise || typeof responsePromise.then !== "function") return;
555
+ return responsePromise;
640
556
  }
641
557
  function decorateProviderPromise(wrappedPromise, responsePropsPromise, requestIdHeader, workspaceIdHeader, preserveThenUnwrap) {
642
- const providerPromise = wrappedPromise;
643
- if (responsePropsPromise) {
644
- providerPromise.asResponse = async () => (await responsePropsPromise).response;
645
- providerPromise.withResponse = async () => {
646
- const [props, data] = await Promise.all([responsePropsPromise, wrappedPromise]);
647
- return {
648
- response: props.response,
649
- data,
650
- request_id: props.response.headers.get(requestIdHeader),
651
- ...(workspaceIdHeader ? {
652
- workspace_id: props.response.headers.get(workspaceIdHeader)
653
- } : {})
654
- };
655
- };
656
- }
657
- if (preserveThenUnwrap) {
658
- providerPromise._thenUnwrap = transform => {
659
- if (!responsePropsPromise) {
660
- throw new Error('The provider promise response metadata is unavailable');
661
- }
662
- const transformedPromise = Promise.all([wrappedPromise, responsePropsPromise]).then(([data, props]) => addResponseIds(transform(data, props), props.response, requestIdHeader, workspaceIdHeader));
663
- return decorateProviderPromise(transformedPromise, responsePropsPromise, requestIdHeader, workspaceIdHeader, true);
664
- };
665
- }
666
- return providerPromise;
558
+ const providerPromise = wrappedPromise;
559
+ if (responsePropsPromise) {
560
+ providerPromise.asResponse = async () => (await responsePropsPromise).response;
561
+ providerPromise.withResponse = async () => {
562
+ const [props, data] = await Promise.all([responsePropsPromise, wrappedPromise]);
563
+ return {
564
+ response: props.response,
565
+ data,
566
+ request_id: props.response.headers.get(requestIdHeader),
567
+ ...workspaceIdHeader ? { workspace_id: props.response.headers.get(workspaceIdHeader) } : {}
568
+ };
569
+ };
570
+ }
571
+ if (preserveThenUnwrap) providerPromise._thenUnwrap = (transform) => {
572
+ if (!responsePropsPromise) throw new Error("The provider promise response metadata is unavailable");
573
+ return decorateProviderPromise(Promise.all([wrappedPromise, responsePropsPromise]).then(([data, props]) => addResponseIds(transform(data, props), props.response, requestIdHeader, workspaceIdHeader)), responsePropsPromise, requestIdHeader, workspaceIdHeader, true);
574
+ };
575
+ return providerPromise;
667
576
  }
668
-
669
- /**
670
- * Keep the provider SDK helpers on a promise whose resolved value is instrumented.
671
- * OpenAI's parse helpers compose create calls through `_thenUnwrap`, while both
672
- * OpenAI and Anthropic expose the raw response through `asResponse` and
673
- * `withResponse`.
674
- */
675
-
676
577
  function preserveProviderPromise(parentPromise, wrappedPromise, options = {}) {
677
- const responsePropsPromise = getResponsePropsPromise(parentPromise);
678
- const preserveThenUnwrap = typeof parentPromise._thenUnwrap === 'function';
679
- const providerPromise = decorateProviderPromise(wrappedPromise, responsePropsPromise, options.requestIdHeader ?? 'x-request-id', options.workspaceIdHeader, preserveThenUnwrap);
680
- if (!responsePropsPromise) {
681
- const asResponse = parentPromise.asResponse?.bind(parentPromise);
682
- if (asResponse) {
683
- providerPromise.asResponse = asResponse;
684
- }
685
- const withResponse = parentPromise.withResponse?.bind(parentPromise);
686
- if (withResponse) {
687
- providerPromise.withResponse = async () => {
688
- const [response, data] = await Promise.all([withResponse(), wrappedPromise]);
689
- return {
690
- ...response,
691
- data
692
- };
693
- };
694
- }
695
- }
696
- return providerPromise;
578
+ const responsePropsPromise = getResponsePropsPromise(parentPromise);
579
+ const preserveThenUnwrap = typeof parentPromise._thenUnwrap === "function";
580
+ const providerPromise = decorateProviderPromise(wrappedPromise, responsePropsPromise, options.requestIdHeader ?? "x-request-id", options.workspaceIdHeader, preserveThenUnwrap);
581
+ if (!responsePropsPromise) {
582
+ const asResponse = parentPromise.asResponse?.bind(parentPromise);
583
+ if (asResponse) providerPromise.asResponse = asResponse;
584
+ const withResponse = parentPromise.withResponse?.bind(parentPromise);
585
+ if (withResponse) providerPromise.withResponse = async () => {
586
+ const [response, data] = await Promise.all([withResponse(), wrappedPromise]);
587
+ return {
588
+ ...response,
589
+ data
590
+ };
591
+ };
592
+ }
593
+ return providerPromise;
697
594
  }
698
-
595
+ //#endregion
596
+ //#region src/stream.ts
699
597
  /**
700
- * Splits an SDK stream into a monitoring branch and a caller branch without
701
- * allowing either branch to read ahead of the other. Unlike the SDKs' `tee()`
702
- * implementations, this keeps at most one result in flight and makes caller
703
- * cancellation terminate the monitoring branch and the source iterator.
704
- */
598
+ * Splits an SDK stream into a monitoring branch and a caller branch without
599
+ * allowing either branch to read ahead of the other. Unlike the SDKs' `tee()`
600
+ * implementations, this keeps at most one result in flight and makes caller
601
+ * cancellation terminate the monitoring branch and the source iterator.
602
+ */
705
603
  function monitoredStreamTee(source, createStream) {
706
- const controller = source.controller ?? new AbortController();
707
- const sourceIterator = source[Symbol.asyncIterator]();
708
- const callerQueue = [];
709
- let monitorPending;
710
- let monitorActive = true;
711
- let operationInFlight = false;
712
- let terminalResult;
713
- let bufferedMonitorResult;
714
- let terminalError;
715
- let hasTerminalError = false;
716
- let cancellationPromise;
717
- let abortListener;
718
- const removeAbortListener = () => {
719
- if (abortListener) {
720
- controller.signal.removeEventListener('abort', abortListener);
721
- abortListener = undefined;
722
- }
723
- };
724
- const settleMonitorTerminal = () => {
725
- if (!monitorPending) {
726
- return;
727
- }
728
- const pending = monitorPending;
729
- monitorPending = undefined;
730
- if (hasTerminalError) {
731
- pending.reject(terminalError);
732
- } else if (terminalResult) {
733
- pending.resolve(terminalResult);
734
- }
735
- };
736
- const settleCallersTerminal = () => {
737
- while (callerQueue.length > 0) {
738
- const pending = callerQueue.shift();
739
- if (hasTerminalError) {
740
- pending.reject(terminalError);
741
- } else if (terminalResult) {
742
- pending.resolve(terminalResult);
743
- }
744
- }
745
- };
746
- const pump = () => {
747
- if (operationInFlight || callerQueue.length === 0 || monitorActive && !monitorPending) {
748
- return;
749
- }
750
- const pendingCaller = callerQueue.shift();
751
- const pendingMonitor = monitorPending;
752
- monitorPending = undefined;
753
- operationInFlight = true;
754
- void sourceIterator.next().then(result => {
755
- operationInFlight = false;
756
- if (result.done) {
757
- terminalResult = result;
758
- removeAbortListener();
759
- }
760
- pendingCaller.resolve(result);
761
- pendingMonitor?.resolve(result);
762
- if (result.done) {
763
- settleCallersTerminal();
764
- } else {
765
- pump();
766
- }
767
- }, error => {
768
- operationInFlight = false;
769
- terminalError = error;
770
- hasTerminalError = true;
771
- removeAbortListener();
772
- pendingCaller.reject(error);
773
- pendingMonitor?.reject(error);
774
- settleCallersTerminal();
775
- });
776
- };
777
- const monitoringStream = {
778
- [Symbol.asyncIterator]() {
779
- return {
780
- next: () => {
781
- if (hasTerminalError) {
782
- return Promise.reject(terminalError);
783
- }
784
- if (terminalResult) {
785
- return Promise.resolve(terminalResult);
786
- }
787
- if (bufferedMonitorResult) {
788
- const result = bufferedMonitorResult;
789
- bufferedMonitorResult = undefined;
790
- return Promise.resolve(result);
791
- }
792
- return new Promise((resolve, reject) => {
793
- monitorPending = {
794
- resolve,
795
- reject
796
- };
797
- pump();
798
- });
799
- },
800
- return: async value => {
801
- monitorActive = false;
802
- monitorPending = undefined;
803
- pump();
804
- return {
805
- done: true,
806
- value: value
807
- };
808
- }
809
- };
810
- }
811
- };
812
- const cancelSource = value => {
813
- if (cancellationPromise) {
814
- return cancellationPromise;
815
- }
816
- removeAbortListener();
817
- if (!controller.signal.aborted) {
818
- controller.abort();
819
- }
820
- cancellationPromise = (async () => {
821
- try {
822
- const defaultResult = {
823
- done: true,
824
- value
825
- };
826
- const result = sourceIterator.return ? await sourceIterator.return(value) : defaultResult;
827
- if (result.done) {
828
- terminalResult = result;
829
- removeAbortListener();
830
- settleMonitorTerminal();
831
- settleCallersTerminal();
832
- } else if (monitorPending) {
833
- monitorPending.resolve(result);
834
- monitorPending = undefined;
835
- cancellationPromise = undefined;
836
- } else {
837
- bufferedMonitorResult = result;
838
- cancellationPromise = undefined;
839
- }
840
- return result;
841
- } catch (error) {
842
- terminalError = error;
843
- hasTerminalError = true;
844
- removeAbortListener();
845
- settleMonitorTerminal();
846
- settleCallersTerminal();
847
- throw error;
848
- }
849
- })();
850
- // An AbortController cancellation has no caller awaiting this promise.
851
- void cancellationPromise.catch(() => undefined);
852
- return cancellationPromise;
853
- };
854
- abortListener = () => {
855
- void cancelSource();
856
- };
857
- if (controller.signal.aborted) {
858
- abortListener();
859
- } else {
860
- controller.signal.addEventListener('abort', abortListener, {
861
- once: true
862
- });
863
- }
864
- const callerStream = createStream(() => ({
865
- next: () => {
866
- if (hasTerminalError) {
867
- return Promise.reject(terminalError);
868
- }
869
- if (terminalResult) {
870
- return Promise.resolve(terminalResult);
871
- }
872
- return new Promise((resolve, reject) => {
873
- callerQueue.push({
874
- resolve,
875
- reject
876
- });
877
- pump();
878
- });
879
- },
880
- return: value => cancelSource(value),
881
- throw: async error => {
882
- if (!sourceIterator.throw) {
883
- await cancelSource();
884
- throw error;
885
- }
886
- try {
887
- const result = await sourceIterator.throw(error);
888
- if (result.done) {
889
- terminalResult = result;
890
- removeAbortListener();
891
- settleCallersTerminal();
892
- }
893
- if (monitorPending) {
894
- monitorPending.resolve(result);
895
- monitorPending = undefined;
896
- } else {
897
- bufferedMonitorResult = result;
898
- }
899
- return result;
900
- } catch (sourceError) {
901
- terminalError = sourceError;
902
- hasTerminalError = true;
903
- removeAbortListener();
904
- settleMonitorTerminal();
905
- settleCallersTerminal();
906
- throw sourceError;
907
- }
908
- }
909
- }), controller);
910
- return [monitoringStream, callerStream];
604
+ const controller = source.controller ?? new AbortController();
605
+ const sourceIterator = source[Symbol.asyncIterator]();
606
+ const callerQueue = [];
607
+ let monitorPending;
608
+ let monitorActive = true;
609
+ let operationInFlight = false;
610
+ let terminalResult;
611
+ let bufferedMonitorResult;
612
+ let terminalError;
613
+ let hasTerminalError = false;
614
+ let cancellationPromise;
615
+ let abortListener;
616
+ const removeAbortListener = () => {
617
+ if (abortListener) {
618
+ controller.signal.removeEventListener("abort", abortListener);
619
+ abortListener = void 0;
620
+ }
621
+ };
622
+ const settleMonitorTerminal = () => {
623
+ if (!monitorPending) return;
624
+ const pending = monitorPending;
625
+ monitorPending = void 0;
626
+ if (hasTerminalError) pending.reject(terminalError);
627
+ else if (terminalResult) pending.resolve(terminalResult);
628
+ };
629
+ const settleCallersTerminal = () => {
630
+ while (callerQueue.length > 0) {
631
+ const pending = callerQueue.shift();
632
+ if (hasTerminalError) pending.reject(terminalError);
633
+ else if (terminalResult) pending.resolve(terminalResult);
634
+ }
635
+ };
636
+ const pump = () => {
637
+ if (operationInFlight || callerQueue.length === 0 || monitorActive && !monitorPending) return;
638
+ const pendingCaller = callerQueue.shift();
639
+ const pendingMonitor = monitorPending;
640
+ monitorPending = void 0;
641
+ operationInFlight = true;
642
+ sourceIterator.next().then((result) => {
643
+ operationInFlight = false;
644
+ if (result.done) {
645
+ terminalResult = result;
646
+ removeAbortListener();
647
+ }
648
+ pendingCaller.resolve(result);
649
+ pendingMonitor?.resolve(result);
650
+ if (result.done) settleCallersTerminal();
651
+ else pump();
652
+ }, (error) => {
653
+ operationInFlight = false;
654
+ terminalError = error;
655
+ hasTerminalError = true;
656
+ removeAbortListener();
657
+ pendingCaller.reject(error);
658
+ pendingMonitor?.reject(error);
659
+ settleCallersTerminal();
660
+ });
661
+ };
662
+ const monitoringStream = { [Symbol.asyncIterator]() {
663
+ return {
664
+ next: () => {
665
+ if (hasTerminalError) return Promise.reject(terminalError);
666
+ if (terminalResult) return Promise.resolve(terminalResult);
667
+ if (bufferedMonitorResult) {
668
+ const result = bufferedMonitorResult;
669
+ bufferedMonitorResult = void 0;
670
+ return Promise.resolve(result);
671
+ }
672
+ return new Promise((resolve, reject) => {
673
+ monitorPending = {
674
+ resolve,
675
+ reject
676
+ };
677
+ pump();
678
+ });
679
+ },
680
+ return: async (value) => {
681
+ monitorActive = false;
682
+ monitorPending = void 0;
683
+ pump();
684
+ return {
685
+ done: true,
686
+ value
687
+ };
688
+ }
689
+ };
690
+ } };
691
+ const cancelSource = (value) => {
692
+ if (cancellationPromise) return cancellationPromise;
693
+ removeAbortListener();
694
+ if (!controller.signal.aborted) controller.abort();
695
+ cancellationPromise = (async () => {
696
+ try {
697
+ const defaultResult = {
698
+ done: true,
699
+ value
700
+ };
701
+ const result = sourceIterator.return ? await sourceIterator.return(value) : defaultResult;
702
+ if (result.done) {
703
+ terminalResult = result;
704
+ removeAbortListener();
705
+ settleMonitorTerminal();
706
+ settleCallersTerminal();
707
+ } else if (monitorPending) {
708
+ monitorPending.resolve(result);
709
+ monitorPending = void 0;
710
+ cancellationPromise = void 0;
711
+ } else {
712
+ bufferedMonitorResult = result;
713
+ cancellationPromise = void 0;
714
+ }
715
+ return result;
716
+ } catch (error) {
717
+ terminalError = error;
718
+ hasTerminalError = true;
719
+ removeAbortListener();
720
+ settleMonitorTerminal();
721
+ settleCallersTerminal();
722
+ throw error;
723
+ }
724
+ })();
725
+ cancellationPromise.catch(() => void 0);
726
+ return cancellationPromise;
727
+ };
728
+ abortListener = () => {
729
+ cancelSource();
730
+ };
731
+ if (controller.signal.aborted) abortListener();
732
+ else controller.signal.addEventListener("abort", abortListener, { once: true });
733
+ return [monitoringStream, createStream(() => ({
734
+ next: () => {
735
+ if (hasTerminalError) return Promise.reject(terminalError);
736
+ if (terminalResult) return Promise.resolve(terminalResult);
737
+ return new Promise((resolve, reject) => {
738
+ callerQueue.push({
739
+ resolve,
740
+ reject
741
+ });
742
+ pump();
743
+ });
744
+ },
745
+ return: (value) => cancelSource(value),
746
+ throw: async (error) => {
747
+ if (!sourceIterator.throw) {
748
+ await cancelSource();
749
+ throw error;
750
+ }
751
+ try {
752
+ const result = await sourceIterator.throw(error);
753
+ if (result.done) {
754
+ terminalResult = result;
755
+ removeAbortListener();
756
+ settleCallersTerminal();
757
+ }
758
+ if (monitorPending) {
759
+ monitorPending.resolve(result);
760
+ monitorPending = void 0;
761
+ } else bufferedMonitorResult = result;
762
+ return result;
763
+ } catch (sourceError) {
764
+ terminalError = sourceError;
765
+ hasTerminalError = true;
766
+ removeAbortListener();
767
+ settleMonitorTerminal();
768
+ settleCallersTerminal();
769
+ throw sourceError;
770
+ }
771
+ }
772
+ }), controller)];
911
773
  }
912
-
913
- class PostHogAnthropic extends AnthropicOriginal__default.default {
914
- constructor(config) {
915
- const {
916
- posthog,
917
- ...anthropicConfig
918
- } = config;
919
- super(anthropicConfig);
920
- this.phClient = posthog;
921
- this.messages = new WrappedMessages(this, this.phClient);
922
- }
923
- }
924
- class WrappedMessages extends AnthropicOriginal__default.default.Messages {
925
- constructor(parentClient, phClient) {
926
- super(parentClient);
927
- this.phClient = phClient;
928
- this.baseURL = parentClient.baseURL;
929
- }
930
- create(body, options) {
931
- const {
932
- providerParams: anthropicParams,
933
- posthogParams
934
- } = extractPosthogParams(body);
935
- const startTime = Date.now();
936
- if (anthropicParams.stream) {
937
- const parentPromise = super.create(anthropicParams, options);
938
- const wrappedPromise = parentPromise.then(value => {
939
- let accumulatedContent = '';
940
- const contentBlocks = [];
941
- const toolsInProgress = new Map();
942
- let currentTextBlock = null;
943
- let firstTokenTime;
944
- let stopReason;
945
- const usage = {
946
- webSearchCount: 0
947
- };
948
- let rawUsage = {};
949
- if (Symbol.asyncIterator in value) {
950
- const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new streaming.Stream(iterator, controller));
951
- (async () => {
952
- try {
953
- for await (const chunk of stream1) {
954
- // Handle content block start events
955
- if (chunk.type === 'content_block_start') {
956
- if (chunk.content_block?.type === 'text') {
957
- currentTextBlock = {
958
- type: 'text',
959
- text: ''
960
- };
961
- contentBlocks.push(currentTextBlock);
962
- } else if (chunk.content_block?.type === 'tool_use') {
963
- if (firstTokenTime === undefined) {
964
- firstTokenTime = Date.now();
965
- }
966
- const toolBlock = {
967
- type: 'function',
968
- id: chunk.content_block.id,
969
- function: {
970
- name: chunk.content_block.name,
971
- arguments: {}
972
- }
973
- };
974
- contentBlocks.push(toolBlock);
975
- toolsInProgress.set(chunk.content_block.id, {
976
- block: toolBlock,
977
- inputString: ''
978
- });
979
- currentTextBlock = null;
980
- }
981
- }
982
-
983
- // Handle text delta events
984
- if ('delta' in chunk) {
985
- if ('text' in chunk.delta) {
986
- const delta = chunk.delta.text;
987
- if (firstTokenTime === undefined) {
988
- firstTokenTime = Date.now();
989
- }
990
- accumulatedContent += delta;
991
- if (currentTextBlock) {
992
- currentTextBlock.text += delta;
993
- }
994
- }
995
- }
996
-
997
- // Handle tool input delta events
998
- if (chunk.type === 'content_block_delta' && chunk.delta?.type === 'input_json_delta') {
999
- const block = chunk.index !== undefined ? contentBlocks[chunk.index] : undefined;
1000
- const toolId = block?.type === 'function' ? block.id : undefined;
1001
- if (toolId && toolsInProgress.has(toolId)) {
1002
- const tool = toolsInProgress.get(toolId);
1003
- if (tool) {
1004
- tool.inputString += chunk.delta.partial_json || '';
1005
- }
1006
- }
1007
- }
1008
-
1009
- // Handle content block stop events
1010
- if (chunk.type === 'content_block_stop') {
1011
- currentTextBlock = null;
1012
-
1013
- // Parse accumulated tool input
1014
- if (chunk.index !== undefined) {
1015
- const block = contentBlocks[chunk.index];
1016
- if (block?.type === 'function' && block.id && toolsInProgress.has(block.id)) {
1017
- const tool = toolsInProgress.get(block.id);
1018
- if (tool) {
1019
- try {
1020
- block.function.arguments = JSON.parse(tool.inputString);
1021
- } catch (e) {
1022
- // Keep empty object if parsing fails
1023
- console.error('Error parsing tool input:', e);
1024
- }
1025
- }
1026
- toolsInProgress.delete(block.id);
1027
- }
1028
- }
1029
- }
1030
- if (chunk.type == 'message_start') {
1031
- rawUsage = {
1032
- ...chunk.message.usage
1033
- };
1034
- usage.inputTokens = chunk.message.usage.input_tokens ?? 0;
1035
- usage.cacheCreationInputTokens = chunk.message.usage.cache_creation_input_tokens ?? 0;
1036
- usage.cacheReadInputTokens = chunk.message.usage.cache_read_input_tokens ?? 0;
1037
- usage.webSearchCount = chunk.message.usage.server_tool_use?.web_search_requests ?? 0;
1038
- }
1039
- if ('usage' in chunk) {
1040
- rawUsage = {
1041
- ...rawUsage,
1042
- ...Object.fromEntries(Object.entries(chunk.usage).filter(([, value]) => value != null))
1043
- };
1044
- usage.outputTokens = chunk.usage.output_tokens ?? 0;
1045
- // Update web search count if present in delta
1046
- if (chunk.usage.server_tool_use?.web_search_requests !== undefined) {
1047
- usage.webSearchCount = chunk.usage.server_tool_use.web_search_requests;
1048
- }
1049
- }
1050
- if (chunk.type === 'message_delta' && 'delta' in chunk) {
1051
- const delta = chunk.delta;
1052
- if ('stop_reason' in delta && typeof delta.stop_reason === 'string' && delta.stop_reason) {
1053
- stopReason = delta.stop_reason;
1054
- }
1055
- }
1056
- }
1057
- usage.rawUsage = rawUsage;
1058
- const latency = (Date.now() - startTime) / 1000;
1059
- const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1060
- const availableTools = extractAvailableToolCalls('anthropic', anthropicParams);
1061
-
1062
- // Format output to match non-streaming version
1063
- const formattedOutput = contentBlocks.length > 0 ? [{
1064
- role: 'assistant',
1065
- content: contentBlocks
1066
- }] : [{
1067
- role: 'assistant',
1068
- content: [{
1069
- type: 'text',
1070
- text: accumulatedContent
1071
- }]
1072
- }];
1073
- await captureAiGeneration(this.phClient, {
1074
- ...posthogParams,
1075
- model: anthropicParams.model,
1076
- provider: 'anthropic',
1077
- input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams, 'anthropic'), this.phClient),
1078
- output: formattedOutput,
1079
- latency,
1080
- timeToFirstToken,
1081
- baseURL: this.baseURL,
1082
- modelParameters: getModelParams(body),
1083
- httpStatus: 200,
1084
- usage,
1085
- stopReason,
1086
- tools: availableTools
1087
- });
1088
- } catch (error) {
1089
- // The final usage delta may never arrive; whatever raw usage did
1090
- // still belongs on the event.
1091
- if (Object.keys(rawUsage).length > 0) {
1092
- usage.rawUsage = rawUsage;
1093
- }
1094
- await captureAiGeneration(this.phClient, {
1095
- ...posthogParams,
1096
- model: anthropicParams.model,
1097
- provider: 'anthropic',
1098
- input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams), this.phClient),
1099
- output: [],
1100
- latency: (Date.now() - startTime) / 1000,
1101
- baseURL: this.baseURL,
1102
- modelParameters: getModelParams(body),
1103
- usage,
1104
- error: error
1105
- });
1106
- throw error;
1107
- }
1108
- })().catch(() => {
1109
- // Swallow: analytics must never crash the host process. The caller
1110
- // already receives this error via their own tee of the stream.
1111
- });
1112
-
1113
- // Return the other stream to the user
1114
- return stream2;
1115
- }
1116
- return value;
1117
- });
1118
- return preserveProviderPromise(parentPromise, wrappedPromise, {
1119
- requestIdHeader: 'request-id',
1120
- workspaceIdHeader: 'anthropic-workspace-id'
1121
- });
1122
- } else {
1123
- const parentPromise = super.create(anthropicParams, options);
1124
- const wrappedPromise = parentPromise.then(async result => {
1125
- if ('content' in result) {
1126
- const latency = (Date.now() - startTime) / 1000;
1127
- const availableTools = extractAvailableToolCalls('anthropic', anthropicParams);
1128
- await captureAiGeneration(this.phClient, {
1129
- ...posthogParams,
1130
- model: anthropicParams.model,
1131
- provider: 'anthropic',
1132
- input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams), this.phClient),
1133
- output: formatResponseAnthropic(result),
1134
- latency,
1135
- baseURL: this.baseURL,
1136
- modelParameters: getModelParams(body),
1137
- httpStatus: 200,
1138
- usage: {
1139
- inputTokens: result.usage.input_tokens ?? 0,
1140
- outputTokens: result.usage.output_tokens ?? 0,
1141
- cacheCreationInputTokens: result.usage.cache_creation_input_tokens ?? 0,
1142
- cacheReadInputTokens: result.usage.cache_read_input_tokens ?? 0,
1143
- webSearchCount: result.usage.server_tool_use?.web_search_requests ?? 0,
1144
- rawUsage: result.usage
1145
- },
1146
- stopReason: result.stop_reason ?? undefined,
1147
- tools: availableTools
1148
- });
1149
- }
1150
- return result;
1151
- }, async error => {
1152
- await captureAiGeneration(this.phClient, {
1153
- ...posthogParams,
1154
- model: anthropicParams.model,
1155
- provider: 'anthropic',
1156
- input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams), this.phClient),
1157
- output: [],
1158
- latency: (Date.now() - startTime) / 1000,
1159
- baseURL: this.baseURL,
1160
- modelParameters: getModelParams(body),
1161
- httpStatus: error?.status ? error.status : 500,
1162
- usage: {},
1163
- error: error
1164
- });
1165
- throw error;
1166
- });
1167
- return preserveProviderPromise(parentPromise, wrappedPromise, {
1168
- requestIdHeader: 'request-id',
1169
- workspaceIdHeader: 'anthropic-workspace-id'
1170
- });
1171
- }
1172
- }
1173
- }
1174
-
774
+ //#endregion
775
+ //#region src/anthropic/index.ts
776
+ var PostHogAnthropic = class extends _anthropic_ai_sdk.default {
777
+ constructor(config) {
778
+ const { posthog, ...anthropicConfig } = config;
779
+ super(anthropicConfig);
780
+ this.phClient = posthog;
781
+ this.messages = new WrappedMessages(this, this.phClient);
782
+ }
783
+ };
784
+ var WrappedMessages = class extends _anthropic_ai_sdk.default.Messages {
785
+ constructor(parentClient, phClient) {
786
+ super(parentClient);
787
+ this.phClient = phClient;
788
+ this.baseURL = parentClient.baseURL;
789
+ }
790
+ create(body, options) {
791
+ const { providerParams: anthropicParams, posthogParams } = extractPosthogParams(body);
792
+ const startTime = Date.now();
793
+ if (anthropicParams.stream) {
794
+ const parentPromise = super.create(anthropicParams, options);
795
+ return preserveProviderPromise(parentPromise, parentPromise.then((value) => {
796
+ let accumulatedContent = "";
797
+ const contentBlocks = [];
798
+ const toolsInProgress = /* @__PURE__ */ new Map();
799
+ let currentTextBlock = null;
800
+ let firstTokenTime;
801
+ let stopReason;
802
+ const usage = { webSearchCount: 0 };
803
+ let rawUsage = {};
804
+ if (Symbol.asyncIterator in value) {
805
+ const [stream1, stream2] = monitoredStreamTee(value, (iterator, controller) => new _anthropic_ai_sdk_streaming.Stream(iterator, controller));
806
+ (async () => {
807
+ try {
808
+ for await (const chunk of stream1) {
809
+ if (chunk.type === "content_block_start") {
810
+ if (chunk.content_block?.type === "text") {
811
+ currentTextBlock = {
812
+ type: "text",
813
+ text: ""
814
+ };
815
+ contentBlocks.push(currentTextBlock);
816
+ } else if (chunk.content_block?.type === "tool_use") {
817
+ if (firstTokenTime === void 0) firstTokenTime = Date.now();
818
+ const toolBlock = {
819
+ type: "function",
820
+ id: chunk.content_block.id,
821
+ function: {
822
+ name: chunk.content_block.name,
823
+ arguments: {}
824
+ }
825
+ };
826
+ contentBlocks.push(toolBlock);
827
+ toolsInProgress.set(chunk.content_block.id, {
828
+ block: toolBlock,
829
+ inputString: ""
830
+ });
831
+ currentTextBlock = null;
832
+ }
833
+ }
834
+ if ("delta" in chunk) {
835
+ if ("text" in chunk.delta) {
836
+ const delta = chunk.delta.text;
837
+ if (firstTokenTime === void 0) firstTokenTime = Date.now();
838
+ accumulatedContent += delta;
839
+ if (currentTextBlock) currentTextBlock.text += delta;
840
+ }
841
+ }
842
+ if (chunk.type === "content_block_delta" && chunk.delta?.type === "input_json_delta") {
843
+ const block = chunk.index !== void 0 ? contentBlocks[chunk.index] : void 0;
844
+ const toolId = block?.type === "function" ? block.id : void 0;
845
+ if (toolId && toolsInProgress.has(toolId)) {
846
+ const tool = toolsInProgress.get(toolId);
847
+ if (tool) tool.inputString += chunk.delta.partial_json || "";
848
+ }
849
+ }
850
+ if (chunk.type === "content_block_stop") {
851
+ currentTextBlock = null;
852
+ if (chunk.index !== void 0) {
853
+ const block = contentBlocks[chunk.index];
854
+ if (block?.type === "function" && block.id && toolsInProgress.has(block.id)) {
855
+ const tool = toolsInProgress.get(block.id);
856
+ if (tool) try {
857
+ block.function.arguments = JSON.parse(tool.inputString);
858
+ } catch (e) {
859
+ console.error("Error parsing tool input:", e);
860
+ }
861
+ toolsInProgress.delete(block.id);
862
+ }
863
+ }
864
+ }
865
+ if (chunk.type == "message_start") {
866
+ rawUsage = { ...chunk.message.usage };
867
+ usage.inputTokens = chunk.message.usage.input_tokens ?? 0;
868
+ usage.cacheCreationInputTokens = chunk.message.usage.cache_creation_input_tokens ?? 0;
869
+ usage.cacheReadInputTokens = chunk.message.usage.cache_read_input_tokens ?? 0;
870
+ usage.webSearchCount = chunk.message.usage.server_tool_use?.web_search_requests ?? 0;
871
+ }
872
+ if ("usage" in chunk) {
873
+ rawUsage = {
874
+ ...rawUsage,
875
+ ...Object.fromEntries(Object.entries(chunk.usage).filter(([, value]) => value != null))
876
+ };
877
+ usage.outputTokens = chunk.usage.output_tokens ?? 0;
878
+ if (chunk.usage.server_tool_use?.web_search_requests !== void 0) usage.webSearchCount = chunk.usage.server_tool_use.web_search_requests;
879
+ }
880
+ if (chunk.type === "message_delta" && "delta" in chunk) {
881
+ const delta = chunk.delta;
882
+ if ("stop_reason" in delta && typeof delta.stop_reason === "string" && delta.stop_reason) stopReason = delta.stop_reason;
883
+ }
884
+ }
885
+ usage.rawUsage = rawUsage;
886
+ const latency = (Date.now() - startTime) / 1e3;
887
+ const timeToFirstToken = firstTokenTime !== void 0 ? (firstTokenTime - startTime) / 1e3 : void 0;
888
+ const availableTools = extractAvailableToolCalls("anthropic", anthropicParams);
889
+ const formattedOutput = contentBlocks.length > 0 ? [{
890
+ role: "assistant",
891
+ content: contentBlocks
892
+ }] : [{
893
+ role: "assistant",
894
+ content: [{
895
+ type: "text",
896
+ text: accumulatedContent
897
+ }]
898
+ }];
899
+ await captureAiGeneration(this.phClient, {
900
+ ...posthogParams,
901
+ model: anthropicParams.model,
902
+ provider: "anthropic",
903
+ input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams, "anthropic"), this.phClient),
904
+ output: formattedOutput,
905
+ latency,
906
+ timeToFirstToken,
907
+ baseURL: this.baseURL,
908
+ modelParameters: getModelParams(body),
909
+ httpStatus: 200,
910
+ usage,
911
+ stopReason,
912
+ tools: availableTools
913
+ });
914
+ } catch (error) {
915
+ if (Object.keys(rawUsage).length > 0) usage.rawUsage = rawUsage;
916
+ await captureAiGeneration(this.phClient, {
917
+ ...posthogParams,
918
+ model: anthropicParams.model,
919
+ provider: "anthropic",
920
+ input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams, "anthropic"), this.phClient),
921
+ output: [],
922
+ latency: (Date.now() - startTime) / 1e3,
923
+ baseURL: this.baseURL,
924
+ modelParameters: getModelParams(body),
925
+ usage,
926
+ error
927
+ });
928
+ throw error;
929
+ }
930
+ })().catch(() => {});
931
+ return stream2;
932
+ }
933
+ return value;
934
+ }), {
935
+ requestIdHeader: "request-id",
936
+ workspaceIdHeader: "anthropic-workspace-id"
937
+ });
938
+ } else {
939
+ const parentPromise = super.create(anthropicParams, options);
940
+ return preserveProviderPromise(parentPromise, parentPromise.then(async (result) => {
941
+ if ("content" in result) {
942
+ const latency = (Date.now() - startTime) / 1e3;
943
+ const availableTools = extractAvailableToolCalls("anthropic", anthropicParams);
944
+ await captureAiGeneration(this.phClient, {
945
+ ...posthogParams,
946
+ model: anthropicParams.model,
947
+ provider: "anthropic",
948
+ input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams, "anthropic"), this.phClient),
949
+ output: formatResponseAnthropic(result),
950
+ latency,
951
+ baseURL: this.baseURL,
952
+ modelParameters: getModelParams(body),
953
+ httpStatus: 200,
954
+ usage: {
955
+ inputTokens: result.usage.input_tokens ?? 0,
956
+ outputTokens: result.usage.output_tokens ?? 0,
957
+ cacheCreationInputTokens: result.usage.cache_creation_input_tokens ?? 0,
958
+ cacheReadInputTokens: result.usage.cache_read_input_tokens ?? 0,
959
+ webSearchCount: result.usage.server_tool_use?.web_search_requests ?? 0,
960
+ rawUsage: result.usage
961
+ },
962
+ stopReason: result.stop_reason ?? void 0,
963
+ tools: availableTools
964
+ });
965
+ }
966
+ return result;
967
+ }, async (error) => {
968
+ await captureAiGeneration(this.phClient, {
969
+ ...posthogParams,
970
+ model: anthropicParams.model,
971
+ provider: "anthropic",
972
+ input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams, "anthropic"), this.phClient),
973
+ output: [],
974
+ latency: (Date.now() - startTime) / 1e3,
975
+ baseURL: this.baseURL,
976
+ modelParameters: getModelParams(body),
977
+ httpStatus: error?.status ? error.status : 500,
978
+ usage: {},
979
+ error
980
+ });
981
+ throw error;
982
+ }), {
983
+ requestIdHeader: "request-id",
984
+ workspaceIdHeader: "anthropic-workspace-id"
985
+ });
986
+ }
987
+ }
988
+ };
989
+ //#endregion
1175
990
  exports.Anthropic = PostHogAnthropic;
1176
991
  exports.PostHogAnthropic = PostHogAnthropic;
1177
- exports.WrappedMessages = WrappedMessages;
1178
992
  exports.default = PostHogAnthropic;
1179
- //# sourceMappingURL=index.cjs.map
993
+ exports.WrappedMessages = WrappedMessages;
994
+
995
+ //# sourceMappingURL=index.cjs.map