@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/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.3";
|
|
399
399
|
|
|
400
400
|
const DEFAULT_MAX_DEPTH = 3;
|
|
401
401
|
const MAX_STACK_LINES = 20;
|
|
@@ -506,120 +506,131 @@ const warnIfPostHogAiGateway = baseURL => {
|
|
|
506
506
|
* so callers can re-throw the original error reference safely.
|
|
507
507
|
*/
|
|
508
508
|
const captureAiGeneration = async (client, options) => {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
warnIfPostHogAiGateway(options.baseURL);
|
|
513
|
-
const traceId = options.traceId ?? v4();
|
|
514
|
-
const eventType = options.eventType ?? AIEvent.Generation;
|
|
515
|
-
const privacyMode = options.privacyMode ?? false;
|
|
516
|
-
const usage = options.usage ?? {};
|
|
517
|
-
const safeInput = sanitizeValues(options.input);
|
|
518
|
-
const safeOutput = sanitizeValues(options.output);
|
|
519
|
-
let httpStatus = options.httpStatus;
|
|
520
|
-
let errorData = {};
|
|
521
|
-
if (options.error) {
|
|
522
|
-
if (httpStatus === undefined) {
|
|
523
|
-
if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
|
|
524
|
-
httpStatus = options.error.status;
|
|
525
|
-
} else {
|
|
526
|
-
httpStatus = 500;
|
|
527
|
-
}
|
|
509
|
+
try {
|
|
510
|
+
if (!client.capture) {
|
|
511
|
+
return;
|
|
528
512
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
513
|
+
warnIfPostHogAiGateway(options.baseURL);
|
|
514
|
+
const traceId = options.traceId ?? v4();
|
|
515
|
+
const eventType = options.eventType ?? AIEvent.Generation;
|
|
516
|
+
const privacyMode = options.privacyMode ?? false;
|
|
517
|
+
const usage = options.usage ?? {};
|
|
518
|
+
|
|
519
|
+
// Check privacy before reading or traversing input/output. Besides avoiding
|
|
520
|
+
// needless work, this ensures hostile getters/proxies cannot observe a value
|
|
521
|
+
// that the caller explicitly requested us to redact.
|
|
522
|
+
const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
|
|
523
|
+
const safeInput = shouldRedact ? null : toJsonSafeValue(options.input);
|
|
524
|
+
const safeOutput = shouldRedact ? null : toJsonSafeValue(options.output);
|
|
525
|
+
let httpStatus = options.httpStatus;
|
|
526
|
+
let errorData = {};
|
|
527
|
+
if (options.error) {
|
|
528
|
+
if (httpStatus === undefined) {
|
|
529
|
+
if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
|
|
530
|
+
httpStatus = options.error.status;
|
|
531
|
+
} else {
|
|
532
|
+
httpStatus = 500;
|
|
533
|
+
}
|
|
537
534
|
}
|
|
535
|
+
let exceptionId;
|
|
536
|
+
if (client.options?.enableExceptionAutocapture) {
|
|
537
|
+
exceptionId = uuidv7();
|
|
538
|
+
client.captureException(options.error, undefined, {
|
|
539
|
+
$ai_trace_id: traceId
|
|
540
|
+
}, exceptionId);
|
|
541
|
+
if (typeof options.error === 'object') {
|
|
542
|
+
;
|
|
543
|
+
options.error.__posthog_previously_captured_error = true;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
errorData = {
|
|
547
|
+
$ai_is_error: true,
|
|
548
|
+
$ai_error: stringifyError(options.error),
|
|
549
|
+
$exception_event_id: exceptionId
|
|
550
|
+
};
|
|
538
551
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
552
|
+
httpStatus = httpStatus ?? 200;
|
|
553
|
+
let costOverrideData = {};
|
|
554
|
+
if (options.costOverride) {
|
|
555
|
+
const inputCostUSD = (options.costOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
|
|
556
|
+
const outputCostUSD = (options.costOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
|
|
557
|
+
costOverrideData = {
|
|
558
|
+
$ai_input_cost_usd: inputCostUSD,
|
|
559
|
+
$ai_output_cost_usd: outputCostUSD,
|
|
560
|
+
$ai_total_cost_usd: inputCostUSD + outputCostUSD
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
const additionalTokenValues = {
|
|
564
|
+
...(usage.reasoningTokens ? {
|
|
565
|
+
$ai_reasoning_tokens: usage.reasoningTokens
|
|
566
|
+
} : {}),
|
|
567
|
+
...(usage.cacheReadInputTokens ? {
|
|
568
|
+
$ai_cache_read_input_tokens: usage.cacheReadInputTokens
|
|
569
|
+
} : {}),
|
|
570
|
+
...(usage.cacheCreationInputTokens ? {
|
|
571
|
+
$ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
|
|
572
|
+
} : {}),
|
|
573
|
+
...(usage.webSearchCount ? {
|
|
574
|
+
$ai_web_search_count: usage.webSearchCount
|
|
575
|
+
} : {}),
|
|
576
|
+
...(usage.rawUsage ? {
|
|
577
|
+
$ai_usage: usage.rawUsage
|
|
578
|
+
} : {})
|
|
543
579
|
};
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
$
|
|
552
|
-
$
|
|
553
|
-
$
|
|
580
|
+
const properties = {
|
|
581
|
+
$ai_lib: 'posthog-ai',
|
|
582
|
+
$ai_lib_version: version,
|
|
583
|
+
$ai_provider: options.providerOverride ?? options.provider,
|
|
584
|
+
$ai_model: options.modelOverride ?? options.model,
|
|
585
|
+
$ai_model_parameters: options.modelParameters ?? {},
|
|
586
|
+
$ai_input: safeInput,
|
|
587
|
+
$ai_output_choices: safeOutput,
|
|
588
|
+
$ai_http_status: httpStatus,
|
|
589
|
+
$ai_input_tokens: usage.inputTokens ?? 0,
|
|
590
|
+
...(usage.outputTokens !== undefined ? {
|
|
591
|
+
$ai_output_tokens: usage.outputTokens
|
|
592
|
+
} : {}),
|
|
593
|
+
...additionalTokenValues,
|
|
594
|
+
$ai_latency: options.latency ?? 0,
|
|
595
|
+
...(options.timeToFirstToken !== undefined ? {
|
|
596
|
+
$ai_time_to_first_token: options.timeToFirstToken
|
|
597
|
+
} : {}),
|
|
598
|
+
$ai_trace_id: traceId,
|
|
599
|
+
$ai_base_url: options.baseURL ?? '',
|
|
600
|
+
...options.properties,
|
|
601
|
+
$ai_tokens_source: getTokensSource(options.properties),
|
|
602
|
+
...(options.distinctId ? {} : {
|
|
603
|
+
$process_person_profile: false
|
|
604
|
+
}),
|
|
605
|
+
...(options.stopReason ? {
|
|
606
|
+
$ai_stop_reason: options.stopReason
|
|
607
|
+
} : {}),
|
|
608
|
+
...(options.tools ? {
|
|
609
|
+
$ai_tools: options.tools
|
|
610
|
+
} : {}),
|
|
611
|
+
...(options.completionId ? {
|
|
612
|
+
$ai_completion_id: options.completionId
|
|
613
|
+
} : {}),
|
|
614
|
+
...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
|
|
615
|
+
$ai_provider_metadata: options.providerMetadata
|
|
616
|
+
} : {}),
|
|
617
|
+
...errorData,
|
|
618
|
+
...costOverrideData
|
|
554
619
|
};
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
...(usage.rawUsage ? {
|
|
570
|
-
$ai_usage: usage.rawUsage
|
|
571
|
-
} : {})
|
|
572
|
-
};
|
|
573
|
-
const properties = {
|
|
574
|
-
$ai_lib: 'posthog-ai',
|
|
575
|
-
$ai_lib_version: version,
|
|
576
|
-
$ai_provider: options.providerOverride ?? options.provider,
|
|
577
|
-
$ai_model: options.modelOverride ?? options.model,
|
|
578
|
-
$ai_model_parameters: options.modelParameters ?? {},
|
|
579
|
-
$ai_input: withPrivacyMode(client, privacyMode, safeInput),
|
|
580
|
-
$ai_output_choices: withPrivacyMode(client, privacyMode, safeOutput),
|
|
581
|
-
$ai_http_status: httpStatus,
|
|
582
|
-
$ai_input_tokens: usage.inputTokens ?? 0,
|
|
583
|
-
...(usage.outputTokens !== undefined ? {
|
|
584
|
-
$ai_output_tokens: usage.outputTokens
|
|
585
|
-
} : {}),
|
|
586
|
-
...additionalTokenValues,
|
|
587
|
-
$ai_latency: options.latency ?? 0,
|
|
588
|
-
...(options.timeToFirstToken !== undefined ? {
|
|
589
|
-
$ai_time_to_first_token: options.timeToFirstToken
|
|
590
|
-
} : {}),
|
|
591
|
-
$ai_trace_id: traceId,
|
|
592
|
-
$ai_base_url: options.baseURL ?? '',
|
|
593
|
-
...options.properties,
|
|
594
|
-
$ai_tokens_source: getTokensSource(options.properties),
|
|
595
|
-
...(options.distinctId ? {} : {
|
|
596
|
-
$process_person_profile: false
|
|
597
|
-
}),
|
|
598
|
-
...(options.stopReason ? {
|
|
599
|
-
$ai_stop_reason: options.stopReason
|
|
600
|
-
} : {}),
|
|
601
|
-
...(options.tools ? {
|
|
602
|
-
$ai_tools: options.tools
|
|
603
|
-
} : {}),
|
|
604
|
-
...(options.completionId ? {
|
|
605
|
-
$ai_completion_id: options.completionId
|
|
606
|
-
} : {}),
|
|
607
|
-
...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
|
|
608
|
-
$ai_provider_metadata: options.providerMetadata
|
|
609
|
-
} : {}),
|
|
610
|
-
...errorData,
|
|
611
|
-
...costOverrideData
|
|
612
|
-
};
|
|
613
|
-
const event = {
|
|
614
|
-
distinctId: options.distinctId ?? traceId,
|
|
615
|
-
event: eventType,
|
|
616
|
-
properties,
|
|
617
|
-
groups: options.groups
|
|
618
|
-
};
|
|
619
|
-
if (options.captureImmediate) {
|
|
620
|
-
await client.captureImmediate(event);
|
|
621
|
-
} else {
|
|
622
|
-
client.capture(event);
|
|
620
|
+
const event = {
|
|
621
|
+
distinctId: options.distinctId ?? traceId,
|
|
622
|
+
event: eventType,
|
|
623
|
+
properties,
|
|
624
|
+
groups: options.groups
|
|
625
|
+
};
|
|
626
|
+
if (options.captureImmediate) {
|
|
627
|
+
await client.captureImmediate(event);
|
|
628
|
+
} else {
|
|
629
|
+
client.capture(event);
|
|
630
|
+
}
|
|
631
|
+
} catch (error) {
|
|
632
|
+
// Telemetry failures must never affect the instrumented provider call.
|
|
633
|
+
console.warn('[PostHog AI] Failed to capture generation telemetry:', error);
|
|
623
634
|
}
|
|
624
635
|
};
|
|
625
636
|
|
|
@@ -1119,86 +1130,97 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1119
1130
|
|
|
1120
1131
|
// Map to track in-progress tool calls
|
|
1121
1132
|
const toolCallsInProgress = new Map();
|
|
1133
|
+
const captureStreamGeneration = async captureOptions => {
|
|
1134
|
+
try {
|
|
1135
|
+
await captureAiGeneration(phClient, captureOptions);
|
|
1136
|
+
} catch (error) {
|
|
1137
|
+
// Telemetry must never change the provider stream's behavior.
|
|
1138
|
+
console.warn('[PostHog AI] Failed to capture Vercel stream telemetry:', error);
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1122
1141
|
try {
|
|
1123
1142
|
const {
|
|
1124
1143
|
stream,
|
|
1125
1144
|
...rest
|
|
1126
1145
|
} = await model.doStream(params);
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1146
|
+
const reader = stream.getReader();
|
|
1147
|
+
let inBandError;
|
|
1148
|
+
let hasInBandError = false;
|
|
1149
|
+
let finalizationPromise;
|
|
1150
|
+
const observeChunk = chunk => {
|
|
1151
|
+
// Handle streaming patterns - compatible with both V2 and V3
|
|
1152
|
+
if (chunk.type === 'text-delta') {
|
|
1153
|
+
if (firstTokenTime === undefined) {
|
|
1154
|
+
firstTokenTime = Date.now();
|
|
1135
1155
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1156
|
+
generatedText += chunk.delta;
|
|
1157
|
+
}
|
|
1158
|
+
if (chunk.type === 'reasoning-delta') {
|
|
1159
|
+
if (firstTokenTime === undefined) {
|
|
1160
|
+
firstTokenTime = Date.now();
|
|
1141
1161
|
}
|
|
1162
|
+
reasoningText += chunk.delta;
|
|
1163
|
+
}
|
|
1142
1164
|
|
|
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
|
-
});
|
|
1154
|
-
}
|
|
1155
|
-
if (chunk.type === 'tool-input-delta') {
|
|
1156
|
-
// Accumulate tool call arguments
|
|
1157
|
-
const toolCall = toolCallsInProgress.get(chunk.id);
|
|
1158
|
-
if (toolCall) {
|
|
1159
|
-
toolCall.input += chunk.delta;
|
|
1160
|
-
}
|
|
1165
|
+
// Handle tool call chunks
|
|
1166
|
+
if (chunk.type === 'tool-input-start') {
|
|
1167
|
+
if (firstTokenTime === undefined) {
|
|
1168
|
+
firstTokenTime = Date.now();
|
|
1161
1169
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1170
|
+
toolCallsInProgress.set(chunk.id, {
|
|
1171
|
+
toolCallId: chunk.id,
|
|
1172
|
+
toolName: chunk.toolName,
|
|
1173
|
+
input: ''
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
if (chunk.type === 'tool-input-delta') {
|
|
1177
|
+
const toolCall = toolCallsInProgress.get(chunk.id);
|
|
1178
|
+
if (toolCall) {
|
|
1179
|
+
toolCall.input += chunk.delta;
|
|
1164
1180
|
}
|
|
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
|
-
});
|
|
1181
|
+
}
|
|
1182
|
+
if (chunk.type === 'tool-call') {
|
|
1183
|
+
if (firstTokenTime === undefined) {
|
|
1184
|
+
firstTokenTime = Date.now();
|
|
1175
1185
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1186
|
+
toolCallsInProgress.set(chunk.toolCallId, {
|
|
1187
|
+
toolCallId: chunk.toolCallId,
|
|
1188
|
+
toolName: chunk.toolName,
|
|
1189
|
+
input: chunk.input
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
if (chunk.type === 'error') {
|
|
1193
|
+
hasInBandError = true;
|
|
1194
|
+
inBandError = chunk.error;
|
|
1195
|
+
}
|
|
1196
|
+
if (chunk.type === 'finish') {
|
|
1197
|
+
providerMetadata = chunk.providerMetadata;
|
|
1198
|
+
const chunkUsage = chunk.usage || {};
|
|
1199
|
+
const additionalTokenValues = extractAdditionalTokenValues(providerMetadata, chunkUsage);
|
|
1200
|
+
usage = {
|
|
1201
|
+
inputTokens: extractTokenCount(chunk.usage?.inputTokens),
|
|
1202
|
+
outputTokens: extractTokenCount(chunk.usage?.outputTokens),
|
|
1203
|
+
reasoningTokens: extractReasoningTokens(chunkUsage),
|
|
1204
|
+
cacheReadInputTokens: extractCacheReadTokens(chunkUsage),
|
|
1205
|
+
...additionalTokenValues
|
|
1206
|
+
};
|
|
1187
1207
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
}
|
|
1208
|
+
// Extract finish reason - V2 returns a string, V3 returns an object with .unified
|
|
1209
|
+
const rawFinishReason = chunk.finishReason;
|
|
1210
|
+
if (typeof rawFinishReason === 'string') {
|
|
1211
|
+
stopReason = rawFinishReason;
|
|
1212
|
+
} else if (rawFinishReason && typeof rawFinishReason === 'object' && 'unified' in rawFinishReason) {
|
|
1213
|
+
stopReason = String(rawFinishReason.unified);
|
|
1195
1214
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
const finalize = (terminalError, isError = false) => {
|
|
1218
|
+
if (finalizationPromise) {
|
|
1219
|
+
return finalizationPromise;
|
|
1220
|
+
}
|
|
1221
|
+
finalizationPromise = (async () => {
|
|
1199
1222
|
const latency = (Date.now() - startTime) / 1000;
|
|
1200
1223
|
const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
|
|
1201
|
-
// Build content array similar to mapVercelOutput structure
|
|
1202
1224
|
const content = [];
|
|
1203
1225
|
if (reasoningText) {
|
|
1204
1226
|
content.push({
|
|
@@ -1212,8 +1234,6 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1212
1234
|
text: truncate(generatedText)
|
|
1213
1235
|
});
|
|
1214
1236
|
}
|
|
1215
|
-
|
|
1216
|
-
// Add completed tool calls to content
|
|
1217
1237
|
for (const toolCall of toolCallsInProgress.values()) {
|
|
1218
1238
|
if (toolCall.toolName) {
|
|
1219
1239
|
content.push({
|
|
@@ -1226,15 +1246,11 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1226
1246
|
});
|
|
1227
1247
|
}
|
|
1228
1248
|
}
|
|
1229
|
-
|
|
1230
|
-
// Structure output like mapVercelOutput does
|
|
1231
1249
|
const output = content.length > 0 ? [{
|
|
1232
1250
|
role: 'assistant',
|
|
1233
1251
|
content: content.length === 1 && content[0].type === 'text' ? content[0].text : content
|
|
1234
1252
|
}] : [];
|
|
1235
1253
|
const webSearchCount = extractWebSearchCount(providerMetadata, usage);
|
|
1236
|
-
|
|
1237
|
-
// Update usage with web search count and raw metadata
|
|
1238
1254
|
const finalUsage = {
|
|
1239
1255
|
...usage,
|
|
1240
1256
|
webSearchCount,
|
|
@@ -1244,29 +1260,65 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1244
1260
|
}
|
|
1245
1261
|
};
|
|
1246
1262
|
adjustAnthropicV3CacheTokens(model, modelId, provider, finalUsage);
|
|
1247
|
-
|
|
1263
|
+
const finishError = stopReason === 'error' ? new Error('Vercel AI SDK stream finished with an error') : undefined;
|
|
1264
|
+
const error = isError ? terminalError ?? new Error('Vercel AI SDK stream failed') : hasInBandError ? inBandError ?? new Error('Vercel AI SDK stream emitted an error chunk') : finishError;
|
|
1265
|
+
await captureStreamGeneration({
|
|
1248
1266
|
...baseOptions,
|
|
1249
1267
|
model: modelId,
|
|
1250
1268
|
provider: provider,
|
|
1251
1269
|
input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
|
|
1252
|
-
output
|
|
1270
|
+
output,
|
|
1253
1271
|
latency,
|
|
1254
1272
|
timeToFirstToken,
|
|
1255
1273
|
baseURL,
|
|
1256
1274
|
modelParameters: getModelParams(mergedParams),
|
|
1257
|
-
httpStatus: 200,
|
|
1275
|
+
httpStatus: error ? undefined : 200,
|
|
1258
1276
|
usage: finalUsage,
|
|
1259
1277
|
stopReason,
|
|
1278
|
+
error,
|
|
1260
1279
|
tools: availableTools
|
|
1261
1280
|
});
|
|
1281
|
+
})().catch(error => {
|
|
1282
|
+
// Building telemetry must not change the provider stream's behavior.
|
|
1283
|
+
console.warn('[PostHog AI] Failed to capture Vercel stream telemetry:', error);
|
|
1284
|
+
});
|
|
1285
|
+
return finalizationPromise;
|
|
1286
|
+
};
|
|
1287
|
+
const instrumentedStream = new ReadableStream({
|
|
1288
|
+
async pull(controller) {
|
|
1289
|
+
let result;
|
|
1290
|
+
try {
|
|
1291
|
+
result = await reader.read();
|
|
1292
|
+
} catch (error) {
|
|
1293
|
+
void finalize(error, true);
|
|
1294
|
+
controller.error(error);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
if (result.done) {
|
|
1298
|
+
controller.close();
|
|
1299
|
+
void finalize();
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
try {
|
|
1303
|
+
observeChunk(result.value);
|
|
1304
|
+
} catch {
|
|
1305
|
+
// Instrumentation must not alter or suppress provider chunks.
|
|
1306
|
+
}
|
|
1307
|
+
controller.enqueue(result.value);
|
|
1308
|
+
},
|
|
1309
|
+
cancel(reason) {
|
|
1310
|
+
void finalize(reason ?? new Error('Vercel AI SDK stream was cancelled'), true);
|
|
1311
|
+
return reader.cancel(reason);
|
|
1262
1312
|
}
|
|
1313
|
+
}, {
|
|
1314
|
+
highWaterMark: 0
|
|
1263
1315
|
});
|
|
1264
1316
|
return {
|
|
1265
|
-
stream:
|
|
1317
|
+
stream: instrumentedStream,
|
|
1266
1318
|
...rest
|
|
1267
1319
|
};
|
|
1268
1320
|
} catch (error) {
|
|
1269
|
-
await
|
|
1321
|
+
await captureStreamGeneration({
|
|
1270
1322
|
...baseOptions,
|
|
1271
1323
|
model: modelId,
|
|
1272
1324
|
provider: provider,
|