@posthog/ai 7.21.0 → 8.0.0
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/README.md +21 -4
- package/dist/anthropic/index.cjs +1 -1
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +1 -1
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +2 -1
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.d.ts +1 -1
- package/dist/gemini/index.mjs +2 -2
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +72 -3500
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +10 -276
- package/dist/index.mjs +72 -3473
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +4 -486
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.mjs +3 -465
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/openai/index.cjs +529 -1
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.d.ts +40 -2
- package/dist/openai/index.mjs +530 -3
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openai-agents/index.cjs +1 -1
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.mjs +1 -1
- package/dist/openai-agents/index.mjs.map +1 -1
- package/dist/otel/index.cjs +14 -14
- package/dist/otel/index.cjs.map +1 -1
- package/dist/otel/index.d.ts +11 -16
- package/dist/otel/index.mjs +14 -14
- package/dist/otel/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +1 -1
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +1 -1
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +24 -9
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import { OpenAI, AzureOpenAI } from 'openai';
|
|
2
|
-
import * as uuid from 'uuid';
|
|
3
1
|
import { v4 } from 'uuid';
|
|
4
2
|
import { uuidv7 } from '@posthog/core';
|
|
5
|
-
import AnthropicOriginal from '@anthropic-ai/sdk';
|
|
6
|
-
import { GoogleGenAI } from '@google/genai';
|
|
7
3
|
|
|
8
4
|
// Type guards for safer type checking
|
|
9
5
|
const isString = value => {
|
|
@@ -158,11 +154,6 @@ const redactor = new BinaryContentRedactor();
|
|
|
158
154
|
function redactBase64DataUrl(str) {
|
|
159
155
|
return redactor.redact(str);
|
|
160
156
|
}
|
|
161
|
-
const sanitizeOpenAI = data => redactor.redact(data);
|
|
162
|
-
const sanitizeOpenAIResponse = data => redactor.redact(data);
|
|
163
|
-
const sanitizeAnthropic = data => redactor.redact(data);
|
|
164
|
-
const sanitizeGemini = data => redactor.redact(data);
|
|
165
|
-
const sanitizeLangChain = data => redactor.redact(data);
|
|
166
157
|
|
|
167
158
|
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']);
|
|
168
159
|
function getTokensSource(posthogProperties) {
|
|
@@ -219,236 +210,6 @@ const getModelParams = params => {
|
|
|
219
210
|
}
|
|
220
211
|
return modelParams;
|
|
221
212
|
};
|
|
222
|
-
const formatResponseAnthropic = response => {
|
|
223
|
-
const output = [];
|
|
224
|
-
const content = [];
|
|
225
|
-
for (const choice of response.content ?? []) {
|
|
226
|
-
if (choice?.type === 'text' && choice?.text) {
|
|
227
|
-
content.push({
|
|
228
|
-
type: 'text',
|
|
229
|
-
text: choice.text
|
|
230
|
-
});
|
|
231
|
-
} else if (choice?.type === 'tool_use' && choice?.name && choice?.id) {
|
|
232
|
-
content.push({
|
|
233
|
-
type: 'function',
|
|
234
|
-
id: choice.id,
|
|
235
|
-
function: {
|
|
236
|
-
name: choice.name,
|
|
237
|
-
arguments: choice.input || {}
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
if (content.length > 0) {
|
|
243
|
-
output.push({
|
|
244
|
-
role: 'assistant',
|
|
245
|
-
content
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
return output;
|
|
249
|
-
};
|
|
250
|
-
const formatResponseOpenAI = response => {
|
|
251
|
-
const output = [];
|
|
252
|
-
if (response.choices) {
|
|
253
|
-
for (const choice of response.choices) {
|
|
254
|
-
const content = [];
|
|
255
|
-
let role = 'assistant';
|
|
256
|
-
if (choice.message) {
|
|
257
|
-
if (choice.message.role) {
|
|
258
|
-
role = choice.message.role;
|
|
259
|
-
}
|
|
260
|
-
if (choice.message.content) {
|
|
261
|
-
content.push({
|
|
262
|
-
type: 'text',
|
|
263
|
-
text: choice.message.content
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
if (choice.message.tool_calls) {
|
|
267
|
-
for (const toolCall of choice.message.tool_calls) {
|
|
268
|
-
content.push({
|
|
269
|
-
type: 'function',
|
|
270
|
-
id: toolCall.id,
|
|
271
|
-
function: {
|
|
272
|
-
name: toolCall.function.name,
|
|
273
|
-
arguments: toolCall.function.arguments
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
// Handle audio output (gpt-4o-audio-preview)
|
|
279
|
-
if (choice.message.audio) {
|
|
280
|
-
content.push({
|
|
281
|
-
type: 'audio',
|
|
282
|
-
...choice.message.audio
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
if (content.length > 0) {
|
|
287
|
-
output.push({
|
|
288
|
-
role,
|
|
289
|
-
content
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
// Handle Responses API format
|
|
295
|
-
if (response.output) {
|
|
296
|
-
const content = [];
|
|
297
|
-
let role = 'assistant';
|
|
298
|
-
for (const item of response.output) {
|
|
299
|
-
if (item.type === 'message') {
|
|
300
|
-
role = item.role;
|
|
301
|
-
if (item.content && Array.isArray(item.content)) {
|
|
302
|
-
for (const contentItem of item.content) {
|
|
303
|
-
if (contentItem.type === 'output_text' && contentItem.text) {
|
|
304
|
-
content.push({
|
|
305
|
-
type: 'text',
|
|
306
|
-
text: contentItem.text
|
|
307
|
-
});
|
|
308
|
-
} else if (contentItem.text) {
|
|
309
|
-
content.push({
|
|
310
|
-
type: 'text',
|
|
311
|
-
text: contentItem.text
|
|
312
|
-
});
|
|
313
|
-
} else if (contentItem.type === 'input_image' && contentItem.image_url) {
|
|
314
|
-
content.push({
|
|
315
|
-
type: 'image',
|
|
316
|
-
image: contentItem.image_url
|
|
317
|
-
});
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
} else if (item.content) {
|
|
321
|
-
content.push({
|
|
322
|
-
type: 'text',
|
|
323
|
-
text: String(item.content)
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
} else if (item.type === 'function_call') {
|
|
327
|
-
content.push({
|
|
328
|
-
type: 'function',
|
|
329
|
-
id: item.call_id || item.id || '',
|
|
330
|
-
function: {
|
|
331
|
-
name: item.name,
|
|
332
|
-
arguments: item.arguments || {}
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (content.length > 0) {
|
|
338
|
-
output.push({
|
|
339
|
-
role,
|
|
340
|
-
content
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return output;
|
|
345
|
-
};
|
|
346
|
-
const buildInlineDataBlock = (mimeType, data) => {
|
|
347
|
-
if (mimeType.startsWith('audio/')) {
|
|
348
|
-
return {
|
|
349
|
-
type: 'audio',
|
|
350
|
-
mime_type: mimeType,
|
|
351
|
-
data
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
if (mimeType.startsWith('image/')) {
|
|
355
|
-
return {
|
|
356
|
-
type: 'image',
|
|
357
|
-
inline_data: {
|
|
358
|
-
mime_type: mimeType,
|
|
359
|
-
data
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
return {
|
|
364
|
-
type: 'document',
|
|
365
|
-
inline_data: {
|
|
366
|
-
mime_type: mimeType,
|
|
367
|
-
data
|
|
368
|
-
}
|
|
369
|
-
};
|
|
370
|
-
};
|
|
371
|
-
const formatResponseGemini = response => {
|
|
372
|
-
const output = [];
|
|
373
|
-
if (response.candidates && Array.isArray(response.candidates)) {
|
|
374
|
-
for (const candidate of response.candidates) {
|
|
375
|
-
if (candidate.content && candidate.content.parts) {
|
|
376
|
-
const content = [];
|
|
377
|
-
for (const part of candidate.content.parts) {
|
|
378
|
-
if (part.text) {
|
|
379
|
-
content.push({
|
|
380
|
-
type: 'text',
|
|
381
|
-
text: part.text
|
|
382
|
-
});
|
|
383
|
-
} else if (part.functionCall) {
|
|
384
|
-
content.push({
|
|
385
|
-
type: 'function',
|
|
386
|
-
function: {
|
|
387
|
-
name: part.functionCall.name,
|
|
388
|
-
arguments: part.functionCall.args
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
} else if (part.inlineData) {
|
|
392
|
-
// Handle inline data (images, audio, documents)
|
|
393
|
-
const mimeType = part.inlineData.mimeType || part.inlineData.mime_type || 'application/octet-stream';
|
|
394
|
-
let data = part.inlineData.data;
|
|
395
|
-
// Handle binary data (Uint8Array/Buffer -> base64)
|
|
396
|
-
if (data instanceof Uint8Array) {
|
|
397
|
-
if (typeof Buffer !== 'undefined') {
|
|
398
|
-
data = Buffer.from(data).toString('base64');
|
|
399
|
-
} else {
|
|
400
|
-
let binary = '';
|
|
401
|
-
for (let i = 0; i < data.length; i++) {
|
|
402
|
-
binary += String.fromCharCode(data[i]);
|
|
403
|
-
}
|
|
404
|
-
data = btoa(binary);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
// Sanitize base64 data for images and other large inline data
|
|
408
|
-
data = redactBase64DataUrl(data);
|
|
409
|
-
content.push(buildInlineDataBlock(mimeType, data));
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
if (content.length > 0) {
|
|
413
|
-
output.push({
|
|
414
|
-
role: 'assistant',
|
|
415
|
-
content
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
} else if (candidate.text) {
|
|
419
|
-
output.push({
|
|
420
|
-
role: 'assistant',
|
|
421
|
-
content: [{
|
|
422
|
-
type: 'text',
|
|
423
|
-
text: candidate.text
|
|
424
|
-
}]
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
} else if (response.text) {
|
|
429
|
-
output.push({
|
|
430
|
-
role: 'assistant',
|
|
431
|
-
content: [{
|
|
432
|
-
type: 'text',
|
|
433
|
-
text: response.text
|
|
434
|
-
}]
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
return output;
|
|
438
|
-
};
|
|
439
|
-
const mergeSystemPrompt = (params, provider) => {
|
|
440
|
-
{
|
|
441
|
-
const messages = params.messages || [];
|
|
442
|
-
if (!params.system) {
|
|
443
|
-
return messages;
|
|
444
|
-
}
|
|
445
|
-
const systemMessage = params.system;
|
|
446
|
-
return [{
|
|
447
|
-
role: 'system',
|
|
448
|
-
content: systemMessage
|
|
449
|
-
}, ...messages];
|
|
450
|
-
}
|
|
451
|
-
};
|
|
452
213
|
const withPrivacyMode = (client, privacyMode, input) => {
|
|
453
214
|
return client.privacy_mode || privacyMode ? null : input;
|
|
454
215
|
};
|
|
@@ -586,28 +347,12 @@ function calculateWebSearchCount(result) {
|
|
|
586
347
|
* These are the tools provided to the LLM, not the tool calls in the response.
|
|
587
348
|
*/
|
|
588
349
|
const extractAvailableToolCalls = (provider, params) => {
|
|
589
|
-
|
|
590
|
-
if (params.tools) {
|
|
591
|
-
return params.tools;
|
|
592
|
-
}
|
|
593
|
-
return null;
|
|
594
|
-
} else if (provider === 'gemini') {
|
|
595
|
-
if (params.config && params.config.tools) {
|
|
596
|
-
return params.config.tools;
|
|
597
|
-
}
|
|
598
|
-
return null;
|
|
599
|
-
} else if (provider === 'openai') {
|
|
600
|
-
if (params.tools) {
|
|
601
|
-
return params.tools;
|
|
602
|
-
}
|
|
603
|
-
return null;
|
|
604
|
-
} else if (provider === 'vercel') {
|
|
350
|
+
{
|
|
605
351
|
if (params.tools) {
|
|
606
352
|
return params.tools;
|
|
607
353
|
}
|
|
608
354
|
return null;
|
|
609
355
|
}
|
|
610
|
-
return null;
|
|
611
356
|
};
|
|
612
357
|
var AIEvent;
|
|
613
358
|
(function (AIEvent) {
|
|
@@ -629,87 +374,8 @@ function sanitizeValues(obj) {
|
|
|
629
374
|
}
|
|
630
375
|
return jsonSafe;
|
|
631
376
|
}
|
|
632
|
-
const POSTHOG_PARAMS_MAP = {
|
|
633
|
-
posthogDistinctId: 'distinctId',
|
|
634
|
-
posthogTraceId: 'traceId',
|
|
635
|
-
posthogProperties: 'properties',
|
|
636
|
-
posthogPrivacyMode: 'privacyMode',
|
|
637
|
-
posthogGroups: 'groups',
|
|
638
|
-
posthogModelOverride: 'modelOverride',
|
|
639
|
-
posthogProviderOverride: 'providerOverride',
|
|
640
|
-
posthogCostOverride: 'costOverride',
|
|
641
|
-
posthogCaptureImmediate: 'captureImmediate'
|
|
642
|
-
};
|
|
643
|
-
function extractPosthogParams(body) {
|
|
644
|
-
const providerParams = {};
|
|
645
|
-
const posthogParams = {};
|
|
646
|
-
for (const [key, value] of Object.entries(body)) {
|
|
647
|
-
if (POSTHOG_PARAMS_MAP[key]) {
|
|
648
|
-
posthogParams[POSTHOG_PARAMS_MAP[key]] = value;
|
|
649
|
-
} else if (key.startsWith('posthog')) {
|
|
650
|
-
console.warn(`Unknown Posthog parameter ${key}`);
|
|
651
|
-
} else {
|
|
652
|
-
providerParams[key] = value;
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
return {
|
|
656
|
-
providerParams: providerParams,
|
|
657
|
-
posthogParams: addDefaults(posthogParams)
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
function addDefaults(params) {
|
|
661
|
-
return {
|
|
662
|
-
...params,
|
|
663
|
-
privacyMode: params.privacyMode ?? false,
|
|
664
|
-
traceId: params.traceId ?? v4()
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
function formatOpenAIResponsesInput(input, instructions) {
|
|
668
|
-
const messages = [];
|
|
669
|
-
if (instructions) {
|
|
670
|
-
messages.push({
|
|
671
|
-
role: 'system',
|
|
672
|
-
content: instructions
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
if (Array.isArray(input)) {
|
|
676
|
-
for (const item of input) {
|
|
677
|
-
if (typeof item === 'string') {
|
|
678
|
-
messages.push({
|
|
679
|
-
role: 'user',
|
|
680
|
-
content: item
|
|
681
|
-
});
|
|
682
|
-
} else if (item && typeof item === 'object') {
|
|
683
|
-
const obj = item;
|
|
684
|
-
const role = isString(obj.role) ? obj.role : 'user';
|
|
685
|
-
// Handle content properly - preserve structure for objects/arrays
|
|
686
|
-
const content = obj.content ?? obj.text ?? item;
|
|
687
|
-
messages.push({
|
|
688
|
-
role,
|
|
689
|
-
content: toContentString(content)
|
|
690
|
-
});
|
|
691
|
-
} else {
|
|
692
|
-
messages.push({
|
|
693
|
-
role: 'user',
|
|
694
|
-
content: toContentString(item)
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
} else if (typeof input === 'string') {
|
|
699
|
-
messages.push({
|
|
700
|
-
role: 'user',
|
|
701
|
-
content: input
|
|
702
|
-
});
|
|
703
|
-
} else if (input) {
|
|
704
|
-
messages.push({
|
|
705
|
-
role: 'user',
|
|
706
|
-
content: toContentString(input)
|
|
707
|
-
});
|
|
708
|
-
}
|
|
709
|
-
return messages;
|
|
710
|
-
}
|
|
711
377
|
|
|
712
|
-
var version = "
|
|
378
|
+
var version = "8.0.0";
|
|
713
379
|
|
|
714
380
|
const DEFAULT_MAX_DEPTH = 3;
|
|
715
381
|
const MAX_STACK_LINES = 20;
|
|
@@ -892,1336 +558,51 @@ const captureAiGeneration = async (client, options) => {
|
|
|
892
558
|
}
|
|
893
559
|
};
|
|
894
560
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
*/
|
|
899
|
-
function isResponseTokenChunk(chunk) {
|
|
900
|
-
return chunk.type === 'response.output_item.added' || chunk.type === 'response.content_part.added' || chunk.type === 'response.output_text.delta' || chunk.type === 'response.reasoning_text.delta' || chunk.type === 'response.reasoning_summary_text.delta' || chunk.type === 'response.audio.delta' || chunk.type === 'response.audio.transcript.delta' || chunk.type === 'response.refusal.delta';
|
|
901
|
-
}
|
|
902
|
-
/**
|
|
903
|
-
* Reads the OpenAI SDK's `_request_id` field from a response object. The SDK
|
|
904
|
-
* attaches the `x-request-id` response header here, but it is not part of the
|
|
905
|
-
* public response types, so it has to be read through a cast. Used to populate
|
|
906
|
-
* `$ai_provider_metadata.request_id`.
|
|
907
|
-
*/
|
|
908
|
-
function extractRequestId(result) {
|
|
909
|
-
return result?._request_id ?? undefined;
|
|
910
|
-
}
|
|
911
|
-
/**
|
|
912
|
-
* Assembles the `$ai_provider_metadata` blob for OpenAI / Azure OpenAI events.
|
|
913
|
-
* Provider-specific fields (system fingerprint, request id) live here rather
|
|
914
|
-
* than in the shared, provider-agnostic `$ai_*` namespace. Only keys with a
|
|
915
|
-
* truthy value are included, and `undefined` is returned when there is nothing
|
|
916
|
-
* to report so the property can be omitted from the event entirely.
|
|
917
|
-
*/
|
|
918
|
-
function buildProviderMetadata(fields) {
|
|
919
|
-
const metadata = {};
|
|
920
|
-
if (fields.systemFingerprint) {
|
|
921
|
-
metadata.system_fingerprint = fields.systemFingerprint;
|
|
922
|
-
}
|
|
923
|
-
if (fields.requestId) {
|
|
924
|
-
metadata.request_id = fields.requestId;
|
|
925
|
-
}
|
|
926
|
-
return Object.keys(metadata).length > 0 ? metadata : undefined;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
const Chat = OpenAI.Chat;
|
|
930
|
-
const Completions = Chat.Completions;
|
|
931
|
-
const Responses = OpenAI.Responses;
|
|
932
|
-
const Embeddings = OpenAI.Embeddings;
|
|
933
|
-
const Audio = OpenAI.Audio;
|
|
934
|
-
const Transcriptions = OpenAI.Audio.Transcriptions;
|
|
935
|
-
function captureAiGenerationInBackground(...args) {
|
|
936
|
-
void captureAiGeneration(...args).catch(() => undefined);
|
|
937
|
-
}
|
|
938
|
-
async function captureAiGenerationAfterSuccess(...args) {
|
|
939
|
-
const [, options] = args;
|
|
940
|
-
if (options.captureImmediate) {
|
|
941
|
-
await captureAiGeneration(...args);
|
|
942
|
-
} else {
|
|
943
|
-
captureAiGenerationInBackground(...args);
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
function preserveAPIPromiseHelpers(parentPromise, wrappedPromise) {
|
|
947
|
-
const apiPromise = wrappedPromise;
|
|
948
|
-
if (typeof parentPromise.asResponse === 'function') {
|
|
949
|
-
apiPromise.asResponse = () => parentPromise.asResponse();
|
|
950
|
-
}
|
|
951
|
-
if (typeof parentPromise.withResponse === 'function') {
|
|
952
|
-
apiPromise.withResponse = async () => {
|
|
953
|
-
const [response, data] = await Promise.all([parentPromise.withResponse(), wrappedPromise]);
|
|
954
|
-
return {
|
|
955
|
-
...response,
|
|
956
|
-
data
|
|
957
|
-
};
|
|
958
|
-
};
|
|
959
|
-
}
|
|
960
|
-
return apiPromise;
|
|
961
|
-
}
|
|
962
|
-
class PostHogOpenAI extends OpenAI {
|
|
963
|
-
constructor(config) {
|
|
964
|
-
const {
|
|
965
|
-
posthog,
|
|
966
|
-
...openAIConfig
|
|
967
|
-
} = config;
|
|
968
|
-
super(openAIConfig);
|
|
969
|
-
this.phClient = posthog;
|
|
970
|
-
this.chat = new WrappedChat$1(this, this.phClient);
|
|
971
|
-
this.responses = new WrappedResponses$1(this, this.phClient);
|
|
972
|
-
this.embeddings = new WrappedEmbeddings$1(this, this.phClient);
|
|
973
|
-
this.audio = new WrappedAudio(this, this.phClient);
|
|
974
|
-
}
|
|
561
|
+
// Type guards
|
|
562
|
+
function isV3Model(model) {
|
|
563
|
+
return model.specificationVersion === 'v3';
|
|
975
564
|
}
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
565
|
+
const mapVercelParams = params => {
|
|
566
|
+
return {
|
|
567
|
+
temperature: params.temperature,
|
|
568
|
+
max_output_tokens: params.maxOutputTokens,
|
|
569
|
+
top_p: params.topP,
|
|
570
|
+
frequency_penalty: params.frequencyPenalty,
|
|
571
|
+
presence_penalty: params.presencePenalty,
|
|
572
|
+
stop: params.stopSequences,
|
|
573
|
+
stream: params.stream
|
|
574
|
+
};
|
|
981
575
|
};
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
posthogParams
|
|
993
|
-
} = extractPosthogParams(body);
|
|
994
|
-
const startTime = Date.now();
|
|
995
|
-
const parentPromise = super.create(openAIParams, options);
|
|
996
|
-
if (openAIParams.stream) {
|
|
997
|
-
const wrappedPromise = parentPromise.then(value => {
|
|
998
|
-
if ('tee' in value) {
|
|
999
|
-
const [stream1, stream2] = value.tee();
|
|
1000
|
-
(async () => {
|
|
1001
|
-
// Hoisted so the catch block can surface whatever was accumulated
|
|
1002
|
-
// from the streamed chunks before the failure.
|
|
1003
|
-
let completionIdFromResponse;
|
|
1004
|
-
let systemFingerprintFromResponse;
|
|
1005
|
-
try {
|
|
1006
|
-
const contentBlocks = [];
|
|
1007
|
-
let accumulatedContent = '';
|
|
1008
|
-
let modelFromResponse;
|
|
1009
|
-
let firstTokenTime;
|
|
1010
|
-
let stopReason;
|
|
1011
|
-
let usage = {
|
|
1012
|
-
inputTokens: 0,
|
|
1013
|
-
outputTokens: 0,
|
|
1014
|
-
webSearchCount: 0
|
|
1015
|
-
};
|
|
1016
|
-
// Map to track in-progress tool calls
|
|
1017
|
-
const toolCallsInProgress = new Map();
|
|
1018
|
-
let rawUsageData;
|
|
1019
|
-
for await (const chunk of stream1) {
|
|
1020
|
-
// Extract model and completion metadata from chunk (Chat Completions chunks carry these fields)
|
|
1021
|
-
if (!modelFromResponse && chunk.model) {
|
|
1022
|
-
modelFromResponse = chunk.model;
|
|
1023
|
-
}
|
|
1024
|
-
if (!completionIdFromResponse && chunk.id) {
|
|
1025
|
-
completionIdFromResponse = chunk.id;
|
|
1026
|
-
}
|
|
1027
|
-
if (!systemFingerprintFromResponse && chunk.system_fingerprint) {
|
|
1028
|
-
systemFingerprintFromResponse = chunk.system_fingerprint;
|
|
1029
|
-
}
|
|
1030
|
-
const choice = chunk?.choices?.[0];
|
|
1031
|
-
if (choice?.finish_reason) {
|
|
1032
|
-
stopReason = choice.finish_reason;
|
|
1033
|
-
}
|
|
1034
|
-
const chunkWebSearchCount = calculateWebSearchCount(chunk);
|
|
1035
|
-
if (chunkWebSearchCount > 0 && chunkWebSearchCount > (usage.webSearchCount ?? 0)) {
|
|
1036
|
-
usage.webSearchCount = chunkWebSearchCount;
|
|
1037
|
-
}
|
|
1038
|
-
// Handle text content
|
|
1039
|
-
const deltaContent = choice?.delta?.content;
|
|
1040
|
-
if (deltaContent) {
|
|
1041
|
-
if (firstTokenTime === undefined) {
|
|
1042
|
-
firstTokenTime = Date.now();
|
|
1043
|
-
}
|
|
1044
|
-
accumulatedContent += deltaContent;
|
|
1045
|
-
}
|
|
1046
|
-
// Handle tool calls
|
|
1047
|
-
const deltaToolCalls = choice?.delta?.tool_calls;
|
|
1048
|
-
if (deltaToolCalls && Array.isArray(deltaToolCalls)) {
|
|
1049
|
-
if (firstTokenTime === undefined) {
|
|
1050
|
-
firstTokenTime = Date.now();
|
|
1051
|
-
}
|
|
1052
|
-
for (const toolCall of deltaToolCalls) {
|
|
1053
|
-
const index = toolCall.index;
|
|
1054
|
-
if (index !== undefined) {
|
|
1055
|
-
if (!toolCallsInProgress.has(index)) {
|
|
1056
|
-
// New tool call
|
|
1057
|
-
toolCallsInProgress.set(index, {
|
|
1058
|
-
id: toolCall.id || '',
|
|
1059
|
-
name: toolCall.function?.name || '',
|
|
1060
|
-
arguments: ''
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
const inProgressCall = toolCallsInProgress.get(index);
|
|
1064
|
-
if (inProgressCall) {
|
|
1065
|
-
// Update tool call data
|
|
1066
|
-
if (toolCall.id) {
|
|
1067
|
-
inProgressCall.id = toolCall.id;
|
|
1068
|
-
}
|
|
1069
|
-
if (toolCall.function?.name) {
|
|
1070
|
-
inProgressCall.name = toolCall.function.name;
|
|
1071
|
-
}
|
|
1072
|
-
if (toolCall.function?.arguments) {
|
|
1073
|
-
inProgressCall.arguments += toolCall.function.arguments;
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
// Handle usage information
|
|
1080
|
-
if (chunk.usage) {
|
|
1081
|
-
rawUsageData = chunk.usage;
|
|
1082
|
-
usage = {
|
|
1083
|
-
...usage,
|
|
1084
|
-
inputTokens: chunk.usage.prompt_tokens ?? 0,
|
|
1085
|
-
outputTokens: chunk.usage.completion_tokens ?? 0,
|
|
1086
|
-
reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? 0,
|
|
1087
|
-
cacheReadInputTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0
|
|
1088
|
-
};
|
|
1089
|
-
}
|
|
1090
|
-
}
|
|
1091
|
-
// Build final content blocks
|
|
1092
|
-
if (accumulatedContent) {
|
|
1093
|
-
contentBlocks.push({
|
|
1094
|
-
type: 'text',
|
|
1095
|
-
text: accumulatedContent
|
|
1096
|
-
});
|
|
1097
|
-
}
|
|
1098
|
-
// Add completed tool calls to content blocks
|
|
1099
|
-
for (const toolCall of toolCallsInProgress.values()) {
|
|
1100
|
-
if (toolCall.name) {
|
|
1101
|
-
contentBlocks.push({
|
|
1102
|
-
type: 'function',
|
|
1103
|
-
id: toolCall.id,
|
|
1104
|
-
function: {
|
|
1105
|
-
name: toolCall.name,
|
|
1106
|
-
arguments: toolCall.arguments
|
|
1107
|
-
}
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
// Format output to match non-streaming version
|
|
1112
|
-
const formattedOutput = contentBlocks.length > 0 ? [{
|
|
1113
|
-
role: 'assistant',
|
|
1114
|
-
content: contentBlocks
|
|
1115
|
-
}] : [{
|
|
1116
|
-
role: 'assistant',
|
|
1117
|
-
content: [{
|
|
1118
|
-
type: 'text',
|
|
1119
|
-
text: ''
|
|
1120
|
-
}]
|
|
1121
|
-
}];
|
|
1122
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1123
|
-
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1124
|
-
const availableTools = extractAvailableToolCalls('openai', openAIParams);
|
|
1125
|
-
await captureAiGeneration(this.phClient, {
|
|
1126
|
-
...posthogParams,
|
|
1127
|
-
model: openAIParams.model ?? modelFromResponse,
|
|
1128
|
-
provider: 'openai',
|
|
1129
|
-
input: sanitizeOpenAI(openAIParams.messages),
|
|
1130
|
-
output: formattedOutput,
|
|
1131
|
-
latency,
|
|
1132
|
-
timeToFirstToken,
|
|
1133
|
-
baseURL: this.baseURL,
|
|
1134
|
-
modelParameters: getModelParams(body),
|
|
1135
|
-
httpStatus: 200,
|
|
1136
|
-
usage: {
|
|
1137
|
-
inputTokens: usage.inputTokens,
|
|
1138
|
-
outputTokens: usage.outputTokens,
|
|
1139
|
-
reasoningTokens: usage.reasoningTokens,
|
|
1140
|
-
cacheReadInputTokens: usage.cacheReadInputTokens,
|
|
1141
|
-
webSearchCount: usage.webSearchCount,
|
|
1142
|
-
rawUsage: rawUsageData
|
|
1143
|
-
},
|
|
1144
|
-
stopReason,
|
|
1145
|
-
tools: availableTools,
|
|
1146
|
-
completionId: completionIdFromResponse,
|
|
1147
|
-
providerMetadata: buildProviderMetadata({
|
|
1148
|
-
systemFingerprint: systemFingerprintFromResponse
|
|
1149
|
-
})
|
|
1150
|
-
});
|
|
1151
|
-
} catch (error) {
|
|
1152
|
-
await captureAiGeneration(this.phClient, {
|
|
1153
|
-
...posthogParams,
|
|
1154
|
-
model: openAIParams.model,
|
|
1155
|
-
provider: 'openai',
|
|
1156
|
-
input: sanitizeOpenAI(openAIParams.messages),
|
|
1157
|
-
output: [],
|
|
1158
|
-
latency: 0,
|
|
1159
|
-
baseURL: this.baseURL,
|
|
1160
|
-
modelParameters: getModelParams(body),
|
|
1161
|
-
usage: {
|
|
1162
|
-
inputTokens: 0,
|
|
1163
|
-
outputTokens: 0
|
|
1164
|
-
},
|
|
1165
|
-
// If the stream fails mid-flight, surface whatever completion
|
|
1166
|
-
// metadata the consumed chunks already provided so the error
|
|
1167
|
-
// event can still be correlated to OpenAI's Logs dashboard.
|
|
1168
|
-
completionId: completionIdFromResponse,
|
|
1169
|
-
providerMetadata: buildProviderMetadata({
|
|
1170
|
-
systemFingerprint: systemFingerprintFromResponse
|
|
1171
|
-
}),
|
|
1172
|
-
error
|
|
1173
|
-
});
|
|
1174
|
-
throw error;
|
|
1175
|
-
}
|
|
1176
|
-
})();
|
|
1177
|
-
// Return the other stream to the user
|
|
1178
|
-
return stream2;
|
|
1179
|
-
}
|
|
1180
|
-
return value;
|
|
1181
|
-
});
|
|
1182
|
-
return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
|
|
576
|
+
const mapVercelPrompt = messages => {
|
|
577
|
+
// Map and truncate individual content
|
|
578
|
+
const inputs = messages.map(message => {
|
|
579
|
+
let content;
|
|
580
|
+
// Handle system role which has string content
|
|
581
|
+
if (message.role === 'system') {
|
|
582
|
+
content = [{
|
|
583
|
+
type: 'text',
|
|
584
|
+
text: truncate(toContentString(message.content))
|
|
585
|
+
}];
|
|
1183
586
|
} else {
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0,
|
|
1204
|
-
webSearchCount: calculateWebSearchCount(result),
|
|
1205
|
-
rawUsage: result.usage
|
|
1206
|
-
},
|
|
1207
|
-
stopReason: result.choices[0]?.finish_reason ?? undefined,
|
|
1208
|
-
tools: availableTools,
|
|
1209
|
-
completionId: result.id,
|
|
1210
|
-
providerMetadata: buildProviderMetadata({
|
|
1211
|
-
systemFingerprint: result.system_fingerprint,
|
|
1212
|
-
requestId: extractRequestId(result)
|
|
1213
|
-
})
|
|
1214
|
-
});
|
|
1215
|
-
}
|
|
1216
|
-
return result;
|
|
1217
|
-
}, async error => {
|
|
1218
|
-
const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
|
|
1219
|
-
await captureAiGeneration(this.phClient, {
|
|
1220
|
-
...posthogParams,
|
|
1221
|
-
model: openAIParams.model,
|
|
1222
|
-
provider: 'openai',
|
|
1223
|
-
input: sanitizeOpenAI(openAIParams.messages),
|
|
1224
|
-
output: [],
|
|
1225
|
-
latency: 0,
|
|
1226
|
-
baseURL: this.baseURL,
|
|
1227
|
-
modelParameters: getModelParams(body),
|
|
1228
|
-
httpStatus,
|
|
1229
|
-
usage: {
|
|
1230
|
-
inputTokens: 0,
|
|
1231
|
-
outputTokens: 0
|
|
1232
|
-
},
|
|
1233
|
-
error
|
|
1234
|
-
});
|
|
1235
|
-
throw error;
|
|
1236
|
-
});
|
|
1237
|
-
return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1240
|
-
};
|
|
1241
|
-
let WrappedResponses$1 = class WrappedResponses extends Responses {
|
|
1242
|
-
constructor(client, phClient) {
|
|
1243
|
-
super(client);
|
|
1244
|
-
this.phClient = phClient;
|
|
1245
|
-
this.baseURL = client.baseURL;
|
|
1246
|
-
}
|
|
1247
|
-
// --- Implementation Signature
|
|
1248
|
-
create(body, options) {
|
|
1249
|
-
const {
|
|
1250
|
-
providerParams: openAIParams,
|
|
1251
|
-
posthogParams
|
|
1252
|
-
} = extractPosthogParams(body);
|
|
1253
|
-
const startTime = Date.now();
|
|
1254
|
-
const parentPromise = super.create(openAIParams, options);
|
|
1255
|
-
if (openAIParams.stream) {
|
|
1256
|
-
const wrappedPromise = parentPromise.then(value => {
|
|
1257
|
-
if ('tee' in value && typeof value.tee === 'function') {
|
|
1258
|
-
const [stream1, stream2] = value.tee();
|
|
1259
|
-
(async () => {
|
|
1260
|
-
// Hoisted so the catch block can surface the completion ID that
|
|
1261
|
-
// was accumulated from the streamed chunks before the failure.
|
|
1262
|
-
let completionIdFromResponse;
|
|
1263
|
-
try {
|
|
1264
|
-
let finalContent = [];
|
|
1265
|
-
let modelFromResponse;
|
|
1266
|
-
let firstTokenTime;
|
|
1267
|
-
let stopReason;
|
|
1268
|
-
let usage = {
|
|
1269
|
-
inputTokens: 0,
|
|
1270
|
-
outputTokens: 0,
|
|
1271
|
-
webSearchCount: 0
|
|
1272
|
-
};
|
|
1273
|
-
let rawUsageData;
|
|
1274
|
-
for await (const chunk of stream1) {
|
|
1275
|
-
// Track first token time on content delta events
|
|
1276
|
-
if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
|
|
1277
|
-
firstTokenTime = Date.now();
|
|
1278
|
-
}
|
|
1279
|
-
if ('response' in chunk && chunk.response) {
|
|
1280
|
-
// Extract model and completion ID from the response object in the chunk (for stored prompts)
|
|
1281
|
-
if (!modelFromResponse && chunk.response.model) {
|
|
1282
|
-
modelFromResponse = chunk.response.model;
|
|
1283
|
-
}
|
|
1284
|
-
if (!completionIdFromResponse && chunk.response.id) {
|
|
1285
|
-
completionIdFromResponse = chunk.response.id;
|
|
1286
|
-
}
|
|
1287
|
-
const chunkWebSearchCount = calculateWebSearchCount(chunk.response);
|
|
1288
|
-
if (chunkWebSearchCount > 0 && chunkWebSearchCount > (usage.webSearchCount ?? 0)) {
|
|
1289
|
-
usage.webSearchCount = chunkWebSearchCount;
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
if (chunk.type === 'response.completed' && 'response' in chunk && chunk.response?.output && chunk.response.output.length > 0) {
|
|
1293
|
-
finalContent = chunk.response.output;
|
|
1294
|
-
if (chunk.response.status) {
|
|
1295
|
-
stopReason = chunk.response.status;
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
if ('response' in chunk && chunk.response?.usage) {
|
|
1299
|
-
rawUsageData = chunk.response.usage;
|
|
1300
|
-
usage = {
|
|
1301
|
-
...usage,
|
|
1302
|
-
inputTokens: chunk.response.usage.input_tokens ?? 0,
|
|
1303
|
-
outputTokens: chunk.response.usage.output_tokens ?? 0,
|
|
1304
|
-
reasoningTokens: chunk.response.usage.output_tokens_details?.reasoning_tokens ?? 0,
|
|
1305
|
-
cacheReadInputTokens: chunk.response.usage.input_tokens_details?.cached_tokens ?? 0
|
|
1306
|
-
};
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1310
|
-
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1311
|
-
const availableTools = extractAvailableToolCalls('openai', openAIParams);
|
|
1312
|
-
await captureAiGeneration(this.phClient, {
|
|
1313
|
-
...posthogParams,
|
|
1314
|
-
model: openAIParams.model ?? modelFromResponse,
|
|
1315
|
-
provider: 'openai',
|
|
1316
|
-
input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
|
|
1317
|
-
output: finalContent,
|
|
1318
|
-
latency,
|
|
1319
|
-
timeToFirstToken,
|
|
1320
|
-
baseURL: this.baseURL,
|
|
1321
|
-
modelParameters: getModelParams(body),
|
|
1322
|
-
httpStatus: 200,
|
|
1323
|
-
usage: {
|
|
1324
|
-
inputTokens: usage.inputTokens,
|
|
1325
|
-
outputTokens: usage.outputTokens,
|
|
1326
|
-
reasoningTokens: usage.reasoningTokens,
|
|
1327
|
-
cacheReadInputTokens: usage.cacheReadInputTokens,
|
|
1328
|
-
webSearchCount: usage.webSearchCount,
|
|
1329
|
-
rawUsage: rawUsageData
|
|
1330
|
-
},
|
|
1331
|
-
stopReason,
|
|
1332
|
-
tools: availableTools,
|
|
1333
|
-
completionId: completionIdFromResponse
|
|
1334
|
-
});
|
|
1335
|
-
} catch (error) {
|
|
1336
|
-
await captureAiGeneration(this.phClient, {
|
|
1337
|
-
...posthogParams,
|
|
1338
|
-
model: openAIParams.model,
|
|
1339
|
-
provider: 'openai',
|
|
1340
|
-
input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
|
|
1341
|
-
output: [],
|
|
1342
|
-
latency: 0,
|
|
1343
|
-
baseURL: this.baseURL,
|
|
1344
|
-
modelParameters: getModelParams(body),
|
|
1345
|
-
usage: {
|
|
1346
|
-
inputTokens: 0,
|
|
1347
|
-
outputTokens: 0
|
|
1348
|
-
},
|
|
1349
|
-
// Surface the completion ID from any chunks consumed before
|
|
1350
|
-
// the stream failed so the error event remains correlatable.
|
|
1351
|
-
completionId: completionIdFromResponse,
|
|
1352
|
-
error
|
|
1353
|
-
});
|
|
1354
|
-
throw error;
|
|
1355
|
-
}
|
|
1356
|
-
})();
|
|
1357
|
-
return stream2;
|
|
1358
|
-
}
|
|
1359
|
-
return value;
|
|
1360
|
-
});
|
|
1361
|
-
return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
|
|
1362
|
-
} else {
|
|
1363
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
1364
|
-
if ('output' in result) {
|
|
1365
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1366
|
-
const availableTools = extractAvailableToolCalls('openai', openAIParams);
|
|
1367
|
-
const formattedOutput = formatResponseOpenAI({
|
|
1368
|
-
output: result.output
|
|
1369
|
-
});
|
|
1370
|
-
await captureAiGenerationAfterSuccess(this.phClient, {
|
|
1371
|
-
...posthogParams,
|
|
1372
|
-
model: openAIParams.model ?? result.model,
|
|
1373
|
-
provider: 'openai',
|
|
1374
|
-
input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
|
|
1375
|
-
output: formattedOutput,
|
|
1376
|
-
latency,
|
|
1377
|
-
baseURL: this.baseURL,
|
|
1378
|
-
modelParameters: getModelParams(body),
|
|
1379
|
-
httpStatus: 200,
|
|
1380
|
-
usage: {
|
|
1381
|
-
inputTokens: result.usage?.input_tokens ?? 0,
|
|
1382
|
-
outputTokens: result.usage?.output_tokens ?? 0,
|
|
1383
|
-
reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
|
|
1384
|
-
cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
|
|
1385
|
-
webSearchCount: calculateWebSearchCount(result),
|
|
1386
|
-
rawUsage: result.usage
|
|
1387
|
-
},
|
|
1388
|
-
stopReason: result.status ?? undefined,
|
|
1389
|
-
tools: availableTools,
|
|
1390
|
-
completionId: result.id,
|
|
1391
|
-
providerMetadata: buildProviderMetadata({
|
|
1392
|
-
requestId: extractRequestId(result)
|
|
1393
|
-
})
|
|
1394
|
-
});
|
|
1395
|
-
}
|
|
1396
|
-
return result;
|
|
1397
|
-
}, async error => {
|
|
1398
|
-
const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
|
|
1399
|
-
await captureAiGeneration(this.phClient, {
|
|
1400
|
-
...posthogParams,
|
|
1401
|
-
model: openAIParams.model,
|
|
1402
|
-
provider: 'openai',
|
|
1403
|
-
input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
|
|
1404
|
-
output: [],
|
|
1405
|
-
latency: 0,
|
|
1406
|
-
baseURL: this.baseURL,
|
|
1407
|
-
modelParameters: getModelParams(body),
|
|
1408
|
-
httpStatus,
|
|
1409
|
-
usage: {
|
|
1410
|
-
inputTokens: 0,
|
|
1411
|
-
outputTokens: 0
|
|
1412
|
-
},
|
|
1413
|
-
error
|
|
1414
|
-
});
|
|
1415
|
-
throw error;
|
|
1416
|
-
});
|
|
1417
|
-
return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
|
|
1418
|
-
}
|
|
1419
|
-
}
|
|
1420
|
-
parse(body, options) {
|
|
1421
|
-
const {
|
|
1422
|
-
providerParams: openAIParams,
|
|
1423
|
-
posthogParams
|
|
1424
|
-
} = extractPosthogParams(body);
|
|
1425
|
-
const startTime = Date.now();
|
|
1426
|
-
const originalCreate = super.create.bind(this);
|
|
1427
|
-
const originalSelfRecord = this;
|
|
1428
|
-
const tempCreate = originalSelfRecord['create'];
|
|
1429
|
-
originalSelfRecord['create'] = originalCreate;
|
|
1430
|
-
try {
|
|
1431
|
-
const parentPromise = super.parse(openAIParams, options);
|
|
1432
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
1433
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1434
|
-
await captureAiGeneration(this.phClient, {
|
|
1435
|
-
...posthogParams,
|
|
1436
|
-
model: openAIParams.model ?? result.model,
|
|
1437
|
-
provider: 'openai',
|
|
1438
|
-
input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
|
|
1439
|
-
output: result.output,
|
|
1440
|
-
latency,
|
|
1441
|
-
baseURL: this.baseURL,
|
|
1442
|
-
modelParameters: getModelParams(body),
|
|
1443
|
-
httpStatus: 200,
|
|
1444
|
-
usage: {
|
|
1445
|
-
inputTokens: result.usage?.input_tokens ?? 0,
|
|
1446
|
-
outputTokens: result.usage?.output_tokens ?? 0,
|
|
1447
|
-
reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
|
|
1448
|
-
cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
|
|
1449
|
-
rawUsage: result.usage
|
|
1450
|
-
},
|
|
1451
|
-
stopReason: result.status ?? undefined,
|
|
1452
|
-
completionId: result.id,
|
|
1453
|
-
providerMetadata: buildProviderMetadata({
|
|
1454
|
-
requestId: extractRequestId(result)
|
|
1455
|
-
})
|
|
1456
|
-
});
|
|
1457
|
-
return result;
|
|
1458
|
-
}, async error => {
|
|
1459
|
-
await captureAiGeneration(this.phClient, {
|
|
1460
|
-
...posthogParams,
|
|
1461
|
-
model: openAIParams.model,
|
|
1462
|
-
provider: 'openai',
|
|
1463
|
-
input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
|
|
1464
|
-
output: [],
|
|
1465
|
-
latency: 0,
|
|
1466
|
-
baseURL: this.baseURL,
|
|
1467
|
-
modelParameters: getModelParams(body),
|
|
1468
|
-
usage: {
|
|
1469
|
-
inputTokens: 0,
|
|
1470
|
-
outputTokens: 0
|
|
1471
|
-
},
|
|
1472
|
-
error
|
|
1473
|
-
});
|
|
1474
|
-
throw error;
|
|
1475
|
-
});
|
|
1476
|
-
return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
|
|
1477
|
-
} finally {
|
|
1478
|
-
// Restore our wrapped create method
|
|
1479
|
-
originalSelfRecord['create'] = tempCreate;
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
};
|
|
1483
|
-
let WrappedEmbeddings$1 = class WrappedEmbeddings extends Embeddings {
|
|
1484
|
-
constructor(client, phClient) {
|
|
1485
|
-
super(client);
|
|
1486
|
-
this.phClient = phClient;
|
|
1487
|
-
this.baseURL = client.baseURL;
|
|
1488
|
-
}
|
|
1489
|
-
create(body, options) {
|
|
1490
|
-
const {
|
|
1491
|
-
providerParams: openAIParams,
|
|
1492
|
-
posthogParams
|
|
1493
|
-
} = extractPosthogParams(body);
|
|
1494
|
-
const startTime = Date.now();
|
|
1495
|
-
const parentPromise = super.create(openAIParams, options);
|
|
1496
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
1497
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1498
|
-
await captureAiGeneration(this.phClient, {
|
|
1499
|
-
...posthogParams,
|
|
1500
|
-
eventType: AIEvent.Embedding,
|
|
1501
|
-
model: openAIParams.model,
|
|
1502
|
-
provider: 'openai',
|
|
1503
|
-
input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
|
|
1504
|
-
output: null,
|
|
1505
|
-
// Embeddings don't have output content
|
|
1506
|
-
latency,
|
|
1507
|
-
baseURL: this.baseURL,
|
|
1508
|
-
modelParameters: getModelParams(body),
|
|
1509
|
-
httpStatus: 200,
|
|
1510
|
-
usage: {
|
|
1511
|
-
inputTokens: result.usage?.prompt_tokens ?? 0,
|
|
1512
|
-
rawUsage: result.usage
|
|
1513
|
-
}
|
|
1514
|
-
});
|
|
1515
|
-
return result;
|
|
1516
|
-
}, async error => {
|
|
1517
|
-
const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
|
|
1518
|
-
await captureAiGeneration(this.phClient, {
|
|
1519
|
-
eventType: AIEvent.Embedding,
|
|
1520
|
-
...posthogParams,
|
|
1521
|
-
model: openAIParams.model,
|
|
1522
|
-
provider: 'openai',
|
|
1523
|
-
input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
|
|
1524
|
-
output: null,
|
|
1525
|
-
// Embeddings don't have output content
|
|
1526
|
-
latency: 0,
|
|
1527
|
-
baseURL: this.baseURL,
|
|
1528
|
-
modelParameters: getModelParams(body),
|
|
1529
|
-
httpStatus,
|
|
1530
|
-
usage: {
|
|
1531
|
-
inputTokens: 0
|
|
1532
|
-
},
|
|
1533
|
-
error
|
|
1534
|
-
});
|
|
1535
|
-
throw error;
|
|
1536
|
-
});
|
|
1537
|
-
return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
|
|
1538
|
-
}
|
|
1539
|
-
};
|
|
1540
|
-
class WrappedAudio extends Audio {
|
|
1541
|
-
constructor(parentClient, phClient) {
|
|
1542
|
-
super(parentClient);
|
|
1543
|
-
this.transcriptions = new WrappedTranscriptions(parentClient, phClient);
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
class WrappedTranscriptions extends Transcriptions {
|
|
1547
|
-
constructor(client, phClient) {
|
|
1548
|
-
super(client);
|
|
1549
|
-
this.phClient = phClient;
|
|
1550
|
-
this.baseURL = client.baseURL;
|
|
1551
|
-
}
|
|
1552
|
-
// --- Implementation Signature
|
|
1553
|
-
create(body, options) {
|
|
1554
|
-
const {
|
|
1555
|
-
providerParams: openAIParams,
|
|
1556
|
-
posthogParams
|
|
1557
|
-
} = extractPosthogParams(body);
|
|
1558
|
-
const startTime = Date.now();
|
|
1559
|
-
const parentPromise = openAIParams.stream ? super.create(openAIParams, options) : super.create(openAIParams, options);
|
|
1560
|
-
if (openAIParams.stream) {
|
|
1561
|
-
const wrappedPromise = parentPromise.then(value => {
|
|
1562
|
-
if ('tee' in value && typeof value.tee === 'function') {
|
|
1563
|
-
const [stream1, stream2] = value.tee();
|
|
1564
|
-
(async () => {
|
|
1565
|
-
try {
|
|
1566
|
-
let finalContent = '';
|
|
1567
|
-
let firstTokenTime;
|
|
1568
|
-
let usage = {
|
|
1569
|
-
inputTokens: 0,
|
|
1570
|
-
outputTokens: 0
|
|
1571
|
-
};
|
|
1572
|
-
const doneEvent = 'transcript.text.done';
|
|
1573
|
-
for await (const chunk of stream1) {
|
|
1574
|
-
// Track first token on text delta events
|
|
1575
|
-
if (firstTokenTime === undefined && chunk.type === 'transcript.text.delta') {
|
|
1576
|
-
firstTokenTime = Date.now();
|
|
1577
|
-
}
|
|
1578
|
-
if (chunk.type === doneEvent && 'text' in chunk && chunk.text && chunk.text.length > 0) {
|
|
1579
|
-
finalContent = chunk.text;
|
|
1580
|
-
}
|
|
1581
|
-
if ('usage' in chunk && chunk.usage) {
|
|
1582
|
-
usage = {
|
|
1583
|
-
inputTokens: chunk.usage?.type === 'tokens' ? chunk.usage.input_tokens ?? 0 : 0,
|
|
1584
|
-
outputTokens: chunk.usage?.type === 'tokens' ? chunk.usage.output_tokens ?? 0 : 0,
|
|
1585
|
-
rawUsage: chunk.usage
|
|
1586
|
-
};
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1590
|
-
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1591
|
-
const availableTools = extractAvailableToolCalls('openai', openAIParams);
|
|
1592
|
-
await captureAiGeneration(this.phClient, {
|
|
1593
|
-
...posthogParams,
|
|
1594
|
-
model: openAIParams.model,
|
|
1595
|
-
provider: 'openai',
|
|
1596
|
-
input: openAIParams.prompt,
|
|
1597
|
-
output: finalContent,
|
|
1598
|
-
latency,
|
|
1599
|
-
timeToFirstToken,
|
|
1600
|
-
baseURL: this.baseURL,
|
|
1601
|
-
modelParameters: getModelParams(body),
|
|
1602
|
-
httpStatus: 200,
|
|
1603
|
-
usage,
|
|
1604
|
-
tools: availableTools
|
|
1605
|
-
});
|
|
1606
|
-
} catch (error) {
|
|
1607
|
-
await captureAiGeneration(this.phClient, {
|
|
1608
|
-
...posthogParams,
|
|
1609
|
-
model: openAIParams.model,
|
|
1610
|
-
provider: 'openai',
|
|
1611
|
-
input: openAIParams.prompt,
|
|
1612
|
-
output: [],
|
|
1613
|
-
latency: 0,
|
|
1614
|
-
baseURL: this.baseURL,
|
|
1615
|
-
modelParameters: getModelParams(body),
|
|
1616
|
-
usage: {
|
|
1617
|
-
inputTokens: 0,
|
|
1618
|
-
outputTokens: 0
|
|
1619
|
-
},
|
|
1620
|
-
error
|
|
1621
|
-
});
|
|
1622
|
-
throw error;
|
|
1623
|
-
}
|
|
1624
|
-
})();
|
|
1625
|
-
return stream2;
|
|
1626
|
-
}
|
|
1627
|
-
return value;
|
|
1628
|
-
});
|
|
1629
|
-
return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
|
|
1630
|
-
} else {
|
|
1631
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
1632
|
-
if (result && typeof result === 'object' && 'text' in result) {
|
|
1633
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1634
|
-
await captureAiGenerationAfterSuccess(this.phClient, {
|
|
1635
|
-
...posthogParams,
|
|
1636
|
-
model: openAIParams.model,
|
|
1637
|
-
provider: 'openai',
|
|
1638
|
-
input: openAIParams.prompt,
|
|
1639
|
-
output: result.text,
|
|
1640
|
-
latency,
|
|
1641
|
-
baseURL: this.baseURL,
|
|
1642
|
-
modelParameters: getModelParams(body),
|
|
1643
|
-
httpStatus: 200,
|
|
1644
|
-
usage: {
|
|
1645
|
-
inputTokens: result.usage?.type === 'tokens' ? result.usage.input_tokens ?? 0 : 0,
|
|
1646
|
-
outputTokens: result.usage?.type === 'tokens' ? result.usage.output_tokens ?? 0 : 0,
|
|
1647
|
-
rawUsage: result.usage
|
|
1648
|
-
}
|
|
1649
|
-
});
|
|
1650
|
-
}
|
|
1651
|
-
return result;
|
|
1652
|
-
}, async error => {
|
|
1653
|
-
await captureAiGeneration(this.phClient, {
|
|
1654
|
-
...posthogParams,
|
|
1655
|
-
model: openAIParams.model,
|
|
1656
|
-
provider: 'openai',
|
|
1657
|
-
input: openAIParams.prompt,
|
|
1658
|
-
output: [],
|
|
1659
|
-
latency: 0,
|
|
1660
|
-
baseURL: this.baseURL,
|
|
1661
|
-
modelParameters: getModelParams(body),
|
|
1662
|
-
usage: {
|
|
1663
|
-
inputTokens: 0,
|
|
1664
|
-
outputTokens: 0
|
|
1665
|
-
},
|
|
1666
|
-
error
|
|
1667
|
-
});
|
|
1668
|
-
throw error;
|
|
1669
|
-
});
|
|
1670
|
-
return preserveAPIPromiseHelpers(parentPromise, wrappedPromise);
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
class PostHogAzureOpenAI extends AzureOpenAI {
|
|
1676
|
-
constructor(config) {
|
|
1677
|
-
const {
|
|
1678
|
-
posthog,
|
|
1679
|
-
...openAIConfig
|
|
1680
|
-
} = config;
|
|
1681
|
-
super(openAIConfig);
|
|
1682
|
-
this.phClient = posthog;
|
|
1683
|
-
this.chat = new WrappedChat(this, this.phClient);
|
|
1684
|
-
this.embeddings = new WrappedEmbeddings(this, this.phClient);
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
class WrappedChat extends AzureOpenAI.Chat {
|
|
1688
|
-
constructor(parentClient, phClient) {
|
|
1689
|
-
super(parentClient);
|
|
1690
|
-
this.completions = new WrappedCompletions(parentClient, phClient);
|
|
1691
|
-
}
|
|
1692
|
-
}
|
|
1693
|
-
class WrappedCompletions extends AzureOpenAI.Chat.Completions {
|
|
1694
|
-
constructor(client, phClient) {
|
|
1695
|
-
super(client);
|
|
1696
|
-
this.phClient = phClient;
|
|
1697
|
-
this.baseURL = client.baseURL;
|
|
1698
|
-
}
|
|
1699
|
-
// --- Implementation Signature
|
|
1700
|
-
create(body, options) {
|
|
1701
|
-
const {
|
|
1702
|
-
providerParams: openAIParams,
|
|
1703
|
-
posthogParams
|
|
1704
|
-
} = extractPosthogParams(body);
|
|
1705
|
-
const startTime = Date.now();
|
|
1706
|
-
const parentPromise = super.create(openAIParams, options);
|
|
1707
|
-
if (openAIParams.stream) {
|
|
1708
|
-
return parentPromise.then(value => {
|
|
1709
|
-
if ('tee' in value) {
|
|
1710
|
-
const [stream1, stream2] = value.tee();
|
|
1711
|
-
(async () => {
|
|
1712
|
-
// Hoisted so the catch block can surface whatever was accumulated
|
|
1713
|
-
// from the streamed chunks before the failure.
|
|
1714
|
-
let completionIdFromResponse;
|
|
1715
|
-
let systemFingerprintFromResponse;
|
|
1716
|
-
try {
|
|
1717
|
-
const contentBlocks = [];
|
|
1718
|
-
let accumulatedContent = '';
|
|
1719
|
-
let modelFromResponse;
|
|
1720
|
-
let firstTokenTime;
|
|
1721
|
-
let usage = {
|
|
1722
|
-
inputTokens: 0,
|
|
1723
|
-
outputTokens: 0
|
|
1724
|
-
};
|
|
1725
|
-
// Map to track in-progress tool calls
|
|
1726
|
-
const toolCallsInProgress = new Map();
|
|
1727
|
-
for await (const chunk of stream1) {
|
|
1728
|
-
// Extract model and completion metadata from chunk (Chat Completions chunks carry these fields)
|
|
1729
|
-
if (!modelFromResponse && chunk.model) {
|
|
1730
|
-
modelFromResponse = chunk.model;
|
|
1731
|
-
}
|
|
1732
|
-
if (!completionIdFromResponse && chunk.id) {
|
|
1733
|
-
completionIdFromResponse = chunk.id;
|
|
1734
|
-
}
|
|
1735
|
-
if (!systemFingerprintFromResponse && chunk.system_fingerprint) {
|
|
1736
|
-
systemFingerprintFromResponse = chunk.system_fingerprint;
|
|
1737
|
-
}
|
|
1738
|
-
const choice = chunk?.choices?.[0];
|
|
1739
|
-
// Handle text content
|
|
1740
|
-
const deltaContent = choice?.delta?.content;
|
|
1741
|
-
if (deltaContent) {
|
|
1742
|
-
if (firstTokenTime === undefined) {
|
|
1743
|
-
firstTokenTime = Date.now();
|
|
1744
|
-
}
|
|
1745
|
-
accumulatedContent += deltaContent;
|
|
1746
|
-
}
|
|
1747
|
-
// Handle tool calls
|
|
1748
|
-
const deltaToolCalls = choice?.delta?.tool_calls;
|
|
1749
|
-
if (deltaToolCalls && Array.isArray(deltaToolCalls)) {
|
|
1750
|
-
if (firstTokenTime === undefined) {
|
|
1751
|
-
firstTokenTime = Date.now();
|
|
1752
|
-
}
|
|
1753
|
-
for (const toolCall of deltaToolCalls) {
|
|
1754
|
-
const index = toolCall.index;
|
|
1755
|
-
if (index !== undefined) {
|
|
1756
|
-
if (!toolCallsInProgress.has(index)) {
|
|
1757
|
-
// New tool call
|
|
1758
|
-
toolCallsInProgress.set(index, {
|
|
1759
|
-
id: toolCall.id || '',
|
|
1760
|
-
name: toolCall.function?.name || '',
|
|
1761
|
-
arguments: ''
|
|
1762
|
-
});
|
|
1763
|
-
}
|
|
1764
|
-
const inProgressCall = toolCallsInProgress.get(index);
|
|
1765
|
-
if (inProgressCall) {
|
|
1766
|
-
// Update tool call data
|
|
1767
|
-
if (toolCall.id) {
|
|
1768
|
-
inProgressCall.id = toolCall.id;
|
|
1769
|
-
}
|
|
1770
|
-
if (toolCall.function?.name) {
|
|
1771
|
-
inProgressCall.name = toolCall.function.name;
|
|
1772
|
-
}
|
|
1773
|
-
if (toolCall.function?.arguments) {
|
|
1774
|
-
inProgressCall.arguments += toolCall.function.arguments;
|
|
1775
|
-
}
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
// Handle usage information
|
|
1781
|
-
if (chunk.usage) {
|
|
1782
|
-
usage = {
|
|
1783
|
-
inputTokens: chunk.usage.prompt_tokens ?? 0,
|
|
1784
|
-
outputTokens: chunk.usage.completion_tokens ?? 0,
|
|
1785
|
-
reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? 0,
|
|
1786
|
-
cacheReadInputTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0
|
|
1787
|
-
};
|
|
1788
|
-
}
|
|
1789
|
-
}
|
|
1790
|
-
// Build final content blocks
|
|
1791
|
-
if (accumulatedContent) {
|
|
1792
|
-
contentBlocks.push({
|
|
1793
|
-
type: 'text',
|
|
1794
|
-
text: accumulatedContent
|
|
1795
|
-
});
|
|
1796
|
-
}
|
|
1797
|
-
// Add completed tool calls to content blocks
|
|
1798
|
-
for (const toolCall of toolCallsInProgress.values()) {
|
|
1799
|
-
if (toolCall.name) {
|
|
1800
|
-
contentBlocks.push({
|
|
1801
|
-
type: 'function',
|
|
1802
|
-
id: toolCall.id,
|
|
1803
|
-
function: {
|
|
1804
|
-
name: toolCall.name,
|
|
1805
|
-
arguments: toolCall.arguments
|
|
1806
|
-
}
|
|
1807
|
-
});
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
// Format output to match non-streaming version
|
|
1811
|
-
const formattedOutput = contentBlocks.length > 0 ? [{
|
|
1812
|
-
role: 'assistant',
|
|
1813
|
-
content: contentBlocks
|
|
1814
|
-
}] : [{
|
|
1815
|
-
role: 'assistant',
|
|
1816
|
-
content: [{
|
|
1817
|
-
type: 'text',
|
|
1818
|
-
text: ''
|
|
1819
|
-
}]
|
|
1820
|
-
}];
|
|
1821
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1822
|
-
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1823
|
-
await captureAiGeneration(this.phClient, {
|
|
1824
|
-
...posthogParams,
|
|
1825
|
-
model: openAIParams.model ?? modelFromResponse,
|
|
1826
|
-
provider: 'azure',
|
|
1827
|
-
input: sanitizeOpenAI(openAIParams.messages),
|
|
1828
|
-
output: formattedOutput,
|
|
1829
|
-
latency,
|
|
1830
|
-
timeToFirstToken,
|
|
1831
|
-
baseURL: this.baseURL,
|
|
1832
|
-
modelParameters: getModelParams(body),
|
|
1833
|
-
httpStatus: 200,
|
|
1834
|
-
usage,
|
|
1835
|
-
completionId: completionIdFromResponse,
|
|
1836
|
-
providerMetadata: buildProviderMetadata({
|
|
1837
|
-
systemFingerprint: systemFingerprintFromResponse
|
|
1838
|
-
})
|
|
1839
|
-
});
|
|
1840
|
-
} catch (error) {
|
|
1841
|
-
await captureAiGeneration(this.phClient, {
|
|
1842
|
-
...posthogParams,
|
|
1843
|
-
model: openAIParams.model,
|
|
1844
|
-
provider: 'azure',
|
|
1845
|
-
input: sanitizeOpenAI(openAIParams.messages),
|
|
1846
|
-
output: [],
|
|
1847
|
-
latency: 0,
|
|
1848
|
-
baseURL: this.baseURL,
|
|
1849
|
-
modelParameters: getModelParams(body),
|
|
1850
|
-
usage: {
|
|
1851
|
-
inputTokens: 0,
|
|
1852
|
-
outputTokens: 0
|
|
1853
|
-
},
|
|
1854
|
-
// If the stream fails mid-flight, surface whatever completion
|
|
1855
|
-
// metadata the consumed chunks already provided so the error
|
|
1856
|
-
// event can still be correlated to OpenAI's Logs dashboard.
|
|
1857
|
-
completionId: completionIdFromResponse,
|
|
1858
|
-
providerMetadata: buildProviderMetadata({
|
|
1859
|
-
systemFingerprint: systemFingerprintFromResponse
|
|
1860
|
-
}),
|
|
1861
|
-
error: error
|
|
1862
|
-
});
|
|
1863
|
-
throw error;
|
|
1864
|
-
}
|
|
1865
|
-
})();
|
|
1866
|
-
// Return the other stream to the user
|
|
1867
|
-
return stream2;
|
|
1868
|
-
}
|
|
1869
|
-
return value;
|
|
1870
|
-
});
|
|
1871
|
-
} else {
|
|
1872
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
1873
|
-
if ('choices' in result) {
|
|
1874
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1875
|
-
await captureAiGeneration(this.phClient, {
|
|
1876
|
-
...posthogParams,
|
|
1877
|
-
model: openAIParams.model ?? result.model,
|
|
1878
|
-
provider: 'azure',
|
|
1879
|
-
input: openAIParams.messages,
|
|
1880
|
-
output: formatResponseOpenAI(result),
|
|
1881
|
-
latency,
|
|
1882
|
-
baseURL: this.baseURL,
|
|
1883
|
-
modelParameters: getModelParams(body),
|
|
1884
|
-
httpStatus: 200,
|
|
1885
|
-
usage: {
|
|
1886
|
-
inputTokens: result.usage?.prompt_tokens ?? 0,
|
|
1887
|
-
outputTokens: result.usage?.completion_tokens ?? 0,
|
|
1888
|
-
reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
|
|
1889
|
-
cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0
|
|
1890
|
-
},
|
|
1891
|
-
completionId: result.id,
|
|
1892
|
-
providerMetadata: buildProviderMetadata({
|
|
1893
|
-
systemFingerprint: result.system_fingerprint,
|
|
1894
|
-
requestId: extractRequestId(result)
|
|
1895
|
-
})
|
|
1896
|
-
});
|
|
1897
|
-
}
|
|
1898
|
-
return result;
|
|
1899
|
-
}, async error => {
|
|
1900
|
-
const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
|
|
1901
|
-
await captureAiGeneration(this.phClient, {
|
|
1902
|
-
...posthogParams,
|
|
1903
|
-
model: openAIParams.model,
|
|
1904
|
-
provider: 'azure',
|
|
1905
|
-
input: openAIParams.messages,
|
|
1906
|
-
output: [],
|
|
1907
|
-
latency: 0,
|
|
1908
|
-
baseURL: this.baseURL,
|
|
1909
|
-
modelParameters: getModelParams(body),
|
|
1910
|
-
httpStatus,
|
|
1911
|
-
usage: {
|
|
1912
|
-
inputTokens: 0,
|
|
1913
|
-
outputTokens: 0
|
|
1914
|
-
},
|
|
1915
|
-
error
|
|
1916
|
-
});
|
|
1917
|
-
throw error;
|
|
1918
|
-
});
|
|
1919
|
-
return wrappedPromise;
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
}
|
|
1923
|
-
class WrappedResponses extends AzureOpenAI.Responses {
|
|
1924
|
-
constructor(client, phClient) {
|
|
1925
|
-
super(client);
|
|
1926
|
-
this.phClient = phClient;
|
|
1927
|
-
this.baseURL = client.baseURL;
|
|
1928
|
-
}
|
|
1929
|
-
// --- Implementation Signature
|
|
1930
|
-
create(body, options) {
|
|
1931
|
-
const {
|
|
1932
|
-
providerParams: openAIParams,
|
|
1933
|
-
posthogParams
|
|
1934
|
-
} = extractPosthogParams(body);
|
|
1935
|
-
const startTime = Date.now();
|
|
1936
|
-
const parentPromise = super.create(openAIParams, options);
|
|
1937
|
-
if (openAIParams.stream) {
|
|
1938
|
-
return parentPromise.then(value => {
|
|
1939
|
-
if ('tee' in value && typeof value.tee === 'function') {
|
|
1940
|
-
const [stream1, stream2] = value.tee();
|
|
1941
|
-
(async () => {
|
|
1942
|
-
// Hoisted so the catch block can surface the completion ID that
|
|
1943
|
-
// was accumulated from the streamed chunks before the failure.
|
|
1944
|
-
let completionIdFromResponse;
|
|
1945
|
-
try {
|
|
1946
|
-
let finalContent = [];
|
|
1947
|
-
let modelFromResponse;
|
|
1948
|
-
let firstTokenTime;
|
|
1949
|
-
let usage = {
|
|
1950
|
-
inputTokens: 0,
|
|
1951
|
-
outputTokens: 0
|
|
1952
|
-
};
|
|
1953
|
-
for await (const chunk of stream1) {
|
|
1954
|
-
// Track first token time on content delta events
|
|
1955
|
-
if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
|
|
1956
|
-
firstTokenTime = Date.now();
|
|
1957
|
-
}
|
|
1958
|
-
if ('response' in chunk && chunk.response) {
|
|
1959
|
-
// Extract model and completion ID from the response object in the chunk (for stored prompts)
|
|
1960
|
-
if (!modelFromResponse && chunk.response.model) {
|
|
1961
|
-
modelFromResponse = chunk.response.model;
|
|
1962
|
-
}
|
|
1963
|
-
if (!completionIdFromResponse && chunk.response.id) {
|
|
1964
|
-
completionIdFromResponse = chunk.response.id;
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
if (chunk.type === 'response.completed' && 'response' in chunk && chunk.response?.output && chunk.response.output.length > 0) {
|
|
1968
|
-
finalContent = chunk.response.output;
|
|
1969
|
-
}
|
|
1970
|
-
if ('usage' in chunk && chunk.usage) {
|
|
1971
|
-
usage = {
|
|
1972
|
-
inputTokens: chunk.usage.input_tokens ?? 0,
|
|
1973
|
-
outputTokens: chunk.usage.output_tokens ?? 0,
|
|
1974
|
-
reasoningTokens: chunk.usage.output_tokens_details?.reasoning_tokens ?? 0,
|
|
1975
|
-
cacheReadInputTokens: chunk.usage.input_tokens_details?.cached_tokens ?? 0
|
|
1976
|
-
};
|
|
1977
|
-
}
|
|
1978
|
-
}
|
|
1979
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
1980
|
-
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1981
|
-
await captureAiGeneration(this.phClient, {
|
|
1982
|
-
...posthogParams,
|
|
1983
|
-
model: openAIParams.model ?? modelFromResponse,
|
|
1984
|
-
provider: 'azure',
|
|
1985
|
-
input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
|
|
1986
|
-
output: finalContent,
|
|
1987
|
-
latency,
|
|
1988
|
-
timeToFirstToken,
|
|
1989
|
-
baseURL: this.baseURL,
|
|
1990
|
-
modelParameters: getModelParams(body),
|
|
1991
|
-
httpStatus: 200,
|
|
1992
|
-
usage,
|
|
1993
|
-
completionId: completionIdFromResponse
|
|
1994
|
-
});
|
|
1995
|
-
} catch (error) {
|
|
1996
|
-
await captureAiGeneration(this.phClient, {
|
|
1997
|
-
...posthogParams,
|
|
1998
|
-
model: openAIParams.model,
|
|
1999
|
-
provider: 'azure',
|
|
2000
|
-
input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
|
|
2001
|
-
output: [],
|
|
2002
|
-
latency: 0,
|
|
2003
|
-
baseURL: this.baseURL,
|
|
2004
|
-
modelParameters: getModelParams(body),
|
|
2005
|
-
usage: {
|
|
2006
|
-
inputTokens: 0,
|
|
2007
|
-
outputTokens: 0
|
|
2008
|
-
},
|
|
2009
|
-
// Surface the completion ID from any chunks consumed before
|
|
2010
|
-
// the stream failed so the error event remains correlatable.
|
|
2011
|
-
completionId: completionIdFromResponse,
|
|
2012
|
-
error: error
|
|
2013
|
-
});
|
|
2014
|
-
throw error;
|
|
2015
|
-
}
|
|
2016
|
-
})();
|
|
2017
|
-
return stream2;
|
|
2018
|
-
}
|
|
2019
|
-
return value;
|
|
2020
|
-
});
|
|
2021
|
-
} else {
|
|
2022
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
2023
|
-
if ('output' in result) {
|
|
2024
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
2025
|
-
await captureAiGeneration(this.phClient, {
|
|
2026
|
-
...posthogParams,
|
|
2027
|
-
model: openAIParams.model ?? result.model,
|
|
2028
|
-
provider: 'azure',
|
|
2029
|
-
input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
|
|
2030
|
-
output: result.output,
|
|
2031
|
-
latency,
|
|
2032
|
-
baseURL: this.baseURL,
|
|
2033
|
-
modelParameters: getModelParams(body),
|
|
2034
|
-
httpStatus: 200,
|
|
2035
|
-
usage: {
|
|
2036
|
-
inputTokens: result.usage?.input_tokens ?? 0,
|
|
2037
|
-
outputTokens: result.usage?.output_tokens ?? 0,
|
|
2038
|
-
reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
|
|
2039
|
-
cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
|
|
2040
|
-
},
|
|
2041
|
-
completionId: result.id,
|
|
2042
|
-
providerMetadata: buildProviderMetadata({
|
|
2043
|
-
requestId: extractRequestId(result)
|
|
2044
|
-
})
|
|
2045
|
-
});
|
|
2046
|
-
}
|
|
2047
|
-
return result;
|
|
2048
|
-
}, async error => {
|
|
2049
|
-
const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
|
|
2050
|
-
await captureAiGeneration(this.phClient, {
|
|
2051
|
-
...posthogParams,
|
|
2052
|
-
model: openAIParams.model,
|
|
2053
|
-
provider: 'azure',
|
|
2054
|
-
input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
|
|
2055
|
-
output: [],
|
|
2056
|
-
latency: 0,
|
|
2057
|
-
baseURL: this.baseURL,
|
|
2058
|
-
modelParameters: getModelParams(body),
|
|
2059
|
-
httpStatus,
|
|
2060
|
-
usage: {
|
|
2061
|
-
inputTokens: 0,
|
|
2062
|
-
outputTokens: 0
|
|
2063
|
-
},
|
|
2064
|
-
error
|
|
2065
|
-
});
|
|
2066
|
-
throw error;
|
|
2067
|
-
});
|
|
2068
|
-
return wrappedPromise;
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
parse(body, options) {
|
|
2072
|
-
const {
|
|
2073
|
-
providerParams: openAIParams,
|
|
2074
|
-
posthogParams
|
|
2075
|
-
} = extractPosthogParams(body);
|
|
2076
|
-
const startTime = Date.now();
|
|
2077
|
-
const parentPromise = super.parse(openAIParams, options);
|
|
2078
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
2079
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
2080
|
-
await captureAiGeneration(this.phClient, {
|
|
2081
|
-
...posthogParams,
|
|
2082
|
-
model: openAIParams.model ?? result.model,
|
|
2083
|
-
provider: 'azure',
|
|
2084
|
-
input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
|
|
2085
|
-
output: result.output,
|
|
2086
|
-
latency,
|
|
2087
|
-
baseURL: this.baseURL,
|
|
2088
|
-
modelParameters: getModelParams(body),
|
|
2089
|
-
httpStatus: 200,
|
|
2090
|
-
usage: {
|
|
2091
|
-
inputTokens: result.usage?.input_tokens ?? 0,
|
|
2092
|
-
outputTokens: result.usage?.output_tokens ?? 0,
|
|
2093
|
-
reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
|
|
2094
|
-
cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
|
|
2095
|
-
},
|
|
2096
|
-
completionId: result.id,
|
|
2097
|
-
providerMetadata: buildProviderMetadata({
|
|
2098
|
-
requestId: extractRequestId(result)
|
|
2099
|
-
})
|
|
2100
|
-
});
|
|
2101
|
-
return result;
|
|
2102
|
-
}, async error => {
|
|
2103
|
-
await captureAiGeneration(this.phClient, {
|
|
2104
|
-
...posthogParams,
|
|
2105
|
-
model: openAIParams.model,
|
|
2106
|
-
provider: 'azure',
|
|
2107
|
-
input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
|
|
2108
|
-
output: [],
|
|
2109
|
-
latency: 0,
|
|
2110
|
-
baseURL: this.baseURL,
|
|
2111
|
-
modelParameters: getModelParams(body),
|
|
2112
|
-
httpStatus: error?.status ? error.status : 500,
|
|
2113
|
-
usage: {
|
|
2114
|
-
inputTokens: 0,
|
|
2115
|
-
outputTokens: 0
|
|
2116
|
-
},
|
|
2117
|
-
error
|
|
2118
|
-
});
|
|
2119
|
-
throw error;
|
|
2120
|
-
});
|
|
2121
|
-
return wrappedPromise;
|
|
2122
|
-
}
|
|
2123
|
-
}
|
|
2124
|
-
class WrappedEmbeddings extends AzureOpenAI.Embeddings {
|
|
2125
|
-
constructor(client, phClient) {
|
|
2126
|
-
super(client);
|
|
2127
|
-
this.phClient = phClient;
|
|
2128
|
-
this.baseURL = client.baseURL;
|
|
2129
|
-
}
|
|
2130
|
-
create(body, options) {
|
|
2131
|
-
const {
|
|
2132
|
-
providerParams: openAIParams,
|
|
2133
|
-
posthogParams
|
|
2134
|
-
} = extractPosthogParams(body);
|
|
2135
|
-
const startTime = Date.now();
|
|
2136
|
-
const parentPromise = super.create(openAIParams, options);
|
|
2137
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
2138
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
2139
|
-
await captureAiGeneration(this.phClient, {
|
|
2140
|
-
eventType: AIEvent.Embedding,
|
|
2141
|
-
...posthogParams,
|
|
2142
|
-
model: openAIParams.model,
|
|
2143
|
-
provider: 'azure',
|
|
2144
|
-
input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
|
|
2145
|
-
output: null,
|
|
2146
|
-
// Embeddings don't have output content
|
|
2147
|
-
latency,
|
|
2148
|
-
baseURL: this.baseURL,
|
|
2149
|
-
modelParameters: getModelParams(body),
|
|
2150
|
-
httpStatus: 200,
|
|
2151
|
-
usage: {
|
|
2152
|
-
inputTokens: result.usage?.prompt_tokens ?? 0
|
|
2153
|
-
}
|
|
2154
|
-
});
|
|
2155
|
-
return result;
|
|
2156
|
-
}, async error => {
|
|
2157
|
-
const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
|
|
2158
|
-
await captureAiGeneration(this.phClient, {
|
|
2159
|
-
eventType: AIEvent.Embedding,
|
|
2160
|
-
...posthogParams,
|
|
2161
|
-
model: openAIParams.model,
|
|
2162
|
-
provider: 'azure',
|
|
2163
|
-
input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
|
|
2164
|
-
output: null,
|
|
2165
|
-
latency: 0,
|
|
2166
|
-
baseURL: this.baseURL,
|
|
2167
|
-
modelParameters: getModelParams(body),
|
|
2168
|
-
httpStatus,
|
|
2169
|
-
usage: {
|
|
2170
|
-
inputTokens: 0
|
|
2171
|
-
},
|
|
2172
|
-
error
|
|
2173
|
-
});
|
|
2174
|
-
throw error;
|
|
2175
|
-
});
|
|
2176
|
-
return wrappedPromise;
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
|
|
2180
|
-
// Type guards
|
|
2181
|
-
function isV3Model(model) {
|
|
2182
|
-
return model.specificationVersion === 'v3';
|
|
2183
|
-
}
|
|
2184
|
-
const mapVercelParams = params => {
|
|
2185
|
-
return {
|
|
2186
|
-
temperature: params.temperature,
|
|
2187
|
-
max_output_tokens: params.maxOutputTokens,
|
|
2188
|
-
top_p: params.topP,
|
|
2189
|
-
frequency_penalty: params.frequencyPenalty,
|
|
2190
|
-
presence_penalty: params.presencePenalty,
|
|
2191
|
-
stop: params.stopSequences,
|
|
2192
|
-
stream: params.stream
|
|
2193
|
-
};
|
|
2194
|
-
};
|
|
2195
|
-
const mapVercelPrompt = messages => {
|
|
2196
|
-
// Map and truncate individual content
|
|
2197
|
-
const inputs = messages.map(message => {
|
|
2198
|
-
let content;
|
|
2199
|
-
// Handle system role which has string content
|
|
2200
|
-
if (message.role === 'system') {
|
|
2201
|
-
content = [{
|
|
2202
|
-
type: 'text',
|
|
2203
|
-
text: truncate(toContentString(message.content))
|
|
2204
|
-
}];
|
|
2205
|
-
} else {
|
|
2206
|
-
// Handle other roles which have array content
|
|
2207
|
-
if (Array.isArray(message.content)) {
|
|
2208
|
-
content = message.content.map(c => {
|
|
2209
|
-
if (c.type === 'text') {
|
|
2210
|
-
return {
|
|
2211
|
-
type: 'text',
|
|
2212
|
-
text: truncate(c.text)
|
|
2213
|
-
};
|
|
2214
|
-
} else if (c.type === 'file') {
|
|
2215
|
-
// For file type, check if it's a data URL and redact if needed
|
|
2216
|
-
let fileData;
|
|
2217
|
-
const contentData = c.data;
|
|
2218
|
-
if (contentData instanceof URL) {
|
|
2219
|
-
fileData = contentData.toString();
|
|
2220
|
-
} else if (isString(contentData)) {
|
|
2221
|
-
// Redact base64 data URLs and raw base64 to prevent oversized events
|
|
2222
|
-
fileData = redactBase64DataUrl(contentData);
|
|
2223
|
-
} else {
|
|
2224
|
-
fileData = 'raw files not supported';
|
|
587
|
+
// Handle other roles which have array content
|
|
588
|
+
if (Array.isArray(message.content)) {
|
|
589
|
+
content = message.content.map(c => {
|
|
590
|
+
if (c.type === 'text') {
|
|
591
|
+
return {
|
|
592
|
+
type: 'text',
|
|
593
|
+
text: truncate(c.text)
|
|
594
|
+
};
|
|
595
|
+
} else if (c.type === 'file') {
|
|
596
|
+
// For file type, check if it's a data URL and redact if needed
|
|
597
|
+
let fileData;
|
|
598
|
+
const contentData = c.data;
|
|
599
|
+
if (contentData instanceof URL) {
|
|
600
|
+
fileData = contentData.toString();
|
|
601
|
+
} else if (isString(contentData)) {
|
|
602
|
+
// Redact base64 data URLs and raw base64 to prevent oversized events
|
|
603
|
+
fileData = redactBase64DataUrl(contentData);
|
|
604
|
+
} else {
|
|
605
|
+
fileData = 'raw files not supported';
|
|
2225
606
|
}
|
|
2226
607
|
return {
|
|
2227
608
|
type: 'file',
|
|
@@ -2798,1794 +1179,19 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
2798
1179
|
return wrappedModel;
|
|
2799
1180
|
};
|
|
2800
1181
|
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
} = config;
|
|
2807
|
-
super(anthropicConfig);
|
|
2808
|
-
this.phClient = posthog;
|
|
2809
|
-
this.messages = new WrappedMessages(this, this.phClient);
|
|
2810
|
-
}
|
|
1182
|
+
/// <reference lib="dom" />
|
|
1183
|
+
const DEFAULT_CACHE_TTL_SECONDS = 300; // 5 minutes
|
|
1184
|
+
const DEFAULT_PROMPTS_HOST = 'https://us.posthog.com';
|
|
1185
|
+
function normalizeApiKey(value) {
|
|
1186
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
2811
1187
|
}
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
this.phClient = phClient;
|
|
2816
|
-
this.baseURL = parentClient.baseURL;
|
|
2817
|
-
}
|
|
2818
|
-
create(body, options) {
|
|
2819
|
-
const {
|
|
2820
|
-
providerParams: anthropicParams,
|
|
2821
|
-
posthogParams
|
|
2822
|
-
} = extractPosthogParams(body);
|
|
2823
|
-
const startTime = Date.now();
|
|
2824
|
-
const parentPromise = super.create(anthropicParams, options);
|
|
2825
|
-
if (anthropicParams.stream) {
|
|
2826
|
-
return parentPromise.then(value => {
|
|
2827
|
-
let accumulatedContent = '';
|
|
2828
|
-
const contentBlocks = [];
|
|
2829
|
-
const toolsInProgress = new Map();
|
|
2830
|
-
let currentTextBlock = null;
|
|
2831
|
-
let firstTokenTime;
|
|
2832
|
-
let stopReason;
|
|
2833
|
-
const usage = {
|
|
2834
|
-
inputTokens: 0,
|
|
2835
|
-
outputTokens: 0,
|
|
2836
|
-
cacheCreationInputTokens: 0,
|
|
2837
|
-
cacheReadInputTokens: 0,
|
|
2838
|
-
webSearchCount: 0
|
|
2839
|
-
};
|
|
2840
|
-
let lastRawUsage;
|
|
2841
|
-
if ('tee' in value) {
|
|
2842
|
-
const [stream1, stream2] = value.tee();
|
|
2843
|
-
(async () => {
|
|
2844
|
-
try {
|
|
2845
|
-
for await (const chunk of stream1) {
|
|
2846
|
-
// Handle content block start events
|
|
2847
|
-
if (chunk.type === 'content_block_start') {
|
|
2848
|
-
if (chunk.content_block?.type === 'text') {
|
|
2849
|
-
currentTextBlock = {
|
|
2850
|
-
type: 'text',
|
|
2851
|
-
text: ''
|
|
2852
|
-
};
|
|
2853
|
-
contentBlocks.push(currentTextBlock);
|
|
2854
|
-
} else if (chunk.content_block?.type === 'tool_use') {
|
|
2855
|
-
if (firstTokenTime === undefined) {
|
|
2856
|
-
firstTokenTime = Date.now();
|
|
2857
|
-
}
|
|
2858
|
-
const toolBlock = {
|
|
2859
|
-
type: 'function',
|
|
2860
|
-
id: chunk.content_block.id,
|
|
2861
|
-
function: {
|
|
2862
|
-
name: chunk.content_block.name,
|
|
2863
|
-
arguments: {}
|
|
2864
|
-
}
|
|
2865
|
-
};
|
|
2866
|
-
contentBlocks.push(toolBlock);
|
|
2867
|
-
toolsInProgress.set(chunk.content_block.id, {
|
|
2868
|
-
block: toolBlock,
|
|
2869
|
-
inputString: ''
|
|
2870
|
-
});
|
|
2871
|
-
currentTextBlock = null;
|
|
2872
|
-
}
|
|
2873
|
-
}
|
|
2874
|
-
// Handle text delta events
|
|
2875
|
-
if ('delta' in chunk) {
|
|
2876
|
-
if ('text' in chunk.delta) {
|
|
2877
|
-
const delta = chunk.delta.text;
|
|
2878
|
-
if (firstTokenTime === undefined) {
|
|
2879
|
-
firstTokenTime = Date.now();
|
|
2880
|
-
}
|
|
2881
|
-
accumulatedContent += delta;
|
|
2882
|
-
if (currentTextBlock) {
|
|
2883
|
-
currentTextBlock.text += delta;
|
|
2884
|
-
}
|
|
2885
|
-
}
|
|
2886
|
-
}
|
|
2887
|
-
// Handle tool input delta events
|
|
2888
|
-
if (chunk.type === 'content_block_delta' && chunk.delta?.type === 'input_json_delta') {
|
|
2889
|
-
const block = chunk.index !== undefined ? contentBlocks[chunk.index] : undefined;
|
|
2890
|
-
const toolId = block?.type === 'function' ? block.id : undefined;
|
|
2891
|
-
if (toolId && toolsInProgress.has(toolId)) {
|
|
2892
|
-
const tool = toolsInProgress.get(toolId);
|
|
2893
|
-
if (tool) {
|
|
2894
|
-
tool.inputString += chunk.delta.partial_json || '';
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
}
|
|
2898
|
-
// Handle content block stop events
|
|
2899
|
-
if (chunk.type === 'content_block_stop') {
|
|
2900
|
-
currentTextBlock = null;
|
|
2901
|
-
// Parse accumulated tool input
|
|
2902
|
-
if (chunk.index !== undefined) {
|
|
2903
|
-
const block = contentBlocks[chunk.index];
|
|
2904
|
-
if (block?.type === 'function' && block.id && toolsInProgress.has(block.id)) {
|
|
2905
|
-
const tool = toolsInProgress.get(block.id);
|
|
2906
|
-
if (tool) {
|
|
2907
|
-
try {
|
|
2908
|
-
block.function.arguments = JSON.parse(tool.inputString);
|
|
2909
|
-
} catch (e) {
|
|
2910
|
-
// Keep empty object if parsing fails
|
|
2911
|
-
console.error('Error parsing tool input:', e);
|
|
2912
|
-
}
|
|
2913
|
-
}
|
|
2914
|
-
toolsInProgress.delete(block.id);
|
|
2915
|
-
}
|
|
2916
|
-
}
|
|
2917
|
-
}
|
|
2918
|
-
if (chunk.type == 'message_start') {
|
|
2919
|
-
lastRawUsage = chunk.message.usage;
|
|
2920
|
-
usage.inputTokens = chunk.message.usage.input_tokens ?? 0;
|
|
2921
|
-
usage.cacheCreationInputTokens = chunk.message.usage.cache_creation_input_tokens ?? 0;
|
|
2922
|
-
usage.cacheReadInputTokens = chunk.message.usage.cache_read_input_tokens ?? 0;
|
|
2923
|
-
usage.webSearchCount = chunk.message.usage.server_tool_use?.web_search_requests ?? 0;
|
|
2924
|
-
}
|
|
2925
|
-
if ('usage' in chunk) {
|
|
2926
|
-
lastRawUsage = chunk.usage;
|
|
2927
|
-
usage.outputTokens = chunk.usage.output_tokens ?? 0;
|
|
2928
|
-
// Update web search count if present in delta
|
|
2929
|
-
if (chunk.usage.server_tool_use?.web_search_requests !== undefined) {
|
|
2930
|
-
usage.webSearchCount = chunk.usage.server_tool_use.web_search_requests;
|
|
2931
|
-
}
|
|
2932
|
-
}
|
|
2933
|
-
if (chunk.type === 'message_delta' && 'delta' in chunk) {
|
|
2934
|
-
const delta = chunk.delta;
|
|
2935
|
-
if ('stop_reason' in delta && typeof delta.stop_reason === 'string' && delta.stop_reason) {
|
|
2936
|
-
stopReason = delta.stop_reason;
|
|
2937
|
-
}
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
usage.rawUsage = lastRawUsage;
|
|
2941
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
2942
|
-
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
2943
|
-
const availableTools = extractAvailableToolCalls('anthropic', anthropicParams);
|
|
2944
|
-
// Format output to match non-streaming version
|
|
2945
|
-
const formattedOutput = contentBlocks.length > 0 ? [{
|
|
2946
|
-
role: 'assistant',
|
|
2947
|
-
content: contentBlocks
|
|
2948
|
-
}] : [{
|
|
2949
|
-
role: 'assistant',
|
|
2950
|
-
content: [{
|
|
2951
|
-
type: 'text',
|
|
2952
|
-
text: accumulatedContent
|
|
2953
|
-
}]
|
|
2954
|
-
}];
|
|
2955
|
-
await captureAiGeneration(this.phClient, {
|
|
2956
|
-
...posthogParams,
|
|
2957
|
-
model: anthropicParams.model,
|
|
2958
|
-
provider: 'anthropic',
|
|
2959
|
-
input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams, 'anthropic')),
|
|
2960
|
-
output: formattedOutput,
|
|
2961
|
-
latency,
|
|
2962
|
-
timeToFirstToken,
|
|
2963
|
-
baseURL: this.baseURL,
|
|
2964
|
-
modelParameters: getModelParams(body),
|
|
2965
|
-
httpStatus: 200,
|
|
2966
|
-
usage,
|
|
2967
|
-
stopReason,
|
|
2968
|
-
tools: availableTools
|
|
2969
|
-
});
|
|
2970
|
-
} catch (error) {
|
|
2971
|
-
await captureAiGeneration(this.phClient, {
|
|
2972
|
-
...posthogParams,
|
|
2973
|
-
model: anthropicParams.model,
|
|
2974
|
-
provider: 'anthropic',
|
|
2975
|
-
input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams)),
|
|
2976
|
-
output: [],
|
|
2977
|
-
latency: 0,
|
|
2978
|
-
baseURL: this.baseURL,
|
|
2979
|
-
modelParameters: getModelParams(body),
|
|
2980
|
-
usage: {
|
|
2981
|
-
inputTokens: 0,
|
|
2982
|
-
outputTokens: 0
|
|
2983
|
-
},
|
|
2984
|
-
error: error
|
|
2985
|
-
});
|
|
2986
|
-
throw error;
|
|
2987
|
-
}
|
|
2988
|
-
})();
|
|
2989
|
-
// Return the other stream to the user
|
|
2990
|
-
return stream2;
|
|
2991
|
-
}
|
|
2992
|
-
return value;
|
|
2993
|
-
});
|
|
2994
|
-
} else {
|
|
2995
|
-
const wrappedPromise = parentPromise.then(async result => {
|
|
2996
|
-
if ('content' in result) {
|
|
2997
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
2998
|
-
const availableTools = extractAvailableToolCalls('anthropic', anthropicParams);
|
|
2999
|
-
await captureAiGeneration(this.phClient, {
|
|
3000
|
-
...posthogParams,
|
|
3001
|
-
model: anthropicParams.model,
|
|
3002
|
-
provider: 'anthropic',
|
|
3003
|
-
input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams)),
|
|
3004
|
-
output: formatResponseAnthropic(result),
|
|
3005
|
-
latency,
|
|
3006
|
-
baseURL: this.baseURL,
|
|
3007
|
-
modelParameters: getModelParams(body),
|
|
3008
|
-
httpStatus: 200,
|
|
3009
|
-
usage: {
|
|
3010
|
-
inputTokens: result.usage.input_tokens ?? 0,
|
|
3011
|
-
outputTokens: result.usage.output_tokens ?? 0,
|
|
3012
|
-
cacheCreationInputTokens: result.usage.cache_creation_input_tokens ?? 0,
|
|
3013
|
-
cacheReadInputTokens: result.usage.cache_read_input_tokens ?? 0,
|
|
3014
|
-
webSearchCount: result.usage.server_tool_use?.web_search_requests ?? 0,
|
|
3015
|
-
rawUsage: result.usage
|
|
3016
|
-
},
|
|
3017
|
-
stopReason: result.stop_reason ?? undefined,
|
|
3018
|
-
tools: availableTools
|
|
3019
|
-
});
|
|
3020
|
-
}
|
|
3021
|
-
return result;
|
|
3022
|
-
}, async error => {
|
|
3023
|
-
await captureAiGeneration(this.phClient, {
|
|
3024
|
-
...posthogParams,
|
|
3025
|
-
model: anthropicParams.model,
|
|
3026
|
-
provider: 'anthropic',
|
|
3027
|
-
input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams)),
|
|
3028
|
-
output: [],
|
|
3029
|
-
latency: 0,
|
|
3030
|
-
baseURL: this.baseURL,
|
|
3031
|
-
modelParameters: getModelParams(body),
|
|
3032
|
-
httpStatus: error?.status ? error.status : 500,
|
|
3033
|
-
usage: {
|
|
3034
|
-
inputTokens: 0,
|
|
3035
|
-
outputTokens: 0
|
|
3036
|
-
},
|
|
3037
|
-
error: error
|
|
3038
|
-
});
|
|
3039
|
-
throw error;
|
|
3040
|
-
});
|
|
3041
|
-
return wrappedPromise;
|
|
3042
|
-
}
|
|
3043
|
-
}
|
|
1188
|
+
function normalizeHost(value) {
|
|
1189
|
+
const normalizedHost = typeof value === 'string' ? value.trim() : '';
|
|
1190
|
+
return (normalizedHost || DEFAULT_PROMPTS_HOST).replace(/\/+$/, '');
|
|
3044
1191
|
}
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
const {
|
|
3049
|
-
posthog,
|
|
3050
|
-
...geminiConfig
|
|
3051
|
-
} = config;
|
|
3052
|
-
this.phClient = posthog;
|
|
3053
|
-
this.client = new GoogleGenAI(geminiConfig);
|
|
3054
|
-
this.models = new WrappedModels(this.client, this.phClient);
|
|
3055
|
-
}
|
|
3056
|
-
}
|
|
3057
|
-
class WrappedModels {
|
|
3058
|
-
constructor(client, phClient) {
|
|
3059
|
-
this.client = client;
|
|
3060
|
-
this.phClient = phClient;
|
|
3061
|
-
}
|
|
3062
|
-
async generateContent(params) {
|
|
3063
|
-
const {
|
|
3064
|
-
providerParams: geminiParams,
|
|
3065
|
-
posthogParams
|
|
3066
|
-
} = extractPosthogParams(params);
|
|
3067
|
-
const startTime = Date.now();
|
|
3068
|
-
try {
|
|
3069
|
-
const response = await this.client.models.generateContent(geminiParams);
|
|
3070
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
3071
|
-
const availableTools = extractAvailableToolCalls('gemini', geminiParams);
|
|
3072
|
-
const metadata = response.usageMetadata;
|
|
3073
|
-
const finishReason = response.candidates?.[0]?.finishReason;
|
|
3074
|
-
await captureAiGeneration(this.phClient, {
|
|
3075
|
-
...posthogParams,
|
|
3076
|
-
model: geminiParams.model,
|
|
3077
|
-
provider: 'gemini',
|
|
3078
|
-
input: this.formatInputForPostHog(geminiParams),
|
|
3079
|
-
output: formatResponseGemini(response),
|
|
3080
|
-
latency,
|
|
3081
|
-
baseURL: 'https://generativelanguage.googleapis.com',
|
|
3082
|
-
modelParameters: getModelParams(params),
|
|
3083
|
-
httpStatus: 200,
|
|
3084
|
-
usage: {
|
|
3085
|
-
inputTokens: metadata?.promptTokenCount ?? 0,
|
|
3086
|
-
outputTokens: metadata?.candidatesTokenCount ?? 0,
|
|
3087
|
-
reasoningTokens: metadata?.thoughtsTokenCount ?? 0,
|
|
3088
|
-
cacheReadInputTokens: metadata?.cachedContentTokenCount ?? 0,
|
|
3089
|
-
webSearchCount: calculateGoogleWebSearchCount(response),
|
|
3090
|
-
rawUsage: metadata
|
|
3091
|
-
},
|
|
3092
|
-
stopReason: finishReason ?? undefined,
|
|
3093
|
-
tools: availableTools
|
|
3094
|
-
});
|
|
3095
|
-
return response;
|
|
3096
|
-
} catch (error) {
|
|
3097
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
3098
|
-
await captureAiGeneration(this.phClient, {
|
|
3099
|
-
...posthogParams,
|
|
3100
|
-
model: geminiParams.model,
|
|
3101
|
-
provider: 'gemini',
|
|
3102
|
-
input: this.formatInputForPostHog(geminiParams),
|
|
3103
|
-
output: [],
|
|
3104
|
-
latency,
|
|
3105
|
-
baseURL: 'https://generativelanguage.googleapis.com',
|
|
3106
|
-
modelParameters: getModelParams(params),
|
|
3107
|
-
usage: {
|
|
3108
|
-
inputTokens: 0,
|
|
3109
|
-
outputTokens: 0
|
|
3110
|
-
},
|
|
3111
|
-
error
|
|
3112
|
-
});
|
|
3113
|
-
throw error;
|
|
3114
|
-
}
|
|
3115
|
-
}
|
|
3116
|
-
async *generateContentStream(params) {
|
|
3117
|
-
const {
|
|
3118
|
-
providerParams: geminiParams,
|
|
3119
|
-
posthogParams
|
|
3120
|
-
} = extractPosthogParams(params);
|
|
3121
|
-
const startTime = Date.now();
|
|
3122
|
-
const accumulatedContent = [];
|
|
3123
|
-
let firstTokenTime;
|
|
3124
|
-
let stopReason;
|
|
3125
|
-
let usage = {
|
|
3126
|
-
inputTokens: 0,
|
|
3127
|
-
outputTokens: 0,
|
|
3128
|
-
webSearchCount: 0,
|
|
3129
|
-
rawUsage: undefined
|
|
3130
|
-
};
|
|
3131
|
-
try {
|
|
3132
|
-
const stream = await this.client.models.generateContentStream(geminiParams);
|
|
3133
|
-
for await (const chunk of stream) {
|
|
3134
|
-
// Track first token time when we get text content
|
|
3135
|
-
if (firstTokenTime === undefined && chunk.text) {
|
|
3136
|
-
firstTokenTime = Date.now();
|
|
3137
|
-
}
|
|
3138
|
-
const chunkWebSearchCount = calculateGoogleWebSearchCount(chunk);
|
|
3139
|
-
if (chunkWebSearchCount > 0 && chunkWebSearchCount > (usage.webSearchCount ?? 0)) {
|
|
3140
|
-
usage.webSearchCount = chunkWebSearchCount;
|
|
3141
|
-
}
|
|
3142
|
-
// Handle text content
|
|
3143
|
-
if (chunk.text) {
|
|
3144
|
-
// Find if we already have a text item to append to
|
|
3145
|
-
let lastTextItem;
|
|
3146
|
-
for (let i = accumulatedContent.length - 1; i >= 0; i--) {
|
|
3147
|
-
if (accumulatedContent[i].type === 'text') {
|
|
3148
|
-
lastTextItem = accumulatedContent[i];
|
|
3149
|
-
break;
|
|
3150
|
-
}
|
|
3151
|
-
}
|
|
3152
|
-
if (lastTextItem && lastTextItem.type === 'text') {
|
|
3153
|
-
lastTextItem.text += chunk.text;
|
|
3154
|
-
} else {
|
|
3155
|
-
accumulatedContent.push({
|
|
3156
|
-
type: 'text',
|
|
3157
|
-
text: chunk.text
|
|
3158
|
-
});
|
|
3159
|
-
}
|
|
3160
|
-
}
|
|
3161
|
-
// Track finish reason from candidates
|
|
3162
|
-
if (chunk.candidates?.[0]?.finishReason) {
|
|
3163
|
-
stopReason = chunk.candidates[0].finishReason;
|
|
3164
|
-
}
|
|
3165
|
-
// Handle function calls from candidates
|
|
3166
|
-
if (chunk.candidates && Array.isArray(chunk.candidates)) {
|
|
3167
|
-
for (const candidate of chunk.candidates) {
|
|
3168
|
-
if (candidate.content && candidate.content.parts) {
|
|
3169
|
-
for (const part of candidate.content.parts) {
|
|
3170
|
-
// Type-safe check for functionCall
|
|
3171
|
-
if ('functionCall' in part) {
|
|
3172
|
-
if (firstTokenTime === undefined) {
|
|
3173
|
-
firstTokenTime = Date.now();
|
|
3174
|
-
}
|
|
3175
|
-
const funcCall = part.functionCall;
|
|
3176
|
-
if (funcCall?.name) {
|
|
3177
|
-
accumulatedContent.push({
|
|
3178
|
-
type: 'function',
|
|
3179
|
-
function: {
|
|
3180
|
-
name: funcCall.name,
|
|
3181
|
-
arguments: funcCall.args || {}
|
|
3182
|
-
}
|
|
3183
|
-
});
|
|
3184
|
-
}
|
|
3185
|
-
}
|
|
3186
|
-
}
|
|
3187
|
-
}
|
|
3188
|
-
}
|
|
3189
|
-
}
|
|
3190
|
-
// Update usage metadata - handle both old and new field names
|
|
3191
|
-
if (chunk.usageMetadata) {
|
|
3192
|
-
const metadata = chunk.usageMetadata;
|
|
3193
|
-
usage = {
|
|
3194
|
-
inputTokens: metadata.promptTokenCount ?? 0,
|
|
3195
|
-
outputTokens: metadata.candidatesTokenCount ?? 0,
|
|
3196
|
-
reasoningTokens: metadata.thoughtsTokenCount ?? 0,
|
|
3197
|
-
cacheReadInputTokens: metadata.cachedContentTokenCount ?? 0,
|
|
3198
|
-
webSearchCount: usage.webSearchCount,
|
|
3199
|
-
rawUsage: metadata
|
|
3200
|
-
};
|
|
3201
|
-
}
|
|
3202
|
-
yield chunk;
|
|
3203
|
-
}
|
|
3204
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
3205
|
-
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
3206
|
-
const availableTools = extractAvailableToolCalls('gemini', geminiParams);
|
|
3207
|
-
// Format output similar to formatResponseGemini
|
|
3208
|
-
const output = accumulatedContent.length > 0 ? [{
|
|
3209
|
-
role: 'assistant',
|
|
3210
|
-
content: accumulatedContent
|
|
3211
|
-
}] : [];
|
|
3212
|
-
await captureAiGeneration(this.phClient, {
|
|
3213
|
-
...posthogParams,
|
|
3214
|
-
model: geminiParams.model,
|
|
3215
|
-
provider: 'gemini',
|
|
3216
|
-
input: this.formatInputForPostHog(geminiParams),
|
|
3217
|
-
output,
|
|
3218
|
-
latency,
|
|
3219
|
-
timeToFirstToken,
|
|
3220
|
-
baseURL: 'https://generativelanguage.googleapis.com',
|
|
3221
|
-
modelParameters: getModelParams(params),
|
|
3222
|
-
httpStatus: 200,
|
|
3223
|
-
usage: {
|
|
3224
|
-
...usage,
|
|
3225
|
-
webSearchCount: usage.webSearchCount,
|
|
3226
|
-
rawUsage: usage.rawUsage
|
|
3227
|
-
},
|
|
3228
|
-
stopReason,
|
|
3229
|
-
tools: availableTools
|
|
3230
|
-
});
|
|
3231
|
-
} catch (error) {
|
|
3232
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
3233
|
-
await captureAiGeneration(this.phClient, {
|
|
3234
|
-
...posthogParams,
|
|
3235
|
-
model: geminiParams.model,
|
|
3236
|
-
provider: 'gemini',
|
|
3237
|
-
input: this.formatInputForPostHog(geminiParams),
|
|
3238
|
-
output: [],
|
|
3239
|
-
latency,
|
|
3240
|
-
baseURL: 'https://generativelanguage.googleapis.com',
|
|
3241
|
-
modelParameters: getModelParams(params),
|
|
3242
|
-
usage: {
|
|
3243
|
-
inputTokens: 0,
|
|
3244
|
-
outputTokens: 0
|
|
3245
|
-
},
|
|
3246
|
-
error
|
|
3247
|
-
});
|
|
3248
|
-
throw error;
|
|
3249
|
-
}
|
|
3250
|
-
}
|
|
3251
|
-
async embedContent(params) {
|
|
3252
|
-
const {
|
|
3253
|
-
providerParams: geminiParams,
|
|
3254
|
-
posthogParams
|
|
3255
|
-
} = extractPosthogParams(params);
|
|
3256
|
-
const startTime = Date.now();
|
|
3257
|
-
try {
|
|
3258
|
-
const response = await this.client.models.embedContent(geminiParams);
|
|
3259
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
3260
|
-
const inputTokens = extractEmbeddingTokenCount(response);
|
|
3261
|
-
await captureAiGeneration(this.phClient, {
|
|
3262
|
-
...posthogParams,
|
|
3263
|
-
eventType: AIEvent.Embedding,
|
|
3264
|
-
model: geminiParams.model,
|
|
3265
|
-
provider: 'gemini',
|
|
3266
|
-
input: withPrivacyMode(this.phClient, posthogParams.privacyMode ?? false, geminiParams.contents),
|
|
3267
|
-
output: null,
|
|
3268
|
-
latency,
|
|
3269
|
-
baseURL: 'https://generativelanguage.googleapis.com',
|
|
3270
|
-
modelParameters: getModelParams(params),
|
|
3271
|
-
httpStatus: 200,
|
|
3272
|
-
usage: {
|
|
3273
|
-
inputTokens
|
|
3274
|
-
}
|
|
3275
|
-
});
|
|
3276
|
-
return response;
|
|
3277
|
-
} catch (error) {
|
|
3278
|
-
const latency = (Date.now() - startTime) / 1000;
|
|
3279
|
-
await captureAiGeneration(this.phClient, {
|
|
3280
|
-
...posthogParams,
|
|
3281
|
-
eventType: AIEvent.Embedding,
|
|
3282
|
-
model: geminiParams.model,
|
|
3283
|
-
provider: 'gemini',
|
|
3284
|
-
input: withPrivacyMode(this.phClient, posthogParams.privacyMode ?? false, geminiParams.contents),
|
|
3285
|
-
output: null,
|
|
3286
|
-
latency,
|
|
3287
|
-
baseURL: 'https://generativelanguage.googleapis.com',
|
|
3288
|
-
modelParameters: getModelParams(params),
|
|
3289
|
-
usage: {
|
|
3290
|
-
inputTokens: 0
|
|
3291
|
-
},
|
|
3292
|
-
error
|
|
3293
|
-
});
|
|
3294
|
-
throw error;
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
formatPartsAsContentBlocks(parts) {
|
|
3298
|
-
const blocks = [];
|
|
3299
|
-
for (const part of parts) {
|
|
3300
|
-
// Handle dict/object with text field
|
|
3301
|
-
if (part && typeof part === 'object' && 'text' in part && part.text) {
|
|
3302
|
-
blocks.push({
|
|
3303
|
-
type: 'text',
|
|
3304
|
-
text: String(part.text)
|
|
3305
|
-
});
|
|
3306
|
-
}
|
|
3307
|
-
// Handle string parts
|
|
3308
|
-
else if (typeof part === 'string') {
|
|
3309
|
-
blocks.push({
|
|
3310
|
-
type: 'text',
|
|
3311
|
-
text: part
|
|
3312
|
-
});
|
|
3313
|
-
}
|
|
3314
|
-
// Handle inlineData (images, audio, PDFs)
|
|
3315
|
-
else if (part && typeof part === 'object' && 'inlineData' in part) {
|
|
3316
|
-
const inlineData = part.inlineData;
|
|
3317
|
-
const mimeType = inlineData.mimeType || inlineData.mime_type || 'application/octet-stream';
|
|
3318
|
-
blocks.push(buildInlineDataBlock(mimeType, inlineData.data));
|
|
3319
|
-
}
|
|
3320
|
-
}
|
|
3321
|
-
return blocks;
|
|
3322
|
-
}
|
|
3323
|
-
formatInput(contents) {
|
|
3324
|
-
if (typeof contents === 'string') {
|
|
3325
|
-
return [{
|
|
3326
|
-
role: 'user',
|
|
3327
|
-
content: contents
|
|
3328
|
-
}];
|
|
3329
|
-
}
|
|
3330
|
-
if (Array.isArray(contents)) {
|
|
3331
|
-
return contents.map(item => {
|
|
3332
|
-
if (typeof item === 'string') {
|
|
3333
|
-
return {
|
|
3334
|
-
role: 'user',
|
|
3335
|
-
content: item
|
|
3336
|
-
};
|
|
3337
|
-
}
|
|
3338
|
-
if (item && typeof item === 'object') {
|
|
3339
|
-
const obj = item;
|
|
3340
|
-
if ('text' in obj && obj.text) {
|
|
3341
|
-
return {
|
|
3342
|
-
role: isString(obj.role) ? obj.role : 'user',
|
|
3343
|
-
content: obj.text
|
|
3344
|
-
};
|
|
3345
|
-
}
|
|
3346
|
-
if ('content' in obj && obj.content) {
|
|
3347
|
-
// If content is a list, format it as content blocks
|
|
3348
|
-
if (Array.isArray(obj.content)) {
|
|
3349
|
-
const contentBlocks = this.formatPartsAsContentBlocks(obj.content);
|
|
3350
|
-
return {
|
|
3351
|
-
role: isString(obj.role) ? obj.role : 'user',
|
|
3352
|
-
content: contentBlocks
|
|
3353
|
-
};
|
|
3354
|
-
}
|
|
3355
|
-
return {
|
|
3356
|
-
role: isString(obj.role) ? obj.role : 'user',
|
|
3357
|
-
content: obj.content
|
|
3358
|
-
};
|
|
3359
|
-
}
|
|
3360
|
-
if ('parts' in obj && Array.isArray(obj.parts)) {
|
|
3361
|
-
const contentBlocks = this.formatPartsAsContentBlocks(obj.parts);
|
|
3362
|
-
return {
|
|
3363
|
-
role: isString(obj.role) ? obj.role : 'user',
|
|
3364
|
-
content: contentBlocks
|
|
3365
|
-
};
|
|
3366
|
-
}
|
|
3367
|
-
}
|
|
3368
|
-
return {
|
|
3369
|
-
role: 'user',
|
|
3370
|
-
content: toContentString(item)
|
|
3371
|
-
};
|
|
3372
|
-
});
|
|
3373
|
-
}
|
|
3374
|
-
if (contents && typeof contents === 'object') {
|
|
3375
|
-
const obj = contents;
|
|
3376
|
-
if ('text' in obj && obj.text) {
|
|
3377
|
-
return [{
|
|
3378
|
-
role: 'user',
|
|
3379
|
-
content: obj.text
|
|
3380
|
-
}];
|
|
3381
|
-
}
|
|
3382
|
-
if ('content' in obj && obj.content) {
|
|
3383
|
-
return [{
|
|
3384
|
-
role: 'user',
|
|
3385
|
-
content: obj.content
|
|
3386
|
-
}];
|
|
3387
|
-
}
|
|
3388
|
-
}
|
|
3389
|
-
return [{
|
|
3390
|
-
role: 'user',
|
|
3391
|
-
content: toContentString(contents)
|
|
3392
|
-
}];
|
|
3393
|
-
}
|
|
3394
|
-
extractSystemInstruction(params) {
|
|
3395
|
-
if (!params || typeof params !== 'object' || !params.config) {
|
|
3396
|
-
return null;
|
|
3397
|
-
}
|
|
3398
|
-
const config = params.config;
|
|
3399
|
-
if (!('systemInstruction' in config)) {
|
|
3400
|
-
return null;
|
|
3401
|
-
}
|
|
3402
|
-
const systemInstruction = config.systemInstruction;
|
|
3403
|
-
if (typeof systemInstruction === 'string') {
|
|
3404
|
-
return systemInstruction;
|
|
3405
|
-
}
|
|
3406
|
-
if (systemInstruction && typeof systemInstruction === 'object' && 'text' in systemInstruction) {
|
|
3407
|
-
return systemInstruction.text;
|
|
3408
|
-
}
|
|
3409
|
-
if (systemInstruction && typeof systemInstruction === 'object' && 'parts' in systemInstruction && Array.isArray(systemInstruction.parts)) {
|
|
3410
|
-
for (const part of systemInstruction.parts) {
|
|
3411
|
-
if (part && typeof part === 'object' && 'text' in part && typeof part.text === 'string') {
|
|
3412
|
-
return part.text;
|
|
3413
|
-
}
|
|
3414
|
-
}
|
|
3415
|
-
}
|
|
3416
|
-
if (Array.isArray(systemInstruction)) {
|
|
3417
|
-
for (const part of systemInstruction) {
|
|
3418
|
-
if (typeof part === 'string') {
|
|
3419
|
-
return part;
|
|
3420
|
-
}
|
|
3421
|
-
if (part && typeof part === 'object' && 'text' in part && typeof part.text === 'string') {
|
|
3422
|
-
return part.text;
|
|
3423
|
-
}
|
|
3424
|
-
}
|
|
3425
|
-
}
|
|
3426
|
-
return null;
|
|
3427
|
-
}
|
|
3428
|
-
formatInputForPostHog(params) {
|
|
3429
|
-
const sanitized = sanitizeGemini(params.contents);
|
|
3430
|
-
const messages = this.formatInput(sanitized);
|
|
3431
|
-
const systemInstruction = this.extractSystemInstruction(params);
|
|
3432
|
-
if (systemInstruction) {
|
|
3433
|
-
const hasSystemMessage = messages.some(msg => msg.role === 'system');
|
|
3434
|
-
if (!hasSystemMessage) {
|
|
3435
|
-
return [{
|
|
3436
|
-
role: 'system',
|
|
3437
|
-
content: systemInstruction
|
|
3438
|
-
}, ...messages];
|
|
3439
|
-
}
|
|
3440
|
-
}
|
|
3441
|
-
return messages;
|
|
3442
|
-
}
|
|
3443
|
-
}
|
|
3444
|
-
/**
|
|
3445
|
-
* Extract total token count from a Gemini embed_content response.
|
|
3446
|
-
* Token counts are only available per-embedding via Vertex AI's statistics.tokenCount.
|
|
3447
|
-
* Returns 0 if no token counts are available.
|
|
3448
|
-
*/
|
|
3449
|
-
function extractEmbeddingTokenCount(response) {
|
|
3450
|
-
let total = 0;
|
|
3451
|
-
if (response.embeddings) {
|
|
3452
|
-
for (const embedding of response.embeddings) {
|
|
3453
|
-
if (embedding.statistics?.tokenCount != null) {
|
|
3454
|
-
total += embedding.statistics.tokenCount;
|
|
3455
|
-
}
|
|
3456
|
-
}
|
|
3457
|
-
}
|
|
3458
|
-
return total;
|
|
3459
|
-
}
|
|
3460
|
-
/**
|
|
3461
|
-
* Detect if Google Search grounding was used in the response.
|
|
3462
|
-
* Gemini bills per request that uses grounding, not per individual query.
|
|
3463
|
-
* Returns 1 if grounding was used, 0 otherwise.
|
|
3464
|
-
*/
|
|
3465
|
-
function calculateGoogleWebSearchCount(response) {
|
|
3466
|
-
if (!response || typeof response !== 'object' || !('candidates' in response)) {
|
|
3467
|
-
return 0;
|
|
3468
|
-
}
|
|
3469
|
-
const candidates = response.candidates;
|
|
3470
|
-
if (!Array.isArray(candidates)) {
|
|
3471
|
-
return 0;
|
|
3472
|
-
}
|
|
3473
|
-
const hasGrounding = candidates.some(candidate => {
|
|
3474
|
-
if (!candidate || typeof candidate !== 'object') {
|
|
3475
|
-
return false;
|
|
3476
|
-
}
|
|
3477
|
-
// Check for grounding metadata
|
|
3478
|
-
if ('groundingMetadata' in candidate && candidate.groundingMetadata) {
|
|
3479
|
-
const metadata = candidate.groundingMetadata;
|
|
3480
|
-
if (typeof metadata === 'object') {
|
|
3481
|
-
// Check if web_search_queries exists and is non-empty
|
|
3482
|
-
if ('webSearchQueries' in metadata && Array.isArray(metadata.webSearchQueries) && metadata.webSearchQueries.length > 0) {
|
|
3483
|
-
return true;
|
|
3484
|
-
}
|
|
3485
|
-
// Check if grounding_chunks exists and is non-empty
|
|
3486
|
-
if ('groundingChunks' in metadata && Array.isArray(metadata.groundingChunks) && metadata.groundingChunks.length > 0) {
|
|
3487
|
-
return true;
|
|
3488
|
-
}
|
|
3489
|
-
}
|
|
3490
|
-
}
|
|
3491
|
-
// Check for google search in function calls
|
|
3492
|
-
if ('content' in candidate && candidate.content && typeof candidate.content === 'object') {
|
|
3493
|
-
const content = candidate.content;
|
|
3494
|
-
if ('parts' in content && Array.isArray(content.parts)) {
|
|
3495
|
-
return content.parts.some(part => {
|
|
3496
|
-
if (!part || typeof part !== 'object' || !('functionCall' in part)) {
|
|
3497
|
-
return false;
|
|
3498
|
-
}
|
|
3499
|
-
const functionCall = part.functionCall;
|
|
3500
|
-
if (functionCall && typeof functionCall === 'object' && 'name' in functionCall && typeof functionCall.name === 'string') {
|
|
3501
|
-
return functionCall.name.includes('google_search') || functionCall.name.includes('grounding');
|
|
3502
|
-
}
|
|
3503
|
-
return false;
|
|
3504
|
-
});
|
|
3505
|
-
}
|
|
3506
|
-
}
|
|
3507
|
-
return false;
|
|
3508
|
-
});
|
|
3509
|
-
return hasGrounding ? 1 : 0;
|
|
3510
|
-
}
|
|
3511
|
-
|
|
3512
|
-
function getDefaultExportFromCjs (x) {
|
|
3513
|
-
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
3514
|
-
}
|
|
3515
|
-
|
|
3516
|
-
var decamelize;
|
|
3517
|
-
var hasRequiredDecamelize;
|
|
3518
|
-
|
|
3519
|
-
function requireDecamelize () {
|
|
3520
|
-
if (hasRequiredDecamelize) return decamelize;
|
|
3521
|
-
hasRequiredDecamelize = 1;
|
|
3522
|
-
decamelize = function (str, sep) {
|
|
3523
|
-
if (typeof str !== 'string') {
|
|
3524
|
-
throw new TypeError('Expected a string');
|
|
3525
|
-
}
|
|
3526
|
-
|
|
3527
|
-
sep = typeof sep === 'undefined' ? '_' : sep;
|
|
3528
|
-
|
|
3529
|
-
return str
|
|
3530
|
-
.replace(/([a-z\d])([A-Z])/g, '$1' + sep + '$2')
|
|
3531
|
-
.replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + sep + '$2')
|
|
3532
|
-
.toLowerCase();
|
|
3533
|
-
};
|
|
3534
|
-
return decamelize;
|
|
3535
|
-
}
|
|
3536
|
-
|
|
3537
|
-
var decamelizeExports = requireDecamelize();
|
|
3538
|
-
var snakeCase = /*@__PURE__*/getDefaultExportFromCjs(decamelizeExports);
|
|
3539
|
-
|
|
3540
|
-
var camelcase = {exports: {}};
|
|
3541
|
-
|
|
3542
|
-
var hasRequiredCamelcase;
|
|
3543
|
-
|
|
3544
|
-
function requireCamelcase () {
|
|
3545
|
-
if (hasRequiredCamelcase) return camelcase.exports;
|
|
3546
|
-
hasRequiredCamelcase = 1;
|
|
3547
|
-
|
|
3548
|
-
const UPPERCASE = /[\p{Lu}]/u;
|
|
3549
|
-
const LOWERCASE = /[\p{Ll}]/u;
|
|
3550
|
-
const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
|
|
3551
|
-
const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
|
|
3552
|
-
const SEPARATORS = /[_.\- ]+/;
|
|
3553
|
-
|
|
3554
|
-
const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
|
|
3555
|
-
const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
|
|
3556
|
-
const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu');
|
|
3557
|
-
|
|
3558
|
-
const preserveCamelCase = (string, toLowerCase, toUpperCase) => {
|
|
3559
|
-
let isLastCharLower = false;
|
|
3560
|
-
let isLastCharUpper = false;
|
|
3561
|
-
let isLastLastCharUpper = false;
|
|
3562
|
-
|
|
3563
|
-
for (let i = 0; i < string.length; i++) {
|
|
3564
|
-
const character = string[i];
|
|
3565
|
-
|
|
3566
|
-
if (isLastCharLower && UPPERCASE.test(character)) {
|
|
3567
|
-
string = string.slice(0, i) + '-' + string.slice(i);
|
|
3568
|
-
isLastCharLower = false;
|
|
3569
|
-
isLastLastCharUpper = isLastCharUpper;
|
|
3570
|
-
isLastCharUpper = true;
|
|
3571
|
-
i++;
|
|
3572
|
-
} else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character)) {
|
|
3573
|
-
string = string.slice(0, i - 1) + '-' + string.slice(i - 1);
|
|
3574
|
-
isLastLastCharUpper = isLastCharUpper;
|
|
3575
|
-
isLastCharUpper = false;
|
|
3576
|
-
isLastCharLower = true;
|
|
3577
|
-
} else {
|
|
3578
|
-
isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
|
|
3579
|
-
isLastLastCharUpper = isLastCharUpper;
|
|
3580
|
-
isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
|
|
3581
|
-
}
|
|
3582
|
-
}
|
|
3583
|
-
|
|
3584
|
-
return string;
|
|
3585
|
-
};
|
|
3586
|
-
|
|
3587
|
-
const preserveConsecutiveUppercase = (input, toLowerCase) => {
|
|
3588
|
-
LEADING_CAPITAL.lastIndex = 0;
|
|
3589
|
-
|
|
3590
|
-
return input.replace(LEADING_CAPITAL, m1 => toLowerCase(m1));
|
|
3591
|
-
};
|
|
3592
|
-
|
|
3593
|
-
const postProcess = (input, toUpperCase) => {
|
|
3594
|
-
SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
|
|
3595
|
-
NUMBERS_AND_IDENTIFIER.lastIndex = 0;
|
|
3596
|
-
|
|
3597
|
-
return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier))
|
|
3598
|
-
.replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m));
|
|
3599
|
-
};
|
|
3600
|
-
|
|
3601
|
-
const camelCase = (input, options) => {
|
|
3602
|
-
if (!(typeof input === 'string' || Array.isArray(input))) {
|
|
3603
|
-
throw new TypeError('Expected the input to be `string | string[]`');
|
|
3604
|
-
}
|
|
3605
|
-
|
|
3606
|
-
options = {
|
|
3607
|
-
pascalCase: false,
|
|
3608
|
-
preserveConsecutiveUppercase: false,
|
|
3609
|
-
...options
|
|
3610
|
-
};
|
|
3611
|
-
|
|
3612
|
-
if (Array.isArray(input)) {
|
|
3613
|
-
input = input.map(x => x.trim())
|
|
3614
|
-
.filter(x => x.length)
|
|
3615
|
-
.join('-');
|
|
3616
|
-
} else {
|
|
3617
|
-
input = input.trim();
|
|
3618
|
-
}
|
|
3619
|
-
|
|
3620
|
-
if (input.length === 0) {
|
|
3621
|
-
return '';
|
|
3622
|
-
}
|
|
3623
|
-
|
|
3624
|
-
const toLowerCase = options.locale === false ?
|
|
3625
|
-
string => string.toLowerCase() :
|
|
3626
|
-
string => string.toLocaleLowerCase(options.locale);
|
|
3627
|
-
const toUpperCase = options.locale === false ?
|
|
3628
|
-
string => string.toUpperCase() :
|
|
3629
|
-
string => string.toLocaleUpperCase(options.locale);
|
|
3630
|
-
|
|
3631
|
-
if (input.length === 1) {
|
|
3632
|
-
return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
|
|
3633
|
-
}
|
|
3634
|
-
|
|
3635
|
-
const hasUpperCase = input !== toLowerCase(input);
|
|
3636
|
-
|
|
3637
|
-
if (hasUpperCase) {
|
|
3638
|
-
input = preserveCamelCase(input, toLowerCase, toUpperCase);
|
|
3639
|
-
}
|
|
3640
|
-
|
|
3641
|
-
input = input.replace(LEADING_SEPARATORS, '');
|
|
3642
|
-
|
|
3643
|
-
if (options.preserveConsecutiveUppercase) {
|
|
3644
|
-
input = preserveConsecutiveUppercase(input, toLowerCase);
|
|
3645
|
-
} else {
|
|
3646
|
-
input = toLowerCase(input);
|
|
3647
|
-
}
|
|
3648
|
-
|
|
3649
|
-
if (options.pascalCase) {
|
|
3650
|
-
input = toUpperCase(input.charAt(0)) + input.slice(1);
|
|
3651
|
-
}
|
|
3652
|
-
|
|
3653
|
-
return postProcess(input, toUpperCase);
|
|
3654
|
-
};
|
|
3655
|
-
|
|
3656
|
-
camelcase.exports = camelCase;
|
|
3657
|
-
// TODO: Remove this for the next major release
|
|
3658
|
-
camelcase.exports.default = camelCase;
|
|
3659
|
-
return camelcase.exports;
|
|
3660
|
-
}
|
|
3661
|
-
|
|
3662
|
-
requireCamelcase();
|
|
3663
|
-
|
|
3664
|
-
//#region src/load/map_keys.ts
|
|
3665
|
-
function keyToJson(key, map) {
|
|
3666
|
-
return map?.[key] || snakeCase(key);
|
|
3667
|
-
}
|
|
3668
|
-
function mapKeys(fields, mapper, map) {
|
|
3669
|
-
const mapped = {};
|
|
3670
|
-
for (const key in fields) if (Object.hasOwn(fields, key)) mapped[mapper(key, map)] = fields[key];
|
|
3671
|
-
return mapped;
|
|
3672
|
-
}
|
|
3673
|
-
|
|
3674
|
-
//#region src/load/validation.ts
|
|
3675
|
-
/**
|
|
3676
|
-
* Sentinel key used to mark escaped user objects during serialization.
|
|
3677
|
-
*
|
|
3678
|
-
* When a plain object contains 'lc' key (which could be confused with LC objects),
|
|
3679
|
-
* we wrap it as `{"__lc_escaped__": {...original...}}`.
|
|
3680
|
-
*/
|
|
3681
|
-
const LC_ESCAPED_KEY = "__lc_escaped__";
|
|
3682
|
-
/**
|
|
3683
|
-
* Check if an object needs escaping to prevent confusion with LC objects.
|
|
3684
|
-
*
|
|
3685
|
-
* An object needs escaping if:
|
|
3686
|
-
* 1. It has an `'lc'` key (could be confused with LC serialization format)
|
|
3687
|
-
* 2. It has only the escape key (would be mistaken for an escaped object)
|
|
3688
|
-
*/
|
|
3689
|
-
function needsEscaping(obj) {
|
|
3690
|
-
return "lc" in obj || Object.keys(obj).length === 1 && LC_ESCAPED_KEY in obj;
|
|
3691
|
-
}
|
|
3692
|
-
/**
|
|
3693
|
-
* Wrap an object in the escape marker.
|
|
3694
|
-
*
|
|
3695
|
-
* @example
|
|
3696
|
-
* ```typescript
|
|
3697
|
-
* {"key": "value"} // becomes {"__lc_escaped__": {"key": "value"}}
|
|
3698
|
-
* ```
|
|
3699
|
-
*/
|
|
3700
|
-
function escapeObject(obj) {
|
|
3701
|
-
return { [LC_ESCAPED_KEY]: obj };
|
|
3702
|
-
}
|
|
3703
|
-
/**
|
|
3704
|
-
* Check if an object looks like a Serializable instance (duck typing).
|
|
3705
|
-
*/
|
|
3706
|
-
function isSerializableLike(obj) {
|
|
3707
|
-
return obj !== null && typeof obj === "object" && "lc_serializable" in obj && typeof obj.toJSON === "function";
|
|
3708
|
-
}
|
|
3709
|
-
/**
|
|
3710
|
-
* Create a "not_implemented" serialization result for objects that cannot be serialized.
|
|
3711
|
-
*/
|
|
3712
|
-
function createNotImplemented(obj) {
|
|
3713
|
-
let id;
|
|
3714
|
-
if (obj !== null && typeof obj === "object") if ("lc_id" in obj && Array.isArray(obj.lc_id)) id = obj.lc_id;
|
|
3715
|
-
else id = [obj.constructor?.name ?? "Object"];
|
|
3716
|
-
else id = [typeof obj];
|
|
3717
|
-
return {
|
|
3718
|
-
lc: 1,
|
|
3719
|
-
type: "not_implemented",
|
|
3720
|
-
id
|
|
3721
|
-
};
|
|
3722
|
-
}
|
|
3723
|
-
/**
|
|
3724
|
-
* Escape a value if it needs escaping (contains `lc` key).
|
|
3725
|
-
*
|
|
3726
|
-
* This is a simpler version of `serializeValue` that doesn't handle Serializable
|
|
3727
|
-
* objects - it's meant to be called on kwargs values that have already been
|
|
3728
|
-
* processed by `toJSON()`.
|
|
3729
|
-
*
|
|
3730
|
-
* @param value - The value to potentially escape.
|
|
3731
|
-
* @param pathSet - WeakSet to track ancestor objects in the current path to detect circular references.
|
|
3732
|
-
* Objects are removed after processing to allow shared references (same object in
|
|
3733
|
-
* multiple places) while still detecting true circular references (ancestor in descendant).
|
|
3734
|
-
* @returns The value with any `lc`-containing objects wrapped in escape markers.
|
|
3735
|
-
*/
|
|
3736
|
-
function escapeIfNeeded(value, pathSet = /* @__PURE__ */ new WeakSet()) {
|
|
3737
|
-
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
3738
|
-
if (pathSet.has(value)) return createNotImplemented(value);
|
|
3739
|
-
if (isSerializableLike(value)) return value;
|
|
3740
|
-
pathSet.add(value);
|
|
3741
|
-
const record = value;
|
|
3742
|
-
if (needsEscaping(record)) {
|
|
3743
|
-
pathSet.delete(value);
|
|
3744
|
-
return escapeObject(record);
|
|
3745
|
-
}
|
|
3746
|
-
const result = {};
|
|
3747
|
-
for (const [key, val] of Object.entries(record)) result[key] = escapeIfNeeded(val, pathSet);
|
|
3748
|
-
pathSet.delete(value);
|
|
3749
|
-
return result;
|
|
3750
|
-
}
|
|
3751
|
-
if (Array.isArray(value)) return value.map((item) => escapeIfNeeded(item, pathSet));
|
|
3752
|
-
return value;
|
|
3753
|
-
}
|
|
3754
|
-
|
|
3755
|
-
function shallowCopy(obj) {
|
|
3756
|
-
return Array.isArray(obj) ? [...obj] : { ...obj };
|
|
3757
|
-
}
|
|
3758
|
-
function replaceSecrets(root, secretsMap) {
|
|
3759
|
-
const result = shallowCopy(root);
|
|
3760
|
-
for (const [path, secretId] of Object.entries(secretsMap)) {
|
|
3761
|
-
const [last, ...partsReverse] = path.split(".").reverse();
|
|
3762
|
-
let current = result;
|
|
3763
|
-
for (const part of partsReverse.reverse()) {
|
|
3764
|
-
if (current[part] === void 0) break;
|
|
3765
|
-
current[part] = shallowCopy(current[part]);
|
|
3766
|
-
current = current[part];
|
|
3767
|
-
}
|
|
3768
|
-
if (current[last] !== void 0) current[last] = {
|
|
3769
|
-
lc: 1,
|
|
3770
|
-
type: "secret",
|
|
3771
|
-
id: [secretId]
|
|
3772
|
-
};
|
|
3773
|
-
}
|
|
3774
|
-
return result;
|
|
3775
|
-
}
|
|
3776
|
-
/**
|
|
3777
|
-
* Get a unique name for the module, rather than parent class implementations.
|
|
3778
|
-
* Should not be subclassed, subclass lc_name above instead.
|
|
3779
|
-
*/
|
|
3780
|
-
function get_lc_unique_name(serializableClass) {
|
|
3781
|
-
const parentClass = Object.getPrototypeOf(serializableClass);
|
|
3782
|
-
if (typeof serializableClass.lc_name === "function" && (typeof parentClass.lc_name !== "function" || serializableClass.lc_name() !== parentClass.lc_name())) return serializableClass.lc_name();
|
|
3783
|
-
else return serializableClass.name;
|
|
3784
|
-
}
|
|
3785
|
-
var Serializable = class Serializable {
|
|
3786
|
-
lc_serializable = false;
|
|
3787
|
-
lc_kwargs;
|
|
3788
|
-
/**
|
|
3789
|
-
* The name of the serializable. Override to provide an alias or
|
|
3790
|
-
* to preserve the serialized module name in minified environments.
|
|
3791
|
-
*
|
|
3792
|
-
* Implemented as a static method to support loading logic.
|
|
3793
|
-
*/
|
|
3794
|
-
static lc_name() {
|
|
3795
|
-
return this.name;
|
|
3796
|
-
}
|
|
3797
|
-
/**
|
|
3798
|
-
* The final serialized identifier for the module.
|
|
3799
|
-
*/
|
|
3800
|
-
get lc_id() {
|
|
3801
|
-
return [...this.lc_namespace, get_lc_unique_name(this.constructor)];
|
|
3802
|
-
}
|
|
3803
|
-
/**
|
|
3804
|
-
* A map of secrets, which will be omitted from serialization.
|
|
3805
|
-
* Keys are paths to the secret in constructor args, e.g. "foo.bar.baz".
|
|
3806
|
-
* Values are the secret ids, which will be used when deserializing.
|
|
3807
|
-
*/
|
|
3808
|
-
get lc_secrets() {}
|
|
3809
|
-
/**
|
|
3810
|
-
* A map of additional attributes to merge with constructor args.
|
|
3811
|
-
* Keys are the attribute names, e.g. "foo".
|
|
3812
|
-
* Values are the attribute values, which will be serialized.
|
|
3813
|
-
* These attributes need to be accepted by the constructor as arguments.
|
|
3814
|
-
*/
|
|
3815
|
-
get lc_attributes() {}
|
|
3816
|
-
/**
|
|
3817
|
-
* A map of aliases for constructor args.
|
|
3818
|
-
* Keys are the attribute names, e.g. "foo".
|
|
3819
|
-
* Values are the alias that will replace the key in serialization.
|
|
3820
|
-
* This is used to eg. make argument names match Python.
|
|
3821
|
-
*/
|
|
3822
|
-
get lc_aliases() {}
|
|
3823
|
-
/**
|
|
3824
|
-
* A manual list of keys that should be serialized.
|
|
3825
|
-
* If not overridden, all fields passed into the constructor will be serialized.
|
|
3826
|
-
*/
|
|
3827
|
-
get lc_serializable_keys() {}
|
|
3828
|
-
constructor(kwargs, ..._args) {
|
|
3829
|
-
if (this.lc_serializable_keys !== void 0) this.lc_kwargs = Object.fromEntries(Object.entries(kwargs || {}).filter(([key]) => this.lc_serializable_keys?.includes(key)));
|
|
3830
|
-
else this.lc_kwargs = kwargs ?? {};
|
|
3831
|
-
}
|
|
3832
|
-
toJSON() {
|
|
3833
|
-
if (!this.lc_serializable) return this.toJSONNotImplemented();
|
|
3834
|
-
if (this.lc_kwargs instanceof Serializable || typeof this.lc_kwargs !== "object" || Array.isArray(this.lc_kwargs)) return this.toJSONNotImplemented();
|
|
3835
|
-
const aliases = {};
|
|
3836
|
-
const secrets = {};
|
|
3837
|
-
const kwargs = Object.keys(this.lc_kwargs).reduce((acc, key) => {
|
|
3838
|
-
acc[key] = key in this ? this[key] : this.lc_kwargs[key];
|
|
3839
|
-
return acc;
|
|
3840
|
-
}, {});
|
|
3841
|
-
for (let current = Object.getPrototypeOf(this); current; current = Object.getPrototypeOf(current)) {
|
|
3842
|
-
Object.assign(aliases, Reflect.get(current, "lc_aliases", this));
|
|
3843
|
-
Object.assign(secrets, Reflect.get(current, "lc_secrets", this));
|
|
3844
|
-
Object.assign(kwargs, Reflect.get(current, "lc_attributes", this));
|
|
3845
|
-
}
|
|
3846
|
-
Object.keys(secrets).forEach((keyPath) => {
|
|
3847
|
-
let read = this;
|
|
3848
|
-
let write = kwargs;
|
|
3849
|
-
const [last, ...partsReverse] = keyPath.split(".").reverse();
|
|
3850
|
-
for (const key of partsReverse.reverse()) {
|
|
3851
|
-
if (!(key in read) || read[key] === void 0) return;
|
|
3852
|
-
if (!(key in write) || write[key] === void 0) {
|
|
3853
|
-
if (typeof read[key] === "object" && read[key] != null) write[key] = {};
|
|
3854
|
-
else if (Array.isArray(read[key])) write[key] = [];
|
|
3855
|
-
}
|
|
3856
|
-
read = read[key];
|
|
3857
|
-
write = write[key];
|
|
3858
|
-
}
|
|
3859
|
-
if (last in read && read[last] !== void 0) write[last] = write[last] || read[last];
|
|
3860
|
-
});
|
|
3861
|
-
const escapedKwargs = {};
|
|
3862
|
-
const pathSet = /* @__PURE__ */ new WeakSet();
|
|
3863
|
-
pathSet.add(this);
|
|
3864
|
-
for (const [key, value] of Object.entries(kwargs)) escapedKwargs[key] = escapeIfNeeded(value, pathSet);
|
|
3865
|
-
const processedKwargs = mapKeys(Object.keys(secrets).length ? replaceSecrets(escapedKwargs, secrets) : escapedKwargs, keyToJson, aliases);
|
|
3866
|
-
return {
|
|
3867
|
-
lc: 1,
|
|
3868
|
-
type: "constructor",
|
|
3869
|
-
id: this.lc_id,
|
|
3870
|
-
kwargs: processedKwargs
|
|
3871
|
-
};
|
|
3872
|
-
}
|
|
3873
|
-
toJSONNotImplemented() {
|
|
3874
|
-
return {
|
|
3875
|
-
lc: 1,
|
|
3876
|
-
type: "not_implemented",
|
|
3877
|
-
id: this.lc_id
|
|
3878
|
-
};
|
|
3879
|
-
}
|
|
3880
|
-
};
|
|
3881
|
-
|
|
3882
|
-
const isDeno = () => typeof Deno !== "undefined";
|
|
3883
|
-
function getEnvironmentVariable(name) {
|
|
3884
|
-
try {
|
|
3885
|
-
if (typeof process !== "undefined") return process.env?.[name];
|
|
3886
|
-
else if (isDeno()) return Deno?.env.get(name);
|
|
3887
|
-
else return;
|
|
3888
|
-
} catch {
|
|
3889
|
-
return;
|
|
3890
|
-
}
|
|
3891
|
-
}
|
|
3892
|
-
|
|
3893
|
-
/**
|
|
3894
|
-
* Abstract class that provides a set of optional methods that can be
|
|
3895
|
-
* overridden in derived classes to handle various events during the
|
|
3896
|
-
* execution of a LangChain application.
|
|
3897
|
-
*/
|
|
3898
|
-
var BaseCallbackHandlerMethodsClass = class {};
|
|
3899
|
-
/**
|
|
3900
|
-
* Abstract base class for creating callback handlers in the LangChain
|
|
3901
|
-
* framework. It provides a set of optional methods that can be overridden
|
|
3902
|
-
* in derived classes to handle various events during the execution of a
|
|
3903
|
-
* LangChain application.
|
|
3904
|
-
*/
|
|
3905
|
-
var BaseCallbackHandler = class extends BaseCallbackHandlerMethodsClass {
|
|
3906
|
-
lc_serializable = false;
|
|
3907
|
-
get lc_namespace() {
|
|
3908
|
-
return [
|
|
3909
|
-
"langchain_core",
|
|
3910
|
-
"callbacks",
|
|
3911
|
-
this.name
|
|
3912
|
-
];
|
|
3913
|
-
}
|
|
3914
|
-
get lc_secrets() {}
|
|
3915
|
-
get lc_attributes() {}
|
|
3916
|
-
get lc_aliases() {}
|
|
3917
|
-
get lc_serializable_keys() {}
|
|
3918
|
-
/**
|
|
3919
|
-
* The name of the serializable. Override to provide an alias or
|
|
3920
|
-
* to preserve the serialized module name in minified environments.
|
|
3921
|
-
*
|
|
3922
|
-
* Implemented as a static method to support loading logic.
|
|
3923
|
-
*/
|
|
3924
|
-
static lc_name() {
|
|
3925
|
-
return this.name;
|
|
3926
|
-
}
|
|
3927
|
-
/**
|
|
3928
|
-
* The final serialized identifier for the module.
|
|
3929
|
-
*/
|
|
3930
|
-
get lc_id() {
|
|
3931
|
-
return [...this.lc_namespace, get_lc_unique_name(this.constructor)];
|
|
3932
|
-
}
|
|
3933
|
-
lc_kwargs;
|
|
3934
|
-
ignoreLLM = false;
|
|
3935
|
-
ignoreChain = false;
|
|
3936
|
-
ignoreAgent = false;
|
|
3937
|
-
ignoreRetriever = false;
|
|
3938
|
-
ignoreCustomEvent = false;
|
|
3939
|
-
raiseError = false;
|
|
3940
|
-
awaitHandlers = getEnvironmentVariable("LANGCHAIN_CALLBACKS_BACKGROUND") === "false";
|
|
3941
|
-
constructor(input) {
|
|
3942
|
-
super();
|
|
3943
|
-
this.lc_kwargs = input || {};
|
|
3944
|
-
if (input) {
|
|
3945
|
-
this.ignoreLLM = input.ignoreLLM ?? this.ignoreLLM;
|
|
3946
|
-
this.ignoreChain = input.ignoreChain ?? this.ignoreChain;
|
|
3947
|
-
this.ignoreAgent = input.ignoreAgent ?? this.ignoreAgent;
|
|
3948
|
-
this.ignoreRetriever = input.ignoreRetriever ?? this.ignoreRetriever;
|
|
3949
|
-
this.ignoreCustomEvent = input.ignoreCustomEvent ?? this.ignoreCustomEvent;
|
|
3950
|
-
this.raiseError = input.raiseError ?? this.raiseError;
|
|
3951
|
-
this.awaitHandlers = this.raiseError || (input._awaitHandler ?? this.awaitHandlers);
|
|
3952
|
-
}
|
|
3953
|
-
}
|
|
3954
|
-
copy() {
|
|
3955
|
-
return new this.constructor(this);
|
|
3956
|
-
}
|
|
3957
|
-
toJSON() {
|
|
3958
|
-
return Serializable.prototype.toJSON.call(this);
|
|
3959
|
-
}
|
|
3960
|
-
toJSONNotImplemented() {
|
|
3961
|
-
return Serializable.prototype.toJSONNotImplemented.call(this);
|
|
3962
|
-
}
|
|
3963
|
-
static fromMethods(methods) {
|
|
3964
|
-
class Handler extends BaseCallbackHandler {
|
|
3965
|
-
name = uuid.v7();
|
|
3966
|
-
constructor() {
|
|
3967
|
-
super();
|
|
3968
|
-
Object.assign(this, methods);
|
|
3969
|
-
}
|
|
3970
|
-
}
|
|
3971
|
-
return new Handler();
|
|
3972
|
-
}
|
|
3973
|
-
};
|
|
3974
|
-
|
|
3975
|
-
class LangChainCallbackHandler extends BaseCallbackHandler {
|
|
3976
|
-
constructor(options) {
|
|
3977
|
-
if (!options.client) {
|
|
3978
|
-
throw new Error('PostHog client is required');
|
|
3979
|
-
}
|
|
3980
|
-
super();
|
|
3981
|
-
this.name = 'PosthogCallbackHandler';
|
|
3982
|
-
this.runs = {};
|
|
3983
|
-
this.parentTree = {};
|
|
3984
|
-
this.client = options.client;
|
|
3985
|
-
this.distinctId = options.distinctId;
|
|
3986
|
-
this.traceId = options.traceId;
|
|
3987
|
-
this.properties = options.properties || {};
|
|
3988
|
-
this.privacyMode = options.privacyMode || false;
|
|
3989
|
-
this.groups = options.groups || {};
|
|
3990
|
-
this.debug = options.debug || false;
|
|
3991
|
-
}
|
|
3992
|
-
// ===== CALLBACK METHODS =====
|
|
3993
|
-
handleChainStart(chain, inputs, runId, parentRunId, tags, metadata, _runType, runName) {
|
|
3994
|
-
this._logDebugEvent('on_chain_start', runId, parentRunId, {
|
|
3995
|
-
inputs,
|
|
3996
|
-
tags
|
|
3997
|
-
});
|
|
3998
|
-
this._setParentOfRun(runId, parentRunId);
|
|
3999
|
-
this._setTraceOrSpanMetadata(chain, inputs, runId, parentRunId, metadata, tags, runName);
|
|
4000
|
-
}
|
|
4001
|
-
handleChainEnd(outputs, runId, parentRunId, tags, _kwargs) {
|
|
4002
|
-
this._logDebugEvent('on_chain_end', runId, parentRunId, {
|
|
4003
|
-
outputs,
|
|
4004
|
-
tags
|
|
4005
|
-
});
|
|
4006
|
-
this._popRunAndCaptureTraceOrSpan(runId, parentRunId, outputs);
|
|
4007
|
-
}
|
|
4008
|
-
handleChainError(error, runId, parentRunId, tags, _kwargs) {
|
|
4009
|
-
this._logDebugEvent('on_chain_error', runId, parentRunId, {
|
|
4010
|
-
error,
|
|
4011
|
-
tags
|
|
4012
|
-
});
|
|
4013
|
-
this._popRunAndCaptureTraceOrSpan(runId, parentRunId, error);
|
|
4014
|
-
}
|
|
4015
|
-
handleChatModelStart(serialized, messages, runId, parentRunId, extraParams, tags, metadata, runName) {
|
|
4016
|
-
this._logDebugEvent('on_chat_model_start', runId, parentRunId, {
|
|
4017
|
-
messages,
|
|
4018
|
-
tags
|
|
4019
|
-
});
|
|
4020
|
-
this._setParentOfRun(runId, parentRunId);
|
|
4021
|
-
// Flatten the two-dimensional messages and convert each message to a plain object
|
|
4022
|
-
const input = messages.flat().map(m => this._convertMessageToDict(m));
|
|
4023
|
-
this._setLLMMetadata(serialized, runId, input, metadata, extraParams, runName);
|
|
4024
|
-
}
|
|
4025
|
-
handleLLMStart(serialized, prompts, runId, parentRunId, extraParams, tags, metadata, runName) {
|
|
4026
|
-
this._logDebugEvent('on_llm_start', runId, parentRunId, {
|
|
4027
|
-
prompts,
|
|
4028
|
-
tags
|
|
4029
|
-
});
|
|
4030
|
-
this._setParentOfRun(runId, parentRunId);
|
|
4031
|
-
this._setLLMMetadata(serialized, runId, prompts, metadata, extraParams, runName);
|
|
4032
|
-
}
|
|
4033
|
-
handleLLMEnd(output, runId, parentRunId, tags, _extraParams) {
|
|
4034
|
-
this._logDebugEvent('on_llm_end', runId, parentRunId, {
|
|
4035
|
-
output,
|
|
4036
|
-
tags
|
|
4037
|
-
});
|
|
4038
|
-
this._popRunAndCaptureGeneration(runId, parentRunId, output);
|
|
4039
|
-
}
|
|
4040
|
-
handleLLMError(err, runId, parentRunId, tags, _extraParams) {
|
|
4041
|
-
this._logDebugEvent('on_llm_error', runId, parentRunId, {
|
|
4042
|
-
err,
|
|
4043
|
-
tags
|
|
4044
|
-
});
|
|
4045
|
-
this._popRunAndCaptureGeneration(runId, parentRunId, err);
|
|
4046
|
-
}
|
|
4047
|
-
handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName) {
|
|
4048
|
-
this._logDebugEvent('on_tool_start', runId, parentRunId, {
|
|
4049
|
-
input,
|
|
4050
|
-
tags
|
|
4051
|
-
});
|
|
4052
|
-
this._setParentOfRun(runId, parentRunId);
|
|
4053
|
-
this._setTraceOrSpanMetadata(tool, input, runId, parentRunId, metadata, tags, runName);
|
|
4054
|
-
}
|
|
4055
|
-
handleToolEnd(output, runId, parentRunId, tags) {
|
|
4056
|
-
this._logDebugEvent('on_tool_end', runId, parentRunId, {
|
|
4057
|
-
output,
|
|
4058
|
-
tags
|
|
4059
|
-
});
|
|
4060
|
-
this._popRunAndCaptureTraceOrSpan(runId, parentRunId, output);
|
|
4061
|
-
}
|
|
4062
|
-
handleToolError(err, runId, parentRunId, tags) {
|
|
4063
|
-
this._logDebugEvent('on_tool_error', runId, parentRunId, {
|
|
4064
|
-
err,
|
|
4065
|
-
tags
|
|
4066
|
-
});
|
|
4067
|
-
this._popRunAndCaptureTraceOrSpan(runId, parentRunId, err);
|
|
4068
|
-
}
|
|
4069
|
-
handleRetrieverStart(retriever, query, runId, parentRunId, tags, metadata, name) {
|
|
4070
|
-
this._logDebugEvent('on_retriever_start', runId, parentRunId, {
|
|
4071
|
-
query,
|
|
4072
|
-
tags
|
|
4073
|
-
});
|
|
4074
|
-
this._setParentOfRun(runId, parentRunId);
|
|
4075
|
-
this._setTraceOrSpanMetadata(retriever, query, runId, parentRunId, metadata, tags, name);
|
|
4076
|
-
}
|
|
4077
|
-
handleRetrieverEnd(documents, runId, parentRunId, tags) {
|
|
4078
|
-
this._logDebugEvent('on_retriever_end', runId, parentRunId, {
|
|
4079
|
-
documents,
|
|
4080
|
-
tags
|
|
4081
|
-
});
|
|
4082
|
-
this._popRunAndCaptureTraceOrSpan(runId, parentRunId, documents);
|
|
4083
|
-
}
|
|
4084
|
-
handleRetrieverError(err, runId, parentRunId, tags) {
|
|
4085
|
-
this._logDebugEvent('on_retriever_error', runId, parentRunId, {
|
|
4086
|
-
err,
|
|
4087
|
-
tags
|
|
4088
|
-
});
|
|
4089
|
-
this._popRunAndCaptureTraceOrSpan(runId, parentRunId, err);
|
|
4090
|
-
}
|
|
4091
|
-
handleAgentAction(action, runId, parentRunId, tags) {
|
|
4092
|
-
this._logDebugEvent('on_agent_action', runId, parentRunId, {
|
|
4093
|
-
action,
|
|
4094
|
-
tags
|
|
4095
|
-
});
|
|
4096
|
-
this._setParentOfRun(runId, parentRunId);
|
|
4097
|
-
this._setTraceOrSpanMetadata(null, action, runId, parentRunId);
|
|
4098
|
-
}
|
|
4099
|
-
handleAgentEnd(action, runId, parentRunId, tags) {
|
|
4100
|
-
this._logDebugEvent('on_agent_finish', runId, parentRunId, {
|
|
4101
|
-
action,
|
|
4102
|
-
tags
|
|
4103
|
-
});
|
|
4104
|
-
this._popRunAndCaptureTraceOrSpan(runId, parentRunId, action);
|
|
4105
|
-
}
|
|
4106
|
-
// ===== PRIVATE HELPERS =====
|
|
4107
|
-
_setParentOfRun(runId, parentRunId) {
|
|
4108
|
-
if (parentRunId) {
|
|
4109
|
-
this.parentTree[runId] = parentRunId;
|
|
4110
|
-
}
|
|
4111
|
-
}
|
|
4112
|
-
_popParentOfRun(runId) {
|
|
4113
|
-
delete this.parentTree[runId];
|
|
4114
|
-
}
|
|
4115
|
-
_findRootRun(runId) {
|
|
4116
|
-
let id = runId;
|
|
4117
|
-
while (this.parentTree[id]) {
|
|
4118
|
-
id = this.parentTree[id];
|
|
4119
|
-
}
|
|
4120
|
-
return id;
|
|
4121
|
-
}
|
|
4122
|
-
_setTraceOrSpanMetadata(serialized, input, runId, parentRunId, ...args) {
|
|
4123
|
-
// Use default names if not provided: if this is a top-level run, we mark it as a trace, otherwise as a span.
|
|
4124
|
-
const defaultName = parentRunId ? 'span' : 'trace';
|
|
4125
|
-
const runName = this._getLangchainRunName(serialized, ...args) || defaultName;
|
|
4126
|
-
this.runs[runId] = {
|
|
4127
|
-
name: runName,
|
|
4128
|
-
input,
|
|
4129
|
-
startTime: Date.now()
|
|
4130
|
-
};
|
|
4131
|
-
}
|
|
4132
|
-
_setLLMMetadata(serialized, runId, messages, metadata, extraParams, runName) {
|
|
4133
|
-
const runNameFound = this._getLangchainRunName(serialized, {
|
|
4134
|
-
extraParams,
|
|
4135
|
-
runName
|
|
4136
|
-
}) || 'generation';
|
|
4137
|
-
const generation = {
|
|
4138
|
-
name: runNameFound,
|
|
4139
|
-
input: sanitizeLangChain(messages),
|
|
4140
|
-
startTime: Date.now()
|
|
4141
|
-
};
|
|
4142
|
-
if (extraParams) {
|
|
4143
|
-
generation.modelParams = getModelParams(extraParams.invocation_params);
|
|
4144
|
-
if (extraParams.invocation_params && extraParams.invocation_params.tools) {
|
|
4145
|
-
generation.tools = extraParams.invocation_params.tools;
|
|
4146
|
-
}
|
|
4147
|
-
}
|
|
4148
|
-
if (metadata) {
|
|
4149
|
-
if (metadata.ls_model_name) {
|
|
4150
|
-
generation.model = metadata.ls_model_name;
|
|
4151
|
-
}
|
|
4152
|
-
if (metadata.ls_provider) {
|
|
4153
|
-
generation.provider = metadata.ls_provider;
|
|
4154
|
-
}
|
|
4155
|
-
}
|
|
4156
|
-
if (serialized && 'kwargs' in serialized && serialized.kwargs.openai_api_base) {
|
|
4157
|
-
generation.baseUrl = serialized.kwargs.openai_api_base;
|
|
4158
|
-
}
|
|
4159
|
-
this.runs[runId] = generation;
|
|
4160
|
-
}
|
|
4161
|
-
_popRunMetadata(runId) {
|
|
4162
|
-
const endTime = Date.now();
|
|
4163
|
-
const run = this.runs[runId];
|
|
4164
|
-
if (!run) {
|
|
4165
|
-
console.warn(`No run metadata found for run ${runId}`);
|
|
4166
|
-
return undefined;
|
|
4167
|
-
}
|
|
4168
|
-
run.endTime = endTime;
|
|
4169
|
-
delete this.runs[runId];
|
|
4170
|
-
return run;
|
|
4171
|
-
}
|
|
4172
|
-
_getTraceId(runId) {
|
|
4173
|
-
return this.traceId ? String(this.traceId) : this._findRootRun(runId);
|
|
4174
|
-
}
|
|
4175
|
-
_getParentRunId(traceId, _runId, parentRunId) {
|
|
4176
|
-
// Replace the parent-run if not found in our stored parent tree.
|
|
4177
|
-
if (parentRunId && !this.parentTree[parentRunId]) {
|
|
4178
|
-
return traceId;
|
|
4179
|
-
}
|
|
4180
|
-
return parentRunId;
|
|
4181
|
-
}
|
|
4182
|
-
_popRunAndCaptureTraceOrSpan(runId, parentRunId, outputs) {
|
|
4183
|
-
const traceId = this._getTraceId(runId);
|
|
4184
|
-
this._popParentOfRun(runId);
|
|
4185
|
-
const run = this._popRunMetadata(runId);
|
|
4186
|
-
if (!run) {
|
|
4187
|
-
return;
|
|
4188
|
-
}
|
|
4189
|
-
if ('modelParams' in run) {
|
|
4190
|
-
console.warn(`Run ${runId} is a generation, but attempted to be captured as a trace/span.`);
|
|
4191
|
-
return;
|
|
4192
|
-
}
|
|
4193
|
-
const actualParentRunId = this._getParentRunId(traceId, runId, parentRunId);
|
|
4194
|
-
this._captureTraceOrSpan(traceId, runId, run, outputs, actualParentRunId);
|
|
4195
|
-
}
|
|
4196
|
-
_captureTraceOrSpan(traceId, runId, run, outputs, parentRunId) {
|
|
4197
|
-
const eventName = parentRunId ? '$ai_span' : '$ai_trace';
|
|
4198
|
-
const latency = run.endTime ? (run.endTime - run.startTime) / 1000 : 0;
|
|
4199
|
-
const eventProperties = {
|
|
4200
|
-
$ai_lib: 'posthog-ai',
|
|
4201
|
-
$ai_lib_version: version,
|
|
4202
|
-
$ai_trace_id: traceId,
|
|
4203
|
-
$ai_input_state: withPrivacyMode(this.client, this.privacyMode, run.input),
|
|
4204
|
-
$ai_latency: latency,
|
|
4205
|
-
$ai_span_name: run.name,
|
|
4206
|
-
$ai_span_id: runId,
|
|
4207
|
-
$ai_framework: 'langchain'
|
|
4208
|
-
};
|
|
4209
|
-
if (parentRunId) {
|
|
4210
|
-
eventProperties['$ai_parent_id'] = parentRunId;
|
|
4211
|
-
}
|
|
4212
|
-
Object.assign(eventProperties, this.properties);
|
|
4213
|
-
if (!this.distinctId) {
|
|
4214
|
-
eventProperties['$process_person_profile'] = false;
|
|
4215
|
-
}
|
|
4216
|
-
if (outputs instanceof Error) {
|
|
4217
|
-
eventProperties['$ai_error'] = stringifyError(outputs);
|
|
4218
|
-
eventProperties['$ai_is_error'] = true;
|
|
4219
|
-
} else if (outputs !== undefined) {
|
|
4220
|
-
eventProperties['$ai_output_state'] = withPrivacyMode(this.client, this.privacyMode, outputs);
|
|
4221
|
-
}
|
|
4222
|
-
this.client.capture({
|
|
4223
|
-
distinctId: this.distinctId ? this.distinctId.toString() : runId,
|
|
4224
|
-
event: eventName,
|
|
4225
|
-
properties: eventProperties,
|
|
4226
|
-
groups: this.groups
|
|
4227
|
-
});
|
|
4228
|
-
}
|
|
4229
|
-
_popRunAndCaptureGeneration(runId, parentRunId, response) {
|
|
4230
|
-
const traceId = this._getTraceId(runId);
|
|
4231
|
-
this._popParentOfRun(runId);
|
|
4232
|
-
const run = this._popRunMetadata(runId);
|
|
4233
|
-
if (!run || typeof run !== 'object' || !('modelParams' in run)) {
|
|
4234
|
-
console.warn(`Run ${runId} is not a generation, but attempted to be captured as such.`);
|
|
4235
|
-
return;
|
|
4236
|
-
}
|
|
4237
|
-
const actualParentRunId = this._getParentRunId(traceId, runId, parentRunId);
|
|
4238
|
-
this._captureGeneration(traceId, runId, run, response, actualParentRunId);
|
|
4239
|
-
}
|
|
4240
|
-
_captureGeneration(traceId, runId, run, output, parentRunId) {
|
|
4241
|
-
const latency = run.endTime ? (run.endTime - run.startTime) / 1000 : 0;
|
|
4242
|
-
const eventProperties = {
|
|
4243
|
-
$ai_lib: 'posthog-ai',
|
|
4244
|
-
$ai_lib_version: version,
|
|
4245
|
-
$ai_trace_id: traceId,
|
|
4246
|
-
$ai_span_id: runId,
|
|
4247
|
-
$ai_span_name: run.name,
|
|
4248
|
-
$ai_parent_id: parentRunId,
|
|
4249
|
-
$ai_provider: run.provider,
|
|
4250
|
-
$ai_model: run.model,
|
|
4251
|
-
$ai_model_parameters: run.modelParams,
|
|
4252
|
-
$ai_input: withPrivacyMode(this.client, this.privacyMode, run.input),
|
|
4253
|
-
$ai_http_status: 200,
|
|
4254
|
-
$ai_latency: latency,
|
|
4255
|
-
$ai_base_url: run.baseUrl,
|
|
4256
|
-
$ai_framework: 'langchain'
|
|
4257
|
-
};
|
|
4258
|
-
if (run.tools) {
|
|
4259
|
-
eventProperties['$ai_tools'] = run.tools;
|
|
4260
|
-
}
|
|
4261
|
-
if (output instanceof Error) {
|
|
4262
|
-
eventProperties['$ai_http_status'] = output.status || 500;
|
|
4263
|
-
eventProperties['$ai_error'] = stringifyError(output);
|
|
4264
|
-
eventProperties['$ai_is_error'] = true;
|
|
4265
|
-
} else {
|
|
4266
|
-
// Handle token usage
|
|
4267
|
-
const [inputTokens, outputTokens, additionalTokenData] = this.parseUsage(output, run.provider, run.model);
|
|
4268
|
-
eventProperties['$ai_input_tokens'] = inputTokens;
|
|
4269
|
-
eventProperties['$ai_output_tokens'] = outputTokens;
|
|
4270
|
-
// Add additional token data to properties
|
|
4271
|
-
if (additionalTokenData.cacheReadInputTokens) {
|
|
4272
|
-
eventProperties['$ai_cache_read_input_tokens'] = additionalTokenData.cacheReadInputTokens;
|
|
4273
|
-
}
|
|
4274
|
-
if (additionalTokenData.cacheWriteInputTokens) {
|
|
4275
|
-
eventProperties['$ai_cache_creation_input_tokens'] = additionalTokenData.cacheWriteInputTokens;
|
|
4276
|
-
}
|
|
4277
|
-
if (additionalTokenData.reasoningTokens) {
|
|
4278
|
-
eventProperties['$ai_reasoning_tokens'] = additionalTokenData.reasoningTokens;
|
|
4279
|
-
}
|
|
4280
|
-
if (additionalTokenData.webSearchCount !== undefined) {
|
|
4281
|
-
eventProperties['$ai_web_search_count'] = additionalTokenData.webSearchCount;
|
|
4282
|
-
}
|
|
4283
|
-
// Extract stop reason from generation info
|
|
4284
|
-
const stopReason = this._extractStopReason(output);
|
|
4285
|
-
if (stopReason) {
|
|
4286
|
-
eventProperties['$ai_stop_reason'] = stopReason;
|
|
4287
|
-
}
|
|
4288
|
-
// Handle generations/completions
|
|
4289
|
-
let completions;
|
|
4290
|
-
if (output.generations && Array.isArray(output.generations)) {
|
|
4291
|
-
const lastGeneration = output.generations[output.generations.length - 1];
|
|
4292
|
-
if (Array.isArray(lastGeneration) && lastGeneration.length > 0) {
|
|
4293
|
-
// Check if this is a ChatGeneration by looking at the first item
|
|
4294
|
-
const isChatGeneration = 'message' in lastGeneration[0] && lastGeneration[0].message;
|
|
4295
|
-
if (isChatGeneration) {
|
|
4296
|
-
// For ChatGeneration, convert messages to dict format
|
|
4297
|
-
completions = lastGeneration.map(gen => {
|
|
4298
|
-
return this._convertMessageToDict(gen.message);
|
|
4299
|
-
});
|
|
4300
|
-
} else {
|
|
4301
|
-
// For non-ChatGeneration, extract raw response
|
|
4302
|
-
completions = lastGeneration.map(gen => {
|
|
4303
|
-
return this._extractRawResponse(gen);
|
|
4304
|
-
});
|
|
4305
|
-
}
|
|
4306
|
-
}
|
|
4307
|
-
}
|
|
4308
|
-
if (completions) {
|
|
4309
|
-
eventProperties['$ai_output_choices'] = withPrivacyMode(this.client, this.privacyMode, completions);
|
|
4310
|
-
}
|
|
4311
|
-
}
|
|
4312
|
-
Object.assign(eventProperties, this.properties);
|
|
4313
|
-
if (!this.distinctId) {
|
|
4314
|
-
eventProperties['$process_person_profile'] = false;
|
|
4315
|
-
}
|
|
4316
|
-
this.client.capture({
|
|
4317
|
-
distinctId: this.distinctId ? this.distinctId.toString() : traceId,
|
|
4318
|
-
event: '$ai_generation',
|
|
4319
|
-
properties: eventProperties,
|
|
4320
|
-
groups: this.groups
|
|
4321
|
-
});
|
|
4322
|
-
}
|
|
4323
|
-
_logDebugEvent(eventName, runId, parentRunId, extra) {
|
|
4324
|
-
if (this.debug) {
|
|
4325
|
-
console.log(`Event: ${eventName}, runId: ${runId}, parentRunId: ${parentRunId}, extra:`, extra);
|
|
4326
|
-
}
|
|
4327
|
-
}
|
|
4328
|
-
_getLangchainRunName(serialized, ...args) {
|
|
4329
|
-
if (args && args.length > 0) {
|
|
4330
|
-
for (const arg of args) {
|
|
4331
|
-
if (arg && typeof arg === 'object' && 'name' in arg) {
|
|
4332
|
-
return arg.name;
|
|
4333
|
-
} else if (arg && typeof arg === 'object' && 'runName' in arg) {
|
|
4334
|
-
return arg.runName;
|
|
4335
|
-
}
|
|
4336
|
-
}
|
|
4337
|
-
}
|
|
4338
|
-
if (serialized && serialized.name) {
|
|
4339
|
-
return serialized.name;
|
|
4340
|
-
}
|
|
4341
|
-
if (serialized && serialized.id) {
|
|
4342
|
-
return Array.isArray(serialized.id) ? serialized.id[serialized.id.length - 1] : serialized.id;
|
|
4343
|
-
}
|
|
4344
|
-
return undefined;
|
|
4345
|
-
}
|
|
4346
|
-
_convertLcToolCallsToOai(toolCalls) {
|
|
4347
|
-
return toolCalls.map(toolCall => ({
|
|
4348
|
-
type: 'function',
|
|
4349
|
-
id: toolCall.id,
|
|
4350
|
-
function: {
|
|
4351
|
-
name: toolCall.name,
|
|
4352
|
-
arguments: JSON.stringify(toolCall.args)
|
|
4353
|
-
}
|
|
4354
|
-
}));
|
|
4355
|
-
}
|
|
4356
|
-
_extractRawResponse(generation) {
|
|
4357
|
-
// Extract the response from the last response of the LLM call
|
|
4358
|
-
// We return the text of the response if not empty
|
|
4359
|
-
if (generation.text != null && generation.text.trim() !== '') {
|
|
4360
|
-
return generation.text.trim();
|
|
4361
|
-
} else if (generation.message) {
|
|
4362
|
-
// Additional kwargs contains the response in case of tool usage
|
|
4363
|
-
return generation.message.additional_kwargs || generation.message.additionalKwargs || {};
|
|
4364
|
-
} else {
|
|
4365
|
-
// Not tool usage, some LLM responses can be simply empty
|
|
4366
|
-
return '';
|
|
4367
|
-
}
|
|
4368
|
-
}
|
|
4369
|
-
_convertMessageToDict(message) {
|
|
4370
|
-
let messageDict = {};
|
|
4371
|
-
const messageType = message.getType();
|
|
4372
|
-
switch (messageType) {
|
|
4373
|
-
case 'human':
|
|
4374
|
-
messageDict = {
|
|
4375
|
-
role: 'user',
|
|
4376
|
-
content: message.content
|
|
4377
|
-
};
|
|
4378
|
-
break;
|
|
4379
|
-
case 'ai':
|
|
4380
|
-
messageDict = {
|
|
4381
|
-
role: 'assistant',
|
|
4382
|
-
content: message.content
|
|
4383
|
-
};
|
|
4384
|
-
if (message.tool_calls) {
|
|
4385
|
-
messageDict.tool_calls = this._convertLcToolCallsToOai(message.tool_calls);
|
|
4386
|
-
}
|
|
4387
|
-
break;
|
|
4388
|
-
case 'system':
|
|
4389
|
-
messageDict = {
|
|
4390
|
-
role: 'system',
|
|
4391
|
-
content: message.content
|
|
4392
|
-
};
|
|
4393
|
-
break;
|
|
4394
|
-
case 'tool':
|
|
4395
|
-
messageDict = {
|
|
4396
|
-
role: 'tool',
|
|
4397
|
-
content: message.content
|
|
4398
|
-
};
|
|
4399
|
-
break;
|
|
4400
|
-
case 'function':
|
|
4401
|
-
messageDict = {
|
|
4402
|
-
role: 'function',
|
|
4403
|
-
content: message.content
|
|
4404
|
-
};
|
|
4405
|
-
break;
|
|
4406
|
-
default:
|
|
4407
|
-
messageDict = {
|
|
4408
|
-
role: messageType,
|
|
4409
|
-
content: toContentString(message.content)
|
|
4410
|
-
};
|
|
4411
|
-
break;
|
|
4412
|
-
}
|
|
4413
|
-
if (message.additional_kwargs) {
|
|
4414
|
-
messageDict = {
|
|
4415
|
-
...messageDict,
|
|
4416
|
-
...message.additional_kwargs
|
|
4417
|
-
};
|
|
4418
|
-
}
|
|
4419
|
-
// Sanitize the message content to redact base64 images
|
|
4420
|
-
return sanitizeLangChain(messageDict);
|
|
4421
|
-
}
|
|
4422
|
-
_extractStopReason(output) {
|
|
4423
|
-
if (!output.generations || !Array.isArray(output.generations)) {
|
|
4424
|
-
return undefined;
|
|
4425
|
-
}
|
|
4426
|
-
const lastGeneration = output.generations[output.generations.length - 1];
|
|
4427
|
-
if (!Array.isArray(lastGeneration) || lastGeneration.length === 0) {
|
|
4428
|
-
return undefined;
|
|
4429
|
-
}
|
|
4430
|
-
const gen = lastGeneration[0];
|
|
4431
|
-
// Check generationInfo for finish_reason (OpenAI format)
|
|
4432
|
-
if (gen.generationInfo?.finish_reason) {
|
|
4433
|
-
return String(gen.generationInfo.finish_reason);
|
|
4434
|
-
}
|
|
4435
|
-
// Check generationInfo for response_metadata.stop_reason (Anthropic format)
|
|
4436
|
-
if (gen.generationInfo?.response_metadata?.stop_reason) {
|
|
4437
|
-
return String(gen.generationInfo.response_metadata.stop_reason);
|
|
4438
|
-
}
|
|
4439
|
-
// Check message response_metadata for finish_reason (common LangChain format)
|
|
4440
|
-
if (gen.generationInfo?.response_metadata?.finish_reason) {
|
|
4441
|
-
return String(gen.generationInfo.response_metadata.finish_reason);
|
|
4442
|
-
}
|
|
4443
|
-
// Check for stop_reason directly in generationInfo
|
|
4444
|
-
if (gen.generationInfo?.stop_reason) {
|
|
4445
|
-
return String(gen.generationInfo.stop_reason);
|
|
4446
|
-
}
|
|
4447
|
-
return undefined;
|
|
4448
|
-
}
|
|
4449
|
-
_parseUsageModel(usage, provider, model) {
|
|
4450
|
-
const conversionList = [['promptTokens', 'input'], ['completionTokens', 'output'], ['input_tokens', 'input'], ['output_tokens', 'output'], ['prompt_token_count', 'input'], ['candidates_token_count', 'output'], ['inputTokenCount', 'input'], ['outputTokenCount', 'output'], ['input_token_count', 'input'], ['generated_token_count', 'output']];
|
|
4451
|
-
const parsedUsage = conversionList.reduce((acc, [modelKey, typeKey]) => {
|
|
4452
|
-
const value = usage[modelKey];
|
|
4453
|
-
if (value != null) {
|
|
4454
|
-
const finalCount = Array.isArray(value) ? value.reduce((sum, tokenCount) => sum + tokenCount, 0) : value;
|
|
4455
|
-
acc[typeKey] = finalCount;
|
|
4456
|
-
}
|
|
4457
|
-
return acc;
|
|
4458
|
-
}, {
|
|
4459
|
-
input: 0,
|
|
4460
|
-
output: 0
|
|
4461
|
-
});
|
|
4462
|
-
// Extract additional token details like cached tokens and reasoning tokens
|
|
4463
|
-
const additionalTokenData = {};
|
|
4464
|
-
// Check for cached tokens in various formats
|
|
4465
|
-
if (usage.prompt_tokens_details?.cached_tokens != null) {
|
|
4466
|
-
additionalTokenData.cacheReadInputTokens = usage.prompt_tokens_details.cached_tokens;
|
|
4467
|
-
} else if (usage.input_token_details?.cache_read != null) {
|
|
4468
|
-
additionalTokenData.cacheReadInputTokens = usage.input_token_details.cache_read;
|
|
4469
|
-
} else if (usage.cachedPromptTokens != null) {
|
|
4470
|
-
additionalTokenData.cacheReadInputTokens = usage.cachedPromptTokens;
|
|
4471
|
-
} else if (usage.cache_read_input_tokens != null) {
|
|
4472
|
-
additionalTokenData.cacheReadInputTokens = usage.cache_read_input_tokens;
|
|
4473
|
-
}
|
|
4474
|
-
// Check for cache write/creation tokens in various formats
|
|
4475
|
-
if (usage.cache_creation_input_tokens != null) {
|
|
4476
|
-
additionalTokenData.cacheWriteInputTokens = usage.cache_creation_input_tokens;
|
|
4477
|
-
} else if (usage.input_token_details?.cache_creation != null) {
|
|
4478
|
-
additionalTokenData.cacheWriteInputTokens = usage.input_token_details.cache_creation;
|
|
4479
|
-
}
|
|
4480
|
-
// Check for reasoning tokens in various formats
|
|
4481
|
-
if (usage.completion_tokens_details?.reasoning_tokens != null) {
|
|
4482
|
-
additionalTokenData.reasoningTokens = usage.completion_tokens_details.reasoning_tokens;
|
|
4483
|
-
} else if (usage.output_token_details?.reasoning != null) {
|
|
4484
|
-
additionalTokenData.reasoningTokens = usage.output_token_details.reasoning;
|
|
4485
|
-
} else if (usage.reasoningTokens != null) {
|
|
4486
|
-
additionalTokenData.reasoningTokens = usage.reasoningTokens;
|
|
4487
|
-
}
|
|
4488
|
-
// Extract web search counts from various provider formats
|
|
4489
|
-
let webSearchCount;
|
|
4490
|
-
// Priority 1: Exact Count
|
|
4491
|
-
// Check Anthropic format (server_tool_use.web_search_requests)
|
|
4492
|
-
if (usage.server_tool_use?.web_search_requests !== undefined) {
|
|
4493
|
-
webSearchCount = usage.server_tool_use.web_search_requests;
|
|
4494
|
-
}
|
|
4495
|
-
// Priority 2: Binary Detection (1 or 0)
|
|
4496
|
-
// Check for citations array (Perplexity)
|
|
4497
|
-
else if (usage.citations && Array.isArray(usage.citations) && usage.citations.length > 0) {
|
|
4498
|
-
webSearchCount = 1;
|
|
4499
|
-
}
|
|
4500
|
-
// Check for search_results array (Perplexity via OpenRouter)
|
|
4501
|
-
else if (usage.search_results && Array.isArray(usage.search_results) && usage.search_results.length > 0) {
|
|
4502
|
-
webSearchCount = 1;
|
|
4503
|
-
}
|
|
4504
|
-
// Check for search_context_size (Perplexity via OpenRouter)
|
|
4505
|
-
else if (usage.search_context_size) {
|
|
4506
|
-
webSearchCount = 1;
|
|
4507
|
-
}
|
|
4508
|
-
// Check for annotations with url_citation type
|
|
4509
|
-
else if (usage.annotations && Array.isArray(usage.annotations)) {
|
|
4510
|
-
const hasUrlCitation = usage.annotations.some(ann => {
|
|
4511
|
-
return ann && typeof ann === 'object' && 'type' in ann && ann.type === 'url_citation';
|
|
4512
|
-
});
|
|
4513
|
-
if (hasUrlCitation) {
|
|
4514
|
-
webSearchCount = 1;
|
|
4515
|
-
}
|
|
4516
|
-
}
|
|
4517
|
-
// Check Gemini format (grounding metadata - binary 0 or 1)
|
|
4518
|
-
else if (usage.grounding_metadata?.grounding_support !== undefined || usage.grounding_metadata?.web_search_queries !== undefined) {
|
|
4519
|
-
webSearchCount = 1;
|
|
4520
|
-
}
|
|
4521
|
-
if (webSearchCount !== undefined) {
|
|
4522
|
-
additionalTokenData.webSearchCount = webSearchCount;
|
|
4523
|
-
}
|
|
4524
|
-
// For Anthropic providers, LangChain reports input_tokens as the sum of all input tokens.
|
|
4525
|
-
// Our cost calculation expects them to be separate for Anthropic, so we subtract cache tokens.
|
|
4526
|
-
// Both cache_read and cache_write tokens should be subtracted since Anthropic's raw API
|
|
4527
|
-
// reports input_tokens as tokens NOT read from or used to create a cache.
|
|
4528
|
-
// For other providers (OpenAI, etc.), input_tokens already excludes cache tokens as expected.
|
|
4529
|
-
// Match logic consistent with plugin-server: exact match on provider OR substring match on model
|
|
4530
|
-
let isAnthropic = false;
|
|
4531
|
-
if (provider && provider.toLowerCase() === 'anthropic') {
|
|
4532
|
-
isAnthropic = true;
|
|
4533
|
-
} else if (model && model.toLowerCase().includes('anthropic')) {
|
|
4534
|
-
isAnthropic = true;
|
|
4535
|
-
}
|
|
4536
|
-
if (isAnthropic && parsedUsage.input) {
|
|
4537
|
-
const cacheTokens = (additionalTokenData.cacheReadInputTokens || 0) + (additionalTokenData.cacheWriteInputTokens || 0);
|
|
4538
|
-
if (cacheTokens > 0) {
|
|
4539
|
-
parsedUsage.input = Math.max(parsedUsage.input - cacheTokens, 0);
|
|
4540
|
-
}
|
|
4541
|
-
}
|
|
4542
|
-
return [parsedUsage.input, parsedUsage.output, additionalTokenData];
|
|
4543
|
-
}
|
|
4544
|
-
parseUsage(response, provider, model) {
|
|
4545
|
-
let llmUsage = [0, 0, {}];
|
|
4546
|
-
const llmUsageKeys = ['token_usage', 'usage', 'tokenUsage'];
|
|
4547
|
-
if (response.llmOutput != null) {
|
|
4548
|
-
const key = llmUsageKeys.find(k => response.llmOutput?.[k] != null);
|
|
4549
|
-
if (key) {
|
|
4550
|
-
llmUsage = this._parseUsageModel(response.llmOutput[key], provider, model);
|
|
4551
|
-
}
|
|
4552
|
-
}
|
|
4553
|
-
// If top-level usage info was not found, try checking the generations.
|
|
4554
|
-
if (llmUsage[0] === 0 && llmUsage[1] === 0 && response.generations) {
|
|
4555
|
-
for (const generation of response.generations) {
|
|
4556
|
-
for (const genChunk of generation) {
|
|
4557
|
-
// Check other paths for usage information
|
|
4558
|
-
if (genChunk.generationInfo?.usage_metadata) {
|
|
4559
|
-
llmUsage = this._parseUsageModel(genChunk.generationInfo.usage_metadata, provider, model);
|
|
4560
|
-
return llmUsage;
|
|
4561
|
-
}
|
|
4562
|
-
const messageChunk = genChunk.generationInfo ?? {};
|
|
4563
|
-
const responseMetadata = messageChunk.response_metadata ?? {};
|
|
4564
|
-
const chunkUsage = responseMetadata['usage'] ?? responseMetadata['amazon-bedrock-invocationMetrics'] ?? messageChunk.usage_metadata;
|
|
4565
|
-
if (chunkUsage) {
|
|
4566
|
-
llmUsage = this._parseUsageModel(chunkUsage, provider, model);
|
|
4567
|
-
return llmUsage;
|
|
4568
|
-
}
|
|
4569
|
-
}
|
|
4570
|
-
}
|
|
4571
|
-
}
|
|
4572
|
-
return llmUsage;
|
|
4573
|
-
}
|
|
4574
|
-
}
|
|
4575
|
-
|
|
4576
|
-
/// <reference lib="dom" />
|
|
4577
|
-
const DEFAULT_CACHE_TTL_SECONDS = 300; // 5 minutes
|
|
4578
|
-
const DEFAULT_PROMPTS_HOST = 'https://us.posthog.com';
|
|
4579
|
-
function normalizeApiKey(value) {
|
|
4580
|
-
return typeof value === 'string' ? value.trim() : '';
|
|
4581
|
-
}
|
|
4582
|
-
function normalizeHost(value) {
|
|
4583
|
-
const normalizedHost = typeof value === 'string' ? value.trim() : '';
|
|
4584
|
-
return (normalizedHost || DEFAULT_PROMPTS_HOST).replace(/\/+$/, '');
|
|
4585
|
-
}
|
|
4586
|
-
function isPromptApiResponse(data) {
|
|
4587
|
-
if (typeof data !== 'object' || data === null) {
|
|
4588
|
-
return false;
|
|
1192
|
+
function isPromptApiResponse(data) {
|
|
1193
|
+
if (typeof data !== 'object' || data === null) {
|
|
1194
|
+
return false;
|
|
4589
1195
|
}
|
|
4590
1196
|
const record = data;
|
|
4591
1197
|
return typeof record.prompt === 'string' && typeof record.name === 'string' && typeof record.version === 'number';
|
|
@@ -4609,18 +1215,18 @@ function isPromptsWithPostHog(options) {
|
|
|
4609
1215
|
* })
|
|
4610
1216
|
*
|
|
4611
1217
|
* // Fetch with caching and fallback
|
|
4612
|
-
* const
|
|
1218
|
+
* const result = await prompts.get('support-system-prompt', {
|
|
4613
1219
|
* cacheTtlSeconds: 300,
|
|
4614
1220
|
* fallback: 'You are a helpful assistant.',
|
|
4615
1221
|
* })
|
|
4616
1222
|
*
|
|
4617
1223
|
* // Or fetch an exact published version
|
|
4618
|
-
* const
|
|
1224
|
+
* const v3 = await prompts.get('support-system-prompt', {
|
|
4619
1225
|
* version: 3,
|
|
4620
1226
|
* })
|
|
4621
1227
|
*
|
|
4622
1228
|
* // Compile with variables
|
|
4623
|
-
* const systemPrompt = prompts.compile(
|
|
1229
|
+
* const systemPrompt = prompts.compile(result.prompt, {
|
|
4624
1230
|
* company: 'Acme Corp',
|
|
4625
1231
|
* tier: 'premium',
|
|
4626
1232
|
* })
|
|
@@ -4629,7 +1235,6 @@ function isPromptsWithPostHog(options) {
|
|
|
4629
1235
|
class Prompts {
|
|
4630
1236
|
constructor(options) {
|
|
4631
1237
|
this.cache = new Map();
|
|
4632
|
-
this.hasWarnedDeprecation = false;
|
|
4633
1238
|
this.defaultCacheTtlSeconds = options.defaultCacheTtlSeconds ?? DEFAULT_CACHE_TTL_SECONDS;
|
|
4634
1239
|
if (isPromptsWithPostHog(options)) {
|
|
4635
1240
|
this.personalApiKey = options.posthog.options.personalApiKey ?? '';
|
|
@@ -4657,32 +1262,26 @@ class Prompts {
|
|
|
4657
1262
|
getPromptLabel(name, version) {
|
|
4658
1263
|
return version === undefined ? `"${name}"` : `"${name}" version ${version}`;
|
|
4659
1264
|
}
|
|
1265
|
+
/**
|
|
1266
|
+
* Fetch a prompt by name from the PostHog API.
|
|
1267
|
+
*
|
|
1268
|
+
* Returns a `PromptResult` object carrying the prompt text alongside `source`,
|
|
1269
|
+
* `name`, and `version` metadata. Read `result.prompt` for the template string.
|
|
1270
|
+
*/
|
|
4660
1271
|
async get(name, options) {
|
|
4661
|
-
const withMetadata = options?.withMetadata;
|
|
4662
|
-
if (withMetadata === undefined && !this.hasWarnedDeprecation) {
|
|
4663
|
-
this.hasWarnedDeprecation = true;
|
|
4664
|
-
console.warn('[PostHog Prompts] Calling get() without { withMetadata: true } is deprecated and will be ' + 'removed in a future major version. Pass { withMetadata: true } to receive a PromptResult ' + 'object with source, name, and version metadata. ' + 'You can pass { withMetadata: false } to silence this warning, but the plain-string return ' + 'will still be removed in the next major version.');
|
|
4665
|
-
}
|
|
4666
1272
|
try {
|
|
4667
|
-
|
|
4668
|
-
if (withMetadata) {
|
|
4669
|
-
return result;
|
|
4670
|
-
}
|
|
4671
|
-
return result.prompt;
|
|
1273
|
+
return await this.getInternal(name, options);
|
|
4672
1274
|
} catch (error) {
|
|
4673
1275
|
const fallback = options?.fallback;
|
|
4674
1276
|
if (fallback !== undefined) {
|
|
4675
1277
|
const promptLabel = this.getPromptLabel(name, options?.version);
|
|
4676
1278
|
console.warn(`[PostHog Prompts] Failed to fetch prompt ${promptLabel}, using fallback:`, error);
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
};
|
|
4684
|
-
}
|
|
4685
|
-
return fallback;
|
|
1279
|
+
return {
|
|
1280
|
+
source: 'code_fallback',
|
|
1281
|
+
prompt: fallback,
|
|
1282
|
+
name: undefined,
|
|
1283
|
+
version: undefined
|
|
1284
|
+
};
|
|
4686
1285
|
}
|
|
4687
1286
|
throw error;
|
|
4688
1287
|
}
|
|
@@ -4820,5 +1419,5 @@ class Prompts {
|
|
|
4820
1419
|
}
|
|
4821
1420
|
}
|
|
4822
1421
|
|
|
4823
|
-
export { AIEvent,
|
|
1422
|
+
export { AIEvent, Prompts, captureAiGeneration, wrapVercelLanguageModel as withTracing };
|
|
4824
1423
|
//# sourceMappingURL=index.mjs.map
|