|
| 1 | +import iso from "../../isomorph"; |
| 2 | + |
| 3 | +/* |
| 4 | + * LiveKit's own PII rule for its telemetry (telemetry/redaction.js and pii.js |
| 5 | + * in @livekit/agents 1.9.x), so the trace omits exactly what LiveKit would. |
| 6 | + */ |
| 7 | + |
| 8 | +/** The span attribute with which LiveKit marks a span of a redacted job. */ |
| 9 | +export const REDACTION_ENABLED = "lk.redaction.enabled"; |
| 10 | + |
| 11 | +/** The text LiveKit substitutes for exception details when PII is not allowed. */ |
| 12 | +export const REDACTED_EXCEPTION_MESSAGE = "exception details redacted"; |
| 13 | + |
| 14 | +const PII_SEGMENT = /(^|\.)pii(\.|$)/i; |
| 15 | +// Flagged by the gen_ai semantic conventions as likely to contain user data. |
| 16 | +const GEN_AI_PII_ATTRIBUTES = new Set([ |
| 17 | + "gen_ai.input.messages", |
| 18 | + "gen_ai.output.messages", |
| 19 | + "gen_ai.system_instructions", |
| 20 | + "gen_ai.tool.call.arguments", |
| 21 | + "gen_ai.tool.call.result", |
| 22 | + "gen_ai.tool.description", |
| 23 | + "gen_ai.tool.definitions", |
| 24 | + "gen_ai.retrieval.query.text", |
| 25 | + "gen_ai.retrieval.documents", |
| 26 | + "gen_ai.memory.query.text", |
| 27 | + "gen_ai.memory.records", |
| 28 | + "gen_ai.evaluation.explanation", |
| 29 | +]); |
| 30 | +const PII_EVENTS = new Set([ |
| 31 | + "gen_ai.system.message", |
| 32 | + "gen_ai.user.message", |
| 33 | + "gen_ai.assistant.message", |
| 34 | + "gen_ai.tool.message", |
| 35 | + "gen_ai.choice", |
| 36 | + "gen_ai.client.inference.operation.details", |
| 37 | +]); |
| 38 | +const EXCEPTION_MESSAGE = "exception.message"; |
| 39 | +const EXCEPTION_STACKTRACE = "exception.stacktrace"; |
| 40 | +const FALSY = new Set(["0", "false", "no", "off"]); |
| 41 | + |
| 42 | +/** |
| 43 | + * Whether LiveKit's configuration allows PII in its telemetry: allowed unless |
| 44 | + * LIVEKIT_TELEMETRY_ALLOW_PII is falsy, or the job enables LiveKit Cloud |
| 45 | + * project redaction (which wins over the environment). |
| 46 | + */ |
| 47 | +export function livekitPiiAllowed(job?: { _redactionEnabled?: boolean }) { |
| 48 | + if (job?._redactionEnabled) return false; |
| 49 | + const allow = iso.getEnv("LIVEKIT_TELEMETRY_ALLOW_PII"); |
| 50 | + return allow === undefined || !FALSY.has(allow.trim().toLowerCase()); |
| 51 | +} |
| 52 | + |
| 53 | +export function isPiiEvent(name: string): boolean { |
| 54 | + return PII_EVENTS.has(name); |
| 55 | +} |
| 56 | + |
| 57 | +/** Native attributes without PII, and with exception details replaced, as LiveKit does. */ |
| 58 | +export function withoutPii( |
| 59 | + attributes: Iterable<[string, unknown]>, |
| 60 | +): Map<string, unknown> { |
| 61 | + const kept = new Map<string, unknown>(); |
| 62 | + for (const [key, value] of attributes) { |
| 63 | + if (key === EXCEPTION_MESSAGE) kept.set(key, REDACTED_EXCEPTION_MESSAGE); |
| 64 | + else if ( |
| 65 | + key !== EXCEPTION_STACKTRACE && |
| 66 | + !PII_SEGMENT.test(key) && |
| 67 | + !GEN_AI_PII_ATTRIBUTES.has(key) && |
| 68 | + !key.startsWith("gen_ai.prompt.variable") |
| 69 | + ) |
| 70 | + kept.set(key, value); |
| 71 | + } |
| 72 | + return kept; |
| 73 | +} |
0 commit comments