redact 0.1.0 copy "redact: ^0.1.0" to clipboard
redact: ^0.1.0 copied to clipboard

On-device, precision-first PII redaction for Dart and Flutter. Scrub emails, phones, cards, SSNs and more from text before it reaches any LLM, then restore the model's reply.

example/redact_example.dart

// Run with: dart run example/redact_example.dart
//
// Demonstrates the five things `redact` is for:
//   1. Scrubbing PII out of text before it reaches an LLM.
//   2. The reversible pipeline: redact -> call the model -> restore the reply.
//   3. Per-category styles (mask a card, placeholder everything else).
//   4. Extending detection with your own domain-specific Detector.
//   5. Multi-turn conversations with a RedactionSession.

import 'package:redact/redact.dart';

void main() {
  _basic();
  _reversiblePipeline();
  _perCategoryStyles();
  _customDetector();
  _conversation();
}

void _basic() {
  print('== 1. Scrub before sending ==');
  final redactor = Redactor();
  final result = redactor.redact(
    'Hi, I am Jane. Email jane@acme.com or call +1 415-555-0132. '
    'Card 4111 1111 1111 1111.',
  );
  print(result.text);
  print('Detected ${result.count} item(s): '
      '${result.types.map((t) => t.name).join(', ')}\n');
}

void _reversiblePipeline() {
  print('== 2. Reversible pipeline ==');
  final redactor = Redactor();
  const userMessage = 'Reset the account for john.doe@example.com please.';

  final result = redactor.redact(userMessage);
  print('Sent to model : ${result.text}');

  // The model only ever sees placeholders; it echoes them back.
  final modelReply = _fakeLlm(result.text);
  print('Model replied : $modelReply');

  final restored = result.restore(modelReply);
  print('Shown to user : $restored\n');
}

void _perCategoryStyles() {
  print('== 3. Per-category styles ==');
  final redactor = Redactor(
    styleOverrides: {PiiType.creditCard: RedactionStyle.mask},
  );
  final result = redactor.redact(
    'Charge 5500 0055 5555 5559 and email the receipt to a@b.com',
  );
  print('${result.text}\n');
}

void _customDetector() {
  print('== 4. Custom detector ==');
  // A hospital-specific medical record number, e.g. "MRN-004512".
  final mrn = PatternDetector(
    name: 'mrn',
    type: PiiType.custom,
    pattern: RegExp(r'\bMRN-\d{4,8}\b'),
    label: 'PATIENT_ID',
  );
  final redactor = Redactor(detectors: [mrn, ...Detectors.defaults]);
  final result =
      redactor.redact('Patient MRN-004512 booked with dr@clinic.org');
  print(result.text);
}

void _conversation() {
  print('== 5. Multi-turn conversation ==');
  // A plain Redactor would call BOTH addresses [EMAIL_1] (one per call) —
  // ambiguous for the model, wrong on restore. A session keeps one vault.
  final session = RedactionSession();
  print('turn 1: ${session.redact('alice@x.com requested a refund').text}');
  print('turn 2: ${session.redact('bob@x.com must approve it').text}');
  // The model replies referencing both turns; restore knows every token:
  print('reply : ${session.restore('Ask [EMAIL_2] to confirm [EMAIL_1].')}');
}

/// A stand-in for a real LLM call. It just echoes a placeholder it was given,
/// proving the model never touches the original PII.
String _fakeLlm(String prompt) {
  final token = RegExp(r'\[EMAIL_\d+\]').firstMatch(prompt)?.group(0) ?? '';
  return 'Done. A reset link was sent to $token.';
}
1
likes
160
points
8
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

On-device, precision-first PII redaction for Dart and Flutter. Scrub emails, phones, cards, SSNs and more from text before it reaches any LLM, then restore the model's reply.

Repository (GitHub)
View/report issues

Topics

#privacy #security #llm #pii #redaction

License

MIT (license)

More

Packages that depend on redact