Optional Dependencies and Assets

Copy Markdown View Source

Obscura's base deterministic path does not require model weights, accelerator libraries, external services, or network access during inference. Model-backed, parser-backed, and ONNX capabilities are explicit additions.

The machine-readable contracts are:

  • priv/obscura/capabilities.json
  • priv/obscura/model_assets.json

Obscura.Capabilities validates and exposes those manifests. Tests verify that the listed Hex dependencies exist in mix.exs, documented environment variables exist in the codebase, stable profiles have capability coverage, and the manifests contain no local absolute paths or obvious credential material.

Product Profile Matrix

ProfileStabilityBase behaviorRequired optional dependenciesAssetsPreferred development backend
:faststableDeterministic structured PIINoneNoneBEAM
:balancedstable, external assets require deployer reviewDeterministic plus TNER NERnx, bumblebeeTNER model and tokenizerEmily on Apple Silicon; EXLA where supported
:accuratestable, external assets require deployer reviewDeterministic plus output-aware TNER/Jean location cascadenx, bumblebeeTwo models and two tokenizersEmily on Apple Silicon; EXLA where supported
:hybrid_gliner_urchadeexperimentalDeterministic plus Urchade GLiNERortex, tokenizersPinned local ONNX, tokenizer, and config exportOrtex CPU
:openmed_piiexperimentalNative model-only OpenMed/Nemotron PIInx, safetensorsValidated Privacy Filter checkpointExplicitly selected Binary, EXLA, or Emily

Optional dependencies are declared with optional: true so downstream applications only install the capabilities they select. Obscura never downloads assets from Obscura.analyze/2 or Obscura.redact/2.

Base Installation

The base library requires only its normal Hex dependencies:

def deps do
  [
    {:obscura, "~> 0.1"}
  ]
end

Use the deterministic product profile explicitly:

Obscura.analyze("Contact jane@example.com", profile: :fast)

No model cache, environment variable, or setup task is required.

Parser-Backed Phone Validation

The optional ex_phone_number integration improves international telephone validation and remains caller-controlled because region policy affects which digit strings are valid.

def deps do
  [
    {:obscura, "~> 0.1"},
    {:ex_phone_number, "~> 0.4.11"}
  ]
end

Select the parser and regions explicitly. Do not assume a global default region is appropriate for every tenant or dataset.

Obscura.analyze(text,
  profile: :fast,
  phone_parser: Obscura.Recognizer.Phone.ExPhoneNumberValidator,
  phone_regions: ["US", "GB"]
)

Obscura rejects obvious date-shaped and repeated-digit junk before accepting parser-backed phone candidates, but applications must still benchmark their region mix.

Bumblebee/Nx Profiles

The :balanced and :accurate profiles use token classification through Bumblebee/Nx.

Host applications should include:

def deps do
  [
    {:obscura, "~> 0.1"},
    {:nx, "~> 0.12"},
    {:bumblebee, "~> 0.7"}
  ]
end

Then choose a backend. BinaryBackend is useful for correctness smoke tests but is not the recommended performance path for large NER models.

Emily on macOS Apple Silicon

Emily is the current development backend used for Metal/GPU model benchmarks:

{:emily, "~> 0.6"}

Project setup:

OBSCURA_REAL_MODEL_BACKEND=emily mix deps.get

For validation, forbid silent fallback:

OBSCURA_REAL_MODEL=1 \
OBSCURA_REAL_MODEL_BACKEND=emily \
OBSCURA_EMILY_DEVICE=gpu \
OBSCURA_EMILY_FALLBACK=raise \
mix test --include real_model

The presence of Emily does not by itself prove GPU use. Runtime and benchmark metadata must identify the requested backend, actual backend, selected device, and fallback policy. If device proof is unavailable, report it as unknown.

Emily is currently validated primarily for macOS Apple Silicon development and benchmarking. It is not yet Obscura's universal production backend promise.

EXLA

EXLA is conditional in the Obscura development project:

OBSCURA_REAL_MODEL=1 mix deps.get

Downstream applications can add:

{:exla, "~> 0.12"}

EXLA does not imply GPU. Record whether the actual client is CPU, CUDA, or another supported target.

Explicit Runtime Preparation

Preparing a profile is an explicit operation which may populate the configured Hugging Face cache:

{:ok, runtime} =
  Obscura.Profile.prepare(:balanced,
    allow_download: true,
    real_model_backend: :emily,
    compile: [batch_size: 1, sequence_length: 128]
  )

Obscura.analyze("Rachel works at Google in Paris.", profile: runtime)

The runtime owns reusable serving resources. Build it during controlled application initialization, supervision setup, or a dedicated warmup phase. Do not build it inside every request.

allow_download defaults to false. Without it, remote repositories are opened with Bumblebee offline mode and only complete cached assets may be used. offline: true always forbids network access, even if download authorization was also passed. This separation makes accepting third-party terms and performing network I/O an explicit deployment action.

Use the preparation task to populate a cache with structured progress:

mix obscura.profile.prepare \
  --profile balanced \
  --backend emily \
  --allow-download \
  --timeout 1800000 \
  --inactivity-timeout 300000

The effective cache directory follows explicit repository cache_dir, then BUMBLEBEE_CACHE_DIR, then the operating-system Bumblebee cache location. The local Mix task prints that path; public diagnostics and telemetry expose only the source and status so home directories do not leak. --json is newline-delimited JSON and suppresses Bumblebee's terminal progress bar.

The controlled preparation worker has an overall timeout and an inactivity timeout. Cache-byte growth and stage transitions reset inactivity. On timeout, abnormal exit, or interrupted transfer, unreferenced files lacking Bumblebee metadata are moved under .obscura-quarantine in the selected cache. A later authorized preparation retries cleanly. Complete metadata-backed entries are never quarantined.

mix obscura.operational.benchmark enforces offline model/tokenizer loading, counts lifecycle construction stages, and fails promotion if the canonical request path reconstructs the runtime. Promoted operational conclusions are summarized in docs/benchmark-status.md.

The accurate profile prepares both models:

{:ok, runtime} =
  Obscura.Profile.prepare(:accurate,
    allow_download: true,
    real_model_backend: :emily
  )

It routes requests by requested entity. For location requests TNER always runs; Jean-Baptiste runs only when TNER has no accepted location. A request that excludes :location does not invoke the specialist, but the runtime prepares both resources up front to keep request handling network-free and reusable.

Model Assets

TNER OntoNotes5

  • Alias: tner_roberta_large_ontonotes5
  • Model: tner/roberta-large-ontonotes5
  • Tokenizer: tner/roberta-large-ontonotes5
  • Pinned revision: 0bce50f7884d5bb040469c907c897d4b061ccbb4
  • Published weight size: approximately 1.42 GB, excluding cache metadata and backend/runtime memory
  • Used by: stable :balanced and :accurate
  • Adapter: Bumblebee token classification
  • License status: not established by Obscura; verify upstream terms

The profile integration is stable, but Obscura does not distribute or license the checkpoint. Deployers must establish whether their selected assets and use are permitted. See docs/model-asset-licensing.md.

Urchade GLiNER Multi PII

  • Alias: urchade_gliner_multi_pii_v1
  • Model: urchade/gliner_multi_pii-v1
  • Pinned revision: 1fcf13e85f4eef5394e1fcd406cf2ca9ea82351d
  • Used by: public experimental profile :hybrid_gliner_urchade
  • Adapter: Obscura.Recognizer.GLiNER.Ortex
  • Dependency: conditional optional Obscura fork of ortex 0.1.10
  • Assets: locally exported model.onnx, tokenizer/config files from microsoft/mdeberta-v3-base
  • Reported license: Apache-2.0; full provenance still requires project-owner review

Host applications selecting this profile must add the CPU runtime and tokenizer dependencies explicitly:

def deps do
  [
    {:obscura, "~> 0.1"},
    {:ortex, "~> 0.1.10"},
    {:tokenizers, "~> 0.5.1"}
  ]
end

The upstream checkpoint does not publish a directly usable ONNX bundle. Generate it with the pinned script and lock in eval/gliner/; do not place weights in the application package or repository. The native adapter passes its parity checks but failed the three :balanced promotion gates. It is publicly available only as an experimental CPU-only general NER option, not as an accuracy replacement for :balanced. Current comparative metrics are in benchmark-status.md.

Prepare and reuse the runtime:

{:ok, runtime} =
  Obscura.Profile.prepare(:hybrid_gliner_urchade,
    model_dir: ".cache/gliner/urchade-gliner-multi-pii-v1"
  )

Obscura.analyze("Rachel works at Google in Paris.", profile: runtime)

The project-local Ortex fork adds structured CoreML options and ONNX Runtime profiling. On Apple Silicon, callers can explicitly request:

Obscura.Recognizer.GLiNER.Ortex.build(
  model: :urchade_gliner_multi_pii_v1,
  model_dir: model_dir,
  execution_providers: [:coreml],
  coreml_options: [
    model_format: :ml_program,
    compute_units: :cpu_and_gpu,
    require_static_input_shapes: false,
    enable_on_subgraphs: false
  ],
  profile_prefix: ".cache/ortex-profiles/urchade"
)

This is a diagnostic path, not a recommended accelerator configuration. The current dynamic export preserves output parity but is substantially slower than CPU and uses extensive CPU fallback. finish_profiling/1 proves provider assignment, not GPU-only execution. Model assets and generated native binaries remain outside the package and repository.

Urchade also has an experimental native Nx/Emily adapter:

Obscura.Recognizer.GLiNER.Native

Enable its development dependency path with OBSCURA_GLINER_NATIVE=1. It requires Emily, Tokenizers, Nx, Safetensors, a pinned local model.safetensors export, tokenizer.json, gliner_config.json, and obscura_native_manifest.json. It does not require Ortex or ONNX Runtime. Both Emily fallback paths are forced to :raise. Layer and decoded-span parity pass, but the adapter is not promoted because it does not beat Ortex CPU latency or the :balanced accuracy gates.

The registry and asset manifest pin the reviewed revision. Authoritative benchmarks must continue recording it so an upstream branch change cannot silently alter profile behavior.

Jean-Baptiste Location Specialist

  • Alias: jean_baptiste_roberta_large_ner_english
  • Model: Jean-Baptiste/roberta-large-ner-english
  • Tokenizer fallback: FacebookAI/roberta-large
  • Model revision: 8f3abc1ef81ffbbb0e80568d4fed1dd10d459548
  • Tokenizer revision: 722cf37b1afa9454edce342e7895e588b6ff1d59
  • Published fine-tuned weight size: approximately 1.42 GB; :accurate also needs the TNER weights and both tokenizer repositories
  • Used by: stable :accurate
  • Adapter: Bumblebee token classification
  • Fine-tuned model license: MIT; verify base/tokenizer terms

The tokenizer fallback is intentional because the fine-tuned repository does not provide the Rust-compatible tokenizer artifact expected by the current path. Authoritative runs must record both pinned repositories and revisions.

Native Privacy Filter

The :openmed_pii profile uses Obscura's native Privacy Filter architecture. It does not use Bumblebee's normal token-classification architecture loader.

Host dependencies:

def deps do
  [
    {:obscura, "~> 0.1"},
    {:nx, "~> 0.12"},
    {:safetensors, "~> 0.1.3"}
  ]
end

Prepare the checkpoint explicitly:

mix obscura.privacy_filter.setup \
  --repo OpenMed/privacy-filter-nemotron-v2 \
  --checkpoint .cache/privacy-filter/openmed-nemotron-v2

Validate before starting inference:

mix obscura.privacy_filter.checkpoint \
  --checkpoint .cache/privacy-filter/openmed-nemotron-v2

Prepare a reusable runtime:

{:ok, runtime} =
  Obscura.Profile.prepare(:openmed_pii,
    checkpoint: ".cache/privacy-filter/openmed-nemotron-v2",
    backend: :emily
  )

At minimum, the native checkpoint requires config.json and one or more Safetensors files. The inspected OpenMed v2 repository also publishes tokenizer.json, tokenizer_config.json, and label_space_fine_v1.json. Python-original layout additionally requires dtypes.json and viterbi_calibration.json and must be selected explicitly.

Checkpoint paths and hashes belong in local configuration and authoritative benchmark metadata, not in the Hex package. The model's inspected license metadata is not a clear production grant; verify it before deployment.

GLiNER, Native Emily, and Generic Ortex

GLiNER and generic token-classification Ortex adapters remain advanced, experimental capabilities rather than stable product profiles.

Project development setup:

OBSCURA_GLINER_ORTEX=1 mix deps.get
OBSCURA_GLINER_NATIVE=1 mix deps.get

Dependencies:

{:ortex, "~> 0.1.10"}
{:tokenizers, "~> 0.5.1"}

GLiNER requires a compatible ONNX graph, tokenizer.json, and GLiNER config. Configure OBSCURA_GLINER_MODEL_DIR or explicit paths. Generic token classification uses OBSCURA_NER_ORTEX_MODEL_DIR or explicit options.

The experimental :nvidia_gliner_pii_v1 model alias uses a pinned local export of nvidia/gliner-PII. It has no public product profile and is not included in the authoritative matrix. The export is approximately 1.78 GB and requires the exact ONNX, tokenizer, and config hashes recorded in eval/gliner/nvidia-export-reference.json. Configure the asset directory with OBSCURA_GLINER_NVIDIA_MODEL_DIR for tagged parity tests or pass model_dir directly.

The native Emily path uses a Safetensors checkpoint instead of ONNX. Configure OBSCURA_GLINER_NATIVE_MODEL_DIR; generated assets remain local and ignored.

No ONNX model is bundled. ONNX Runtime execution provider availability is platform-specific and must be recorded in benchmark metadata.

Python Reference Environments

Python environments under .presidio-venv/, .opf-venv/, and .privacy-filter-reference-venv/ are ignored development tools. They are not runtime dependencies of the library.

Each authoritative Python comparison must record:

  • Python version;
  • fully pinned package versions;
  • model repository and immutable revision;
  • dataset fingerprint and sample IDs;
  • entity mapping and offset policy;
  • checkpoint hashes;
  • exact command;
  • whether CPU or GPU was actually used.

Network Policy

OperationNetwork allowed by default?
Obscura.analyze/2 with local profilesNo
Obscura.redact/2 with local profilesNo
Obscura.Profile.validate_runtime/2No
Obscura.Profile.prepare/2 with Hugging Face sourcesYes, explicit operation
Privacy Filter setup Mix taskYes, explicit operation

External-service calls are application-owned custom-recognizer behavior and are not part of Obscura's built-in profiles or capability manifest.

Readiness checks must be offline. They inspect modules, paths, layouts, hashes, and reusable serving presence without contacting model hubs or providers.

Cache and Cleanup

Obscura does not own one global cache. The host controls:

  • Hugging Face/Bumblebee cache location;
  • .cache/privacy-filter checkpoint directories;
  • GLiNER/Ortex model directories;
  • Mix dependency/build caches;
  • ignored Python virtual environments.

Never delete shared caches automatically from library code. Cleanup commands are operator actions and should target only an explicitly configured directory or dependency.

Common Diagnostics

CodeMeaning
missing_optional_dependencyRequired optional Hex dependency is unavailable
missing_model_assetReusable serving or model files were not prepared
missing_tokenizer_assetRequired tokenizer is unavailable or mismatched
missing_checkpointPrivacy Filter checkpoint path was not supplied/found
checkpoint_incompleteRequired checkpoint files are missing/truncated
checkpoint_layout_mismatchNative versus Python-original contract is wrong
checkpoint_hash_mismatchLocal file differs from the pinned manifest
unsupported_backendBackend name is not supported by the adapter
backend_device_unavailableRequested CPU/GPU device could not be selected
backend_fallback_forbiddenRuntime attempted a forbidden fallback
model_load_failedModel architecture/files could not be loaded
tokenizer_load_failedTokenizer files or architecture are incompatible
serving_build_failedRuntime serving could not be constructed

Use Obscura.Diagnostic.format/1 for human output and inspect its stable code for programmatic handling. Diagnostic inspection omits nested causes and local paths by default.

Production Checklist

Before deploying an optional capability:

  1. Pin Obscura and optional dependency versions.
  2. Pin immutable model and tokenizer revisions.
  3. Record and verify local asset hashes.
  4. Review every model and base-model license.
  5. Run profile readiness offline.
  6. Warm reusable serving resources before accepting traffic.
  7. Prove actual backend/device and forbid silent fallback.
  8. Benchmark the deployment hardware and representative data.
  9. Define memory, concurrency, timeout, and failure behavior.
  10. Ensure reports, telemetry, and logs omit raw PII and credentials.