Introduction
Dedomena Edge is the on-premise edition of the Dedomena.AI platform: a Python library you install on your own servers to check the quality of your datasets, detect sensitive data, anonymize it and train synthesizers for synthetic data, without your data ever leaving your infrastructure.
This guide explains how to download and install Dedomena Edge, how it is organized, what you need to run it and how to use each of its features. At the end you will find a reference manual with every command, variable and error message.
> Your data stays in your environment. Edge only connects to Dedomena.AI to download the package and the models. Detection, anonymization, training and validation always run on your machine, and the licence is verified locally, offline.
What Dedomena Edge includes
Edge brings three libraries together in a single Python package, dedomena:
| Library | What it is for | Main functions |
|---|---|---|
Axon (dedomena.axon) | Checking and preparing a dataset before using it | Inspector, clean, ApplyRules, report, risk, insight |
Detection (dedomena.detection) | Finding what is sensitive in an asset, and where | detect, detect_in_text, detect_columns |
Nucleus (dedomena.nucleus) | Changing the data: anonymizing it, or training synthesizers for synthetic data | Anonymization, Generic, Transactional, TimeSeries, Relational, load_synthesizer |
Together they cover every kind of asset:
| Asset | Detect | Anonymize | Train synthesizers | Validate with Axon |
|---|---|---|---|---|
| Tables (CSV, Parquet, Excel, databases) | ✔ | ✔ | ✔ | ✔ |
| Images (PNG, JPG, TIFF, BMP) | ✔ | ✔ | — | — |
| Documents (PDF, DOCX, TXT) | ✔ | ✔ | — | — |
| Audio (WAV, MP3, M4A, WMA) | ✔ | ✔ | — | — |
| Video (MP4, MOV, AVI) | ✔ | — | — | — |
Anonymization method names, sensitive element types and synthesis algorithms are the same as in the Dedomena.AI cloud platform. A configuration written for the cloud works the same way in Edge.
Architecture and requirements
How an installation is organized
An Edge installation has three parts, and all three live in your infrastructure:
-
The library is the code you run from Python. It is installed in a virtual environment and processes your data locally.
-
The models are the AI models used by detection. They are downloaded separately from the code and stored in a local folder. You can choose their size, or use none at all (see Model size: small, full or none).
-
The licence is a signed token you get from the Edge page of the platform. The library verifies it locally, without connecting to anything.
The Dedomena.AI platform is only involved in downloading the package, the models and the token from the Edge page, and in giving you the results once you upload to it the encrypted files your workflows produce: for example, the synthetic data generated from a synthesizer you trained with Edge (see Generating data).
What leaves your machine and what does not:
| When | Connection to Dedomena.AI |
|---|---|
| Installing with pip | Yes: the package and its dependencies are downloaded. |
| Installing from the full download | No: the downloaded folder carries everything needed. |
Downloading the models (dedomena-edge setup) | Yes: the models are downloaded. |
| Detecting, anonymizing, training, validating | No. Everything runs locally. |
| Generating synthetic data | Yes: you upload the synthesizer to the Dedomena.AI platform and generate the data there, or through the API (see Generating data). |
| Checking the licence | No: it is verified locally. |
| Errors and diagnostics | No: they are written to local files. You only send them to support if you need to (see Messages, logs and support). |
Modules
The package is delivered in modules: you choose the ones you need and only those are installed. You can choose several at once (for example, Text + Vision).
| Module | What it includes | Also includes |
|---|---|---|
Tables (tables) | Detection of sensitive columns, table anonymization, and synthesizer training and evaluation | Axon |
Text (text) | Detection and anonymization of free text with the language model, including the text columns of tables and the methods that need that model | Tables and Axon |
Vision (vision) | Image detection and anonymization: faces, signatures, logos, fingerprints and the text inside the image | Axon |
Audio (audio) | Transcription, detection of data spoken out loud and audio anonymization | Axon |
Documents (documents) | Detection and anonymization of PDF, DOCX and TXT | Axon |
Axon (axon) | Dataset inspection, cleaning, rules and quality report | — (always included) |
All (all) | All the modules above | — |
A few things to keep in mind:
-
Axon is always included, whatever you choose.
-
Text includes Tables: the language model is also used on the free-text columns of tables.
-
If you try to use something your package does not include, the library does not fail with a Python error: it tells you that module is not part of your installation and which modules it does include.
Model size: small, full or none
The code package is the same for every size. The size only decides which models are downloaded:
| Size | What it is | Where it runs |
|---|---|---|
| small | Lightweight models. They cover all five modalities with just over 2 GB in total. The recommended option to start with. | CPU (a GPU is optional) |
| full | Models with the highest detection quality. | Designed for an NVIDIA GPU (the text and vision ones require it) |
| none | No models. Detection uses patterns and validations only (emails, Spanish IDs, IBAN, cards, phone numbers…). It does not find names or companies in free text. | CPU |
Models downloaded for each module and size:
| Module | small | full | none |
|---|---|---|---|
| Tables | Lightweight text detection (0.18 GB) | Lightweight text detection (0.18 GB) | ✔ available |
| Documents | Lightweight text detection (0.18 GB) | Lightweight text detection (0.18 GB) | ✔ available |
| Text | Lightweight text detection (0.18 GB) | Full text model (28.7 GB) · GPU | ✔ available |
| Vision | Lightweight image detection, signature detection and lightweight text detection (≈1.5 GB) | Full image model, resolution enhancement, signature detection and lightweight text detection (≈36.5 GB) · GPU | ✘ not available |
| Audio | Lightweight transcription and lightweight text detection (≈0.85 GB; ≈2.7 GB in its GPU variant) | Full transcription and lightweight text detection (≈0.7 GB) · runs on CPU | ✘ not available |
| All | ≈2.2 GB | ≈65.5 GB · GPU | ✘ not available |
| Axon only | Uses no models | Uses no models | — |
-
With several modules, the union of their models is downloaded, without repeats. When the full model of a type is included, the lightweight model of that same type is not downloaded as well.
-
Vision and audio always need a model. Without one there is no image detection and no transcription, so the
nonesize is not offered with those modules. -
Generative image editing (optional). An additional model of about 57.7 GB that replaces the data in an image with other data that looks natural, instead of drawing it on top. Only available with the Vision module and GPU hardware.
System requirements
Operating system and Python
| Requirement | Value |
|---|---|
| Operating system | Linux x86_64 with glibc 2.28 or later (RHEL/Rocky/Alma 8 or later, Ubuntu 20.04 or later, Debian 10 or later). Windows and macOS are not supported yet. |
| Python | 3.10, 3.11 or 3.12 (the same package works for all three). If your distribution ships another version, install one of these first. |
| Environment | A dedicated virtual environment for Edge is recommended (the installer creates one if needed). |
CPU and memory
| Use | Recommendation |
|---|---|
Tables, Axon, documents and small models | Multi-core CPU (8 or more cores recommended) and 32 GB of RAM (16 GB minimum) |
| Training synthesizers on large tables | More cores and more RAM speed up training. A GPU is optional and speeds it up too. |
GPU (only for gpu hardware)
| Requirement | Value |
|---|---|
| Card | NVIDIA GPU |
| Driver | NVIDIA 580 or later (CUDA 13). The installer does not check it: verify it beforehand with nvidia-smi. |
full text and vision models | Ada Lovelace, Hopper or Blackwell architecture. Video memory: 48 GB minimum (with a reduced context for the image model), 80 GB recommended and 96 GB to keep the text and image models loaded at the same time. With less memory, the library loads them in turns. |
| Generative image editing | GPU with 80 GB of memory or more. |
small models | No GPU needed. If there is one, transcription can use it (GPU variant) and the others can be moved to it (see Registering a model explicitly). |
Before loading any model you can check whether they fit on your card with models.report() (see Which engine will be used).
Disk
-
Library and dependencies: a few GB (more in the GPU variant, which includes the NVIDIA libraries).
-
Models: as shown in the table in Model size: small, full or none. Leave extra room for your data and results.
Network
-
Installing with pip: outbound HTTPS to
api.dedomena.aiand to the public Python package repositories during installation. -
Downloading models with
dedomena-edge setup: outbound HTTPS toapi.dedomena.aiand to the download servers it redirects to. Ask Dedomena.AI for the list of domains if your firewall works with an allowlist. -
Installing from the full download: the target machine needs no network.
-
Usage: no network needed.
Which combination to choose
| If you want to… | Modules | Hardware | Size |
|---|---|---|---|
| Anonymize tables and train synthesizers | Tables | CPU | small (or none if your columns have no free text with names) |
| The above, plus finding names and companies in free text with the highest quality | Text | GPU | full |
| Anonymize ID cards, passports, scanned contracts or photos | Vision (+ Documents) | CPU | small |
| The same, with the highest detection quality | Vision (+ Documents) | GPU | full |
| Anonymize recorded calls | Audio | CPU | small or full |
| Only validate and clean datasets | Axon | CPU | — |
| Everything | All | GPU | full |
Download and installation
There are two ways to install Edge. Both start from the Edge page of the Dedomena.AI platform:
| Full download | Install with pip | |
|---|---|---|
| How | You download a folder with the package, all its dependencies, the models and an installer | You install the package with pip from the Dedomena.AI index and download the models with a command |
| Network on the target machine | Not needed | Yes, during installation |
| Recommended for | Servers without internet access, regulated environments, reproducible installations | Machines with internet access, quick updates |
> You need a platform account with a plan that includes Dedomena Edge. If your plan does not include it, the download replies that you have no access to Edge: contact Dedomena.AI.
The Edge page

The page has three blocks, from top to bottom:
-
The package options: operating system, modules, hardware and model size.
-
Your licence token.
-
The two ways to install: Download and Install with pip.
Choosing the options
-
Operating system: Linux.
-
Modules: select one or more. Modules included with another one are selected automatically and locked (for example, selecting Text selects Tables). Axon is always selected. All installs everything.
-
Hardware:
-
CPU: for machines without a GPU. Installs the lightweight CPU build of the computing libraries.
-
GPU: for machines with an NVIDIA GPU (CUDA 13, driver 580 or later).
-
-
Model size: small, full or none (see Model size: small, full or none). The page disables the combinations that are not possible:
-
full with CPU hardware, if you choose Text, Vision or All (those models need a GPU);
-
none, if you choose Vision, Audio or All (they need a model).
-
-
Generative image editing (optional): only shown with Vision and GPU hardware.
Below the download button, the page shows what the download contains and how much the models weigh.
Your licence token

The token is your Edge licence. You need it to install, and the library uses it every time you work with it. It is personal: do not share it or commit it to a code repository.
-
Show displays the token and Copy copies it.
-
Generate new token generates a new one. Use it when the current one expires.
-
Valid until shows the expiry date.
The installation commands on the page already include your token. More details in The licence.
Option A: full download (no internet on the target machine)

-
Click Download and choose a folder on your computer.
-
In Chrome and Edge, the browser writes the files directly into that folder, checks each one and, if the download is interrupted, you can resume it where it stopped.
-
In Firefox and Safari, the download is saved as a single
.zip. If it is interrupted, it has to start over. Withfullmodels (tens of GB) we recommend Chrome or Edge.
-
-
When it finishes you have a
dedomena-edgefolder with these contents:Item Contents install.shThe installer. wheels/The Edge package and all its dependencies, for Python 3.10, 3.11 and 3.12. models/The chosen models. Not present with the nonesize. -
Copy the folder to the machine where you will use Edge, by whatever means you normally use (SFTP, disk, internal repository…). If you downloaded a
.zip, extract it there. -
Install:
bashcd dedomena-edge bash install.sh \<TOKEN\> . ./env.sh # in every new session, before using the library
What install.sh does:
-
Chooses where to install (see the table below) and checks that the Python version is 3.10, 3.11 or 3.12.
-
Installs Edge from
./wheels, without an internet connection. -
Writes
env.sh, which activates the virtual environment (if it created one) and points the library to the./modelsfolder (if there are models). -
Saves your licence on the machine, so the library finds it without you having to pass it again.
-
Runs
dedomena-edge status, which shows the installed modules and models.
Where it installs:
| Command | Where it installs |
|---|---|
bash install.sh \<TOKEN\> | In the Python environment you have active (venv or conda). If none is active, it creates a new one in ./venv. |
bash install.sh \<TOKEN\> --venv | Always in a new environment: ./venv, or another path with VENV=/path bash install.sh \<TOKEN\> --venv. |
bash install.sh \<TOKEN\> --no-venv | Always in the active environment, without creating another one. |
Additional options:
-
PYTHON=/path/to/python3.11 bash install.sh \<TOKEN\>uses a specific interpreter. -
The token can also be passed in the
DEDOMENA_TOKENvariable instead of as an argument. -
bash install.sh --helpshows the help.
> Watch out with conda. If your terminal activates conda's base environment automatically, the installer treats it as the "active environment" and installs there. Use --venv if you prefer a separate environment.
> Do not move the folder after installing. The virtual environment and env.sh use absolute paths, and the models stay in ./models. If you need another location, copy the folder to its final place before running install.sh.
If something fails during installation:
| Message | What to do |
|---|---|
ERROR: these packages are for Python 3.10 3.11 3.12 on Linux x86_64, and none was found | Install Python 3.10, 3.11 or 3.12, or point to it: PYTHON=/path/to/python bash install.sh \<TOKEN\>. |
ERROR: … cannot create virtual environments: the venv module is missing. | On Debian/Ubuntu install the package the message names (for example apt install python3.11-venv), or activate an environment you already have and use --no-venv. |
ERROR: the active environment (…) is Python X.Y; these packages are for Python 3.10 3.11 3.12. | Activate an environment with a supported version, or use --venv. |
ERROR: unknown option: … | Check the command. bash install.sh --help shows the valid options. |
| Error while saving the licence (expired or invalid token) | The library is already installed. Generate a new token on the page and save it as explained in Saving the licence on the machine. |
Option B: install with pip

-
Create and activate a virtual environment for Edge:
bashpython3.11 -m venv /opt/dedomena/venv . /opt/dedomena/venv/bin/activate -
Install the package with the command shown on the page. It already carries your modules, your hardware and your token:
bashpip install --index-url "https://licence:\<TOKEN\>@api.dedomena.ai/edge/\<modules\>/\<hardware\>/simple/" dedomena-edge-
\<modules\>:all, one module (tables,text,vision,audio,documents,axon) or several joined with+(for exampletext+vision). The order does not matter. -
\<hardware\>:cpuorgpu. -
The token is the index password; the user name (
licence) is ignored.
With
gpuhardware andfullmodels, also install the GPU engine used by the full models:bashpip install --index-url "https://licence:\<TOKEN\>@api.dedomena.ai/edge/\<modules\>/gpu/simple/" "dedomena-edge[gpu]" -
-
Download the models and save the licence on the machine:
bashdedomena-edge setup --size small --token "\<TOKEN\>" # or --size full / --size nonesetupreads which modules your installation has and downloads the models they need for that size. If a download is interrupted, run the same command again: it resumes where it stopped. Each file is verified when it finishes, and a half-downloaded model is never used. -
Check the installation:
bashdedomena-edge status
dedomena-edge setup options:
| Option | What it does |
|---|---|
--size small|full|none | Required. The model size. With none nothing is downloaded, but the licence is saved. |
--token "\<TOKEN\>" | Saves the licence on this machine. |
--gpu auto|yes|no | Default auto: detects whether there is an NVIDIA GPU. With a GPU, the GPU variants are downloaded where they exist. Use yes if you are downloading the models for another machine that does have a GPU. |
--image-edit | Also downloads the generative image editing model. |
--dir /path | Downloads the models to another folder (default ~/.local/share/dedomena/models). If you use it, export DEDOMENA_MODELS_DIR=/path afterwards. |
--type text|vision|audio | Downloads only the models of that type. Can be repeated. |
--force | Downloads again even if they are already there. |
--quiet | No progress bar. |
--workers N | Parallel downloads (8 by default). |
setup messages you may see:
| Message | Meaning |
|---|---|
These models need an NVIDIA GPU and this machine has none: … | You asked for the full text or vision models on a machine without a GPU. Use --size small, or --gpu yes if you are downloading them for another machine. |
These modules need a model: vision, audio. Use --size small | --size none is not available with Vision or Audio. |
No models: detection uses patterns only … | With --size none: the licence is saved and nothing is downloaded. |
The full models run on the GPU engine, which is an optional extra … | Install dedomena-edge[gpu] with the command from step 2. |
The model server refused the request (403): … | Your plan does not include Edge, or your user is disabled. |
Could not reach the model server … | Check the connection and the firewall (see System requirements). |
Download kept failing … run it again to resume | Run the same command again: the download resumes. |
Common pip errors:
| Symptom | Cause |
|---|---|
| pip asks for a user name and password, or replies 401 | The token is invalid or has expired. Generate a new one on the page. |
| 403 | Your plan does not include Edge. |
| 404 | The module or hardware combination in the URL does not exist. Copy the command exactly as shown on the page. |
No matching distribution | Your Python is not 3.10, 3.11 or 3.12, or your system is not Linux x86_64. |
Checking the installation
dedomena-edge status
dedomena-edge 5.6.2
Modules : axon, small, tables, text
Models dir : /home/user/.local/share/dedomena/models
GPU : no
small-text 1.0.0
-
Modules shows the modules of the installed package.
smallis the code for the lightweight models, which comes with every package that includes detection. -
Models dir is the folder where the library looks for the models.
-
Below, one line per installed model, with its version.
To see the licence:
python -c "import dedomena; print(dedomena.auth_status())"
And to see which detection engine each modality will use and whether the models fit on your GPU:
python -c "from dedomena.detection import models; print(models.report())"
Updating, extending or changing the installation
| I want to… | What to do |
|---|---|
| Install a new version of Edge | Full download: download again and run install.sh in the new folder. pip: repeat the installation command with --upgrade. |
| Add modules | The package is different for each combination of modules: download or install the package with the new combination in a new environment (or with pip install --upgrade --force-reinstall in the same one). Then run dedomena-edge setup to download any missing models. |
| Change the model size | dedomena-edge setup --size full (or small). It only downloads what is missing. |
| Renew the licence | Generate a new token on the page and save it (see Saving the licence on the machine). No need to reinstall. |
| Use the models from another folder | Move the models folder and export DEDOMENA_MODELS_DIR=/new/path. |
| Prepare the models for a machine without internet | On a machine with internet: dedomena-edge setup --size full --gpu yes --dir /path/models. Copy that folder to the target machine and export DEDOMENA_MODELS_DIR there. |
The licence
The licence is the token from the Edge page. It is a signed token that carries your identifier, your permissions and its expiry date. The library verifies it locally, without connecting to anything, so it works on machines without internet access.
Saving the licence on the machine
install.sh and dedomena-edge setup --token already save it for you. If you need to do it by hand (for example, when renewing it):
import dedomena
dedomena.login("\<TOKEN\>", persist=True) # verifies and saves it
dedomena.auth_status()
# Session(subject='...', scopes=('axon', 'detection', 'nucleus.anonymize', ...), expires=..., source=...)
From then on you do not need to pass the token to any function: they all find it on their own.
-
persist=Truesaves the token in~/.config/dedomena/token, readable only by your user. Withoutpersist=True,login()applies to the current Python session and writes nothing to disk. -
dedomena.logout()closes the session.
Where the library looks for the token
In this order:
-
The
token=argument of the function you call. -
The session opened with
dedomena.login(...). -
The
DEDOMENA_TOKENenvironment variable. -
The saved licence file (
~/.config/dedomena/token).
Containers, CI and secret managers. If your policy does not allow credentials to be stored on disk, pass the token through the DEDOMENA_TOKEN variable and set DEDOMENA_NO_PERSIST=1: the library will not write anything to the user's home directory. DEDOMENA_CONFIG_DIR changes the folder of the licence file.
Permissions
The token states what you can do:
| Permission | Allows |
|---|---|
axon | Validating and preparing datasets |
detection | Using detection directly and downloading models |
nucleus.anonymize | Anonymizing and reversing anonymizations |
nucleus.synthesis | Training synthesizers |
If you try something your licence does not cover, the library says so, naming the activity: Your Dedomena licence does not cover training a synthesizer. It covers: …. In that case, ask Dedomena.AI for the corresponding permission.
Expiry and renewal
-
The expiry date is shown on the page (Valid until) and in
dedomena.auth_status(). -
15 days before it expires, the library warns:
Warning: Your Dedomena token expires on … (N day(s) left). -
Once it expires, functions stop running with the message
Your Dedomena token expired on …. Generate a new token on the page and save it withdedomena.login("\<NEW_TOKEN\>", persist=True). No need to reinstall anything. -
Turning the system clock back does not extend the licence.
Getting started with the library
Before you start
If you installed with the full download, load the environment in every new session:
. /path/to/dedomena-edge/env.sh
Then:
import dedomena
print(dedomena.__version__)
from dedomena.nucleus import anonymize # anonymization
from dedomena.nucleus import Auto, Generic # synthesizers
from dedomena.detection import detect # detection
from dedomena.axon import Inspector, clean # data quality
import dedomena loads nothing heavy: each library is loaded the first time you use it.
Data sources
Every function that takes data accepts the same sources:
| Source | How to specify it |
|---|---|
| File | The path: "data/customers.csv". The type is inferred from the extension. |
| CSV | .csv. The separator and the encoding are detected automatically. |
| Parquet | .parquet |
| Excel | .xlsx |
| Sparse matrix | .mtx, or source_format="MTX" |
| pandas DataFrame | The DataFrame itself. |
| Database | The connection URL, with source_format="DATABASE" and query= (a table name or an SQL query). |
| Image, document, audio, video | The file path (see the formats in What Dedomena Edge includes). |
Databases. PostgreSQL, MySQL/MariaDB, SQL Server, Oracle, SQLite and IBM Db2 are supported:
anonymize(
"postgresql://user:password@server:5432/crm",
source_format="DATABASE",
query="SELECT * FROM customers WHERE signup \>= '2026-01-01'",
config="auto",
output="output/customers_anonymized.parquet",
output_format="PARQUET",
)
If query is a single word, it is read as a table name (SELECT * FROM "customers").
IBM Db2. Download IBM's JDBC driver (db2jcc4.jar) and copy it into the DB2Driver folder inside the installed package. You also need a Java runtime on the machine:
python -c "import dedomena, os; print(os.path.join(os.path.dirname(dedomena.__file__), 'DB2Driver'))"
The URL looks like this:
jdbc:db2://\<host\>:\<port\>/\<database\>:sslConnection=true;user=\<user\>;password=\<password\>;
Db2 JDBC drivers: <https://www.ibm.com/support/pages/db2-jdbc-driver-versions-and-downloads\>
The asset (Asset)
Almost every function returns an asset (dedomena.core.Asset): an object that carries the data and what is known about it.
| Attribute | What it holds |
|---|---|
content | The data: a DataFrame, or the path of the file written. |
kind | table, image, document, audio or video. |
sensitive_elements | The sensitive elements found: [\{'value', 'type', …location\}]. |
schema, quality, info | What Axon has learned about the dataset. |
lineage | The history of operations applied (inspection, cleaning, anonymization, synthesis…). |
ai_generated | Whether any operation generated new content (see Generated-content marking). |
You can pass an asset from one function to the next: Axon prepares, detection finds, Nucleus anonymizes or synthesizes, and the history is kept. To save it:
asset.save("output/", data_format="CSV") # output/data.csv + output/meta.json
Messages, logs and support
The library does not show Python tracebacks. When something fails, you see a single line:
Dedomena error [3f9a1c02]: Failed to train the synthesizer.
Details were written to /path/logHistory.log
| File | What it contains | For whom |
|---|---|---|
logHistory.log | The run log: what was done, and one line per error with its identifier. Written to the results folder or to the current folder. | For you |
dedomena-support.enc | The technical diagnostics of the errors, encrypted so that only Dedomena.AI support can read them. It does not contain your data: values are removed before encryption. | For sending to support if you need to |
DEDOMENA_LOG_FILE=/path/my_log.logchanges the location of the log. Useful with several jobs in parallel.
If you need help, send support the error identifier ([3f9a1c02]), logHistory.log and, if present, dedomena-support.enc.
Sensitive element detection
Detection finds the sensitive data in an asset and says where it is. Anonymization runs it on its own when needed, but you can also use it directly: to audit what data you hold, to review the findings before anonymizing, or to feed your own processes.
detect()
from dedomena.detection import detect
detect("documents/id_card.png") # image
detect("documents/contract.pdf") # PDF, DOCX or TXT
detect("calls/support.mp3") # audio
detect(dataframe) # table: which column is of which type
detect("My DNI is 12345678Z") # free text
It returns a list of elements with the same shape for every modality: the value, its type and where it is.
| Asset | Location | Example |
|---|---|---|
| Text | start, end (character positions) | \{'value': '12345678Z', 'type': 'dni', 'start': 10, 'end': 19\} |
| Table | column | \{'value': None, 'type': 'email', 'column': 'mail'\} |
| Image | box (pixels: x0, y0, x1, y1) | \{'value': '', 'type': 'face', 'box': (120, 80, 260, 240)\} |
page and box | \{'value': 'Ana García', 'type': 'name', 'page': 0, 'box': (…)\} | |
| DOCX | paragraph | |
| Audio | start, end (seconds) | \{'value': '612 345 678', 'type': 'phone_number', 'start': 12.4, 'end': 15.1\} |
| Video | start, end and box |
It requires the detection permission. If your package does not include the asset's modality, the function tells you so with the message Detection for '…' is not part of this build.
How detection works
Edge combines several layers, and they always work together:
-
Patterns and validations (always on): emails, phone numbers, Spanish DNI/NIE/CIF with their check letter, IBAN with its check digits, cards with their verification algorithm, licence plates, IP addresses… They win any overlap: a number that passes its validation is a fact.
-
AI models (depending on the installed size): they find what no pattern can reach, such as names, companies, free-form addresses, special categories of data (health, religion, trade union membership…), faces, signatures, logos and fingerprints.
-
Reading text in images (in Vision and in scanned PDFs): the text in the image is read and analysed like any other text, in Spanish, English, Basque, Catalan and Galician.
-
Transcription (in Audio): the audio is transcribed with per-word timestamps, and detection runs on the transcript. Numbers dictated in words ("six, one, two…") are also recovered when they pass their validation.
The library automatically picks the best engine installed for each modality (see Models: automatic, registered and your own). The shape of the result does not change with the engine; only the coverage does.
> No image model, no image detection. If your package includes Vision and you have not downloaded any model, the first image fails with a message that gives the command: dedomena-edge setup --size small. That way an image is never delivered after being only half-checked.
Sensitive element types
Each element has a type: the code you use in the anonymization configuration. The types are the same as in the cloud platform.
| Category | Codes |
|---|---|
| Identity | name, first_name, last_name, birthdate, age, gender, nationality, dni, nie, nif, vat, ssn, passport_number, document_support_number, health_card |
| Identity (visual) | face, signature, fingerprint |
| Address and location | address, street_address, street_name, city, postcode, country, country_code, coordinate, latitude, longitude |
| Network | email, company_email, url, domain_name, hostname, ipv4, ipv4_private, ipv4_public, ipv6, mac_address |
| Telecommunications | phone_number, msisdn, country_calling_code |
| Banking and financial | iban, bban, aba, swift, swift8, swift11, bank_country, credit_card_number, credit_card_full, credit_card_expire, credit_card_provider, credit_card_security_code |
| Company and brand | company, logo (visual) |
| Vehicle | license_plate, vin |
| Barcodes | ean, ean8, ean13, localized_ean, localized_ean8, localized_ean13 |
| Credentials | password, pin, api_key, access_token, refresh_token, private_key, secret |
| Special categories and health | religion, ethnicity, sexual_orientation, political_opinion, trade_union, medical_condition, mental_health, biometric_data, genetic_data |
To limit text detection to specific types:
from dedomena.detection import detect_in_text
detect_in_text("Call 612 345 678 or write to ana@example.com", types=["phone_number"])
detect_in_text(text, certain_only=True) returns only the values that passed a validation (check letter, check digits…).
Tables: which column is of which type
from dedomena.detection import detect_columns
detect_columns(dataframe)
# [\{'value': None, 'type': 'email', 'column': 'mail'\}, \{'value': None, 'type': 'dni', 'column': 'document'\}, ...]
It needs the Tables module. Columns are classified by their name and by a sample of their values.
Reviewing and reusing a detection
You can detect, review the result (or have a person review it) and then anonymize exactly what was reviewed, without detecting again:
from dedomena.core import Asset
from dedomena.detection import detect
from dedomena.nucleus import anonymize
asset = Asset("documents/id_card.png")
asset.sensitive_elements = detect(asset)
asset.save_elements("review/id_card.elements.json") # JSON to review or edit
# ... review ...
reviewed = Asset.load_elements("review/id_card.elements.json")
anonymize(reviewed, config="auto", output="output/id_card_anonymized.png")
An asset with its elements assigned is considered already reviewed: anonymization does not detect again and does not overwrite the review.
Audio in batches
To process many audio files, use the batch functions. Each file writes its own result, so a long run can be resumed:
from dedomena.detection import audio
paths = audio.discover("/data/calls", recursive=True)
results = audio.detect_many(
paths,
output_dir="/data/output/detections", # one \<audio\>.detection.json per file
types=\{"iban", "credit_card_number", "credit_card_security_code", "credit_card_expire"\},
resume=True, # skips files already processed
return_transcript=True,
workers=1,
)
audio.write_summary(results, "/data/output/detections")
For a single file:
aligned, unaligned, transcript = audio.transcribe_and_detect(
"call.mp3", language="es", return_transcript=True)
-
alignedare the elements with their time range. -
unalignedare the detected values that could not be placed in time. They are returned separately so they are never silently lost. -
If you keep the transcript and its timestamps, you can repeat detection with other types without transcribing again:
audio.detect_in_transcript(transcript, words, types=…). -
device="auto"(default) uses the GPU for lightweight transcription if available.device="cpu"forces the CPU.
Anonymization
Anonymization modifies the sensitive parts of your real data and leaves everything else exactly as it was. The result is a new anonymized file. It requires the nucleus.anonymize permission; the detection it runs internally does not need the detection permission.
First example
import dedomena
from dedomena.nucleus import anonymize
result = anonymize(
"data/customers.csv",
config="auto", # detects and applies each element's default method
output="output/customers_anonymized.csv",
output_dir="results", # mapping, run log and conformance report
)
print(result.content) # 'output/customers_anonymized.csv'
anonymize() works the same way with a table, an image, a document or an audio file: the type is inferred from the source.
anonymize() parameters:
| Parameter | Default | Description |
|---|---|---|
source | — | Path, DataFrame, database URL or Asset. |
config | "auto" | Which method to apply to each column or element (see The configuration). |
output | \<name\>_anonymized.\<ext\> in the current folder | Where to write the result. We recommend always setting it. |
output_format | from the extension | For tables: CSV, PARQUET, EXCEL or DATAFRAME (returns the result in memory, in result.content, without writing a file). |
output_dir | "results" | Folder for the reversal mapping, the run log and the conformance report. |
source_format, query | — | For databases ("DATABASE" and the query) or to force a file's format. |
country | "Spain" | Country of the data (in English): decides the format of the generated values (names, IDs, IBANs, phone numbers…). |
sensitive_elements | — | Elements already detected (by you or in a review). When given, detection does not run again. For tables it also accepts \{column: type\}. |
image_options | — | Image options (see Images). |
audio_options | — | Audio options (see Audio). |
noise_level | 0.05 | Default noise for perturb. |
seeds | — | Seeds to make the generated values reproducible. |
verbose | True | Show progress. |
It returns an Asset whose content is the path written (or the DataFrame, with output_format="DATAFRAME") and whose history records the anonymization.
The configuration
The configuration says what to do with each column (in tables) or with each element type (in images, documents and audio).
Automatic mode. config="auto" detects the sensitive elements and applies each one the default method of its type (see Which methods each type accepts).
Short form. The method name:
config = \{
"email": "mask",
"salary": "perturb",
"notes": "drop",
\}
Extended form. A dictionary with the method and, if it takes one, its parameter (param):
config = \{
"dni": \{"method": "pseudonym", "param": r"CUS-\d\{6\}"\},
"name": \{"method": "simulation", "param": "name"\},
"email": \{"method": "simulation", "param": "email", "derive_from": "name"\},
"postcode": \{"method": "mask", "param": 3\},
"signup_date": \{"method": "date_shift", "param": "customer_id"\},
"code": \{"method": "mask", "type": "string"\},
\}
| Key | What for |
|---|---|
method | The method (see Anonymization methods). |
param | The method's single parameter, if it has one. |
type | Forces the column type: string, categorical, numeric, integer, float, boolean, datetime or text. Use it, for example, so that a postcode like 08001 does not lose its leading zero. |
derive_from | Generates the value from another, already anonymized column. For example, the simulated email is built from the simulated name. |
references | "table.column": the column is a foreign key and reuses the value that entity received in the other table (see Deliveries of several assets). |
group_variants | On by default: variants of the same entity ("Ana García" and "Ana") receive a consistent replacement. |
-
In tables, the keys are the column names. Columns not listed in the configuration are left untouched.
-
In images, documents, audio and video, the keys are the element type codes (see Sensitive element types). Upper case, spaces and underscores are all accepted.
-
Method names accept synonyms:
simulateissimulation,tokenizeiscoding,blackoutisredact,muteissilence, and so on. The names used by the cloud platform are also accepted.
Anonymization methods
| Method | What it does | Parameter (param) | Reversible |
|---|---|---|---|
mask | Hides characters of the value. | Mode: asterisks (default, keeps the first and last characters), opaque (hides everything and keeps separators) or a number n (keeps the first n characters: 28001 with 3 → 280**) | Yes |
hash | Replaces the value with its SHA-256 fingerprint. The same value always gives the same fingerprint. | — | No |
pseudonym | Replaces each value with a unique, stable pseudonym. | Optional pattern, for example EMP-\d\{6\} | Yes |
simulation | Replaces the value with another realistic one of the same type (a name with another name, an IBAN with another valid IBAN), in the format of the data's country. | Type of value to generate: any code from Sensitive element types (default name) | Yes |
coding | In free text, replaces each sensitive item with a label (\{\{ Name_1 \}\}) or with an invented value. | Mode: tokens (default, labels) or invented (invented values) | Yes |
text_regenerate | Rewrites a whole free text with the language model, without personal data, keeping the column's language, tone and format. | — | Yes |
shuffle | Shuffles the column's values across rows. | — | No |
generalize | Groups values into ranges (1980-1990). For categories, groups the infrequent ones into Others. | Range size (default 10) | Yes |
perturb | Adds random noise to numeric values or dates. | Noise level between 0 and 1 (default 0.05) | No |
date_shift | Shifts dates by a random interval, the same for every date of an entity. | Entity column (for example customer_id) to keep the same shift across all its rows and tables | Yes |
drop | Removes the column from the result. | — | No |
blur | Blurs the region (face, signature, logo, fingerprint). The strength adapts to the region's size. | — | No |
pixelate | Pixelates the region. | Block size, from 4 to 64 (default, based on the region's size) | No |
redact | Covers the region with an opaque rectangle. | — | No |
beep | Replaces the audio segment with a beep. | — | No |
silence | Silences the audio segment, without changing its length. | — | No |
remove | Cuts the audio segment out. | — | No |
Methods that need the language model. text_regenerate needs the Text module with a registered model (see Registering a model explicitly). Without it, anonymization stops with an error instead of delivering the column empty or untouched. It cannot be used on columns that are not free text either: for a column of URLs or codes, use simulation or coding.
Which methods each type accepts
The method must be valid for the type of the column or element. The first one in each list is the one config="auto" applies.
| Type | Methods (the first is the default) |
|---|---|
| Text element (in images, documents and free text) | mask, coding, simulation, pseudonym, hash |
| Visual element (face, signature, logo, fingerprint) | pixelate, blur, redact |
| Audio element | beep, silence, remove |
| Text column | mask, simulation, pseudonym, coding, hash, shuffle, drop, generalize, text_regenerate, date_shift |
| Categorical column | mask, simulation, pseudonym, shuffle, hash, coding, drop, generalize, text_regenerate |
| Numeric column | perturb, shuffle, mask, hash, drop |
| Boolean column | pseudonym, shuffle, mask, simulation, hash, drop |
| Date column | date_shift, mask, shuffle, simulation, drop, perturb |
date_shift is also accepted on text columns, because dates read from a CSV usually arrive as text.
Tables
from dedomena.nucleus import anonymize
anonymize(
"data/employees.parquet",
config=\{
"dni": \{"method": "pseudonym", "param": r"EMP-\d\{6\}"\},
"name": \{"method": "simulation", "param": "name"\},
"email": \{"method": "simulation", "param": "email", "derive_from": "name"\},
"phone": "mask",
"salary": \{"method": "perturb", "param": 0.1\},
"birth_year": \{"method": "generalize", "param": 10\},
"comments": \{"method": "coding", "param": "tokens"\},
\},
output="output/employees_anonymized.parquet",
output_format="PARQUET",
output_dir="results",
country="Spain",
)
-
Tables are anonymized on CPU. They only need a model if you use
text_regenerate, or if you wantcodingto find names and companies inside free text (with the lightweight or the full model). -
To get the result in memory:
output_format="DATAFRAME", and it is inresult.content. -
If you do not set
output, the file is written to the current folder as\<name\>_anonymized.\<ext\>.
Images
anonymize(
"documents/id_card.png",
config=\{
"face": "blur",
"signature": "redact",
"name": \{"method": "simulation", "param": "name"\},
"dni": "mask",
"birthdate": "mask",
\},
output="output/id_card_anonymized.png",
)
-
Visual elements (face, signature, logo, fingerprint): the whole region is hidden, including any text on top of it.
-
Text inside the image: the original value is erased and the new value is drawn to fit its space, with colours taken from the image itself.
-
MRZ zone of passports and ID documents: replaced by a valid synthetic MRZ (with its check digits), consistent with the document's already anonymized data. If it cannot be rebuilt safely, it is pixelated.
-
Generative editing (optional, requires its model and a GPU): if installed, text is replaced with a natural look instead of being drawn on top. If not installed, the drawing method is used, which is always predictable.
-
If a configured element has no location, anonymization fails instead of delivering an image that looks anonymized but is not.
Image options (image_options):
| Option | Default | Description |
|---|---|---|
blur_radius | based on size | Fixed blur strength. |
pixelate_block | based on size | Fixed pixelation block size. |
fill_color | (0, 0, 0) | Colour used by redact. |
text_color, background_color | taken from the image | Colours of the redrawn text. |
font_path | automatic | Font for the redrawn text. |
padding | 2 | Margin in pixels around each region. |
mrz_synth | True | Replace the MRZ zone with a valid synthetic one. |
generative_replace | True | Try generative editing if its model is installed. With False, drawing is always used. |
Documents
anonymize(
"documents/contract.pdf",
config=\{"name": "pseudonym", "address": "mask", "iban": "hash", "signature": "redact"\},
output="output/contract_anonymized.pdf",
)
-
PDF: sensitive text is removed from the document, not just covered (it cannot be recovered by selecting or copying it), and the replacement is written with the original font, size and colour. Form fields, embedded images and visual regions (signatures, faces, logos) are anonymized too.
-
DOCX: the body, tables, headers and footers are processed. A modified paragraph may lose any character formatting it had inside.
-
TXT: direct text replacement.
Audio
anonymize(
"calls/customer_support.mp3",
config=\{"name": "beep", "phone_number": "beep", "iban": "silence", "address": "beep"\},
output="output/customer_support_anonymized.mp3",
audio_options=\{"language": "es"\},
)
Audio options (audio_options):
| Option | Default | Description |
|---|---|---|
language | automatic detection | Language of the recording ("es", "en", "fr"…). Setting it improves transcription. |
beep_frequency | 1000 | Beep frequency, in Hz. |
beep_gain_db | -3 | Beep volume, relative to the recording's average volume. |
padding_ms | 50 | Margin added on each side of the anonymized segment, in milliseconds. |
fail_on_unaligned | False | If True, anonymization fails when a detected value cannot be placed in time. If False, it is recorded in the conformance report. |
-
If two segments overlap, the most destructive method is applied (
remove>silence>beep). -
The result keeps the input format.
Deliveries of several assets
When you anonymize several related assets (several tables from the same system, or a table and the documents of those same customers), use the Anonymization class. It guarantees that:
-
the same person receives the same replacement in every asset;
-
the dates of the same entity are shifted equally in every table;
-
foreign keys (
references) still match after anonymization.
from dedomena.nucleus import Anonymization
policy = \{
"customer_id": \{"method": "pseudonym", "param": r"C-\d\{6\}"\},
"name": \{"method": "simulation", "param": "name"\},
"email": \{"method": "simulation", "param": "email", "derive_from": "name"\},
"date": \{"method": "date_shift", "param": "customer_id"\},
\}
tables = [
\{"name": "orders", "data": "data/orders.csv",
"config": \{**policy, "customer_id": \{"method": "pseudonym", "references": "customers.customer_id"\}\}\},
\{"name": "customers", "data": "data/customers.csv", "config": policy\},
]
anon = Anonymization(config=policy, output_dir="delivery_october", country="Spain")
for table in anon.ordered(tables): # referenced tables first
anon.run(table["data"], name=table["name"], config=table["config"],
output=f"delivery_october/\{table['name']\}.csv")
anon.conformance() # conformance report as PDF and JSON
print(anon.findings) # anything left untreated
-
ordered()sorts the tables so that a referenced table is anonymized before the tables that reference it. -
run()accepts the same sources asanonymize(), plusname(the table's name in the delivery, whichreferencespoint to) andconfig(to override the policy for that asset).
Files generated
| File | Where | What it contains |
|---|---|---|
| The anonymized asset | output | Your file, anonymized. |
metadata_anonymization_mapping.json | output_dir | The \{original: anonymized\} mapping of the reversible methods. It is the only file that contains the original values: keep it protected. |
metadata_anonymization_run.json | output_dir | Which assets were processed, under which policy and what was treated in each. |
conformance.json | output_dir | The conformance record, updated after each asset. |
conformance.pdf | output_dir | The conformance report, when calling Anonymization.conformance(). |
\<file\>-metadata.json | next to the asset | The generated-content marking (see Generated-content marking), only if a method that generates values was applied. |
logHistory.log | output_dir | The run log. |
The conformance report describes what was detected, what was treated and with which method, which values changed, which entities are shared between assets, and what each treatment protects against (and what it does not). If anything was left untreated (a configured column that does not exist, an audio value that could not be placed in time…), it appears in the report and is also printed on screen when that asset finishes, even with verbose=False.
Generated-content marking
When a method writes new values (simulation, text_regenerate or coding in invented mode) and has treated at least one element, Edge marks the result as AI-generated content, as required by Article 50 of the EU AI Act:
-
the asset's history says so (
result.ai_generatedisTrue); -
a
\<file\>-metadata.jsonfile with the provenance information is written next to the file.
Methods that hide, remove or transform what was there (mask, hash, pseudonym, blur, pixelate, redact, beep, drop, perturb, shuffle, generalize, date_shift) do not mark the result. Synthetic data is always marked.
Reversal and incremental deliveries
Reversible methods (mask, pseudonym, simulation, generalize, coding, date_shift, text_regenerate) save their mapping in output_dir. With it, an authorized person can reverse an anonymized table:
from dedomena.nucleus import deanonymize
original = deanonymize("output/customers_anonymized.csv",
mapping_dir="results",
output="reversed/customers.csv")
hash, shuffle, perturb, drop and the visual and audio methods cannot be reversed. Reversal applies to tables.
Incremental deliveries. To anonymize the next month's data with exactly the same replacements as the previous delivery (the same person gets the same pseudonym, their dates are shifted the same way), load the previous delivery:
from dedomena.nucleus import Anonymization
anon = Anonymization.load("delivery_october")
anon.run("data/customers_november.csv", name="customers",
output="delivery_november/customers.csv")
Synthetic data
A synthesizer learns the statistical properties of your real data, so that new synthetic data can be generated from it without copying the original records. The workflow is always the same:
-
Train the synthesizer on your data, on your machine (
train). -
Evaluate its quality, privacy and utility (
evaluate). -
Save it to a file (
save). -
Upload the synthesizer to the Dedomena.AI platform.
-
Generate the synthetic data from the platform, or through the API.
Training requires the Tables module and the nucleus.synthesis permission. Synthetic data is generated on the Dedomena.AI platform, from the synthesizer you upload (see Generating data).
Choosing the algorithm
| Algorithm | Class | For what data |
|---|---|---|
| Generic | Generic | A table in which each row is independent (customers, policies, patients…). |
| Transactional | Transactional | Movements of an entity over time, with date, amount and description (bank transactions, purchases…). |
| Time series | TimeSeries | Periodic measurements of several entities (meter readings, sensors…). |
| Relational | Relational | Several tables related by keys (customers → orders → order lines). |
| Automatic | Auto | Reads the data and chooses between Generic, Transactional and Relational. |
from dedomena.nucleus import Auto, choose_algorithm
choose_algorithm("data/sales.csv") # ('transactional', 'reason…'), without training anything
model = Auto(epochs=150)
model.train("data/sales.csv")
print(model.algorithm, model.reason)
Auto never chooses TimeSeries: if your data is a time series, use that class directly.
Full example (Generic)
import dedomena
from dedomena.nucleus import Generic
# 1. Configure
model = Generic(
synthesizer_name="customers_v1",
synthesizer_description="Retail customers 2026",
epochs=200,
batch_size=256,
configuration=\{
"categorical_columns": ["province", "segment"],
"integer_columns": ["age"],
"float_columns": ["income"],
"date_columns": \{"signup_date": "%Y-%m-%d"\},
"id_columns": \{"customer_id": r"CUS-\d\{6\}"\},
"target": "segment",
\},
sensitive=\{"name": "name", "email": "email", "dni": "dni"\},
constraints=["age\>=18", "income\>=0"],
)
# 2. Train
model.train("data/customers.csv")
# 3. Evaluate (writes report/report.pdf and report/evaluation.json)
privacy, quality, utility = model.evaluate(output_dir="report")
# 4. Save (writes synthesizers/synthesizer.zip, with the report inside)
model.save("synthesizers")
# 5. Upload synthesizers/synthesizer.zip to the Dedomena.AI platform and generate the data there
Common options
These options are passed when creating Generic, Transactional, TimeSeries or Auto:
| Option | Default | Description |
|---|---|---|
epochs | 150 | Training passes. Between 150 and 300 is recommended. It can also be set in train(epochs=…). |
batch_size | 128 | Batch size (128, 256, 512…). |
configuration | \{\} | The column types (see the table below). |
sensitive | \{\} | \{column: type\}. Sensitive columns that, in the generated data, are filled with realistic fictitious values of the given type (name, first_name, last_name, email, phone_number, address, city, iban, credit_card_number, company, dni, nie, nif, cif, ssn, birthdate, gender, nationality, health_card, passport_number…). |
datasets_country | "Spain" | Country of the data (in English): decides the format of the fictitious values. |
constraints | [] | Rules the generated data must always meet (see Constraints). |
amplify | "default" | "default" generates new records. "quality" maximizes fidelity by starting from the real records, slightly modified: it offers much less privacy and requires at least 150 passes. Use it only when privacy is not the goal. |
impute | True | Fills missing values during training, and the generated data has no gaps. With False, gaps are kept. |
max_categories | — | Maximum number of distinct values a categorical column keeps; the less frequent ones are grouped into Others - DM. |
min_freq_categories | — | Minimum frequency to keep a value; less frequent ones are grouped into Others - DM. |
num_cat | — | Number of most frequent numeric values treated as categories (improves common prices, round amounts, zeros…). |
coordinate_columns | [] | Latitude and longitude pairs: [\{"latitude": "lat", "longitude": "lon"\}]. |
drop_primary | False | Excludes from the model categorical columns that are practically a key (99 % distinct values). |
cuda | True | Trains on GPU if one is available; otherwise on CPU. cuda=False forces the CPU. |
synthesizer_name, synthesizer_description | — | Name and description, shown in the metadata and in the report. |
Column types (inside configuration):
| Key | Format | Description |
|---|---|---|
categorical_columns | list | Columns with discrete values. |
integer_columns | list | Whole numbers. |
float_columns | list | Decimal numbers. |
boolean_columns | list | True/false. |
date_columns | list or \{column: format\} | Dates. With a dictionary, the generated data is written in that format ("%Y-%m-%d"). |
id_columns | list or \{column: pattern\} | Identifiers. With a pattern (r"ID_\d\{4\}"), they are generated from it; in a list, they are numbered 0, 1, 2… |
text_columns | \{column: instruction\} | Free-text columns (see Free-text columns and privacy). |
target | text | The column you are most interested in predicting. Used to measure utility in the evaluation. |
Columns you do not declare are classified automatically, and the library tells you how it classified them. No column is discarded: if you do not want a column in the model, remove it from the data before training.
> Column types go inside configuration. Passing them as separate arguments (categorical_columns=[…]), as in previous versions, raises an error when creating the model.
Transactional
For an entity's movements over time. Use columns_mapping to say which column of your data plays each role:
| Role | Meaning | Required |
|---|---|---|
txn_date | Date of the movement | Yes |
amount | Amount (negative = expense, positive = income) | Yes |
concept | Description of the movement | Yes |
user_id | Customer or account | No (without it, a single history is assumed) |
balance | Balance | No |
cat_id | Category of the movement | No |
from dedomena.nucleus import Transactional
model = Transactional(
columns_mapping=\{"user_id": "account", "txn_date": "date", "amount": "amount_eur",
"concept": "description", "balance": "balance_eur", "cat_id": "category"\},
balance_updated=True,
transform_descriptions="level2",
configuration=\{"categorical_columns": ["category"], "date_columns": \{"date": "%Y-%m-%d"\}\},
datasets_country="Spain",
epochs=250,
synthesizer_name="transactions_v1",
)
model.train("data/transactions.parquet")
model.save("synthesizers/transactions")
-
balance_updated:Trueif each row's balance already includes that row's amount;Falseif it includes the previous row's. The balance of the generated data is recomputed from the amounts. -
With a
cat_idcolumn, consistency between description and category is preserved.
Transaction descriptions (transform_descriptions, default "level2"): the generated descriptions are rewritten so they do not repeat real texts and are consistent with their row's amount and date.
| Level | What is replaced in the description |
|---|---|
level1 | Dates, amounts, card numbers and account numbers (IBAN). |
level2 | Everything above, plus people's names, addresses and cities. |
level3 | Everything above, plus merchant names, replaced with others from the same sector (for example, one fast-food chain with another). |
Levels 2 and 3 make use of the detection and text models if they are installed. Without them, they rely on patterns and fictitious values.
Time series
For periodic measurements of several entities. Use columns_mapping to give the entity (user_id), the date (txn_date) and the measurement (amount). At least 5 distinct entities are needed.
from dedomena.nucleus import TimeSeries
model = TimeSeries(
columns_mapping=\{"user_id": "meter", "txn_date": "date", "amount": "kwh"\},
static_columns=["tariff", "province"], # constant for each entity
epochs=300,
batch_size=64,
)
model.train("data/readings.csv")
model.save("synthesizers/readings")
-
Series are modelled with one observation per day. Date columns other than
txn_dateare not used. -
When you generate data from a time-series synthesizer, the amount you request is the number of series (entities), not of rows.
Relational
For several related tables. Describe each table in datasets_config and pass the data of all of them to train():
from dedomena.nucleus import Relational
config = \{
"customers": \{
"primary_key": "customer_id",
"categorical_columns": ["segment", "province"],
"integer_columns": ["age"],
"sensitive": \{"name": "name"\},
"amplify": "default",
\},
"orders": \{
"primary_key": "order_id",
"foreign_key": \{"customer_id": "customers"\},
"date_columns": ["order_date"],
"float_columns": ["total"],
"amplify": "default",
\},
\}
model = Relational(datasets_config=config, epochs=200, datasets_country="Spain",
synthesizer_name="shop_v1")
model.train(\{"customers": "data/customers.csv", "orders": "data/orders.parquet"\})
model.save("synthesizers/shop")
Per-table keys in datasets_config:
| Key | Description |
|---|---|
primary_key | The table's primary key (unique values). |
foreign_key | \{column: "table"\}: the column points to that table's primary key. If the column has a different name: \{column: \{"dataset": "table", "field": "key"\}\}. |
categorical_columns, integer_columns, float_columns, boolean_columns, date_columns | The table's column types. |
text_columns, sensitive, target, constraints, columns_mapping, max_categories, min_freq_categories, num_cat, drop_primary | As in the other classes, for that table. |
algorithm | "generic" (default) or "transactional". With "transactional", also transform_descriptions and balance_updated. |
impute | False by default in relational. |
amplify | "quality" by default in relational. Set "default" on each table if you want new records and maximum privacy (see Common options). |
-
Relationships must form a graph without cycles, and every foreign key must point to a primary key. Rows whose keys point nowhere are discarded during training, with a warning.
-
Relational training requires 150 or more passes.
-
When you generate data from a relational synthesizer, the amount you request is the number of complete copies of the schema. Keys still match across the generated tables.
-
A relational synthesizer cannot be retrained: train a new one with the updated data.
Constraints
Rules the generated data always meets. They are written without spaces:
| Form | Meaning |
|---|---|
col1\<-\>col2\<-\>col3 | Only combinations that exist in the real data are generated (for example, description ↔ subcategory ↔ category). |
col1\<=col2, col1\<col2, col1\ />=col2, col1\>col2 | Comparison between two columns (for example, start_date\<=end_date). |
col1\<=col2\<=col3 | Order across three columns. |
col1\>0, col1\>=0, col1\<0, col1\<=0 | Positive or negative values. |
col1\>=18 | Minimum value (whole number). |
0\<=col1\<=100 | Value between a minimum and a maximum (whole numbers). |
col1//1000 | Multiple of 1000. |
A malformed constraint raises an error during training.
Generating data
Synthetic data is generated on the Dedomena.AI platform, from the synthesizer you train with Edge:
-
Save the synthesizer with
model.save(folder). It writessynthesizer.zipin that folder. -
Upload
synthesizer.zipto the Dedomena.AI platform, from the web application or through the API. -
Generate the synthetic data from the platform, or through the API.
The generation options (number of records, conditional generation…) are described in the platform and API documentation. Synthetic data is always marked as AI-generated content.
Saving, loading and retraining
model.save("synthesizers/customers") # → synthesizers/customers/synthesizer.zip
model = load_synthesizer("synthesizers/customers/synthesizer.zip")
model.metadata # name, algorithm, columns, scores…
-
save()takes a folder and always writessynthesizer.zipinside it. If you ranevaluate()before, the report is included. -
load_synthesizer(path)loads a saved synthesizer, for example to retrain it. -
Protect
synthesizer.zipas sensitive data: it contains the trained model and may contain samples of the training data.
Retraining with new data (Generic, Transactional and TimeSeries only):
model = load_synthesizer("synthesizers/customers/synthesizer.zip")
model.retrain(data="data/customers_november.csv", epochs=100)
model.save("synthesizers/customers_v2")
-
Always set
epochs. -
With
amplify="quality",forgetfulness_factor(between 0 and 1) decides how much of the previous data is forgotten:0keeps everything,1starts from scratch.
Evaluation and report
privacy, quality, utility = model.evaluate(output_dir="report")
It measures the synthesizer against the real data it was trained on and returns three scores from 0 to 100:
| Score | What it measures |
|---|---|
| Privacy | How hard it is to link the synthetic data to real people: exact copies, closeness to real records, singling-out and linkability risk. |
| Quality | How well the distributions, correlations and coverage of the real data are preserved (and the time patterns, if there are dates). |
| Utility | Whether a machine learning model trained on synthetic data performs like one trained on real data, on the target column. |
The following are written to output_dir:
-
report.pdf: the report, with the scores, each metric explained and its charts; -
evaluation.json: the same results as data, to integrate into your processes; -
logHistory.log.
Each score comes with a rating: critical (up to 40), poor (up to 60), fair (up to 80) or excellent.
-
Control data. To measure singling-out and linkability risk, set aside, before training, a part of the real data the model does not see, and pass it as
control_data:model.evaluate(output_dir="report", control_data="data/control.csv"). -
Evaluation needs at least 3 typed columns that are neither sensitive nor identifiers.
-
Run
evaluate()beforesave()so the report is included in the synthesizer. -
In relational,
evaluate(output_dir=…)returns the average scores and includes referential integrity.
Evaluating any two datasets. To compare a real dataset with synthetic data you generated on the Dedomena.AI platform:
from dedomena.nucleus.evaluations import synthesizer_evaluations
privacy, quality, utility = synthesizer_evaluations(
real_data="data/real.parquet",
synthetic_data="data/synthetic.parquet",
categorical_columns=["province", "segment"],
integer_columns=["age"],
float_columns=["income"],
date_columns=["signup_date"],
target="segment",
output_dir="report",
synthesizer_name="customers_v1",
)
For several tables, synthesizer_evaluations_multitable(configuration=\{table: \{...\}\}, output_dir="report") produces a single combined report.
Free-text columns and privacy
Free-text columns (text_columns) are modelled as categories: the generated texts are taken from the real training texts. If those columns contain personal data, anonymize them before training, for example with coding or text_regenerate (see Anonymization), or remove them from the data.
The exception is transaction descriptions in Transactional with transform_descriptions, which are rewritten when generating (see Transactional).
Starting from data prepared with Axon
If you pass train() an asset prepared with Axon, the synthesizer uses the column types Axon detected. What you declare in configuration takes precedence. See From Axon to Nucleus.
Axon: dataset quality and preparation
Axon checks and prepares a dataset before you anonymize or synthesize it: it infers column types, detects outliers and missing-value patterns, cleans the text, applies transformation rules and produces a quality report in PDF. It is included in every package and requires the axon permission.
Each step returns a new asset and leaves the original unchanged; the history accumulates.
Inspecting
from dedomena.axon import Inspector
asset = Inspector().run("data/customers.csv", name="customers")
asset.schema["types"] # categorical_columns, integer_columns, float_columns, boolean_columns, text_columns, date_columns
asset.schema["semantic"] # identifier, phone, email/URL, coordinate columns…
asset.schema["outliers"] # \{column: [rows with outliers]\}
asset.info["rows"], asset.info["columns"]
asset.info["axon"]["unavailable"] # \{\} if everything could run
Inspector option | Default | Description |
|---|---|---|
profile | True | Statistical profile of each column. |
outliers | True | Outliers in numeric columns. |
correlations | True | Correlations between columns. Turn it off for very wide tables. |
semantic | True | Semantic types (identifiers, phone numbers, emails, coordinates…). |
missing | True | Missing-value patterns. |
sample | — | Analyse only the first N rows, for very large tables. |
The short form is inspect_dataset(source, **options).
Cleaning
from dedomena.axon import clean
cleaned = clean("data/customers.csv", name="customers")
cleaned.info["axon"]["cleaning"] # duplicate rows, repeated headers, missing values, spaces fixed…
cleaned.quality["cleaning_after"] # indicative score from 0 to 1
Cleaning:
-
replaces tabs and line breaks with spaces, collapses repeated spaces and trims each cell;
-
removes typographic or unbalanced quotes;
-
converts to numbers the columns whose decimals are written with a comma (
"1,5").
It also counts duplicate rows and headers repeated inside the data, but does not remove them. Remove them yourself if needed, for example with df.drop_duplicates().
After cleaning, the columns are left as text. To give them back their type, apply cast_type rules (next section) and inspect again.
Applying rules
from dedomena.axon import ApplyRules, Inspector
rules = \{
"email": \{"type": "normalize_nulls"\},
"name": \{"type": "normalize_text_casing", "params": \{"proposed_case": "title"\}\},
"age": [\{"type": "cast_type", "params": \{"target_type": "integer", "mode": "force"\}\},
\{"type": "nullify_out_of_range", "params": \{"min": 0, "max": 120\}\}],
"amount": \{"type": "cast_type", "params": \{"target_type": "float", "mode": "force"\}\},
"city": \{"type": "enforce_category", "params": \{"allowed": ["Madrid", "Barcelona", "Valencia"]\}\},
\}
prepared = ApplyRules(rules).run(cleaned, name="customers")
prepared.lineage[-1].details["applied"] # rules applied per column
prepared.lineage[-1].details["failed"] # rules that could not be applied (e.g. missing column)
prepared = Inspector().run(prepared, name="customers") # final types
Each column takes one rule or a list of rules, applied in order:
| Rule | Parameters | What it does |
|---|---|---|
normalize_nulls | — | Turns null, none, nan, na, n/a, -, -- and empty strings into missing values. |
cast_type | target_type: float, integer, string, category, boolean, datetime; mode: force | Changes the column type. Understands European decimals (1.234,56) and booleans in Spanish and English (sí/no, yes/no, 1/0). With mode: "force", values that cannot be converted become missing; without it, they are kept. |
format_datetime | strftime | Writes dates in the given format. |
normalize_text_casing | proposed_case: upper, lower, title | Upper case, lower case or title case. |
impute_nulls | method: median, median_int, mode, ffill, constant (with value) | Fills missing values. |
nullify_out_of_range | min, max | Sets values outside the range to missing. |
clamp_range | min, max | Clamps values outside the range to the limit. |
enforce_category | allowed (list) | Fixes case and accents towards the allowed value and sets values that are not allowed to missing. |
enforce_format | pattern (# = one letter or digit, * = anything) | Sets values that do not follow the pattern to missing. |
replace_value | mapping, case_sensitive, regex | Replaces values. |
round_decimals | decimals (default 2) | Rounds. |
For anonymization and synthesis to see a column's new type, use cast_type with "mode": "force".
Quality report in PDF
from dedomena.axon import report
path = report(prepared, "reports/customer_quality", lang="en", name="Customers 2026")
# → 'reports/customer_quality.pdf'
-
lang:"en"or"es". -
The report is built from what the asset already knows: inspect it before generating the report.
-
The report includes example values from each column. Treat it with the same confidentiality as the dataset. The same applies to
meta.jsonwhen you save an inspected asset.
Risk and executive summary
These two functions need the language model: the Text module with a model installed.
from dedomena.detection import detect, llm
from dedomena.axon import risk, insight, report
llm.register() # needed for insight()
prepared.sensitive_elements = detect(prepared) # detect before computing the risk
with_risk = risk(prepared, name="customers")
summary = with_risk.info["risk"]["summary"]
summary["label"], summary["level"], summary["sensitivity_index"], summary["avg_risk_level_sensitive"]
with_insight = insight(with_risk, language="english", context="Online channel sign-ups", rows=200)
print(with_insight.info["insight"]["text"])
report(with_insight, "reports/customer_risk.pdf", lang="en") # adds the risk and insights sections
-
risk()computes the re-identification risk level from the detected sensitive columns: rundetect()first; without detection, the result is "no risk". -
insight()writes an executive summary of what the data says, from the firstrowsrows.languageaccepts"spanish"or"english".
From Axon to Nucleus
The recommended workflow:
-
Inspect the original data with
Inspector, to get a diagnosis. -
Clean with
clean. -
Apply rules with
ApplyRules, usingcast_typewith"mode": "force"to fix the types. -
Inspect again with
Inspector, so the schema describes the prepared data. -
Anonymize or synthesize with Nucleus.
from dedomena.nucleus import anonymize, Generic
anonymized = anonymize(prepared, config="auto", output="output/customers_anon.csv")
model = Generic()
model.train(prepared) # uses the types Axon detected
-
Synthesis: uses Axon's column types for everything you have not declared in
configuration. -
Anonymization: uses the cleaned content and, if the asset already has detected elements, uses them without detecting again.
-
Always set
output=when anonymizing an Axon asset (oroutput_format="DATAFRAME").
Checking the Axon engine
from dedomena.axon import engine_report
engine_report() # \{'basic_cleaner': None, 'profiling': None, ...\} None = works
If part of the engine cannot run on your machine (for example, because of a dependency), Inspector skips it, warns you and records it in asset.info["axon"]["unavailable"] with the reason. The rest of the analysis continues.
Models: automatic, registered and your own
How the library chooses
You do not need to configure anything: the library uses the best engine it finds installed for each modality.
| Modality | Order of preference |
|---|---|
| Text | Full text model → lightweight text model → patterns only |
| Image (also scanned pages and video frames) | Full image model → lightweight image model → error: a model is required |
| Audio | Lightweight transcription → full transcription |
-
Patterns and validations are always applied, with any engine.
-
When loaded, each engine writes a line to the log with what was chosen and the device (
text detection engine selected: …,small-text device selected: cpu…). -
If an engine cannot be loaded, the library moves on to the next one and warns you. If what is missing are the models, the warning includes the command to download them:
codetext detection engine selected: patterns (...). Download the models with your licence: `dedomena-edge setup --size small`. -
Lightweight models run on CPU even when there is a GPU, so they do not take up card memory. You can move them to the GPU (see Registering a model explicitly).
Which engine will be used
from dedomena.detection import models
print(models.report()) # what will be used for each modality, and whether it fits on the GPU
GPU: 96 GB
residency: shared
runs on:
audio: small-audio on cpu
images: …
text: …
-
models.runtime_report()returns the same as a dictionary. -
models.plan()tells you, for the full models, whether they fit on your GPU:fits,tight(they fit with a reduced context) orno. -
None of this loads the models.
Per modality:
from dedomena.detection import llm, small, audio
llm.model_report() # (path, engine, problem): problem None = it can be loaded
small.report() # available lightweight models, and what is missing
audio.transcriber_report(device="auto")
Registering a model explicitly
Registering a model loads it into memory and connects it to detection and anonymization. The library does this on its own when it needs to, but registering it yourself lets you choose which capabilities to enable, on which device, and when to pay the loading time.
Language model (Text module):
from dedomena.detection import llm
llm.register() # with the installed models
llm.register option | Default | What it enables |
|---|---|---|
model | — | Path to a specific model (see Using a model you already have). |
detect | True | The model detects sensitive data in free text (in addition to the patterns). |
generate | True | Enables text_regenerate and text rewriting. Required for text_regenerate. |
confirm | False | The model reviews the elements another detector proposes: it removes false positives and corrects types, never adds. Useful with detect=False (patterns propose and the model judges) or with elements that arrive already detected. |
insight | True | Enables axon.insight(). |
| others | — | Engine options, such as n_ctx (context, 8192 by default) or temperature (0 by default, for reproducible results). |
llm.unregister() hands text detection back to the patterns. llm.available() tells you whether it can be loaded.
> text_regenerate needs the model to be registered. Automatic detection loads the language model for detection only. To use text_regenerate, call llm.register() first.
Lightweight models:
from dedomena.detection import small
small.register(device="cuda") # moves the lightweight text and image models to the GPU
small.upgrade() # swaps the lightweight models for the full ones, if installed
Full image model:
from dedomena.detection.images import vlm
vlm.register(confirm=True) # also reviews each proposed region by cropping it
Using a model you already have
If your organization already has its own language model, Edge can use it for free-text detection, text_regenerate and Axon's executive summaries. You need the Text module.
Model requirements:
-
A conversational language model (instruction-tuned), with a context of at least 8,192 tokens.
-
Multilingual, if your data is not in English.
-
Edge sends it its own instructions. Detection quality depends on the model: test it with your data before using it in production.
Option 1: your weights, on your disk
Give the path of the model:
export DEDOMENA_LLM_MODEL=/models/my-model
or when registering it:
from dedomena.detection import llm
llm.register(model="/models/my-model")
| Format | Where it runs | What you need |
|---|---|---|
Standard model folder, with config.json, the tokenizer and the weights | GPU with enough memory (GPU engine, dedomena-edge[gpu]); without a GPU, on CPU, much more slowly | gpu hardware to use the GPU |
A single quantized .gguf file | CPU | The text extra and a C++ compiler to install it: pip install --index-url "https://licence:\<TOKEN\>@api.dedomena.ai/edge/\<modules\>/cpu/simple/" "dedomena-edge[text]" |
If the folder contains both a model folder and a .gguf file, the folder wins.
Option 2: your own model server
If you already serve a model on your internal network with an API compatible with OpenAI's chat API (/v1/chat/completions):
export DEDOMENA_LLM_ENDPOINT=http://models.internal:8000 # without /v1
export DEDOMENA_LLM_ENDPOINT_MODEL=model-name-on-your-server
from dedomena.detection import llm
llm.register()
-
Edge calls
\<DEDOMENA_LLM_ENDPOINT\>/v1/chat/completions. -
The server must be on your network: that way the data still does not leave your infrastructure.
-
Edge does not send an authentication header. If your server requires one, expose it behind an internal proxy that adds it.
-
With this variable set, Edge uses the server instead of a local model.
Your own image model
It works the same way with DEDOMENA_VISION_MODEL (a model folder) or with DEDOMENA_VISION_ENDPOINT and DEDOMENA_VISION_ENDPOINT_MODEL (a compatible server). Edge asks it to locate regions (faces, signatures, logos, fingerprints) and values within the image and to return their coordinates, so it must be a vision-language model capable of doing so. We recommend using Dedomena.AI's image model, unless you validate yours with your documents.
Forcing an engine
The library chooses on its own how to run the model, based on its format and your hardware. If you need to force a different way of running it (for example, because the GPU is busy with another process), ask Dedomena.AI support about the DEDOMENA_LLM_BACKEND and DEDOMENA_VISION_BACKEND variables.
Registering your own detector
If you have your own detector (for example, an internal model for a document type of your own), you can plug it in and every function in the library will use it instead of the built-in engine:
from dedomena.detection import register_detector, detect
def my_text_detector(text, types=None):
"""Receives a text and returns the elements it finds."""
results = []
for number in find_policy_numbers(text): # your logic
results.append(\{"value": number, "type": "health_card"\})
return results
register_detector("text", my_text_detector)
detect("Policy AX-99812 is in the name of…")
| Channel | Receives | Returns |
|---|---|---|
"text" | (text, types=None) | [\{'value', 'type'\}], with optional start/end (if not given, every occurrence of the value in the text is located) |
"image" | An image (PIL) | [\{'value', 'type', 'box': (x0, y0, x1, y1)\}] |
"audio" | (path, language=None) | Segments with value, type, start, end (seconds) |
-
Patterns still run before your detector and win overlaps.
-
A text value that does not appear verbatim in the text is discarded.
-
If your detector fails, the library falls back to the patterns for that batch and warns you in the log.
-
To remove it:
from dedomena.detection.registry import unregister_detector; unregister_detector("text").
In the same way, register_generator("text_regenerate", function) lets you plug in your own text generator. The function receives (prompts, max_tokens=...) and returns one string per prompt.
Where the models are
The library looks for each model in this order:
-
The path you give when registering it (
model=). -
Its environment variable:
DEDOMENA_LLM_MODEL,DEDOMENA_VISION_MODEL,DEDOMENA_AUDIO_MODEL,DEDOMENA_SMALL_TEXT_MODEL,DEDOMENA_SMALL_VISION_MODEL,DEDOMENA_SMALL_AUDIO_MODEL… -
The models folder:
DEDOMENA_MODELS_DIR, or~/.local/share/dedomena/modelsif it is not set.
dedomena-edge setup downloads into the same folder where the library looks, so you do not need to configure anything. With the full download, env.sh sets DEDOMENA_MODELS_DIR to ./models.
Integrity. Each model carries a manifest with the size and fingerprint of each file. The fingerprint is checked on download, and the size every time the model is loaded: a half-copied model is not used.
GPU: memory and sharing
If your package has the full text and image models, both share the GPU:
-
if they fit at the same time, both are loaded (
shared); -
if not, they are loaded in turns (
exclusive): each one gets the whole card and the other is unloaded when switching. Each switch costs a load, so order the work by modality (tables first, then images).
The library decides by measuring the card. You can force it with DEDOMENA_GPU_POLICY=shared|exclusive, and set each model's share of memory with DEDOMENA_LLM_VRAM and DEDOMENA_VISION_VRAM (for example, 0.45).
Reference manual
Commands
| Command | What it does |
|---|---|
bash install.sh \<TOKEN\> [--venv | --no-venv] | Installs from the full download (see Option A: full download (no internet on the target machine)). |
. ./env.sh | Loads the environment of an installation made with install.sh. |
dedomena-edge setup --size small|full|none [--token …] [options] | Downloads your installation's models and saves the licence (see Option B: install with pip). |
dedomena-edge status | Shows the version, the modules, the models folder, whether there is a GPU and the installed models. |
Environment variables
All of them are optional: without any, the library decides on its own.
| Variable | What for |
|---|---|
DEDOMENA_TOKEN | The licence, without saving it to disk. |
DEDOMENA_NO_PERSIST | With 1, the library writes nothing to the user's home directory. |
DEDOMENA_CONFIG_DIR | Folder of the licence file (default ~/.config/dedomena). |
DEDOMENA_MODELS_DIR | Models folder (default ~/.local/share/dedomena/models). |
DEDOMENA_LLM_MODEL, DEDOMENA_VISION_MODEL, DEDOMENA_AUDIO_MODEL | Path of a specific full model. |
DEDOMENA_SMALL_TEXT_MODEL, DEDOMENA_SMALL_VISION_MODEL, DEDOMENA_SMALL_AUDIO_MODEL | Path of a specific lightweight model. |
DEDOMENA_LLM_ENDPOINT, DEDOMENA_LLM_ENDPOINT_MODEL | Your language model server and the model's name on it. |
DEDOMENA_VISION_ENDPOINT, DEDOMENA_VISION_ENDPOINT_MODEL | Your image model server and the model's name on it. |
DEDOMENA_LLM_BACKEND, DEDOMENA_VISION_BACKEND | Force the way the model runs. Only when support tells you to (see Using a model you already have). |
DEDOMENA_GPU_POLICY | shared or exclusive (see GPU: memory and sharing). |
DEDOMENA_LLM_VRAM, DEDOMENA_VISION_VRAM | Share of GPU memory for each model. |
DEDOMENA_LOG_FILE | Path of the run log. |
Common errors
| Message | Cause and solution |
|---|---|
No Dedomena token found | No licence. Save it with dedomena.login("\<TOKEN\>", persist=True) or set DEDOMENA_TOKEN. |
Your Dedomena token expired on … | The licence has expired. Generate a new token on the Edge page and save it. |
Your Dedomena licence does not cover … | Your licence does not include that permission (see Permissions). |
… is not part of this build. It includes: … | Your package does not include that module. Install a package that does (see Updating, extending or changing the installation). |
'Auto' needs the tables module, which is not part of this build | Synthesis is in the Tables module (or Text). |
Image detection needs a vision model and none is installed | Run dedomena-edge setup --size small (or full). |
These models need an NVIDIA GPU … | The full text and image models need a GPU. Use --size small. |
Unknown anonymization method '…' | The method does not exist. Check Anonymization methods. |
None of the columns could be reversed | The mapping in mapping_dir does not belong to that file. |
IBM Db2 driver not found | Copy db2jcc4.jar into the package's DB2Driver folder (see Data sources). |
Dedomena error [id]: … Details were written to …/logHistory.log | Check the log. If you need help, send support the identifier, the log and dedomena-support.enc. |