@posthog/ai 8.16.1 → 8.16.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/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 +141 -8
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.mjs +141 -8
- 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 +3 -3
package/dist/anthropic/index.mjs
CHANGED
|
@@ -224,6 +224,10 @@ const redactor$1 = new BinaryContentRedactor();
|
|
|
224
224
|
const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor$1.redact(data);
|
|
225
225
|
const sanitizeAnthropic = (data, client) => sanitize(data, client);
|
|
226
226
|
//#endregion
|
|
227
|
+
//#region src/privacyMode.ts
|
|
228
|
+
/** Per-call settings can turn redaction on but never off. */
|
|
229
|
+
const isPrivacyModeEnabled = (client, privacyMode = false) => privacyMode === true || client?.privacyMode === true || client?.privacy_mode === true || client?.options?.privacyMode === true;
|
|
230
|
+
//#endregion
|
|
227
231
|
//#region src/utils.ts
|
|
228
232
|
const TOKEN_PROPERTY_KEYS = /* @__PURE__ */ new Set([
|
|
229
233
|
"$ai_input_tokens",
|
|
@@ -304,7 +308,6 @@ const mergeSystemPrompt = (params, provider) => {
|
|
|
304
308
|
}
|
|
305
309
|
return params.messages;
|
|
306
310
|
};
|
|
307
|
-
const isPrivacyModeEnabled = (client, privacyMode) => Boolean(client.privacy_mode || privacyMode);
|
|
308
311
|
function toSafeString(input) {
|
|
309
312
|
if (input === void 0 || input === null) return "";
|
|
310
313
|
if (typeof input === "string") return input;
|
|
@@ -385,11 +388,12 @@ function addDefaults(params) {
|
|
|
385
388
|
}
|
|
386
389
|
//#endregion
|
|
387
390
|
//#region package.json
|
|
388
|
-
var version = "8.16.
|
|
391
|
+
var version = "8.16.2";
|
|
389
392
|
//#endregion
|
|
390
393
|
//#region src/serializeError.ts
|
|
391
394
|
const DEFAULT_MAX_DEPTH = 3;
|
|
392
395
|
const MAX_STACK_LINES = 20;
|
|
396
|
+
const OMITTED_FIELD = "requestBodyValues";
|
|
393
397
|
function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
394
398
|
if (depth < 0 || value === null || typeof value !== "object") return value;
|
|
395
399
|
if (value instanceof Error) {
|
|
@@ -398,16 +402,17 @@ function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
|
398
402
|
message: value.message,
|
|
399
403
|
stack: truncateStack(value.stack)
|
|
400
404
|
};
|
|
401
|
-
for (const key of Object.keys(value)) out[key] = serializeError(value
|
|
405
|
+
for (const key of Object.keys(value)) if (key !== OMITTED_FIELD) out[key] = serializeError(Reflect.get(value, key), depth - 1);
|
|
402
406
|
if (value.cause !== void 0) out.cause = serializeError(value.cause, depth - 1);
|
|
403
407
|
return out;
|
|
404
408
|
}
|
|
405
409
|
if (Array.isArray(value)) return value.map((item) => serializeError(item, depth - 1));
|
|
406
410
|
return value;
|
|
407
411
|
}
|
|
412
|
+
const omitRequestBody = (key, value) => key === OMITTED_FIELD ? void 0 : value;
|
|
408
413
|
function stringifyError(error) {
|
|
409
414
|
try {
|
|
410
|
-
return JSON.stringify(sanitizeValues(serializeError(error)));
|
|
415
|
+
return JSON.stringify(sanitizeValues(serializeError(error)), omitRequestBody);
|
|
411
416
|
} catch {
|
|
412
417
|
if (error instanceof Error) return JSON.stringify({
|
|
413
418
|
name: error.name,
|
|
@@ -416,6 +421,105 @@ function stringifyError(error) {
|
|
|
416
421
|
return JSON.stringify({ message: String(error) });
|
|
417
422
|
}
|
|
418
423
|
}
|
|
424
|
+
const CLASS_NAME = /^[A-Za-z_$][A-Za-z0-9_$.]{0,127}$/;
|
|
425
|
+
const PROVIDER_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
426
|
+
const PARAM = /^[A-Za-z0-9_.[\]-]{1,128}$/;
|
|
427
|
+
const REQUEST_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
428
|
+
const MAX_CAUSES = 3;
|
|
429
|
+
const readField = (value, key) => {
|
|
430
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return;
|
|
431
|
+
try {
|
|
432
|
+
return Reflect.get(value, key);
|
|
433
|
+
} catch {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
const matching = (value, grammar) => typeof value === "string" && grammar.test(value) ? value : void 0;
|
|
438
|
+
const firstMatching = (values, grammar) => {
|
|
439
|
+
for (const value of values) {
|
|
440
|
+
const match = matching(value, grammar);
|
|
441
|
+
if (match !== void 0) return match;
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
const className = (value) => {
|
|
445
|
+
if (!(value instanceof Error)) return "NonErrorThrown";
|
|
446
|
+
const name = readField(value, "name");
|
|
447
|
+
return firstMatching([
|
|
448
|
+
name === "Error" ? void 0 : name,
|
|
449
|
+
readField(readField(value, "constructor"), "name"),
|
|
450
|
+
name
|
|
451
|
+
], CLASS_NAME) ?? "Error";
|
|
452
|
+
};
|
|
453
|
+
const httpStatus = (error) => {
|
|
454
|
+
for (const key of ["status", "statusCode"]) {
|
|
455
|
+
const value = readField(error, key);
|
|
456
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599) return value;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
const unwrapEnvelope = (body) => {
|
|
460
|
+
const inner = readField(body, "error");
|
|
461
|
+
return inner !== null && typeof inner === "object" ? inner : body;
|
|
462
|
+
};
|
|
463
|
+
const parsedResponseBody = (error) => {
|
|
464
|
+
const responseBody = readField(error, "responseBody");
|
|
465
|
+
if (typeof responseBody !== "string") return;
|
|
466
|
+
try {
|
|
467
|
+
return unwrapEnvelope(JSON.parse(responseBody));
|
|
468
|
+
} catch {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
const causeNames = (error) => {
|
|
473
|
+
const names = [];
|
|
474
|
+
let cause = readField(error, "cause");
|
|
475
|
+
while (cause !== void 0 && cause !== null && names.length < MAX_CAUSES) {
|
|
476
|
+
names.push(className(cause));
|
|
477
|
+
cause = readField(cause, "cause");
|
|
478
|
+
}
|
|
479
|
+
return names;
|
|
480
|
+
};
|
|
481
|
+
const describeError = (error) => {
|
|
482
|
+
const sources = [
|
|
483
|
+
error,
|
|
484
|
+
unwrapEnvelope(readField(error, "error")),
|
|
485
|
+
parsedResponseBody(error)
|
|
486
|
+
];
|
|
487
|
+
const codes = /* @__PURE__ */ new Set();
|
|
488
|
+
for (const source of sources) for (const key of [
|
|
489
|
+
"code",
|
|
490
|
+
"type",
|
|
491
|
+
"status"
|
|
492
|
+
]) {
|
|
493
|
+
const code = matching(readField(source, key), PROVIDER_CODE);
|
|
494
|
+
if (code !== void 0) codes.add(code);
|
|
495
|
+
}
|
|
496
|
+
const status = httpStatus(error);
|
|
497
|
+
const param = firstMatching(sources.map((source) => readField(source, "param")), PARAM);
|
|
498
|
+
const requestId = firstMatching([readField(error, "requestID"), readField(error, "request_id")], REQUEST_ID);
|
|
499
|
+
const causes = causeNames(error);
|
|
500
|
+
let summary = className(error);
|
|
501
|
+
if (status !== void 0) summary += `: ${status}`;
|
|
502
|
+
for (const code of codes) summary += ` ${code}`;
|
|
503
|
+
if (param !== void 0) summary += ` param=${param}`;
|
|
504
|
+
if (requestId !== void 0) summary += ` request_id=${requestId}`;
|
|
505
|
+
if (causes.length > 0) summary += ` (caused by ${causes.join(", ")})`;
|
|
506
|
+
return summary;
|
|
507
|
+
};
|
|
508
|
+
/**
|
|
509
|
+
* Describes an error for `$ai_error` without its message, stack, or arbitrary fields, which
|
|
510
|
+
* routinely echo the prompt. Only structured fields that pass a strict grammar are kept.
|
|
511
|
+
* The format matches the Python SDK.
|
|
512
|
+
*/
|
|
513
|
+
function summarizeErrorForPrivacy(error) {
|
|
514
|
+
try {
|
|
515
|
+
return describeError(error);
|
|
516
|
+
} catch {
|
|
517
|
+
return error instanceof Error ? "Error" : "NonErrorThrown";
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function formatAiError(error, privacyMode) {
|
|
521
|
+
return privacyMode ? summarizeErrorForPrivacy(error) : stringifyError(error);
|
|
522
|
+
}
|
|
419
523
|
function truncateStack(stack) {
|
|
420
524
|
if (!stack) return stack;
|
|
421
525
|
const lines = stack.split("\n");
|
|
@@ -423,6 +527,33 @@ function truncateStack(stack) {
|
|
|
423
527
|
return [...lines.slice(0, MAX_STACK_LINES), "... (truncated)"].join("\n");
|
|
424
528
|
}
|
|
425
529
|
//#endregion
|
|
530
|
+
//#region src/mcpCredentials.ts
|
|
531
|
+
const MCP_CREDENTIAL_FIELDS = ["headers", "authorization"];
|
|
532
|
+
const MCP_PROVIDER_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
533
|
+
"openai.mcp",
|
|
534
|
+
"xai.mcp",
|
|
535
|
+
"google.mcp_server"
|
|
536
|
+
]);
|
|
537
|
+
const isMcpProviderTool = (tool) => (tool.type === "provider" || tool.type === "provider-defined") && typeof tool.id === "string" && MCP_PROVIDER_TOOL_IDS.has(tool.id);
|
|
538
|
+
const withoutMcpCredentials = (config) => {
|
|
539
|
+
const redacted = { ...config };
|
|
540
|
+
for (const field of MCP_CREDENTIAL_FIELDS) if (field in redacted) redacted[field] = "[REDACTED]";
|
|
541
|
+
return redacted;
|
|
542
|
+
};
|
|
543
|
+
/** Hides the credentials of a remote MCP tool definition; everything else is kept as sent. */
|
|
544
|
+
function redactMcpToolDefinition(tool) {
|
|
545
|
+
if (!isObject(tool)) return tool;
|
|
546
|
+
if (tool.type === "mcp") return withoutMcpCredentials(tool);
|
|
547
|
+
if (isMcpProviderTool(tool) && isObject(tool.args)) return {
|
|
548
|
+
...tool,
|
|
549
|
+
args: withoutMcpCredentials(tool.args)
|
|
550
|
+
};
|
|
551
|
+
return tool;
|
|
552
|
+
}
|
|
553
|
+
function redactMcpToolCredentials(tools) {
|
|
554
|
+
return Array.isArray(tools) ? tools.map(redactMcpToolDefinition) : tools;
|
|
555
|
+
}
|
|
556
|
+
//#endregion
|
|
426
557
|
//#region src/gatewayWarning.ts
|
|
427
558
|
const POSTHOG_AI_GATEWAY_HOSTS = [
|
|
428
559
|
"gateway.posthog.com",
|
|
@@ -485,13 +616,15 @@ const captureAiGeneration = async (client, options) => {
|
|
|
485
616
|
}
|
|
486
617
|
let exceptionId;
|
|
487
618
|
if (client.options?.enableExceptionAutocapture) {
|
|
488
|
-
|
|
489
|
-
|
|
619
|
+
if (!shouldRedact) {
|
|
620
|
+
exceptionId = uuidv7();
|
|
621
|
+
client.captureException(options.error, void 0, { $ai_trace_id: traceId }, exceptionId);
|
|
622
|
+
}
|
|
490
623
|
if (typeof options.error === "object") options.error.__posthog_previously_captured_error = true;
|
|
491
624
|
}
|
|
492
625
|
errorData = {
|
|
493
626
|
$ai_is_error: true,
|
|
494
|
-
$ai_error:
|
|
627
|
+
$ai_error: formatAiError(options.error, shouldRedact),
|
|
495
628
|
$exception_event_id: exceptionId
|
|
496
629
|
};
|
|
497
630
|
}
|
|
@@ -532,7 +665,7 @@ const captureAiGeneration = async (client, options) => {
|
|
|
532
665
|
$ai_tokens_source: getTokensSource(options.properties),
|
|
533
666
|
...options.distinctId ? {} : { $process_person_profile: false },
|
|
534
667
|
...options.stopReason ? { $ai_stop_reason: options.stopReason } : {},
|
|
535
|
-
...options.tools ? { $ai_tools: options.tools } : {},
|
|
668
|
+
...options.tools ? { $ai_tools: redactMcpToolCredentials(options.tools) } : {},
|
|
536
669
|
...options.completionId ? { $ai_completion_id: options.completionId } : {},
|
|
537
670
|
...options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? { $ai_provider_metadata: options.providerMetadata } : {},
|
|
538
671
|
...errorData,
|