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