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