@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/adk/index.mjs
CHANGED
|
@@ -2,7 +2,12 @@ import { BasePlugin } from "@google/adk";
|
|
|
2
2
|
import { v4 } from "uuid";
|
|
3
3
|
import { toJsonSafeValue, uuidv7 } from "@posthog/core";
|
|
4
4
|
//#region package.json
|
|
5
|
-
var version = "8.16.
|
|
5
|
+
var version = "8.16.2";
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region src/typeGuards.ts
|
|
8
|
+
const isObject = (value) => {
|
|
9
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
};
|
|
6
11
|
//#endregion
|
|
7
12
|
//#region src/captureAiEvent.ts
|
|
8
13
|
/** @internal */
|
|
@@ -224,6 +229,10 @@ function redactBase64DataUrl(str, mediaType) {
|
|
|
224
229
|
const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
|
|
225
230
|
const sanitizeGemini = (data, client) => sanitize(data, client);
|
|
226
231
|
//#endregion
|
|
232
|
+
//#region src/privacyMode.ts
|
|
233
|
+
/** Per-call settings can turn redaction on but never off. */
|
|
234
|
+
const isPrivacyModeEnabled = (client, privacyMode = false) => privacyMode === true || client?.privacyMode === true || client?.privacy_mode === true || client?.options?.privacyMode === true;
|
|
235
|
+
//#endregion
|
|
227
236
|
//#region src/utils.ts
|
|
228
237
|
const TOKEN_PROPERTY_KEYS = /* @__PURE__ */ new Set([
|
|
229
238
|
"$ai_input_tokens",
|
|
@@ -347,7 +356,6 @@ const formatResponseGemini = (response, client) => {
|
|
|
347
356
|
});
|
|
348
357
|
return output;
|
|
349
358
|
};
|
|
350
|
-
const isPrivacyModeEnabled = (client, privacyMode) => Boolean(client.privacy_mode || privacyMode);
|
|
351
359
|
const withPrivacyMode = (client, privacyMode, input) => {
|
|
352
360
|
return isPrivacyModeEnabled(client, privacyMode) ? null : input;
|
|
353
361
|
};
|
|
@@ -363,6 +371,7 @@ function sanitizeValues(obj) {
|
|
|
363
371
|
//#region src/serializeError.ts
|
|
364
372
|
const DEFAULT_MAX_DEPTH = 3;
|
|
365
373
|
const MAX_STACK_LINES = 20;
|
|
374
|
+
const OMITTED_FIELD = "requestBodyValues";
|
|
366
375
|
function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
367
376
|
if (depth < 0 || value === null || typeof value !== "object") return value;
|
|
368
377
|
if (value instanceof Error) {
|
|
@@ -371,16 +380,17 @@ function serializeError(value, depth = DEFAULT_MAX_DEPTH) {
|
|
|
371
380
|
message: value.message,
|
|
372
381
|
stack: truncateStack(value.stack)
|
|
373
382
|
};
|
|
374
|
-
for (const key of Object.keys(value)) out[key] = serializeError(value
|
|
383
|
+
for (const key of Object.keys(value)) if (key !== OMITTED_FIELD) out[key] = serializeError(Reflect.get(value, key), depth - 1);
|
|
375
384
|
if (value.cause !== void 0) out.cause = serializeError(value.cause, depth - 1);
|
|
376
385
|
return out;
|
|
377
386
|
}
|
|
378
387
|
if (Array.isArray(value)) return value.map((item) => serializeError(item, depth - 1));
|
|
379
388
|
return value;
|
|
380
389
|
}
|
|
390
|
+
const omitRequestBody = (key, value) => key === OMITTED_FIELD ? void 0 : value;
|
|
381
391
|
function stringifyError(error) {
|
|
382
392
|
try {
|
|
383
|
-
return JSON.stringify(sanitizeValues(serializeError(error)));
|
|
393
|
+
return JSON.stringify(sanitizeValues(serializeError(error)), omitRequestBody);
|
|
384
394
|
} catch {
|
|
385
395
|
if (error instanceof Error) return JSON.stringify({
|
|
386
396
|
name: error.name,
|
|
@@ -389,6 +399,105 @@ function stringifyError(error) {
|
|
|
389
399
|
return JSON.stringify({ message: String(error) });
|
|
390
400
|
}
|
|
391
401
|
}
|
|
402
|
+
const CLASS_NAME = /^[A-Za-z_$][A-Za-z0-9_$.]{0,127}$/;
|
|
403
|
+
const PROVIDER_CODE = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
404
|
+
const PARAM = /^[A-Za-z0-9_.[\]-]{1,128}$/;
|
|
405
|
+
const REQUEST_ID = /^[A-Za-z0-9_.:-]{1,128}$/;
|
|
406
|
+
const MAX_CAUSES = 3;
|
|
407
|
+
const readField = (value, key) => {
|
|
408
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return;
|
|
409
|
+
try {
|
|
410
|
+
return Reflect.get(value, key);
|
|
411
|
+
} catch {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
const matching = (value, grammar) => typeof value === "string" && grammar.test(value) ? value : void 0;
|
|
416
|
+
const firstMatching = (values, grammar) => {
|
|
417
|
+
for (const value of values) {
|
|
418
|
+
const match = matching(value, grammar);
|
|
419
|
+
if (match !== void 0) return match;
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
const className = (value) => {
|
|
423
|
+
if (!(value instanceof Error)) return "NonErrorThrown";
|
|
424
|
+
const name = readField(value, "name");
|
|
425
|
+
return firstMatching([
|
|
426
|
+
name === "Error" ? void 0 : name,
|
|
427
|
+
readField(readField(value, "constructor"), "name"),
|
|
428
|
+
name
|
|
429
|
+
], CLASS_NAME) ?? "Error";
|
|
430
|
+
};
|
|
431
|
+
const httpStatus = (error) => {
|
|
432
|
+
for (const key of ["status", "statusCode"]) {
|
|
433
|
+
const value = readField(error, key);
|
|
434
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599) return value;
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
const unwrapEnvelope = (body) => {
|
|
438
|
+
const inner = readField(body, "error");
|
|
439
|
+
return inner !== null && typeof inner === "object" ? inner : body;
|
|
440
|
+
};
|
|
441
|
+
const parsedResponseBody = (error) => {
|
|
442
|
+
const responseBody = readField(error, "responseBody");
|
|
443
|
+
if (typeof responseBody !== "string") return;
|
|
444
|
+
try {
|
|
445
|
+
return unwrapEnvelope(JSON.parse(responseBody));
|
|
446
|
+
} catch {
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
const causeNames = (error) => {
|
|
451
|
+
const names = [];
|
|
452
|
+
let cause = readField(error, "cause");
|
|
453
|
+
while (cause !== void 0 && cause !== null && names.length < MAX_CAUSES) {
|
|
454
|
+
names.push(className(cause));
|
|
455
|
+
cause = readField(cause, "cause");
|
|
456
|
+
}
|
|
457
|
+
return names;
|
|
458
|
+
};
|
|
459
|
+
const describeError = (error) => {
|
|
460
|
+
const sources = [
|
|
461
|
+
error,
|
|
462
|
+
unwrapEnvelope(readField(error, "error")),
|
|
463
|
+
parsedResponseBody(error)
|
|
464
|
+
];
|
|
465
|
+
const codes = /* @__PURE__ */ new Set();
|
|
466
|
+
for (const source of sources) for (const key of [
|
|
467
|
+
"code",
|
|
468
|
+
"type",
|
|
469
|
+
"status"
|
|
470
|
+
]) {
|
|
471
|
+
const code = matching(readField(source, key), PROVIDER_CODE);
|
|
472
|
+
if (code !== void 0) codes.add(code);
|
|
473
|
+
}
|
|
474
|
+
const status = httpStatus(error);
|
|
475
|
+
const param = firstMatching(sources.map((source) => readField(source, "param")), PARAM);
|
|
476
|
+
const requestId = firstMatching([readField(error, "requestID"), readField(error, "request_id")], REQUEST_ID);
|
|
477
|
+
const causes = causeNames(error);
|
|
478
|
+
let summary = className(error);
|
|
479
|
+
if (status !== void 0) summary += `: ${status}`;
|
|
480
|
+
for (const code of codes) summary += ` ${code}`;
|
|
481
|
+
if (param !== void 0) summary += ` param=${param}`;
|
|
482
|
+
if (requestId !== void 0) summary += ` request_id=${requestId}`;
|
|
483
|
+
if (causes.length > 0) summary += ` (caused by ${causes.join(", ")})`;
|
|
484
|
+
return summary;
|
|
485
|
+
};
|
|
486
|
+
/**
|
|
487
|
+
* Describes an error for `$ai_error` without its message, stack, or arbitrary fields, which
|
|
488
|
+
* routinely echo the prompt. Only structured fields that pass a strict grammar are kept.
|
|
489
|
+
* The format matches the Python SDK.
|
|
490
|
+
*/
|
|
491
|
+
function summarizeErrorForPrivacy(error) {
|
|
492
|
+
try {
|
|
493
|
+
return describeError(error);
|
|
494
|
+
} catch {
|
|
495
|
+
return error instanceof Error ? "Error" : "NonErrorThrown";
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function formatAiError(error, privacyMode) {
|
|
499
|
+
return privacyMode ? summarizeErrorForPrivacy(error) : stringifyError(error);
|
|
500
|
+
}
|
|
392
501
|
function truncateStack(stack) {
|
|
393
502
|
if (!stack) return stack;
|
|
394
503
|
const lines = stack.split("\n");
|
|
@@ -396,6 +505,33 @@ function truncateStack(stack) {
|
|
|
396
505
|
return [...lines.slice(0, MAX_STACK_LINES), "... (truncated)"].join("\n");
|
|
397
506
|
}
|
|
398
507
|
//#endregion
|
|
508
|
+
//#region src/mcpCredentials.ts
|
|
509
|
+
const MCP_CREDENTIAL_FIELDS = ["headers", "authorization"];
|
|
510
|
+
const MCP_PROVIDER_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
511
|
+
"openai.mcp",
|
|
512
|
+
"xai.mcp",
|
|
513
|
+
"google.mcp_server"
|
|
514
|
+
]);
|
|
515
|
+
const isMcpProviderTool = (tool) => (tool.type === "provider" || tool.type === "provider-defined") && typeof tool.id === "string" && MCP_PROVIDER_TOOL_IDS.has(tool.id);
|
|
516
|
+
const withoutMcpCredentials = (config) => {
|
|
517
|
+
const redacted = { ...config };
|
|
518
|
+
for (const field of MCP_CREDENTIAL_FIELDS) if (field in redacted) redacted[field] = "[REDACTED]";
|
|
519
|
+
return redacted;
|
|
520
|
+
};
|
|
521
|
+
/** Hides the credentials of a remote MCP tool definition; everything else is kept as sent. */
|
|
522
|
+
function redactMcpToolDefinition(tool) {
|
|
523
|
+
if (!isObject(tool)) return tool;
|
|
524
|
+
if (tool.type === "mcp") return withoutMcpCredentials(tool);
|
|
525
|
+
if (isMcpProviderTool(tool) && isObject(tool.args)) return {
|
|
526
|
+
...tool,
|
|
527
|
+
args: withoutMcpCredentials(tool.args)
|
|
528
|
+
};
|
|
529
|
+
return tool;
|
|
530
|
+
}
|
|
531
|
+
function redactMcpToolCredentials(tools) {
|
|
532
|
+
return Array.isArray(tools) ? tools.map(redactMcpToolDefinition) : tools;
|
|
533
|
+
}
|
|
534
|
+
//#endregion
|
|
399
535
|
//#region src/gatewayWarning.ts
|
|
400
536
|
const POSTHOG_AI_GATEWAY_HOSTS = [
|
|
401
537
|
"gateway.posthog.com",
|
|
@@ -458,13 +594,15 @@ const captureAiGeneration = async (client, options) => {
|
|
|
458
594
|
}
|
|
459
595
|
let exceptionId;
|
|
460
596
|
if (client.options?.enableExceptionAutocapture) {
|
|
461
|
-
|
|
462
|
-
|
|
597
|
+
if (!shouldRedact) {
|
|
598
|
+
exceptionId = uuidv7();
|
|
599
|
+
client.captureException(options.error, void 0, { $ai_trace_id: traceId }, exceptionId);
|
|
600
|
+
}
|
|
463
601
|
if (typeof options.error === "object") options.error.__posthog_previously_captured_error = true;
|
|
464
602
|
}
|
|
465
603
|
errorData = {
|
|
466
604
|
$ai_is_error: true,
|
|
467
|
-
$ai_error:
|
|
605
|
+
$ai_error: formatAiError(options.error, shouldRedact),
|
|
468
606
|
$exception_event_id: exceptionId
|
|
469
607
|
};
|
|
470
608
|
}
|
|
@@ -505,7 +643,7 @@ const captureAiGeneration = async (client, options) => {
|
|
|
505
643
|
$ai_tokens_source: getTokensSource(options.properties),
|
|
506
644
|
...options.distinctId ? {} : { $process_person_profile: false },
|
|
507
645
|
...options.stopReason ? { $ai_stop_reason: options.stopReason } : {},
|
|
508
|
-
...options.tools ? { $ai_tools: options.tools } : {},
|
|
646
|
+
...options.tools ? { $ai_tools: redactMcpToolCredentials(options.tools) } : {},
|
|
509
647
|
...options.completionId ? { $ai_completion_id: options.completionId } : {},
|
|
510
648
|
...options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? { $ai_provider_metadata: options.providerMetadata } : {},
|
|
511
649
|
...errorData,
|
|
@@ -805,7 +943,7 @@ var PostHogADKPlugin = class extends BasePlugin {
|
|
|
805
943
|
...context.agentName ? { $ai_agent_name: context.agentName } : {},
|
|
806
944
|
...error ? {
|
|
807
945
|
$ai_is_error: true,
|
|
808
|
-
$ai_error:
|
|
946
|
+
$ai_error: formatAiError(error, isPrivacyModeEnabled(this._client, this._privacyMode))
|
|
809
947
|
} : {}
|
|
810
948
|
});
|
|
811
949
|
}
|