@posthog/ai 8.6.1 → 8.6.3
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 +334 -108
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +335 -109
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +121 -110
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.mjs +122 -111
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +237 -183
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +238 -184
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +19 -32
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.d.ts +1 -0
- package/dist/langchain/index.mjs +19 -32
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/openai/index.cjs +351 -125
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +352 -126
- 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 +238 -186
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +239 -187
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -380,7 +380,7 @@ function sanitizeValues(obj) {
|
|
|
380
380
|
return jsonSafe;
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
var version = "8.6.
|
|
383
|
+
var version = "8.6.3";
|
|
384
384
|
|
|
385
385
|
const DEFAULT_MAX_DEPTH = 3;
|
|
386
386
|
const MAX_STACK_LINES = 20;
|
|
@@ -482,120 +482,130 @@ const warnIfPostHogAiGateway = baseURL => {
|
|
|
482
482
|
* so callers can re-throw the original error reference safely.
|
|
483
483
|
*/
|
|
484
484
|
const captureAiGeneration = async (client, options) => {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
warnIfPostHogAiGateway(options.baseURL);
|
|
489
|
-
const traceId = options.traceId ?? uuid.v4();
|
|
490
|
-
const eventType = options.eventType ?? exports.AIEvent.Generation;
|
|
491
|
-
const privacyMode = options.privacyMode ?? false;
|
|
492
|
-
const usage = options.usage ?? {};
|
|
493
|
-
const safeInput = sanitizeValues(options.input);
|
|
494
|
-
const safeOutput = sanitizeValues(options.output);
|
|
495
|
-
let httpStatus = options.httpStatus;
|
|
496
|
-
let errorData = {};
|
|
497
|
-
if (options.error) {
|
|
498
|
-
if (httpStatus === undefined) {
|
|
499
|
-
if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
|
|
500
|
-
httpStatus = options.error.status;
|
|
501
|
-
} else {
|
|
502
|
-
httpStatus = 500;
|
|
503
|
-
}
|
|
485
|
+
try {
|
|
486
|
+
if (!client.capture) {
|
|
487
|
+
return;
|
|
504
488
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
489
|
+
warnIfPostHogAiGateway(options.baseURL);
|
|
490
|
+
const traceId = options.traceId ?? uuid.v4();
|
|
491
|
+
const eventType = options.eventType ?? exports.AIEvent.Generation;
|
|
492
|
+
const privacyMode = options.privacyMode ?? false;
|
|
493
|
+
const usage = options.usage ?? {};
|
|
494
|
+
// Check privacy before reading or traversing input/output. Besides avoiding
|
|
495
|
+
// needless work, this ensures hostile getters/proxies cannot observe a value
|
|
496
|
+
// that the caller explicitly requested us to redact.
|
|
497
|
+
const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
|
|
498
|
+
const safeInput = shouldRedact ? null : core.toJsonSafeValue(options.input);
|
|
499
|
+
const safeOutput = shouldRedact ? null : core.toJsonSafeValue(options.output);
|
|
500
|
+
let httpStatus = options.httpStatus;
|
|
501
|
+
let errorData = {};
|
|
502
|
+
if (options.error) {
|
|
503
|
+
if (httpStatus === undefined) {
|
|
504
|
+
if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
|
|
505
|
+
httpStatus = options.error.status;
|
|
506
|
+
} else {
|
|
507
|
+
httpStatus = 500;
|
|
508
|
+
}
|
|
513
509
|
}
|
|
510
|
+
let exceptionId;
|
|
511
|
+
if (client.options?.enableExceptionAutocapture) {
|
|
512
|
+
exceptionId = core.uuidv7();
|
|
513
|
+
client.captureException(options.error, undefined, {
|
|
514
|
+
$ai_trace_id: traceId
|
|
515
|
+
}, exceptionId);
|
|
516
|
+
if (typeof options.error === 'object') {
|
|
517
|
+
;
|
|
518
|
+
options.error.__posthog_previously_captured_error = true;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
errorData = {
|
|
522
|
+
$ai_is_error: true,
|
|
523
|
+
$ai_error: stringifyError(options.error),
|
|
524
|
+
$exception_event_id: exceptionId
|
|
525
|
+
};
|
|
514
526
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
527
|
+
httpStatus = httpStatus ?? 200;
|
|
528
|
+
let costOverrideData = {};
|
|
529
|
+
if (options.costOverride) {
|
|
530
|
+
const inputCostUSD = (options.costOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
|
|
531
|
+
const outputCostUSD = (options.costOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
|
|
532
|
+
costOverrideData = {
|
|
533
|
+
$ai_input_cost_usd: inputCostUSD,
|
|
534
|
+
$ai_output_cost_usd: outputCostUSD,
|
|
535
|
+
$ai_total_cost_usd: inputCostUSD + outputCostUSD
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
const additionalTokenValues = {
|
|
539
|
+
...(usage.reasoningTokens ? {
|
|
540
|
+
$ai_reasoning_tokens: usage.reasoningTokens
|
|
541
|
+
} : {}),
|
|
542
|
+
...(usage.cacheReadInputTokens ? {
|
|
543
|
+
$ai_cache_read_input_tokens: usage.cacheReadInputTokens
|
|
544
|
+
} : {}),
|
|
545
|
+
...(usage.cacheCreationInputTokens ? {
|
|
546
|
+
$ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
|
|
547
|
+
} : {}),
|
|
548
|
+
...(usage.webSearchCount ? {
|
|
549
|
+
$ai_web_search_count: usage.webSearchCount
|
|
550
|
+
} : {}),
|
|
551
|
+
...(usage.rawUsage ? {
|
|
552
|
+
$ai_usage: usage.rawUsage
|
|
553
|
+
} : {})
|
|
519
554
|
};
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
$
|
|
528
|
-
$
|
|
529
|
-
$
|
|
555
|
+
const properties = {
|
|
556
|
+
$ai_lib: 'posthog-ai',
|
|
557
|
+
$ai_lib_version: version,
|
|
558
|
+
$ai_provider: options.providerOverride ?? options.provider,
|
|
559
|
+
$ai_model: options.modelOverride ?? options.model,
|
|
560
|
+
$ai_model_parameters: options.modelParameters ?? {},
|
|
561
|
+
$ai_input: safeInput,
|
|
562
|
+
$ai_output_choices: safeOutput,
|
|
563
|
+
$ai_http_status: httpStatus,
|
|
564
|
+
$ai_input_tokens: usage.inputTokens ?? 0,
|
|
565
|
+
...(usage.outputTokens !== undefined ? {
|
|
566
|
+
$ai_output_tokens: usage.outputTokens
|
|
567
|
+
} : {}),
|
|
568
|
+
...additionalTokenValues,
|
|
569
|
+
$ai_latency: options.latency ?? 0,
|
|
570
|
+
...(options.timeToFirstToken !== undefined ? {
|
|
571
|
+
$ai_time_to_first_token: options.timeToFirstToken
|
|
572
|
+
} : {}),
|
|
573
|
+
$ai_trace_id: traceId,
|
|
574
|
+
$ai_base_url: options.baseURL ?? '',
|
|
575
|
+
...options.properties,
|
|
576
|
+
$ai_tokens_source: getTokensSource(options.properties),
|
|
577
|
+
...(options.distinctId ? {} : {
|
|
578
|
+
$process_person_profile: false
|
|
579
|
+
}),
|
|
580
|
+
...(options.stopReason ? {
|
|
581
|
+
$ai_stop_reason: options.stopReason
|
|
582
|
+
} : {}),
|
|
583
|
+
...(options.tools ? {
|
|
584
|
+
$ai_tools: options.tools
|
|
585
|
+
} : {}),
|
|
586
|
+
...(options.completionId ? {
|
|
587
|
+
$ai_completion_id: options.completionId
|
|
588
|
+
} : {}),
|
|
589
|
+
...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
|
|
590
|
+
$ai_provider_metadata: options.providerMetadata
|
|
591
|
+
} : {}),
|
|
592
|
+
...errorData,
|
|
593
|
+
...costOverrideData
|
|
530
594
|
};
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
...(usage.rawUsage ? {
|
|
546
|
-
$ai_usage: usage.rawUsage
|
|
547
|
-
} : {})
|
|
548
|
-
};
|
|
549
|
-
const properties = {
|
|
550
|
-
$ai_lib: 'posthog-ai',
|
|
551
|
-
$ai_lib_version: version,
|
|
552
|
-
$ai_provider: options.providerOverride ?? options.provider,
|
|
553
|
-
$ai_model: options.modelOverride ?? options.model,
|
|
554
|
-
$ai_model_parameters: options.modelParameters ?? {},
|
|
555
|
-
$ai_input: withPrivacyMode(client, privacyMode, safeInput),
|
|
556
|
-
$ai_output_choices: withPrivacyMode(client, privacyMode, safeOutput),
|
|
557
|
-
$ai_http_status: httpStatus,
|
|
558
|
-
$ai_input_tokens: usage.inputTokens ?? 0,
|
|
559
|
-
...(usage.outputTokens !== undefined ? {
|
|
560
|
-
$ai_output_tokens: usage.outputTokens
|
|
561
|
-
} : {}),
|
|
562
|
-
...additionalTokenValues,
|
|
563
|
-
$ai_latency: options.latency ?? 0,
|
|
564
|
-
...(options.timeToFirstToken !== undefined ? {
|
|
565
|
-
$ai_time_to_first_token: options.timeToFirstToken
|
|
566
|
-
} : {}),
|
|
567
|
-
$ai_trace_id: traceId,
|
|
568
|
-
$ai_base_url: options.baseURL ?? '',
|
|
569
|
-
...options.properties,
|
|
570
|
-
$ai_tokens_source: getTokensSource(options.properties),
|
|
571
|
-
...(options.distinctId ? {} : {
|
|
572
|
-
$process_person_profile: false
|
|
573
|
-
}),
|
|
574
|
-
...(options.stopReason ? {
|
|
575
|
-
$ai_stop_reason: options.stopReason
|
|
576
|
-
} : {}),
|
|
577
|
-
...(options.tools ? {
|
|
578
|
-
$ai_tools: options.tools
|
|
579
|
-
} : {}),
|
|
580
|
-
...(options.completionId ? {
|
|
581
|
-
$ai_completion_id: options.completionId
|
|
582
|
-
} : {}),
|
|
583
|
-
...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
|
|
584
|
-
$ai_provider_metadata: options.providerMetadata
|
|
585
|
-
} : {}),
|
|
586
|
-
...errorData,
|
|
587
|
-
...costOverrideData
|
|
588
|
-
};
|
|
589
|
-
const event = {
|
|
590
|
-
distinctId: options.distinctId ?? traceId,
|
|
591
|
-
event: eventType,
|
|
592
|
-
properties,
|
|
593
|
-
groups: options.groups
|
|
594
|
-
};
|
|
595
|
-
if (options.captureImmediate) {
|
|
596
|
-
await client.captureImmediate(event);
|
|
597
|
-
} else {
|
|
598
|
-
client.capture(event);
|
|
595
|
+
const event = {
|
|
596
|
+
distinctId: options.distinctId ?? traceId,
|
|
597
|
+
event: eventType,
|
|
598
|
+
properties,
|
|
599
|
+
groups: options.groups
|
|
600
|
+
};
|
|
601
|
+
if (options.captureImmediate) {
|
|
602
|
+
await client.captureImmediate(event);
|
|
603
|
+
} else {
|
|
604
|
+
client.capture(event);
|
|
605
|
+
}
|
|
606
|
+
} catch (error) {
|
|
607
|
+
// Telemetry failures must never affect the instrumented provider call.
|
|
608
|
+
console.warn('[PostHog AI] Failed to capture generation telemetry:', error);
|
|
599
609
|
}
|
|
600
610
|
};
|
|
601
611
|
|
|
@@ -1070,84 +1080,95 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1070
1080
|
const baseURL = extractBaseURL(model);
|
|
1071
1081
|
// Map to track in-progress tool calls
|
|
1072
1082
|
const toolCallsInProgress = new Map();
|
|
1083
|
+
const captureStreamGeneration = async captureOptions => {
|
|
1084
|
+
try {
|
|
1085
|
+
await captureAiGeneration(phClient, captureOptions);
|
|
1086
|
+
} catch (error) {
|
|
1087
|
+
// Telemetry must never change the provider stream's behavior.
|
|
1088
|
+
console.warn('[PostHog AI] Failed to capture Vercel stream telemetry:', error);
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1073
1091
|
try {
|
|
1074
1092
|
const {
|
|
1075
1093
|
stream,
|
|
1076
1094
|
...rest
|
|
1077
1095
|
} = await model.doStream(params);
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
if (chunk.type === 'reasoning-delta') {
|
|
1088
|
-
if (firstTokenTime === undefined) {
|
|
1089
|
-
firstTokenTime = Date.now();
|
|
1090
|
-
}
|
|
1091
|
-
reasoningText += chunk.delta;
|
|
1096
|
+
const reader = stream.getReader();
|
|
1097
|
+
let inBandError;
|
|
1098
|
+
let hasInBandError = false;
|
|
1099
|
+
let finalizationPromise;
|
|
1100
|
+
const observeChunk = chunk => {
|
|
1101
|
+
// Handle streaming patterns - compatible with both V2 and V3
|
|
1102
|
+
if (chunk.type === 'text-delta') {
|
|
1103
|
+
if (firstTokenTime === undefined) {
|
|
1104
|
+
firstTokenTime = Date.now();
|
|
1092
1105
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
// Initialize a new tool call
|
|
1099
|
-
toolCallsInProgress.set(chunk.id, {
|
|
1100
|
-
toolCallId: chunk.id,
|
|
1101
|
-
toolName: chunk.toolName,
|
|
1102
|
-
input: ''
|
|
1103
|
-
});
|
|
1106
|
+
generatedText += chunk.delta;
|
|
1107
|
+
}
|
|
1108
|
+
if (chunk.type === 'reasoning-delta') {
|
|
1109
|
+
if (firstTokenTime === undefined) {
|
|
1110
|
+
firstTokenTime = Date.now();
|
|
1104
1111
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1112
|
+
reasoningText += chunk.delta;
|
|
1113
|
+
}
|
|
1114
|
+
// Handle tool call chunks
|
|
1115
|
+
if (chunk.type === 'tool-input-start') {
|
|
1116
|
+
if (firstTokenTime === undefined) {
|
|
1117
|
+
firstTokenTime = Date.now();
|
|
1111
1118
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1119
|
+
toolCallsInProgress.set(chunk.id, {
|
|
1120
|
+
toolCallId: chunk.id,
|
|
1121
|
+
toolName: chunk.toolName,
|
|
1122
|
+
input: ''
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
if (chunk.type === 'tool-input-delta') {
|
|
1126
|
+
const toolCall = toolCallsInProgress.get(chunk.id);
|
|
1127
|
+
if (toolCall) {
|
|
1128
|
+
toolCall.input += chunk.delta;
|
|
1114
1129
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
// Direct tool call chunk (complete tool call)
|
|
1120
|
-
toolCallsInProgress.set(chunk.toolCallId, {
|
|
1121
|
-
toolCallId: chunk.toolCallId,
|
|
1122
|
-
toolName: chunk.toolName,
|
|
1123
|
-
input: chunk.input
|
|
1124
|
-
});
|
|
1130
|
+
}
|
|
1131
|
+
if (chunk.type === 'tool-call') {
|
|
1132
|
+
if (firstTokenTime === undefined) {
|
|
1133
|
+
firstTokenTime = Date.now();
|
|
1125
1134
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1135
|
+
toolCallsInProgress.set(chunk.toolCallId, {
|
|
1136
|
+
toolCallId: chunk.toolCallId,
|
|
1137
|
+
toolName: chunk.toolName,
|
|
1138
|
+
input: chunk.input
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
if (chunk.type === 'error') {
|
|
1142
|
+
hasInBandError = true;
|
|
1143
|
+
inBandError = chunk.error;
|
|
1144
|
+
}
|
|
1145
|
+
if (chunk.type === 'finish') {
|
|
1146
|
+
providerMetadata = chunk.providerMetadata;
|
|
1147
|
+
const chunkUsage = chunk.usage || {};
|
|
1148
|
+
const additionalTokenValues = extractAdditionalTokenValues(providerMetadata, chunkUsage);
|
|
1149
|
+
usage = {
|
|
1150
|
+
inputTokens: extractTokenCount(chunk.usage?.inputTokens),
|
|
1151
|
+
outputTokens: extractTokenCount(chunk.usage?.outputTokens),
|
|
1152
|
+
reasoningTokens: extractReasoningTokens(chunkUsage),
|
|
1153
|
+
cacheReadInputTokens: extractCacheReadTokens(chunkUsage),
|
|
1154
|
+
...additionalTokenValues
|
|
1155
|
+
};
|
|
1156
|
+
// Extract finish reason - V2 returns a string, V3 returns an object with .unified
|
|
1157
|
+
const rawFinishReason = chunk.finishReason;
|
|
1158
|
+
if (typeof rawFinishReason === 'string') {
|
|
1159
|
+
stopReason = rawFinishReason;
|
|
1160
|
+
} else if (rawFinishReason && typeof rawFinishReason === 'object' && 'unified' in rawFinishReason) {
|
|
1161
|
+
stopReason = String(rawFinishReason.unified);
|
|
1144
1162
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
const finalize = (terminalError, isError = false) => {
|
|
1166
|
+
if (finalizationPromise) {
|
|
1167
|
+
return finalizationPromise;
|
|
1168
|
+
}
|
|
1169
|
+
finalizationPromise = (async () => {
|
|
1148
1170
|
const latency = (Date.now() - startTime) / 1000;
|
|
1149
1171
|
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1150
|
-
// Build content array similar to mapVercelOutput structure
|
|
1151
1172
|
const content = [];
|
|
1152
1173
|
if (reasoningText) {
|
|
1153
1174
|
content.push({
|
|
@@ -1161,7 +1182,6 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1161
1182
|
text: truncate(generatedText)
|
|
1162
1183
|
});
|
|
1163
1184
|
}
|
|
1164
|
-
// Add completed tool calls to content
|
|
1165
1185
|
for (const toolCall of toolCallsInProgress.values()) {
|
|
1166
1186
|
if (toolCall.toolName) {
|
|
1167
1187
|
content.push({
|
|
@@ -1174,13 +1194,11 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1174
1194
|
});
|
|
1175
1195
|
}
|
|
1176
1196
|
}
|
|
1177
|
-
// Structure output like mapVercelOutput does
|
|
1178
1197
|
const output = content.length > 0 ? [{
|
|
1179
1198
|
role: 'assistant',
|
|
1180
1199
|
content: content.length === 1 && content[0].type === 'text' ? content[0].text : content
|
|
1181
1200
|
}] : [];
|
|
1182
1201
|
const webSearchCount = extractWebSearchCount(providerMetadata, usage);
|
|
1183
|
-
// Update usage with web search count and raw metadata
|
|
1184
1202
|
const finalUsage = {
|
|
1185
1203
|
...usage,
|
|
1186
1204
|
webSearchCount,
|
|
@@ -1190,29 +1208,65 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1190
1208
|
}
|
|
1191
1209
|
};
|
|
1192
1210
|
adjustAnthropicV3CacheTokens(model, modelId, provider, finalUsage);
|
|
1193
|
-
|
|
1211
|
+
const finishError = stopReason === 'error' ? new Error('Vercel AI SDK stream finished with an error') : undefined;
|
|
1212
|
+
const error = isError ? terminalError ?? new Error('Vercel AI SDK stream failed') : hasInBandError ? inBandError ?? new Error('Vercel AI SDK stream emitted an error chunk') : finishError;
|
|
1213
|
+
await captureStreamGeneration({
|
|
1194
1214
|
...baseOptions,
|
|
1195
1215
|
model: modelId,
|
|
1196
1216
|
provider: provider,
|
|
1197
1217
|
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
|
|
1198
|
-
output
|
|
1218
|
+
output,
|
|
1199
1219
|
latency,
|
|
1200
1220
|
timeToFirstToken,
|
|
1201
1221
|
baseURL,
|
|
1202
1222
|
modelParameters: getModelParams(mergedParams),
|
|
1203
|
-
httpStatus: 200,
|
|
1223
|
+
httpStatus: error ? undefined : 200,
|
|
1204
1224
|
usage: finalUsage,
|
|
1205
1225
|
stopReason,
|
|
1226
|
+
error,
|
|
1206
1227
|
tools: availableTools
|
|
1207
1228
|
});
|
|
1229
|
+
})().catch(error => {
|
|
1230
|
+
// Building telemetry must not change the provider stream's behavior.
|
|
1231
|
+
console.warn('[PostHog AI] Failed to capture Vercel stream telemetry:', error);
|
|
1232
|
+
});
|
|
1233
|
+
return finalizationPromise;
|
|
1234
|
+
};
|
|
1235
|
+
const instrumentedStream = new ReadableStream({
|
|
1236
|
+
async pull(controller) {
|
|
1237
|
+
let result;
|
|
1238
|
+
try {
|
|
1239
|
+
result = await reader.read();
|
|
1240
|
+
} catch (error) {
|
|
1241
|
+
void finalize(error, true);
|
|
1242
|
+
controller.error(error);
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
if (result.done) {
|
|
1246
|
+
controller.close();
|
|
1247
|
+
void finalize();
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
try {
|
|
1251
|
+
observeChunk(result.value);
|
|
1252
|
+
} catch {
|
|
1253
|
+
// Instrumentation must not alter or suppress provider chunks.
|
|
1254
|
+
}
|
|
1255
|
+
controller.enqueue(result.value);
|
|
1256
|
+
},
|
|
1257
|
+
cancel(reason) {
|
|
1258
|
+
void finalize(reason ?? new Error('Vercel AI SDK stream was cancelled'), true);
|
|
1259
|
+
return reader.cancel(reason);
|
|
1208
1260
|
}
|
|
1261
|
+
}, {
|
|
1262
|
+
highWaterMark: 0
|
|
1209
1263
|
});
|
|
1210
1264
|
return {
|
|
1211
|
-
stream:
|
|
1265
|
+
stream: instrumentedStream,
|
|
1212
1266
|
...rest
|
|
1213
1267
|
};
|
|
1214
1268
|
} catch (error) {
|
|
1215
|
-
await
|
|
1269
|
+
await captureStreamGeneration({
|
|
1216
1270
|
...baseOptions,
|
|
1217
1271
|
model: modelId,
|
|
1218
1272
|
provider: provider,
|