@posthog/ai 7.6.1 → 7.8.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/dist/anthropic/index.cjs +14 -1
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +14 -1
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +15 -1
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.mjs +15 -1
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +239 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +88 -1
- package/dist/index.mjs +239 -2
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +1 -1
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.mjs +1 -1
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/openai/index.cjs +36 -1
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +36 -1
- package/dist/openai/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +20 -1
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +20 -1
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -30,7 +30,7 @@ function _interopNamespace(e) {
|
|
|
30
30
|
var uuid__namespace = /*#__PURE__*/_interopNamespace(uuid);
|
|
31
31
|
var AnthropicOriginal__default = /*#__PURE__*/_interopDefault(AnthropicOriginal);
|
|
32
32
|
|
|
33
|
-
var version = "7.
|
|
33
|
+
var version = "7.8.0";
|
|
34
34
|
|
|
35
35
|
// Type guards for safer type checking
|
|
36
36
|
const isString = value => {
|
|
@@ -746,6 +746,7 @@ const sendEventToPosthog = async ({
|
|
|
746
746
|
input,
|
|
747
747
|
output,
|
|
748
748
|
latency,
|
|
749
|
+
timeToFirstToken,
|
|
749
750
|
baseURL,
|
|
750
751
|
params,
|
|
751
752
|
httpStatus = 200,
|
|
@@ -812,6 +813,9 @@ const sendEventToPosthog = async ({
|
|
|
812
813
|
} : {}),
|
|
813
814
|
...additionalTokenValues,
|
|
814
815
|
$ai_latency: latency,
|
|
816
|
+
...(timeToFirstToken !== undefined ? {
|
|
817
|
+
$ai_time_to_first_token: timeToFirstToken
|
|
818
|
+
} : {}),
|
|
815
819
|
$ai_trace_id: traceId,
|
|
816
820
|
$ai_base_url: baseURL,
|
|
817
821
|
...params.posthogProperties,
|
|
@@ -883,6 +887,14 @@ function formatOpenAIResponsesInput(input, instructions) {
|
|
|
883
887
|
return messages;
|
|
884
888
|
}
|
|
885
889
|
|
|
890
|
+
/**
|
|
891
|
+
* Checks if a ResponseStreamEvent chunk represents the first token/content from the model.
|
|
892
|
+
* This includes various content types like text, reasoning, audio, and refusals.
|
|
893
|
+
*/
|
|
894
|
+
function isResponseTokenChunk(chunk) {
|
|
895
|
+
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';
|
|
896
|
+
}
|
|
897
|
+
|
|
886
898
|
const Chat = openai.OpenAI.Chat;
|
|
887
899
|
const Completions = Chat.Completions;
|
|
888
900
|
const Responses = openai.OpenAI.Responses;
|
|
@@ -932,6 +944,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
|
|
|
932
944
|
const contentBlocks = [];
|
|
933
945
|
let accumulatedContent = '';
|
|
934
946
|
let modelFromResponse;
|
|
947
|
+
let firstTokenTime;
|
|
935
948
|
let usage = {
|
|
936
949
|
inputTokens: 0,
|
|
937
950
|
outputTokens: 0,
|
|
@@ -953,11 +966,17 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
|
|
|
953
966
|
// Handle text content
|
|
954
967
|
const deltaContent = choice?.delta?.content;
|
|
955
968
|
if (deltaContent) {
|
|
969
|
+
if (firstTokenTime === undefined) {
|
|
970
|
+
firstTokenTime = Date.now();
|
|
971
|
+
}
|
|
956
972
|
accumulatedContent += deltaContent;
|
|
957
973
|
}
|
|
958
974
|
// Handle tool calls
|
|
959
975
|
const deltaToolCalls = choice?.delta?.tool_calls;
|
|
960
976
|
if (deltaToolCalls && Array.isArray(deltaToolCalls)) {
|
|
977
|
+
if (firstTokenTime === undefined) {
|
|
978
|
+
firstTokenTime = Date.now();
|
|
979
|
+
}
|
|
961
980
|
for (const toolCall of deltaToolCalls) {
|
|
962
981
|
const index = toolCall.index;
|
|
963
982
|
if (index !== undefined) {
|
|
@@ -1029,6 +1048,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
|
|
|
1029
1048
|
}]
|
|
1030
1049
|
}];
|
|
1031
1050
|
const latency = (Date.now() - startTime) / 1000;
|
|
1051
|
+
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1032
1052
|
const availableTools = extractAvailableToolCalls('openai', openAIParams);
|
|
1033
1053
|
await sendEventToPosthog({
|
|
1034
1054
|
client: this.phClient,
|
|
@@ -1038,6 +1058,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
|
|
|
1038
1058
|
input: sanitizeOpenAI(openAIParams.messages),
|
|
1039
1059
|
output: formattedOutput,
|
|
1040
1060
|
latency,
|
|
1061
|
+
timeToFirstToken,
|
|
1041
1062
|
baseURL: this.baseURL,
|
|
1042
1063
|
params: body,
|
|
1043
1064
|
httpStatus: 200,
|
|
@@ -1152,6 +1173,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
|
|
|
1152
1173
|
try {
|
|
1153
1174
|
let finalContent = [];
|
|
1154
1175
|
let modelFromResponse;
|
|
1176
|
+
let firstTokenTime;
|
|
1155
1177
|
let usage = {
|
|
1156
1178
|
inputTokens: 0,
|
|
1157
1179
|
outputTokens: 0,
|
|
@@ -1159,6 +1181,10 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
|
|
|
1159
1181
|
};
|
|
1160
1182
|
let rawUsageData;
|
|
1161
1183
|
for await (const chunk of stream1) {
|
|
1184
|
+
// Track first token time on content delta events
|
|
1185
|
+
if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
|
|
1186
|
+
firstTokenTime = Date.now();
|
|
1187
|
+
}
|
|
1162
1188
|
if ('response' in chunk && chunk.response) {
|
|
1163
1189
|
// Extract model from response object in chunk (for stored prompts)
|
|
1164
1190
|
if (!modelFromResponse && chunk.response.model) {
|
|
@@ -1184,6 +1210,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
|
|
|
1184
1210
|
}
|
|
1185
1211
|
}
|
|
1186
1212
|
const latency = (Date.now() - startTime) / 1000;
|
|
1213
|
+
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1187
1214
|
const availableTools = extractAvailableToolCalls('openai', openAIParams);
|
|
1188
1215
|
await sendEventToPosthog({
|
|
1189
1216
|
client: this.phClient,
|
|
@@ -1193,6 +1220,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
|
|
|
1193
1220
|
input: formatOpenAIResponsesInput(sanitizeOpenAIResponse(openAIParams.input), openAIParams.instructions),
|
|
1194
1221
|
output: finalContent,
|
|
1195
1222
|
latency,
|
|
1223
|
+
timeToFirstToken,
|
|
1196
1224
|
baseURL: this.baseURL,
|
|
1197
1225
|
params: body,
|
|
1198
1226
|
httpStatus: 200,
|
|
@@ -1431,12 +1459,17 @@ class WrappedTranscriptions extends Transcriptions {
|
|
|
1431
1459
|
(async () => {
|
|
1432
1460
|
try {
|
|
1433
1461
|
let finalContent = '';
|
|
1462
|
+
let firstTokenTime;
|
|
1434
1463
|
let usage = {
|
|
1435
1464
|
inputTokens: 0,
|
|
1436
1465
|
outputTokens: 0
|
|
1437
1466
|
};
|
|
1438
1467
|
const doneEvent = 'transcript.text.done';
|
|
1439
1468
|
for await (const chunk of stream1) {
|
|
1469
|
+
// Track first token on text delta events
|
|
1470
|
+
if (firstTokenTime === undefined && chunk.type === 'transcript.text.delta') {
|
|
1471
|
+
firstTokenTime = Date.now();
|
|
1472
|
+
}
|
|
1440
1473
|
if (chunk.type === doneEvent && 'text' in chunk && chunk.text && chunk.text.length > 0) {
|
|
1441
1474
|
finalContent = chunk.text;
|
|
1442
1475
|
}
|
|
@@ -1449,6 +1482,7 @@ class WrappedTranscriptions extends Transcriptions {
|
|
|
1449
1482
|
}
|
|
1450
1483
|
}
|
|
1451
1484
|
const latency = (Date.now() - startTime) / 1000;
|
|
1485
|
+
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1452
1486
|
const availableTools = extractAvailableToolCalls('openai', openAIParams);
|
|
1453
1487
|
await sendEventToPosthog({
|
|
1454
1488
|
client: this.phClient,
|
|
@@ -1458,6 +1492,7 @@ class WrappedTranscriptions extends Transcriptions {
|
|
|
1458
1492
|
input: openAIParams.prompt,
|
|
1459
1493
|
output: finalContent,
|
|
1460
1494
|
latency,
|
|
1495
|
+
timeToFirstToken,
|
|
1461
1496
|
baseURL: this.baseURL,
|
|
1462
1497
|
params: body,
|
|
1463
1498
|
httpStatus: 200,
|
|
@@ -1576,6 +1611,7 @@ class WrappedCompletions extends openai.AzureOpenAI.Chat.Completions {
|
|
|
1576
1611
|
const contentBlocks = [];
|
|
1577
1612
|
let accumulatedContent = '';
|
|
1578
1613
|
let modelFromResponse;
|
|
1614
|
+
let firstTokenTime;
|
|
1579
1615
|
let usage = {
|
|
1580
1616
|
inputTokens: 0,
|
|
1581
1617
|
outputTokens: 0
|
|
@@ -1591,11 +1627,17 @@ class WrappedCompletions extends openai.AzureOpenAI.Chat.Completions {
|
|
|
1591
1627
|
// Handle text content
|
|
1592
1628
|
const deltaContent = choice?.delta?.content;
|
|
1593
1629
|
if (deltaContent) {
|
|
1630
|
+
if (firstTokenTime === undefined) {
|
|
1631
|
+
firstTokenTime = Date.now();
|
|
1632
|
+
}
|
|
1594
1633
|
accumulatedContent += deltaContent;
|
|
1595
1634
|
}
|
|
1596
1635
|
// Handle tool calls
|
|
1597
1636
|
const deltaToolCalls = choice?.delta?.tool_calls;
|
|
1598
1637
|
if (deltaToolCalls && Array.isArray(deltaToolCalls)) {
|
|
1638
|
+
if (firstTokenTime === undefined) {
|
|
1639
|
+
firstTokenTime = Date.now();
|
|
1640
|
+
}
|
|
1599
1641
|
for (const toolCall of deltaToolCalls) {
|
|
1600
1642
|
const index = toolCall.index;
|
|
1601
1643
|
if (index !== undefined) {
|
|
@@ -1665,6 +1707,7 @@ class WrappedCompletions extends openai.AzureOpenAI.Chat.Completions {
|
|
|
1665
1707
|
}]
|
|
1666
1708
|
}];
|
|
1667
1709
|
const latency = (Date.now() - startTime) / 1000;
|
|
1710
|
+
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1668
1711
|
await sendEventToPosthog({
|
|
1669
1712
|
client: this.phClient,
|
|
1670
1713
|
...posthogParams,
|
|
@@ -1673,6 +1716,7 @@ class WrappedCompletions extends openai.AzureOpenAI.Chat.Completions {
|
|
|
1673
1716
|
input: sanitizeOpenAI(openAIParams.messages),
|
|
1674
1717
|
output: formattedOutput,
|
|
1675
1718
|
latency,
|
|
1719
|
+
timeToFirstToken,
|
|
1676
1720
|
baseURL: this.baseURL,
|
|
1677
1721
|
params: body,
|
|
1678
1722
|
httpStatus: 200,
|
|
@@ -1774,11 +1818,16 @@ class WrappedResponses extends openai.AzureOpenAI.Responses {
|
|
|
1774
1818
|
try {
|
|
1775
1819
|
let finalContent = [];
|
|
1776
1820
|
let modelFromResponse;
|
|
1821
|
+
let firstTokenTime;
|
|
1777
1822
|
let usage = {
|
|
1778
1823
|
inputTokens: 0,
|
|
1779
1824
|
outputTokens: 0
|
|
1780
1825
|
};
|
|
1781
1826
|
for await (const chunk of stream1) {
|
|
1827
|
+
// Track first token time on content delta events
|
|
1828
|
+
if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
|
|
1829
|
+
firstTokenTime = Date.now();
|
|
1830
|
+
}
|
|
1782
1831
|
if ('response' in chunk && chunk.response) {
|
|
1783
1832
|
// Extract model from response if not in params (for stored prompts)
|
|
1784
1833
|
if (!modelFromResponse && chunk.response.model) {
|
|
@@ -1798,6 +1847,7 @@ class WrappedResponses extends openai.AzureOpenAI.Responses {
|
|
|
1798
1847
|
}
|
|
1799
1848
|
}
|
|
1800
1849
|
const latency = (Date.now() - startTime) / 1000;
|
|
1850
|
+
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1801
1851
|
await sendEventToPosthog({
|
|
1802
1852
|
client: this.phClient,
|
|
1803
1853
|
...posthogParams,
|
|
@@ -1806,6 +1856,7 @@ class WrappedResponses extends openai.AzureOpenAI.Responses {
|
|
|
1806
1856
|
input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
|
|
1807
1857
|
output: finalContent,
|
|
1808
1858
|
latency,
|
|
1859
|
+
timeToFirstToken,
|
|
1809
1860
|
baseURL: this.baseURL,
|
|
1810
1861
|
params: body,
|
|
1811
1862
|
httpStatus: 200,
|
|
@@ -2375,6 +2426,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
2375
2426
|
doStream: {
|
|
2376
2427
|
value: async params => {
|
|
2377
2428
|
const startTime = Date.now();
|
|
2429
|
+
let firstTokenTime;
|
|
2378
2430
|
let generatedText = '';
|
|
2379
2431
|
let reasoningText = '';
|
|
2380
2432
|
let usage = {};
|
|
@@ -2398,13 +2450,22 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
2398
2450
|
transform(chunk, controller) {
|
|
2399
2451
|
// Handle streaming patterns - compatible with both V2 and V3
|
|
2400
2452
|
if (chunk.type === 'text-delta') {
|
|
2453
|
+
if (firstTokenTime === undefined) {
|
|
2454
|
+
firstTokenTime = Date.now();
|
|
2455
|
+
}
|
|
2401
2456
|
generatedText += chunk.delta;
|
|
2402
2457
|
}
|
|
2403
2458
|
if (chunk.type === 'reasoning-delta') {
|
|
2459
|
+
if (firstTokenTime === undefined) {
|
|
2460
|
+
firstTokenTime = Date.now();
|
|
2461
|
+
}
|
|
2404
2462
|
reasoningText += chunk.delta;
|
|
2405
2463
|
}
|
|
2406
2464
|
// Handle tool call chunks
|
|
2407
2465
|
if (chunk.type === 'tool-input-start') {
|
|
2466
|
+
if (firstTokenTime === undefined) {
|
|
2467
|
+
firstTokenTime = Date.now();
|
|
2468
|
+
}
|
|
2408
2469
|
// Initialize a new tool call
|
|
2409
2470
|
toolCallsInProgress.set(chunk.id, {
|
|
2410
2471
|
toolCallId: chunk.id,
|
|
@@ -2423,6 +2484,9 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
2423
2484
|
// Tool call is complete, keep it in the map for final processing
|
|
2424
2485
|
}
|
|
2425
2486
|
if (chunk.type === 'tool-call') {
|
|
2487
|
+
if (firstTokenTime === undefined) {
|
|
2488
|
+
firstTokenTime = Date.now();
|
|
2489
|
+
}
|
|
2426
2490
|
// Direct tool call chunk (complete tool call)
|
|
2427
2491
|
toolCallsInProgress.set(chunk.toolCallId, {
|
|
2428
2492
|
toolCallId: chunk.toolCallId,
|
|
@@ -2446,6 +2510,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
2446
2510
|
},
|
|
2447
2511
|
flush: async () => {
|
|
2448
2512
|
const latency = (Date.now() - startTime) / 1000;
|
|
2513
|
+
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
2449
2514
|
// Build content array similar to mapVercelOutput structure
|
|
2450
2515
|
const content = [];
|
|
2451
2516
|
if (reasoningText) {
|
|
@@ -2498,6 +2563,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
2498
2563
|
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
|
|
2499
2564
|
output: output,
|
|
2500
2565
|
latency,
|
|
2566
|
+
timeToFirstToken,
|
|
2501
2567
|
baseURL,
|
|
2502
2568
|
params: mergedParams,
|
|
2503
2569
|
httpStatus: 200,
|
|
@@ -2572,6 +2638,7 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
|
|
|
2572
2638
|
const contentBlocks = [];
|
|
2573
2639
|
const toolsInProgress = new Map();
|
|
2574
2640
|
let currentTextBlock = null;
|
|
2641
|
+
let firstTokenTime;
|
|
2575
2642
|
const usage = {
|
|
2576
2643
|
inputTokens: 0,
|
|
2577
2644
|
outputTokens: 0,
|
|
@@ -2594,6 +2661,9 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
|
|
|
2594
2661
|
};
|
|
2595
2662
|
contentBlocks.push(currentTextBlock);
|
|
2596
2663
|
} else if (chunk.content_block?.type === 'tool_use') {
|
|
2664
|
+
if (firstTokenTime === undefined) {
|
|
2665
|
+
firstTokenTime = Date.now();
|
|
2666
|
+
}
|
|
2597
2667
|
const toolBlock = {
|
|
2598
2668
|
type: 'function',
|
|
2599
2669
|
id: chunk.content_block.id,
|
|
@@ -2614,6 +2684,9 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
|
|
|
2614
2684
|
if ('delta' in chunk) {
|
|
2615
2685
|
if ('text' in chunk.delta) {
|
|
2616
2686
|
const delta = chunk.delta.text;
|
|
2687
|
+
if (firstTokenTime === undefined) {
|
|
2688
|
+
firstTokenTime = Date.now();
|
|
2689
|
+
}
|
|
2617
2690
|
accumulatedContent += delta;
|
|
2618
2691
|
if (currentTextBlock) {
|
|
2619
2692
|
currentTextBlock.text += delta;
|
|
@@ -2669,6 +2742,7 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
|
|
|
2669
2742
|
}
|
|
2670
2743
|
usage.rawUsage = lastRawUsage;
|
|
2671
2744
|
const latency = (Date.now() - startTime) / 1000;
|
|
2745
|
+
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
2672
2746
|
const availableTools = extractAvailableToolCalls('anthropic', anthropicParams);
|
|
2673
2747
|
// Format output to match non-streaming version
|
|
2674
2748
|
const formattedOutput = contentBlocks.length > 0 ? [{
|
|
@@ -2689,6 +2763,7 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
|
|
|
2689
2763
|
input: sanitizeAnthropic(mergeSystemPrompt(anthropicParams, 'anthropic')),
|
|
2690
2764
|
output: formattedOutput,
|
|
2691
2765
|
latency,
|
|
2766
|
+
timeToFirstToken,
|
|
2692
2767
|
baseURL: this.baseURL,
|
|
2693
2768
|
params: body,
|
|
2694
2769
|
httpStatus: 200,
|
|
@@ -2850,6 +2925,7 @@ class WrappedModels {
|
|
|
2850
2925
|
} = extractPosthogParams(params);
|
|
2851
2926
|
const startTime = Date.now();
|
|
2852
2927
|
const accumulatedContent = [];
|
|
2928
|
+
let firstTokenTime;
|
|
2853
2929
|
let usage = {
|
|
2854
2930
|
inputTokens: 0,
|
|
2855
2931
|
outputTokens: 0,
|
|
@@ -2859,6 +2935,10 @@ class WrappedModels {
|
|
|
2859
2935
|
try {
|
|
2860
2936
|
const stream = await this.client.models.generateContentStream(geminiParams);
|
|
2861
2937
|
for await (const chunk of stream) {
|
|
2938
|
+
// Track first token time when we get text content
|
|
2939
|
+
if (firstTokenTime === undefined && chunk.text) {
|
|
2940
|
+
firstTokenTime = Date.now();
|
|
2941
|
+
}
|
|
2862
2942
|
const chunkWebSearchCount = calculateGoogleWebSearchCount(chunk);
|
|
2863
2943
|
if (chunkWebSearchCount > 0 && chunkWebSearchCount > (usage.webSearchCount ?? 0)) {
|
|
2864
2944
|
usage.webSearchCount = chunkWebSearchCount;
|
|
@@ -2889,6 +2969,9 @@ class WrappedModels {
|
|
|
2889
2969
|
for (const part of candidate.content.parts) {
|
|
2890
2970
|
// Type-safe check for functionCall
|
|
2891
2971
|
if ('functionCall' in part) {
|
|
2972
|
+
if (firstTokenTime === undefined) {
|
|
2973
|
+
firstTokenTime = Date.now();
|
|
2974
|
+
}
|
|
2892
2975
|
const funcCall = part.functionCall;
|
|
2893
2976
|
if (funcCall?.name) {
|
|
2894
2977
|
accumulatedContent.push({
|
|
@@ -2919,6 +3002,7 @@ class WrappedModels {
|
|
|
2919
3002
|
yield chunk;
|
|
2920
3003
|
}
|
|
2921
3004
|
const latency = (Date.now() - startTime) / 1000;
|
|
3005
|
+
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
2922
3006
|
const availableTools = extractAvailableToolCalls('gemini', geminiParams);
|
|
2923
3007
|
// Format output similar to formatResponseGemini
|
|
2924
3008
|
const output = accumulatedContent.length > 0 ? [{
|
|
@@ -2933,6 +3017,7 @@ class WrappedModels {
|
|
|
2933
3017
|
input: this.formatInputForPostHog(geminiParams),
|
|
2934
3018
|
output,
|
|
2935
3019
|
latency,
|
|
3020
|
+
timeToFirstToken,
|
|
2936
3021
|
baseURL: 'https://generativelanguage.googleapis.com',
|
|
2937
3022
|
params: params,
|
|
2938
3023
|
httpStatus: 200,
|
|
@@ -4286,10 +4371,163 @@ class LangChainCallbackHandler extends BaseCallbackHandler {
|
|
|
4286
4371
|
}
|
|
4287
4372
|
}
|
|
4288
4373
|
|
|
4374
|
+
/// <reference lib="dom" />
|
|
4375
|
+
const DEFAULT_CACHE_TTL_SECONDS = 300; // 5 minutes
|
|
4376
|
+
function isPromptApiResponse(data) {
|
|
4377
|
+
return typeof data === 'object' && data !== null && 'prompt' in data && typeof data.prompt === 'string';
|
|
4378
|
+
}
|
|
4379
|
+
function isPromptsWithPostHog(options) {
|
|
4380
|
+
return 'posthog' in options;
|
|
4381
|
+
}
|
|
4382
|
+
/**
|
|
4383
|
+
* Prompts class for fetching and compiling LLM prompts from PostHog
|
|
4384
|
+
*
|
|
4385
|
+
* @example
|
|
4386
|
+
* ```ts
|
|
4387
|
+
* // With PostHog client
|
|
4388
|
+
* const prompts = new Prompts({ posthog })
|
|
4389
|
+
*
|
|
4390
|
+
* // Or with direct options (no PostHog client needed)
|
|
4391
|
+
* const prompts = new Prompts({
|
|
4392
|
+
* personalApiKey: 'phx_xxx',
|
|
4393
|
+
* host: 'https://us.i.posthog.com',
|
|
4394
|
+
* })
|
|
4395
|
+
*
|
|
4396
|
+
* // Fetch with caching and fallback
|
|
4397
|
+
* const template = await prompts.get('support-system-prompt', {
|
|
4398
|
+
* cacheTtlSeconds: 300,
|
|
4399
|
+
* fallback: 'You are a helpful assistant.',
|
|
4400
|
+
* })
|
|
4401
|
+
*
|
|
4402
|
+
* // Compile with variables
|
|
4403
|
+
* const systemPrompt = prompts.compile(template, {
|
|
4404
|
+
* company: 'Acme Corp',
|
|
4405
|
+
* tier: 'premium',
|
|
4406
|
+
* })
|
|
4407
|
+
* ```
|
|
4408
|
+
*/
|
|
4409
|
+
class Prompts {
|
|
4410
|
+
constructor(options) {
|
|
4411
|
+
this.cache = new Map();
|
|
4412
|
+
this.defaultCacheTtlSeconds = options.defaultCacheTtlSeconds ?? DEFAULT_CACHE_TTL_SECONDS;
|
|
4413
|
+
if (isPromptsWithPostHog(options)) {
|
|
4414
|
+
this.personalApiKey = options.posthog.options.personalApiKey ?? '';
|
|
4415
|
+
this.host = options.posthog.host;
|
|
4416
|
+
} else {
|
|
4417
|
+
// Direct options
|
|
4418
|
+
this.personalApiKey = options.personalApiKey;
|
|
4419
|
+
this.host = options.host ?? 'https://us.i.posthog.com';
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
/**
|
|
4423
|
+
* Fetch a prompt by name from the PostHog API
|
|
4424
|
+
*
|
|
4425
|
+
* @param name - The name of the prompt to fetch
|
|
4426
|
+
* @param options - Optional settings for caching and fallback
|
|
4427
|
+
* @returns The prompt string
|
|
4428
|
+
* @throws Error if the prompt cannot be fetched and no fallback is provided
|
|
4429
|
+
*/
|
|
4430
|
+
async get(name, options) {
|
|
4431
|
+
const cacheTtlSeconds = options?.cacheTtlSeconds ?? this.defaultCacheTtlSeconds;
|
|
4432
|
+
const fallback = options?.fallback;
|
|
4433
|
+
// Check cache first
|
|
4434
|
+
const cached = this.cache.get(name);
|
|
4435
|
+
const now = Date.now();
|
|
4436
|
+
if (cached) {
|
|
4437
|
+
const isFresh = now - cached.fetchedAt < cacheTtlSeconds * 1000;
|
|
4438
|
+
if (isFresh) {
|
|
4439
|
+
return cached.prompt;
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
// Try to fetch from API
|
|
4443
|
+
try {
|
|
4444
|
+
const prompt = await this.fetchPromptFromApi(name);
|
|
4445
|
+
const fetchedAt = Date.now();
|
|
4446
|
+
// Update cache
|
|
4447
|
+
this.cache.set(name, {
|
|
4448
|
+
prompt,
|
|
4449
|
+
fetchedAt
|
|
4450
|
+
});
|
|
4451
|
+
return prompt;
|
|
4452
|
+
} catch (error) {
|
|
4453
|
+
// Fallback order:
|
|
4454
|
+
// 1. Return stale cache (with warning)
|
|
4455
|
+
if (cached) {
|
|
4456
|
+
console.warn(`[PostHog Prompts] Failed to fetch prompt "${name}", using stale cache:`, error);
|
|
4457
|
+
return cached.prompt;
|
|
4458
|
+
}
|
|
4459
|
+
// 2. Return fallback (with warning)
|
|
4460
|
+
if (fallback !== undefined) {
|
|
4461
|
+
console.warn(`[PostHog Prompts] Failed to fetch prompt "${name}", using fallback:`, error);
|
|
4462
|
+
return fallback;
|
|
4463
|
+
}
|
|
4464
|
+
// 3. Throw error
|
|
4465
|
+
throw error;
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
/**
|
|
4469
|
+
* Compile a prompt template with variable substitution
|
|
4470
|
+
*
|
|
4471
|
+
* Variables in the format `{{variableName}}` will be replaced with values from the variables object.
|
|
4472
|
+
* Unmatched variables are left unchanged.
|
|
4473
|
+
*
|
|
4474
|
+
* @param prompt - The prompt template string
|
|
4475
|
+
* @param variables - Object containing variable values
|
|
4476
|
+
* @returns The compiled prompt string
|
|
4477
|
+
*/
|
|
4478
|
+
compile(prompt, variables) {
|
|
4479
|
+
return prompt.replace(/\{\{([\w.-]+)\}\}/g, (match, variableName) => {
|
|
4480
|
+
if (variableName in variables) {
|
|
4481
|
+
return String(variables[variableName]);
|
|
4482
|
+
}
|
|
4483
|
+
return match;
|
|
4484
|
+
});
|
|
4485
|
+
}
|
|
4486
|
+
/**
|
|
4487
|
+
* Clear the cache for a specific prompt or all prompts
|
|
4488
|
+
*
|
|
4489
|
+
* @param name - Optional prompt name to clear. If not provided, clears all cached prompts.
|
|
4490
|
+
*/
|
|
4491
|
+
clearCache(name) {
|
|
4492
|
+
if (name !== undefined) {
|
|
4493
|
+
this.cache.delete(name);
|
|
4494
|
+
} else {
|
|
4495
|
+
this.cache.clear();
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
async fetchPromptFromApi(name) {
|
|
4499
|
+
if (!this.personalApiKey) {
|
|
4500
|
+
throw new Error('[PostHog Prompts] personalApiKey is required to fetch prompts. ' + 'Please provide it when initializing the Prompts instance.');
|
|
4501
|
+
}
|
|
4502
|
+
const url = `${this.host}/api/projects/@current/llm_prompts/name/${encodeURIComponent(name)}/`;
|
|
4503
|
+
const response = await fetch(url, {
|
|
4504
|
+
method: 'GET',
|
|
4505
|
+
headers: {
|
|
4506
|
+
Authorization: `Bearer ${this.personalApiKey}`
|
|
4507
|
+
}
|
|
4508
|
+
});
|
|
4509
|
+
if (!response.ok) {
|
|
4510
|
+
if (response.status === 404) {
|
|
4511
|
+
throw new Error(`[PostHog Prompts] Prompt "${name}" not found`);
|
|
4512
|
+
}
|
|
4513
|
+
if (response.status === 403) {
|
|
4514
|
+
throw new Error(`[PostHog Prompts] Access denied for prompt "${name}". ` + 'Check that your personalApiKey has the correct permissions and the LLM prompts feature is enabled.');
|
|
4515
|
+
}
|
|
4516
|
+
throw new Error(`[PostHog Prompts] Failed to fetch prompt "${name}": HTTP ${response.status}`);
|
|
4517
|
+
}
|
|
4518
|
+
const data = await response.json();
|
|
4519
|
+
if (!isPromptApiResponse(data)) {
|
|
4520
|
+
throw new Error(`[PostHog Prompts] Invalid response format for prompt "${name}"`);
|
|
4521
|
+
}
|
|
4522
|
+
return data.prompt;
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4289
4526
|
exports.Anthropic = PostHogAnthropic;
|
|
4290
4527
|
exports.AzureOpenAI = PostHogAzureOpenAI;
|
|
4291
4528
|
exports.GoogleGenAI = PostHogGoogleGenAI;
|
|
4292
4529
|
exports.LangChainCallbackHandler = LangChainCallbackHandler;
|
|
4293
4530
|
exports.OpenAI = PostHogOpenAI;
|
|
4531
|
+
exports.Prompts = Prompts;
|
|
4294
4532
|
exports.withTracing = wrapVercelLanguageModel;
|
|
4295
4533
|
//# sourceMappingURL=index.cjs.map
|