@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/openai/index.cjs
CHANGED
|
@@ -232,6 +232,10 @@ const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redac
|
|
|
232
232
|
const sanitizeOpenAI = (data, client) => sanitize(data, client);
|
|
233
233
|
const sanitizeOpenAIResponse = (data, client) => sanitize(data, client);
|
|
234
234
|
//#endregion
|
|
235
|
+
//#region src/privacyMode.ts
|
|
236
|
+
/** Per-call settings can turn redaction on but never off. */
|
|
237
|
+
const isPrivacyModeEnabled = (client, privacyMode = false) => privacyMode === true || client?.privacyMode === true || client?.privacy_mode === true || client?.options?.privacyMode === true;
|
|
238
|
+
//#endregion
|
|
235
239
|
//#region src/utils.ts
|
|
236
240
|
const TOKEN_PROPERTY_KEYS = /* @__PURE__ */ new Set([
|
|
237
241
|
"$ai_input_tokens",
|
|
@@ -378,7 +382,6 @@ const formatResponseOpenAI = (response) => {
|
|
|
378
382
|
}
|
|
379
383
|
return output;
|
|
380
384
|
};
|
|
381
|
-
const isPrivacyModeEnabled = (client, privacyMode) => Boolean(client.privacy_mode || privacyMode);
|
|
382
385
|
const withPrivacyMode = (client, privacyMode, input) => {
|
|
383
386
|
return isPrivacyModeEnabled(client, privacyMode) ? null : input;
|
|
384
387
|
};
|
|
@@ -549,11 +552,12 @@ function formatOpenAIResponsesInput(input, instructions) {
|
|
|
549
552
|
}
|
|
550
553
|
//#endregion
|
|
551
554
|
//#region package.json
|
|
552
|
-
var version = "8.16.
|
|
555
|
+
var version = "8.16.3";
|
|
553
556
|
//#endregion
|
|
554
557
|
//#region src/serializeError.ts
|
|
555
558
|
const DEFAULT_MAX_DEPTH = 3;
|
|
556
559
|
const MAX_STACK_LINES = 20;
|
|
560
|
+
const OMITTED_FIELD = "requestBodyValues";
|
|
557
561
|
function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
558
562
|
if (depth < 0 || value === null || typeof value !== "object") return value;
|
|
559
563
|
if (value instanceof Error) {
|
|
@@ -562,16 +566,17 @@ function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
|
562
566
|
message: value.message,
|
|
563
567
|
stack: truncateStack(value.stack)
|
|
564
568
|
};
|
|
565
|
-
for (const key of Object.keys(value)) out[key] = serializeError(value
|
|
569
|
+
for (const key of Object.keys(value)) if (key !== OMITTED_FIELD) out[key] = serializeError(Reflect.get(value, key), depth - 1);
|
|
566
570
|
if (value.cause !== void 0) out.cause = serializeError(value.cause, depth - 1);
|
|
567
571
|
return out;
|
|
568
572
|
}
|
|
569
573
|
if (Array.isArray(value)) return value.map((item) => serializeError(item, depth - 1));
|
|
570
574
|
return value;
|
|
571
575
|
}
|
|
576
|
+
const omitRequestBody = (key, value) => key === OMITTED_FIELD ? void 0 : value;
|
|
572
577
|
function stringifyError(error) {
|
|
573
578
|
try {
|
|
574
|
-
return JSON.stringify(sanitizeValues(serializeError(error)));
|
|
579
|
+
return JSON.stringify(sanitizeValues(serializeError(error)), omitRequestBody);
|
|
575
580
|
} catch {
|
|
576
581
|
if (error instanceof Error) return JSON.stringify({
|
|
577
582
|
name: error.name,
|
|
@@ -580,6 +585,105 @@ function stringifyError(error) {
|
|
|
580
585
|
return JSON.stringify({ message: String(error) });
|
|
581
586
|
}
|
|
582
587
|
}
|
|
588
|
+
const CLASS_NAME = /^[A-Za-z_$][A-Za-z0-9_$.]{0,127}$/;
|
|
589
|
+
const PROVIDER_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
590
|
+
const PARAM = /^[A-Za-z0-9_.[\]-]{1,128}$/;
|
|
591
|
+
const REQUEST_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
592
|
+
const MAX_CAUSES = 3;
|
|
593
|
+
const readField = (value, key) => {
|
|
594
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return;
|
|
595
|
+
try {
|
|
596
|
+
return Reflect.get(value, key);
|
|
597
|
+
} catch {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
const matching = (value, grammar) => typeof value === "string" && grammar.test(value) ? value : void 0;
|
|
602
|
+
const firstMatching = (values, grammar) => {
|
|
603
|
+
for (const value of values) {
|
|
604
|
+
const match = matching(value, grammar);
|
|
605
|
+
if (match !== void 0) return match;
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
const className = (value) => {
|
|
609
|
+
if (!(value instanceof Error)) return "NonErrorThrown";
|
|
610
|
+
const name = readField(value, "name");
|
|
611
|
+
return firstMatching([
|
|
612
|
+
name === "Error" ? void 0 : name,
|
|
613
|
+
readField(readField(value, "constructor"), "name"),
|
|
614
|
+
name
|
|
615
|
+
], CLASS_NAME) ?? "Error";
|
|
616
|
+
};
|
|
617
|
+
const httpStatus = (error) => {
|
|
618
|
+
for (const key of ["status", "statusCode"]) {
|
|
619
|
+
const value = readField(error, key);
|
|
620
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599) return value;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
const unwrapEnvelope = (body) => {
|
|
624
|
+
const inner = readField(body, "error");
|
|
625
|
+
return inner !== null && typeof inner === "object" ? inner : body;
|
|
626
|
+
};
|
|
627
|
+
const parsedResponseBody = (error) => {
|
|
628
|
+
const responseBody = readField(error, "responseBody");
|
|
629
|
+
if (typeof responseBody !== "string") return;
|
|
630
|
+
try {
|
|
631
|
+
return unwrapEnvelope(JSON.parse(responseBody));
|
|
632
|
+
} catch {
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
const causeNames = (error) => {
|
|
637
|
+
const names = [];
|
|
638
|
+
let cause = readField(error, "cause");
|
|
639
|
+
while (cause !== void 0 && cause !== null && names.length < MAX_CAUSES) {
|
|
640
|
+
names.push(className(cause));
|
|
641
|
+
cause = readField(cause, "cause");
|
|
642
|
+
}
|
|
643
|
+
return names;
|
|
644
|
+
};
|
|
645
|
+
const describeError = (error) => {
|
|
646
|
+
const sources = [
|
|
647
|
+
error,
|
|
648
|
+
unwrapEnvelope(readField(error, "error")),
|
|
649
|
+
parsedResponseBody(error)
|
|
650
|
+
];
|
|
651
|
+
const codes = /* @__PURE__ */ new Set();
|
|
652
|
+
for (const source of sources) for (const key of [
|
|
653
|
+
"code",
|
|
654
|
+
"type",
|
|
655
|
+
"status"
|
|
656
|
+
]) {
|
|
657
|
+
const code = matching(readField(source, key), PROVIDER_CODE);
|
|
658
|
+
if (code !== void 0) codes.add(code);
|
|
659
|
+
}
|
|
660
|
+
const status = httpStatus(error);
|
|
661
|
+
const param = firstMatching(sources.map((source) => readField(source, "param")), PARAM);
|
|
662
|
+
const requestId = firstMatching([readField(error, "requestID"), readField(error, "request_id")], REQUEST_ID);
|
|
663
|
+
const causes = causeNames(error);
|
|
664
|
+
let summary = className(error);
|
|
665
|
+
if (status !== void 0) summary += `: ${status}`;
|
|
666
|
+
for (const code of codes) summary += ` ${code}`;
|
|
667
|
+
if (param !== void 0) summary += ` param=${param}`;
|
|
668
|
+
if (requestId !== void 0) summary += ` request_id=${requestId}`;
|
|
669
|
+
if (causes.length > 0) summary += ` (caused by ${causes.join(", ")})`;
|
|
670
|
+
return summary;
|
|
671
|
+
};
|
|
672
|
+
/**
|
|
673
|
+
* Describes an error for `$ai_error` without its message, stack, or arbitrary fields, which
|
|
674
|
+
* routinely echo the prompt. Only structured fields that pass a strict grammar are kept.
|
|
675
|
+
* The format matches the Python SDK.
|
|
676
|
+
*/
|
|
677
|
+
function summarizeErrorForPrivacy(error) {
|
|
678
|
+
try {
|
|
679
|
+
return describeError(error);
|
|
680
|
+
} catch {
|
|
681
|
+
return error instanceof Error ? "Error" : "NonErrorThrown";
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function formatAiError(error, privacyMode) {
|
|
685
|
+
return privacyMode ? summarizeErrorForPrivacy(error) : stringifyError(error);
|
|
686
|
+
}
|
|
583
687
|
function truncateStack(stack) {
|
|
584
688
|
if (!stack) return stack;
|
|
585
689
|
const lines = stack.split("\n");
|
|
@@ -587,6 +691,33 @@ function truncateStack(stack) {
|
|
|
587
691
|
return [...lines.slice(0, MAX_STACK_LINES), "... (truncated)"].join("\n");
|
|
588
692
|
}
|
|
589
693
|
//#endregion
|
|
694
|
+
//#region src/mcpCredentials.ts
|
|
695
|
+
const MCP_CREDENTIAL_FIELDS = ["headers", "authorization"];
|
|
696
|
+
const MCP_PROVIDER_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
697
|
+
"openai.mcp",
|
|
698
|
+
"xai.mcp",
|
|
699
|
+
"google.mcp_server"
|
|
700
|
+
]);
|
|
701
|
+
const isMcpProviderTool = (tool) => (tool.type === "provider" || tool.type === "provider-defined") && typeof tool.id === "string" && MCP_PROVIDER_TOOL_IDS.has(tool.id);
|
|
702
|
+
const withoutMcpCredentials = (config) => {
|
|
703
|
+
const redacted = { ...config };
|
|
704
|
+
for (const field of MCP_CREDENTIAL_FIELDS) if (field in redacted) redacted[field] = "[REDACTED]";
|
|
705
|
+
return redacted;
|
|
706
|
+
};
|
|
707
|
+
/** Hides the credentials of a remote MCP tool definition; everything else is kept as sent. */
|
|
708
|
+
function redactMcpToolDefinition(tool) {
|
|
709
|
+
if (!isObject(tool)) return tool;
|
|
710
|
+
if (tool.type === "mcp") return withoutMcpCredentials(tool);
|
|
711
|
+
if (isMcpProviderTool(tool) && isObject(tool.args)) return {
|
|
712
|
+
...tool,
|
|
713
|
+
args: withoutMcpCredentials(tool.args)
|
|
714
|
+
};
|
|
715
|
+
return tool;
|
|
716
|
+
}
|
|
717
|
+
function redactMcpToolCredentials(tools) {
|
|
718
|
+
return Array.isArray(tools) ? tools.map(redactMcpToolDefinition) : tools;
|
|
719
|
+
}
|
|
720
|
+
//#endregion
|
|
590
721
|
//#region src/gatewayWarning.ts
|
|
591
722
|
const POSTHOG_AI_GATEWAY_HOSTS = [
|
|
592
723
|
"gateway.posthog.com",
|
|
@@ -649,13 +780,15 @@ const captureAiGeneration$1 = async (client, options) => {
|
|
|
649
780
|
}
|
|
650
781
|
let exceptionId;
|
|
651
782
|
if (client.options?.enableExceptionAutocapture) {
|
|
652
|
-
|
|
653
|
-
|
|
783
|
+
if (!shouldRedact) {
|
|
784
|
+
exceptionId = (0, _posthog_core.uuidv7)();
|
|
785
|
+
client.captureException(options.error, void 0, { $ai_trace_id: traceId }, exceptionId);
|
|
786
|
+
}
|
|
654
787
|
if (typeof options.error === "object") options.error.__posthog_previously_captured_error = true;
|
|
655
788
|
}
|
|
656
789
|
errorData = {
|
|
657
790
|
$ai_is_error: true,
|
|
658
|
-
$ai_error:
|
|
791
|
+
$ai_error: formatAiError(options.error, shouldRedact),
|
|
659
792
|
$exception_event_id: exceptionId
|
|
660
793
|
};
|
|
661
794
|
}
|
|
@@ -696,7 +829,7 @@ const captureAiGeneration$1 = async (client, options) => {
|
|
|
696
829
|
$ai_tokens_source: getTokensSource(options.properties),
|
|
697
830
|
...options.distinctId ? {} : { $process_person_profile: false },
|
|
698
831
|
...options.stopReason ? { $ai_stop_reason: options.stopReason } : {},
|
|
699
|
-
...options.tools ? { $ai_tools: options.tools } : {},
|
|
832
|
+
...options.tools ? { $ai_tools: redactMcpToolCredentials(options.tools) } : {},
|
|
700
833
|
...options.completionId ? { $ai_completion_id: options.completionId } : {},
|
|
701
834
|
...options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? { $ai_provider_metadata: options.providerMetadata } : {},
|
|
702
835
|
...errorData,
|
|
@@ -1174,8 +1307,8 @@ var OpenAIChatStreamAccumulator = class {
|
|
|
1174
1307
|
if (Array.isArray(choice?.delta?.tool_calls)) {
|
|
1175
1308
|
this.firstTokenTime ??= receivedAt;
|
|
1176
1309
|
for (const toolCall of choice.delta.tool_calls) {
|
|
1177
|
-
|
|
1178
|
-
const current = this.toolCalls.get(
|
|
1310
|
+
const key = this.toolCallKey(toolCall);
|
|
1311
|
+
const current = this.toolCalls.get(key) ?? {
|
|
1179
1312
|
id: "",
|
|
1180
1313
|
name: "",
|
|
1181
1314
|
arguments: ""
|
|
@@ -1183,7 +1316,8 @@ var OpenAIChatStreamAccumulator = class {
|
|
|
1183
1316
|
if (toolCall.id) current.id = toolCall.id;
|
|
1184
1317
|
if (toolCall.function?.name) current.name = toolCall.function.name;
|
|
1185
1318
|
if (toolCall.function?.arguments) current.arguments += toolCall.function.arguments;
|
|
1186
|
-
this.toolCalls.set(
|
|
1319
|
+
this.toolCalls.set(key, current);
|
|
1320
|
+
this.lastToolCallKey = key;
|
|
1187
1321
|
}
|
|
1188
1322
|
}
|
|
1189
1323
|
if (chunk.usage) this.usage = {
|
|
@@ -1196,6 +1330,18 @@ var OpenAIChatStreamAccumulator = class {
|
|
|
1196
1330
|
rawUsage: chunk.usage
|
|
1197
1331
|
};
|
|
1198
1332
|
}
|
|
1333
|
+
/**
|
|
1334
|
+
* Some OpenAI-compatible providers stream tool calls without an `index`. Match those deltas by a known
|
|
1335
|
+
* `id`; a new `id` or a function name opens a new call, and anything else continues the latest call.
|
|
1336
|
+
*/
|
|
1337
|
+
toolCallKey(toolCall) {
|
|
1338
|
+
if (toolCall.index !== void 0) return toolCall.index;
|
|
1339
|
+
if (toolCall.id) {
|
|
1340
|
+
for (const [key, existing] of this.toolCalls) if (existing.id === toolCall.id) return key;
|
|
1341
|
+
}
|
|
1342
|
+
if (toolCall.id || toolCall.function?.name || this.lastToolCallKey === void 0) return `no-index-${this.toolCalls.size}`;
|
|
1343
|
+
return this.lastToolCallKey;
|
|
1344
|
+
}
|
|
1199
1345
|
result() {
|
|
1200
1346
|
const content = [];
|
|
1201
1347
|
if (this.accumulatedContent) content.push({
|