@posthog/ai 8.16.1 → 8.16.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/README.md +12 -0
- package/dist/adk/index.cjs +147 -9
- package/dist/adk/index.cjs.map +1 -1
- package/dist/adk/index.mjs +147 -9
- package/dist/adk/index.mjs.map +1 -1
- package/dist/anthropic/index.cjs +141 -8
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.mjs +141 -8
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/claude-agent-sdk/index.cjs +147 -9
- package/dist/claude-agent-sdk/index.cjs.map +1 -1
- package/dist/claude-agent-sdk/index.mjs +147 -9
- package/dist/claude-agent-sdk/index.mjs.map +1 -1
- package/dist/gemini/index.cjs +141 -8
- package/dist/gemini/index.cjs.map +1 -1
- package/dist/gemini/index.mjs +141 -8
- package/dist/gemini/index.mjs.map +1 -1
- package/dist/index.cjs +149 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +149 -15
- package/dist/index.mjs.map +1 -1
- package/dist/langchain/index.cjs +141 -7
- package/dist/langchain/index.cjs.map +1 -1
- package/dist/langchain/index.d.ts +1 -0
- package/dist/langchain/index.mjs +141 -7
- package/dist/langchain/index.mjs.map +1 -1
- package/dist/langchain/middleware/index.cjs +150 -11
- package/dist/langchain/middleware/index.cjs.map +1 -1
- package/dist/langchain/middleware/index.mjs +150 -11
- package/dist/langchain/middleware/index.mjs.map +1 -1
- package/dist/openai/index.cjs +157 -11
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +157 -11
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openai-agents/index.cjs +104 -3
- package/dist/openai-agents/index.cjs.map +1 -1
- package/dist/openai-agents/index.mjs +104 -3
- package/dist/openai-agents/index.mjs.map +1 -1
- package/dist/otel/index.cjs +216 -4
- package/dist/otel/index.cjs.map +1 -1
- package/dist/otel/index.d.ts +22 -1
- package/dist/otel/index.mjs +216 -4
- package/dist/otel/index.mjs.map +1 -1
- package/dist/vercel/index.cjs +149 -15
- package/dist/vercel/index.cjs.map +1 -1
- package/dist/vercel/index.mjs +149 -15
- package/dist/vercel/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/vercel/index.mjs
CHANGED
|
@@ -228,6 +228,10 @@ function redactBase64DataUrl(str, mediaType) {
|
|
|
228
228
|
const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
|
|
229
229
|
const sanitizeVercel = (data, client) => sanitize(data, client);
|
|
230
230
|
//#endregion
|
|
231
|
+
//#region src/privacyMode.ts
|
|
232
|
+
/** Per-call settings can turn redaction on but never off. */
|
|
233
|
+
const isPrivacyModeEnabled = (client, privacyMode = false) => privacyMode === true || client?.privacyMode === true || client?.privacy_mode === true || client?.options?.privacyMode === true;
|
|
234
|
+
//#endregion
|
|
231
235
|
//#region src/utils.ts
|
|
232
236
|
const TOKEN_PROPERTY_KEYS = /* @__PURE__ */ new Set([
|
|
233
237
|
"$ai_input_tokens",
|
|
@@ -297,7 +301,6 @@ const getModelParams = (params, responseServiceTier) => {
|
|
|
297
301
|
if (responseServiceTier != null) modelParams.service_tier = responseServiceTier;
|
|
298
302
|
return modelParams;
|
|
299
303
|
};
|
|
300
|
-
const isPrivacyModeEnabled = (client, privacyMode) => Boolean(client.privacy_mode || privacyMode);
|
|
301
304
|
function toSafeString(input) {
|
|
302
305
|
if (input === void 0 || input === null) return "";
|
|
303
306
|
if (typeof input === "string") return input;
|
|
@@ -404,11 +407,12 @@ function sanitizeValues(obj) {
|
|
|
404
407
|
}
|
|
405
408
|
//#endregion
|
|
406
409
|
//#region package.json
|
|
407
|
-
var version = "8.16.
|
|
410
|
+
var version = "8.16.3";
|
|
408
411
|
//#endregion
|
|
409
412
|
//#region src/serializeError.ts
|
|
410
413
|
const DEFAULT_MAX_DEPTH = 3;
|
|
411
414
|
const MAX_STACK_LINES = 20;
|
|
415
|
+
const OMITTED_FIELD = "requestBodyValues";
|
|
412
416
|
function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
413
417
|
if (depth < 0 || value === null || typeof value !== "object") return value;
|
|
414
418
|
if (value instanceof Error) {
|
|
@@ -417,16 +421,17 @@ function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
|
417
421
|
message: value.message,
|
|
418
422
|
stack: truncateStack(value.stack)
|
|
419
423
|
};
|
|
420
|
-
for (const key of Object.keys(value)) out[key] = serializeError(value
|
|
424
|
+
for (const key of Object.keys(value)) if (key !== OMITTED_FIELD) out[key] = serializeError(Reflect.get(value, key), depth - 1);
|
|
421
425
|
if (value.cause !== void 0) out.cause = serializeError(value.cause, depth - 1);
|
|
422
426
|
return out;
|
|
423
427
|
}
|
|
424
428
|
if (Array.isArray(value)) return value.map((item) => serializeError(item, depth - 1));
|
|
425
429
|
return value;
|
|
426
430
|
}
|
|
431
|
+
const omitRequestBody = (key, value) => key === OMITTED_FIELD ? void 0 : value;
|
|
427
432
|
function stringifyError(error) {
|
|
428
433
|
try {
|
|
429
|
-
return JSON.stringify(sanitizeValues(serializeError(error)));
|
|
434
|
+
return JSON.stringify(sanitizeValues(serializeError(error)), omitRequestBody);
|
|
430
435
|
} catch {
|
|
431
436
|
if (error instanceof Error) return JSON.stringify({
|
|
432
437
|
name: error.name,
|
|
@@ -435,6 +440,105 @@ function stringifyError(error) {
|
|
|
435
440
|
return JSON.stringify({ message: String(error) });
|
|
436
441
|
}
|
|
437
442
|
}
|
|
443
|
+
const CLASS_NAME = /^[A-Za-z_$][A-Za-z0-9_$.]{0,127}$/;
|
|
444
|
+
const PROVIDER_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
445
|
+
const PARAM = /^[A-Za-z0-9_.[\]-]{1,128}$/;
|
|
446
|
+
const REQUEST_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
447
|
+
const MAX_CAUSES = 3;
|
|
448
|
+
const readField = (value, key) => {
|
|
449
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return;
|
|
450
|
+
try {
|
|
451
|
+
return Reflect.get(value, key);
|
|
452
|
+
} catch {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
const matching = (value, grammar) => typeof value === "string" && grammar.test(value) ? value : void 0;
|
|
457
|
+
const firstMatching = (values, grammar) => {
|
|
458
|
+
for (const value of values) {
|
|
459
|
+
const match = matching(value, grammar);
|
|
460
|
+
if (match !== void 0) return match;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
const className = (value) => {
|
|
464
|
+
if (!(value instanceof Error)) return "NonErrorThrown";
|
|
465
|
+
const name = readField(value, "name");
|
|
466
|
+
return firstMatching([
|
|
467
|
+
name === "Error" ? void 0 : name,
|
|
468
|
+
readField(readField(value, "constructor"), "name"),
|
|
469
|
+
name
|
|
470
|
+
], CLASS_NAME) ?? "Error";
|
|
471
|
+
};
|
|
472
|
+
const httpStatus = (error) => {
|
|
473
|
+
for (const key of ["status", "statusCode"]) {
|
|
474
|
+
const value = readField(error, key);
|
|
475
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599) return value;
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
const unwrapEnvelope = (body) => {
|
|
479
|
+
const inner = readField(body, "error");
|
|
480
|
+
return inner !== null && typeof inner === "object" ? inner : body;
|
|
481
|
+
};
|
|
482
|
+
const parsedResponseBody = (error) => {
|
|
483
|
+
const responseBody = readField(error, "responseBody");
|
|
484
|
+
if (typeof responseBody !== "string") return;
|
|
485
|
+
try {
|
|
486
|
+
return unwrapEnvelope(JSON.parse(responseBody));
|
|
487
|
+
} catch {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
const causeNames = (error) => {
|
|
492
|
+
const names = [];
|
|
493
|
+
let cause = readField(error, "cause");
|
|
494
|
+
while (cause !== void 0 && cause !== null && names.length < MAX_CAUSES) {
|
|
495
|
+
names.push(className(cause));
|
|
496
|
+
cause = readField(cause, "cause");
|
|
497
|
+
}
|
|
498
|
+
return names;
|
|
499
|
+
};
|
|
500
|
+
const describeError = (error) => {
|
|
501
|
+
const sources = [
|
|
502
|
+
error,
|
|
503
|
+
unwrapEnvelope(readField(error, "error")),
|
|
504
|
+
parsedResponseBody(error)
|
|
505
|
+
];
|
|
506
|
+
const codes = /* @__PURE__ */ new Set();
|
|
507
|
+
for (const source of sources) for (const key of [
|
|
508
|
+
"code",
|
|
509
|
+
"type",
|
|
510
|
+
"status"
|
|
511
|
+
]) {
|
|
512
|
+
const code = matching(readField(source, key), PROVIDER_CODE);
|
|
513
|
+
if (code !== void 0) codes.add(code);
|
|
514
|
+
}
|
|
515
|
+
const status = httpStatus(error);
|
|
516
|
+
const param = firstMatching(sources.map((source) => readField(source, "param")), PARAM);
|
|
517
|
+
const requestId = firstMatching([readField(error, "requestID"), readField(error, "request_id")], REQUEST_ID);
|
|
518
|
+
const causes = causeNames(error);
|
|
519
|
+
let summary = className(error);
|
|
520
|
+
if (status !== void 0) summary += `: ${status}`;
|
|
521
|
+
for (const code of codes) summary += ` ${code}`;
|
|
522
|
+
if (param !== void 0) summary += ` param=${param}`;
|
|
523
|
+
if (requestId !== void 0) summary += ` request_id=${requestId}`;
|
|
524
|
+
if (causes.length > 0) summary += ` (caused by ${causes.join(", ")})`;
|
|
525
|
+
return summary;
|
|
526
|
+
};
|
|
527
|
+
/**
|
|
528
|
+
* Describes an error for `$ai_error` without its message, stack, or arbitrary fields, which
|
|
529
|
+
* routinely echo the prompt. Only structured fields that pass a strict grammar are kept.
|
|
530
|
+
* The format matches the Python SDK.
|
|
531
|
+
*/
|
|
532
|
+
function summarizeErrorForPrivacy(error) {
|
|
533
|
+
try {
|
|
534
|
+
return describeError(error);
|
|
535
|
+
} catch {
|
|
536
|
+
return error instanceof Error ? "Error" : "NonErrorThrown";
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
function formatAiError(error, privacyMode) {
|
|
540
|
+
return privacyMode ? summarizeErrorForPrivacy(error) : stringifyError(error);
|
|
541
|
+
}
|
|
438
542
|
function truncateStack(stack) {
|
|
439
543
|
if (!stack) return stack;
|
|
440
544
|
const lines = stack.split("\n");
|
|
@@ -442,6 +546,33 @@ function truncateStack(stack) {
|
|
|
442
546
|
return [...lines.slice(0, MAX_STACK_LINES), "... (truncated)"].join("\n");
|
|
443
547
|
}
|
|
444
548
|
//#endregion
|
|
549
|
+
//#region src/mcpCredentials.ts
|
|
550
|
+
const MCP_CREDENTIAL_FIELDS = ["headers", "authorization"];
|
|
551
|
+
const MCP_PROVIDER_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
552
|
+
"openai.mcp",
|
|
553
|
+
"xai.mcp",
|
|
554
|
+
"google.mcp_server"
|
|
555
|
+
]);
|
|
556
|
+
const isMcpProviderTool = (tool) => (tool.type === "provider" || tool.type === "provider-defined") && typeof tool.id === "string" && MCP_PROVIDER_TOOL_IDS.has(tool.id);
|
|
557
|
+
const withoutMcpCredentials = (config) => {
|
|
558
|
+
const redacted = { ...config };
|
|
559
|
+
for (const field of MCP_CREDENTIAL_FIELDS) if (field in redacted) redacted[field] = "[REDACTED]";
|
|
560
|
+
return redacted;
|
|
561
|
+
};
|
|
562
|
+
/** Hides the credentials of a remote MCP tool definition; everything else is kept as sent. */
|
|
563
|
+
function redactMcpToolDefinition(tool) {
|
|
564
|
+
if (!isObject(tool)) return tool;
|
|
565
|
+
if (tool.type === "mcp") return withoutMcpCredentials(tool);
|
|
566
|
+
if (isMcpProviderTool(tool) && isObject(tool.args)) return {
|
|
567
|
+
...tool,
|
|
568
|
+
args: withoutMcpCredentials(tool.args)
|
|
569
|
+
};
|
|
570
|
+
return tool;
|
|
571
|
+
}
|
|
572
|
+
function redactMcpToolCredentials(tools) {
|
|
573
|
+
return Array.isArray(tools) ? tools.map(redactMcpToolDefinition) : tools;
|
|
574
|
+
}
|
|
575
|
+
//#endregion
|
|
445
576
|
//#region src/gatewayWarning.ts
|
|
446
577
|
const POSTHOG_AI_GATEWAY_HOSTS = [
|
|
447
578
|
"gateway.posthog.com",
|
|
@@ -504,13 +635,15 @@ const captureAiGeneration = async (client, options) => {
|
|
|
504
635
|
}
|
|
505
636
|
let exceptionId;
|
|
506
637
|
if (client.options?.enableExceptionAutocapture) {
|
|
507
|
-
|
|
508
|
-
|
|
638
|
+
if (!shouldRedact) {
|
|
639
|
+
exceptionId = uuidv7();
|
|
640
|
+
client.captureException(options.error, void 0, { $ai_trace_id: traceId }, exceptionId);
|
|
641
|
+
}
|
|
509
642
|
if (typeof options.error === "object") options.error.__posthog_previously_captured_error = true;
|
|
510
643
|
}
|
|
511
644
|
errorData = {
|
|
512
645
|
$ai_is_error: true,
|
|
513
|
-
$ai_error:
|
|
646
|
+
$ai_error: formatAiError(options.error, shouldRedact),
|
|
514
647
|
$exception_event_id: exceptionId
|
|
515
648
|
};
|
|
516
649
|
}
|
|
@@ -551,7 +684,7 @@ const captureAiGeneration = async (client, options) => {
|
|
|
551
684
|
$ai_tokens_source: getTokensSource(options.properties),
|
|
552
685
|
...options.distinctId ? {} : { $process_person_profile: false },
|
|
553
686
|
...options.stopReason ? { $ai_stop_reason: options.stopReason } : {},
|
|
554
|
-
...options.tools ? { $ai_tools: options.tools } : {},
|
|
687
|
+
...options.tools ? { $ai_tools: redactMcpToolCredentials(options.tools) } : {},
|
|
555
688
|
...options.completionId ? { $ai_completion_id: options.completionId } : {},
|
|
556
689
|
...options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? { $ai_provider_metadata: options.providerMetadata } : {},
|
|
557
690
|
...errorData,
|
|
@@ -815,12 +948,13 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
815
948
|
$ai_framework_version: model.specificationVersion === "v3" ? "6" : "5"
|
|
816
949
|
}
|
|
817
950
|
};
|
|
951
|
+
const privacyMode = isPrivacyModeEnabled(phClient, mergedOptions.posthogPrivacyMode);
|
|
818
952
|
const baseOptions = {
|
|
819
953
|
distinctId: mergedOptions.posthogDistinctId,
|
|
820
954
|
traceId,
|
|
821
955
|
properties: mergedOptions.posthogProperties,
|
|
822
956
|
groups: mergedOptions.posthogGroups,
|
|
823
|
-
privacyMode
|
|
957
|
+
privacyMode,
|
|
824
958
|
modelOverride: mergedOptions.posthogModelOverride,
|
|
825
959
|
providerOverride: mergedOptions.posthogProviderOverride,
|
|
826
960
|
costOverride: mergedOptions.posthogCostOverride,
|
|
@@ -848,7 +982,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
848
982
|
const usageObj = result.usage;
|
|
849
983
|
const rawUsageData = {
|
|
850
984
|
usage: result.usage,
|
|
851
|
-
providerMetadata
|
|
985
|
+
...privacyMode ? {} : { providerMetadata }
|
|
852
986
|
};
|
|
853
987
|
if (result.response && typeof result.response === "object") {
|
|
854
988
|
const responseBody = result.response.body;
|
|
@@ -870,7 +1004,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
870
1004
|
...baseOptions,
|
|
871
1005
|
model: modelId,
|
|
872
1006
|
provider,
|
|
873
|
-
input:
|
|
1007
|
+
input: privacyMode ? "" : mapVercelPrompt(params.prompt, phClient),
|
|
874
1008
|
output: content,
|
|
875
1009
|
latency,
|
|
876
1010
|
baseURL,
|
|
@@ -887,7 +1021,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
887
1021
|
...baseOptions,
|
|
888
1022
|
model: modelId,
|
|
889
1023
|
provider: extractProvider(model),
|
|
890
|
-
input:
|
|
1024
|
+
input: privacyMode ? "" : mapVercelPrompt(params.prompt, phClient),
|
|
891
1025
|
output: [],
|
|
892
1026
|
latency: (Date.now() - startTime) / 1e3,
|
|
893
1027
|
baseURL,
|
|
@@ -1013,7 +1147,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1013
1147
|
const finalUsage = {
|
|
1014
1148
|
...usage,
|
|
1015
1149
|
webSearchCount,
|
|
1016
|
-
rawUsage: {
|
|
1150
|
+
rawUsage: privacyMode ? { usage } : {
|
|
1017
1151
|
usage,
|
|
1018
1152
|
providerMetadata
|
|
1019
1153
|
}
|
|
@@ -1024,7 +1158,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1024
1158
|
...baseOptions,
|
|
1025
1159
|
model: modelId,
|
|
1026
1160
|
provider,
|
|
1027
|
-
input:
|
|
1161
|
+
input: privacyMode ? "" : mapVercelPrompt(params.prompt, phClient),
|
|
1028
1162
|
output,
|
|
1029
1163
|
latency,
|
|
1030
1164
|
timeToFirstToken,
|
|
@@ -1074,7 +1208,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
|
|
|
1074
1208
|
...baseOptions,
|
|
1075
1209
|
model: modelId,
|
|
1076
1210
|
provider,
|
|
1077
|
-
input:
|
|
1211
|
+
input: privacyMode ? "" : mapVercelPrompt(params.prompt, phClient),
|
|
1078
1212
|
output: [],
|
|
1079
1213
|
latency: (Date.now() - startTime) / 1e3,
|
|
1080
1214
|
baseURL,
|