@posthog/ai 8.6.0 → 8.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anthropic/index.cjs +10 -5
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +11 -6
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +10 -5
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.mjs +11 -6
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +126 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +127 -79
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +31 -32
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.mjs +31 -32
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/openai/index.cjs +10 -5
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +11 -6
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openai-agents/index.cjs +24 -4
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.d.ts +1 -1
- package/dist/openai-agents/index.mjs +24 -4
- package/dist/openai-agents/index.mjs.map +1 -1
- package/dist/otel/index.cjs +5 -3
- package/dist/otel/index.cjs.map +1 -1
- package/dist/otel/index.mjs +5 -3
- package/dist/otel/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +127 -81
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +128 -82
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +6 -6
package/dist/vercel/index.cjs
CHANGED
|
@@ -397,7 +397,7 @@ function sanitizeValues(obj) {
|
|
|
397
397
|
return jsonSafe;
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
-
var version = "8.6.
|
|
400
|
+
var version = "8.6.2";
|
|
401
401
|
|
|
402
402
|
const DEFAULT_MAX_DEPTH = 3;
|
|
403
403
|
const MAX_STACK_LINES = 20;
|
|
@@ -516,8 +516,13 @@ const captureAiGeneration = async (client, options) => {
|
|
|
516
516
|
const eventType = options.eventType ?? AIEvent.Generation;
|
|
517
517
|
const privacyMode = options.privacyMode ?? false;
|
|
518
518
|
const usage = options.usage ?? {};
|
|
519
|
-
|
|
520
|
-
|
|
519
|
+
|
|
520
|
+
// Check privacy before reading or traversing input/output. Besides avoiding
|
|
521
|
+
// needless work, this ensures hostile getters/proxies cannot observe a value
|
|
522
|
+
// that the caller explicitly requested us to redact.
|
|
523
|
+
const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
|
|
524
|
+
const safeInput = shouldRedact ? null : core.toJsonSafeValue(options.input);
|
|
525
|
+
const safeOutput = shouldRedact ? null : core.toJsonSafeValue(options.output);
|
|
521
526
|
let httpStatus = options.httpStatus;
|
|
522
527
|
let errorData = {};
|
|
523
528
|
if (options.error) {
|
|
@@ -578,8 +583,8 @@ const captureAiGeneration = async (client, options) => {
|
|
|
578
583
|
$ai_provider: options.providerOverride ?? options.provider,
|
|
579
584
|
$ai_model: options.modelOverride ?? options.model,
|
|
580
585
|
$ai_model_parameters: options.modelParameters ?? {},
|
|
581
|
-
$ai_input:
|
|
582
|
-
$ai_output_choices:
|
|
586
|
+
$ai_input: safeInput,
|
|
587
|
+
$ai_output_choices: safeOutput,
|
|
583
588
|
$ai_http_status: httpStatus,
|
|
584
589
|
$ai_input_tokens: usage.inputTokens ?? 0,
|
|
585
590
|
...(usage.outputTokens !== undefined ? {
|
|
@@ -1121,86 +1126,97 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1121
1126
|
|
|
1122
1127
|
// Map to track in-progress tool calls
|
|
1123
1128
|
const toolCallsInProgress = new Map();
|
|
1129
|
+
const captureStreamGeneration = async captureOptions => {
|
|
1130
|
+
try {
|
|
1131
|
+
await captureAiGeneration(phClient, captureOptions);
|
|
1132
|
+
} catch (error) {
|
|
1133
|
+
// Telemetry must never change the provider stream's behavior.
|
|
1134
|
+
console.warn('[PostHog AI] Failed to capture Vercel stream telemetry:', error);
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1124
1137
|
try {
|
|
1125
1138
|
const {
|
|
1126
1139
|
stream,
|
|
1127
1140
|
...rest
|
|
1128
1141
|
} = await model.doStream(params);
|
|
1129
|
-
const
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1142
|
+
const reader = stream.getReader();
|
|
1143
|
+
let inBandError;
|
|
1144
|
+
let hasInBandError = false;
|
|
1145
|
+
let finalizationPromise;
|
|
1146
|
+
const observeChunk = chunk => {
|
|
1147
|
+
// Handle streaming patterns - compatible with both V2 and V3
|
|
1148
|
+
if (chunk.type === 'text-delta') {
|
|
1149
|
+
if (firstTokenTime === undefined) {
|
|
1150
|
+
firstTokenTime = Date.now();
|
|
1137
1151
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1152
|
+
generatedText += chunk.delta;
|
|
1153
|
+
}
|
|
1154
|
+
if (chunk.type === 'reasoning-delta') {
|
|
1155
|
+
if (firstTokenTime === undefined) {
|
|
1156
|
+
firstTokenTime = Date.now();
|
|
1143
1157
|
}
|
|
1158
|
+
reasoningText += chunk.delta;
|
|
1159
|
+
}
|
|
1144
1160
|
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
}
|
|
1150
|
-
// Initialize a new tool call
|
|
1151
|
-
toolCallsInProgress.set(chunk.id, {
|
|
1152
|
-
toolCallId: chunk.id,
|
|
1153
|
-
toolName: chunk.toolName,
|
|
1154
|
-
input: ''
|
|
1155
|
-
});
|
|
1161
|
+
// Handle tool call chunks
|
|
1162
|
+
if (chunk.type === 'tool-input-start') {
|
|
1163
|
+
if (firstTokenTime === undefined) {
|
|
1164
|
+
firstTokenTime = Date.now();
|
|
1156
1165
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
+
toolCallsInProgress.set(chunk.id, {
|
|
1167
|
+
toolCallId: chunk.id,
|
|
1168
|
+
toolName: chunk.toolName,
|
|
1169
|
+
input: ''
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
if (chunk.type === 'tool-input-delta') {
|
|
1173
|
+
const toolCall = toolCallsInProgress.get(chunk.id);
|
|
1174
|
+
if (toolCall) {
|
|
1175
|
+
toolCall.input += chunk.delta;
|
|
1166
1176
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
// Direct tool call chunk (complete tool call)
|
|
1172
|
-
toolCallsInProgress.set(chunk.toolCallId, {
|
|
1173
|
-
toolCallId: chunk.toolCallId,
|
|
1174
|
-
toolName: chunk.toolName,
|
|
1175
|
-
input: chunk.input
|
|
1176
|
-
});
|
|
1177
|
+
}
|
|
1178
|
+
if (chunk.type === 'tool-call') {
|
|
1179
|
+
if (firstTokenTime === undefined) {
|
|
1180
|
+
firstTokenTime = Date.now();
|
|
1177
1181
|
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1182
|
+
toolCallsInProgress.set(chunk.toolCallId, {
|
|
1183
|
+
toolCallId: chunk.toolCallId,
|
|
1184
|
+
toolName: chunk.toolName,
|
|
1185
|
+
input: chunk.input
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
if (chunk.type === 'error') {
|
|
1189
|
+
hasInBandError = true;
|
|
1190
|
+
inBandError = chunk.error;
|
|
1191
|
+
}
|
|
1192
|
+
if (chunk.type === 'finish') {
|
|
1193
|
+
providerMetadata = chunk.providerMetadata;
|
|
1194
|
+
const chunkUsage = chunk.usage || {};
|
|
1195
|
+
const additionalTokenValues = extractAdditionalTokenValues(providerMetadata, chunkUsage);
|
|
1196
|
+
usage = {
|
|
1197
|
+
inputTokens: extractTokenCount(chunk.usage?.inputTokens),
|
|
1198
|
+
outputTokens: extractTokenCount(chunk.usage?.outputTokens),
|
|
1199
|
+
reasoningTokens: extractReasoningTokens(chunkUsage),
|
|
1200
|
+
cacheReadInputTokens: extractCacheReadTokens(chunkUsage),
|
|
1201
|
+
...additionalTokenValues
|
|
1202
|
+
};
|
|
1189
1203
|
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
}
|
|
1204
|
+
// Extract finish reason - V2 returns a string, V3 returns an object with .unified
|
|
1205
|
+
const rawFinishReason = chunk.finishReason;
|
|
1206
|
+
if (typeof rawFinishReason === 'string') {
|
|
1207
|
+
stopReason = rawFinishReason;
|
|
1208
|
+
} else if (rawFinishReason && typeof rawFinishReason === 'object' && 'unified' in rawFinishReason) {
|
|
1209
|
+
stopReason = String(rawFinishReason.unified);
|
|
1197
1210
|
}
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
const finalize = (terminalError, isError = false) => {
|
|
1214
|
+
if (finalizationPromise) {
|
|
1215
|
+
return finalizationPromise;
|
|
1216
|
+
}
|
|
1217
|
+
finalizationPromise = (async () => {
|
|
1201
1218
|
const latency = (Date.now() - startTime) / 1000;
|
|
1202
1219
|
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1203
|
-
// Build content array similar to mapVercelOutput structure
|
|
1204
1220
|
const content = [];
|
|
1205
1221
|
if (reasoningText) {
|
|
1206
1222
|
content.push({
|
|
@@ -1214,8 +1230,6 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1214
1230
|
text: truncate(generatedText)
|
|
1215
1231
|
});
|
|
1216
1232
|
}
|
|
1217
|
-
|
|
1218
|
-
// Add completed tool calls to content
|
|
1219
1233
|
for (const toolCall of toolCallsInProgress.values()) {
|
|
1220
1234
|
if (toolCall.toolName) {
|
|
1221
1235
|
content.push({
|
|
@@ -1228,15 +1242,11 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1228
1242
|
});
|
|
1229
1243
|
}
|
|
1230
1244
|
}
|
|
1231
|
-
|
|
1232
|
-
// Structure output like mapVercelOutput does
|
|
1233
1245
|
const output = content.length > 0 ? [{
|
|
1234
1246
|
role: 'assistant',
|
|
1235
1247
|
content: content.length === 1 && content[0].type === 'text' ? content[0].text : content
|
|
1236
1248
|
}] : [];
|
|
1237
1249
|
const webSearchCount = extractWebSearchCount(providerMetadata, usage);
|
|
1238
|
-
|
|
1239
|
-
// Update usage with web search count and raw metadata
|
|
1240
1250
|
const finalUsage = {
|
|
1241
1251
|
...usage,
|
|
1242
1252
|
webSearchCount,
|
|
@@ -1246,29 +1256,65 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1246
1256
|
}
|
|
1247
1257
|
};
|
|
1248
1258
|
adjustAnthropicV3CacheTokens(model, modelId, provider, finalUsage);
|
|
1249
|
-
|
|
1259
|
+
const finishError = stopReason === 'error' ? new Error('Vercel AI SDK stream finished with an error') : undefined;
|
|
1260
|
+
const error = isError ? terminalError ?? new Error('Vercel AI SDK stream failed') : hasInBandError ? inBandError ?? new Error('Vercel AI SDK stream emitted an error chunk') : finishError;
|
|
1261
|
+
await captureStreamGeneration({
|
|
1250
1262
|
...baseOptions,
|
|
1251
1263
|
model: modelId,
|
|
1252
1264
|
provider: provider,
|
|
1253
1265
|
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
|
|
1254
|
-
output
|
|
1266
|
+
output,
|
|
1255
1267
|
latency,
|
|
1256
1268
|
timeToFirstToken,
|
|
1257
1269
|
baseURL,
|
|
1258
1270
|
modelParameters: getModelParams(mergedParams),
|
|
1259
|
-
httpStatus: 200,
|
|
1271
|
+
httpStatus: error ? undefined : 200,
|
|
1260
1272
|
usage: finalUsage,
|
|
1261
1273
|
stopReason,
|
|
1274
|
+
error,
|
|
1262
1275
|
tools: availableTools
|
|
1263
1276
|
});
|
|
1277
|
+
})().catch(error => {
|
|
1278
|
+
// Building telemetry must not change the provider stream's behavior.
|
|
1279
|
+
console.warn('[PostHog AI] Failed to capture Vercel stream telemetry:', error);
|
|
1280
|
+
});
|
|
1281
|
+
return finalizationPromise;
|
|
1282
|
+
};
|
|
1283
|
+
const instrumentedStream = new ReadableStream({
|
|
1284
|
+
async pull(controller) {
|
|
1285
|
+
let result;
|
|
1286
|
+
try {
|
|
1287
|
+
result = await reader.read();
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
void finalize(error, true);
|
|
1290
|
+
controller.error(error);
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
if (result.done) {
|
|
1294
|
+
controller.close();
|
|
1295
|
+
void finalize();
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
try {
|
|
1299
|
+
observeChunk(result.value);
|
|
1300
|
+
} catch {
|
|
1301
|
+
// Instrumentation must not alter or suppress provider chunks.
|
|
1302
|
+
}
|
|
1303
|
+
controller.enqueue(result.value);
|
|
1304
|
+
},
|
|
1305
|
+
cancel(reason) {
|
|
1306
|
+
void finalize(reason ?? new Error('Vercel AI SDK stream was cancelled'), true);
|
|
1307
|
+
return reader.cancel(reason);
|
|
1264
1308
|
}
|
|
1309
|
+
}, {
|
|
1310
|
+
highWaterMark: 0
|
|
1265
1311
|
});
|
|
1266
1312
|
return {
|
|
1267
|
-
stream:
|
|
1313
|
+
stream: instrumentedStream,
|
|
1268
1314
|
...rest
|
|
1269
1315
|
};
|
|
1270
1316
|
} catch (error) {
|
|
1271
|
-
await
|
|
1317
|
+
await captureStreamGeneration({
|
|
1272
1318
|
...baseOptions,
|
|
1273
1319
|
model: modelId,
|
|
1274
1320
|
provider: provider,
|