Skip to content

Unplug SDK Architecture

Unplug the bad AI.

Public-safe overview of the unplug-ai Python SDK (import unplug).

Layering

flowchart TB
  Guard[Guard guard.py]
  Pipelines[pipelines/]
  Scanners[scanners/]
  Core[core/ subpackages]
  Data[data/ YAML TOML YARA]

  Guard --> Pipelines
  Pipelines --> Scanners
  Scanners --> Core
  Scanners --> Data

Rule: Guard → Pipelines → Scanners → Core. Never skip layers.

Layer Role
guard.py Entry point: config, registry, scan API, session taint
pipelines/ Input, output, tool-call orchestration; fail-closed wrapper
scanners/ Detection: regex, YARA, Presidio PII, ML span
core/ Engine primitives: taint, normalize, policy, agent hardening, privacy, runtime
data/ Packaged patterns (YAML), maps (TOML), YARA rules: no Python logic
optional/ Fail-loud import boundaries for extras
ml/ Span models, HF catalog, checkpoint store
config/ Pydantic GuardConfig, loader, policy, limits
api/ Wire types: Finding, ScanResult, Action, Source

Trust and taint

TaintedText carries provenance (TrustLevel: USER, TRUSTED, TOOL_OUTPUT, RETRIEVED, EXTERNAL, UNKNOWN). Scanners gate on trust, e.g. leakage skips USER/TRUSTED; harmful scans tool output and retrieved content.

Session taint tracks cross-turn contamination from fetch/RAG tools.

Fail-closed

Scanner or pipeline errors produce a full-span Finding with stage="error" and score=1.0 and never allow silently.

Optional extras

Extra Module Used by
presidio optional/presidio.py scanners/pii.py
yara optional/yara.py scanners/yara_scanner.py
ml optional/ml.py ml/store.py, span models
haystack integrations/haystack.py (lazy Haystack import) RAG document guard
litellm optional/litellm.py judge/litellm_judge.py
scrape optional/scrape.py providers/content/firecrawl.py

If a scanner is listed in config but its extra is missing, Guard.__init__ raises ConfigError with an install hint (pii, yara).

Concurrency and session state

Use one Guard per request or agent session. ExecutionContext tracks session taint, tool history, trajectory, and normalize cache — shared concurrent scans on the same instance can race.

For server embeddings and streaming, prefer scan_request(..., isolated=True) or a fresh ExecutionContext per call so scan cache and policy overrides do not bleed across tenants.

Public API

Stable imports from unplug:

from unplug import Guard, Finding, ScanResult, TrustLevel, GuardConfig, ScanPolicy, ConfigError, ServerError

Everything else: submodule imports (from unplug.scanners.injection import InjectionScanner).

Deprecated paths

Old New Removed
unplug.safeguards.* unplug.scanners.* v1.0
unplug.scanners.* shims (pre-0.4) real code in scanners/ done
Flat unplug.core.* modules core/{taint,normalize,policy,...}/ + shims v1.0