Tick a plugin on in the settings dialog and that feature turns on. Adding a new feature is the
same "pluginize" move every time: drop a FeaturePlugin subclass into plugins/, register it,
and it appears in the UI.
Every PR runs the suite on four Python versions across three operating systems, gets an
automated architecture review, and merges itself only when both pass — so main is always a
build you can install.
Why a plugin architecture
Most Anki power-features touch the same few seams — they rewrite a card's grade, inject JS
into the reviewer, call an AI provider, or read config. Omnia builds those seams once
in core/ and keeps each feature thin and isolated:
- Plugin system —
FeaturePlugin + @register + a PluginManager lifecycle.
- Reviewer ease pipeline — one wrap of
Reviewer._answerCard; features register
ordered ease transformers and cooperate instead of fighting over the monkeypatch.
- Web injector — one place to inject reviewer JS/CSS and route the
pycmd bridge.
- Provider layer —
LLMProvider / TTSProvider interfaces so AI features work against
an interface; adding a provider is one subclass.
Bundled feature plugins
| Plugin |
What it does |
auto_flip |
Auto-advances question → answer → grade after a configurable delay, waiting for the card's audio to finish. |
typed_accuracy |
Grades a typed card again/hard/good/easy from how accurately you typed it. |
display_interval |
Shows the predicted next interval on the answer side, and exposes it to card templates. |
overdue_guard |
Forces very overdue cards to Hard/Again regardless of what you press. |
word_lookup |
Search a word across your collection from the reviewer or a clipper, with word-form matching. |
note_maintenance |
Batch clean-up of notes you already have — deterministic, no AI. Configured per note type (fields picked from dropdowns, several note types in one run); preview the diff, then apply with full undo. |
smart_notes |
Fills note fields (text, image, TTS audio) — see below. |
Step-by-step guides for the larger features, including running your own models, are in
docs/guidance/.
Generating fields without paying for it
smart_notes does not have to mean "call an LLM". Each generated field runs an ordered chain
of tools, and the model is only reached when the deterministic ones decline:
| Tool |
Cost |
What it does |
cloze |
free |
Hides the note's word in its example sentence, in the one format you pick: a letter hint (s______, s_____e) or Anki's own {{c1::…}} markup, with or without the hint. Matches inflections both ways (run ⇄ ran, survive ⇄ survived). |
cloze_audio |
TTS only |
Speaks the sentence with the answer replaced by silence or a beep — a listening cloze. It never speaks the answer: if it cannot mask, it fails rather than falling back to plain speech. |
ai |
tokens |
The LLM path. |
user:<yours> |
free |
A tool you describe once in your own words; the generated Python is shown to you, tested, and saved as a file that then runs offline forever. |
A field configured cloze → ai costs nothing when the word really is in the sentence, and only
reaches the provider when it is not.
Install on your machine
Omnia runs inside Anki (25.09+ / 26.x, which bundles Python 3.13 + PyQt6). It is not
a server — there is nothing to run separately.
There are three versions you can install, and they differ only in where the code comes from.
Pick one — installing two at once means two copies of every feature fighting over the same
seams:
| Version |
Who it is for |
How you install it |
Updates |
A · AnkiWeb — code 726991726 |
everyone |
paste the code into Anki |
Anki updates it for you |
B · .ankiaddon file |
a specific build; offline or air-gapped installs |
Install from file… |
manual, by installing the next file |
| C · a clone of this repo |
developers, and anyone who wants to edit the code |
python scripts/sync_to_anki.py |
git pull + re-run that one command |
All three end in the same place: Tools → Omnia. Which plugins are on and how they are
configured is done entirely in that GUI and stored in your collection — see Where your
settings & data live below. There is no config file to edit unless you use the AI
features, which need providers.toml (next section).
Whichever route you take, the download is deliberately small (~1 MB): the offline piper
TTS voice models (~60 MB each) are not bundled, so you don't re-download them on every
add-on update. The first time you actually synthesize with a piper voice, Omnia fetches it
into user_files/ once per machine and reuses it forever after. When you triggered the
generation yourself (the editor button, a Studio preview, a voice test) the fetch reports its
progress in Anki's progress dialog and its Cancel button stops it; review-time
pre-generation runs silently in the background by design, so there it is invisible. Every
other TTS provider needs no download at all. Note that piper also needs its opt-in native
runtime (Smart Notes → Options → Advanced) — Omnia checks that first, so it never
downloads a voice it could not have used.
Installing on another machine? Just repeat the same route there. Your plugin settings sync
automatically with the rest of your collection through AnkiWeb (they live in the collection,
not in a local file). The only per-machine step is re-creating providers.toml with your API
keys, because secrets are deliberately not synced (see below).
A · From AnkiWeb (the normal install)
- In Anki: Tools → Add-ons → Get Add-ons…
- Paste the code
726991726 and confirm.
- Restart Anki.
Anki puts it in addons21/726991726 and checks for updates on its own, so this is the version
to use unless you have a reason not to.
B · From an .ankiaddon file
- Get the file — build it from a checkout with
python scripts/build_addon.py, which writes
dist/omnia.ankiaddon.
- In Anki: Tools → Add-ons → Install from file… and pick it.
- Restart Anki.
This installs into addons21/omnia (the name in manifest.json), not the numeric folder, so
it never collides with an AnkiWeb copy — which also means Anki will not update it. Use it for
an offline machine, or to try a build before it is published.
C · From a clone of this repo (developers)
git clone --recurse-submodules https://github.com/osirisQdt2810/omnia.git
cd omnia
python scripts/sync_to_anki.py # the whole install, one command
Then restart Anki. That is it — sync_to_anki.py checks your interpreter and the vendored
deps, reports whether the piper voice weights came down as real files or Git LFS pointers
(either is fine), and assembles src/omnia + vendor/ + models/ + config/ into
addons21/omnia. Re-run it any time; it never touches user_files/ (your config, secrets, and
downloaded voices).
The assembled folder is made of links, so editing core/, gui/ or plugins/ in the repo
changes what Anki loads on its next start — no reinstall. Only the three top-level files
(__init__.py, envs.py, manifest.json) need a re-run after you edit them, and only on
Windows (see below).
python scripts/sync_to_anki.py --dev # also create .venv + install pytest/ruff/black/mypy
python scripts/sync_to_anki.py --copy # a snapshot instead of links (re-run after edits)
python scripts/sync_to_anki.py --submodules # also fetch the companion clippers
Windows: no Developer Mode, no admin shell, nothing to enable. Creating a symlink on
Windows needs one of those, so where the OS refuses, each directory is placed as a junction
instead (just as live as a symlink) and the three top-level files are copied. The run prints
exactly which items are links and which are snapshots.
Installing into a second, throwaway Anki (a scratch collection you don't mind writing to)?
Set ANKI_BASE to that base folder for both Anki and the script, and they will agree on where
the add-on goes: ANKI_BASE=/tmp/anki-test python scripts/sync_to_anki.py.
Where your settings & data live (no files in this repo — by design)
You will not find storage.json, usage.json, voices.json, or a live config in this
repository. That is intentional: Omnia keeps its runtime state inside Anki's own database,
created on your machine on first run (ADR-006). Nothing runtime is committed to the repo.
| What |
Where it is stored |
Synced by AnkiWeb? |
| Plugin settings + enable toggles |
Anki collection config (col.set_config) |
✅ Yes — follows your collection to every device |
| AI usage accounting (LLM/TTS call counts) |
Anki collection config (col.set_config) |
✅ Yes — usage aggregates across your devices |
| Fetched-voice cache (TTS voice lists) |
Anki collection config |
✅ Yes |
| A tiny backend marker |
user_files/.storage.json |
❌ No — device-local |
| AI provider config + API keys |
user_files/config/providers.toml + user_files/config/.secrets/ |
❌ No — stays a local file, never synced |
| Downloaded piper voice models (~60 MB each) |
user_files/models/piper/<voice>.onnx (+ .onnx.json) |
❌ No — device-local, fetched on first use |
The piper voice is the largest thing Omnia ever writes to your disk, and it is safe to
delete: the next synthesis with that voice simply fetches it again.
user_files/ is the one directory Anki preserves across add-on updates, so your local
config and secrets survive upgrades (the rest of the add-on folder is replaced on update).
Swappable backends. Each storage concern above is dispatched by an environment knob so a
backend can be swapped without losing data — OMNIA_CONFIG_STORAGE, OMNIA_USAGE_STORAGE,
OMNIA_VOICE_CACHE_STORAGE (each defaults to database). When you change a knob, Omnia reads
the user_files/.storage.json marker, notices the change, and syncs the concern's data from
the old backend into the new one before using it — so switching is safe. Most users never
touch these; the default (everything in the Anki DB) is what the tables above describe.
Set up AI providers (only for smart_notes / TTS)
The AI features need provider credentials. You do not create or copy any file — the add-on
auto-creates user_files/config/providers.toml (from the shipped template) on first run.
Configure everything in the GUI:
Tools → Omnia, open the Smart Notes plugin's Configure, go to the Usage & Keys
tab, pick your LLM/TTS provider + model and paste your API key. That's it — the dialog writes
providers.toml and stores the key under user_files/config/.secrets/ for you.
Models on a server of your own (a GPU box, or your Mac) connect the same way, with the
server's address and a token: see Self-hosted models.
Keys are the one thing kept in a local file rather than the synced collection, so they never
sync to AnkiWeb or land in the DB. Advanced: you can edit user_files/config/providers.toml
directly instead — config/providers.example.toml documents every option and
config/secrets.README.md explains the .secrets/ references. Both providers.toml and
.secrets/ are gitignored and live-only. (Do not copy the template over an existing
providers.toml — that would overwrite your keys.)
Companion clippers (capture into Anki from anywhere)
Two optional companion tools push words + context into your running Anki (via AnkiConnect),
where smart_notes auto-generates the rest of the card. They are tracked here as git
submodules under 3rdparty/ — each has its own repo and its own README with
per-machine install steps:
Enable each in Tools → Omnia → the Smart Notes plugin's Configure → Integrations
tab. If you cloned without --recurse-submodules, run git submodule update --init --recursive.
Develop
python scripts/sync_to_anki.py --dev # .venv + tooling + hooks + install into Anki
source .venv/bin/activate # Windows: .venv\Scripts\activate
pytest tests/ -vv # logic tests (Anki is stubbed)
python scripts/sync_to_anki.py # re-assemble after changing the layout
python scripts/build_addon.py # -> dist/omnia.ankiaddon (no *.onnx weights)
sync_to_anki.py is the front door — it calls install_addon.py, which does the assembling
and can still be run directly if that is all you want.
The add-on runs inside Anki's bundled Python (3.13, latest Anki) with PyQt6 — no server, no external
services. Third-party runtime deps (if any) are vendored, pure-Python, cross-platform.
See .claude/CLAUDE.md for the full architecture and .claude/CONVENTIONS.md for coding
standards. references/ holds read-only snapshots of the add-ons Omnia draws from.