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