Skip to content

AI Confirmation ​

Semantic confirmation is an opt-in layer that uses AI (Jev from TypeSafe's System One) to verify detected PII candidates before redacting them. It reduces false positives by asking a semantic model "yes/no" questions about each candidate.

Overview ​

  • Opt-in feature — no behavior change unless configured
  • Only affects redactAsync() — sync redact() and stream() are unaffected
  • Only detectors with a semanticConfirm() method participate
  • Currently only person_name_lite is opted in
  • Fails open: if the AI provider is unavailable, all detections are kept

Installation ​

The @typesafe-ai/sdk package is an optional peer dependency:

bash
bun add @typesafe-ai/sdk

Configuration ​

Add a semantic block to your RedactorConfig:

ts
import { createRedactor } from "sensored";

const redactor = createRedactor({
  rules: { person_name_lite: { action: "redact" } },
  semantic: {
    provider: "jev",
    apiKey: process.env.TYPESAFE_API_KEY!,
  },
});

const result = await redactor.redactAsync("Contact John Smith today");
// result.text: "Contact [PERSON_NAME] today"
// result.detections[0].semanticConfirmed: true
// result.detections[0].noul: 0.95

SemanticConfig ​

ts
interface SemanticConfig {
  readonly provider: "jev";
  readonly apiKey: string;
  readonly model?: string;
  readonly thresholds?: Readonly<Record<string, number>>;
  readonly contextWindow?: number;
}

Options ​

  • provider — Must be "jev". Only Jev is supported in v1.
  • apiKey — Your TypeSafe API key.
  • model — Jev model to use. Defaults to "jev-latest".
  • thresholds — Per-rule noul thresholds (0–1). Detections with noul below the threshold are dropped. Defaults to 0.5. Use "default" as a catch-all key.
  • contextWindow — Characters of context before and after each candidate sent to Jev. Defaults to 200.

Thresholds ​

ts
semantic: {
  provider: "jev",
  apiKey: process.env.TYPESAFE_API_KEY!,
  thresholds: {
    person_name_lite: 0.7,  // stricter for person names
    default: 0.5,           // fallback for other detectors
  },
}

redactAsync() ​

redactAsync() is the async variant of redact(). It runs sync detection first, then sends opted-in candidates to Jev for confirmation, drops unconfirmed detections, and renders the final text.

ts
const result = await redactor.redactAsync(text);

Returns AsyncRedactResult:

ts
interface AsyncRedactResult {
  readonly text: string;
  readonly map?: RestorationMap;
  readonly detections: readonly SemanticDetection[];
  readonly warnings?: readonly string[];
}
  • text — The redacted text (same format as redact()).
  • map — Present when restore: true is set in config.
  • detections — All detections that survived semantic confirmation. Each includes semanticConfirmed: boolean and optional noul: number.
  • warnings — Present when the AI provider fails. All detections are kept (fail open).

How it works ​

  1. Sync detection runs all active detectors (same as redact())
  2. Candidates from detectors with semanticConfirm() are collected
  3. If no candidates, early exit with all detections confirmed
  4. All candidates are batched into a single Jev call
  5. Detections with noul below threshold are dropped
  6. Remaining detections are rendered (same as redact())

Streaming ​

Streaming stays sync-only. If semantic config is present, stream() emits a console warning and proceeds without semantic confirmation.

Custom detectors ​

Custom detectors can opt in to semantic confirmation by providing a semanticConfirm function:

ts
const customDetector: DetectorDefinition = {
  id: "custom_entity",
  entityType: "custom_entity",
  replacement: "[CUSTOM_ENTITY]",
  pattern: /\b[A-Z]{3}\d{4}\b/g,
  semanticConfirm({ value, before, after }) {
    return {
      instructions: "Determine whether the candidate is a custom entity.",
      criteria: {
        true: "The candidate matches the expected format.",
        false: "The candidate is not a custom entity.",
      },
    };
  },
};

The semanticConfirm function receives { value, before, after } and returns a SemanticQuestion with instructions and optional criteria (with true and false strings).

Error handling ​

Semantic confirmation fails open. If the Jev API call fails (network error, invalid API key, rate limit, etc.), all detections are kept and a warning string is added to warnings[]. The redacted text is still returned.

ts
const result = await redactor.redactAsync("Contact John Smith today");
if (result.warnings?.length) {
  console.warn("Semantic confirmation failed:", result.warnings);
  // All detections kept — text is still redacted
}

Released under the MIT License.