AI-powered file renamer β any format, any language, entirely local.
Feed it a folder of messy filenames. Tell it what you want. Done.
norma sends batches of filenames to a local LLM and renames them to whatever format you define β regardless of input language, naming convention, or domain.
harry_potter_jk_rowling.epub β J.K. Rowling - Harry Potter.epub
4.LISA_KLEYPAS_Scandal_in_primavara.pdf β Lisa Kleypas - Scandal in primavara.pdf
Haralamb_Zinca_Interpolul_transmite β Haralamb ZincΔ - Interpolul transmite arestaΕ£i.epub
1365135809.epub β Unknown - 1365135809.epub
INV-2024-ACME-CORP-000432.pdf β ACME Corp / Invoice / 2024-000432.pdf
smith2019_ml_paper_final_v2.pdf β Smith (2019) - ML Paper Final.pdf
The format is entirely up to you. norma adapts to any {Field} template you give it.
Privacy-first: every filename stays on your machine. No data is sent to any external service.
- Universal format β define any output template with
{Field}tokens:{Author} - {Title},{Client} / Invoice / {Date},{Artist} - {Album} ({Year}), anything - Any language β handles English, Romanian, Spanish, Japanese, mixed scripts; preserves diacritics
- Batched inference β sends 100 filenames per LLM call instead of one; ~10Γ faster than naive approaches
- Concurrent workers β 16 threads submit batches in parallel, saturating the GPU queue
- Auto-retry β failed files are automatically retried up to 3 times before being moved to
_errors/ - Dry-run mode β preview every rename in a table before touching a single file
- Two backends β works with Ollama or LM Studio
- Collision-safe β duplicate output names get
(2),(3)suffixes automatically - Large libraries β auto-splits folders >3000 files into named subfolders before processing
- TUI mode β real-time visualization of all transformations as they happen; catch mistakes, select bad ones, edit inline
Recommended model (Ollama):
ollama pull qwen2.5:3bLM Studio: load qwen2.5-3b-instruct in the Developer tab and start the local server.
# Isolated install (recommended)
pipx install .
# Development install
pip install -e .norma run ./my-ebooks --format "{Author} - {Title}" --dry-runnorma run ./my-ebooks --format "{Author} - {Title}"norma tui ./my-ebooks --format "{Author} - {Title}"The TUI gives you a real-time view of every transformation as it completes:
- Log feed (top) β newest entries first, like a live log
- File table (middle) β virtual-scrolled, loads gradually as you scroll down; handles 100k+ files smoothly
- Edit inline β press
eon any row to open a correction modal pre-filled with the LLM suggestion - Retry β press
ron a selected row orato retry all errors
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ! harry_potter_jk_rowling.epub -> J.K. Rowling - Harry Potter β
β ! 4.LISA_KLEYPAS_Scandal_in_primavara.pdf -> ... β
β [green]V 142[/green] [red]X 3[/red] 145 total {Author} - β
β [b]r[/b]etry [b]e[/b]dit [b]q[/b]uit β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Keys: ββ navigate | Enter/e edit | r retry selected | a retry all | g top | G bottom | q quit
norma status
norma status --backend lmstudio# Invoices
norma run ./invoices --format "{Client} / Invoice / {Date}"
# Research papers
norma run ./papers --format "{Author} ({Year}) - {Title}"
# Music albums
norma run ./albums --format "{Artist} - {Album} ({Year})"
# Legal documents
norma run ./contracts --format "{Company} - {Type} - {Date}"
# Rename to a different folder
norma run ./source --format "{Author} - {Title}" --output ./renamednorma infers what each {Field} means from its name. You do not need to configure field definitions.
# Check LM Studio connectivity
norma status --backend lmstudio
# Run with LM Studio
norma run ./books --format "{Author} - {Title}" --backend lmstudioLM Studio ignores the --model flag β it uses whichever model is currently loaded in the Developer tab.
norma run <folder> [options]
--format -f Output naming template default: "{Author} - {Title}"
--output -o Destination folder default: <input>/../norma-output
--model -m Model name default: qwen2.5:3b
--workers -w Concurrent worker threads default: 16
--batch-size -b Files per LLM call default: 100
--dry-run -n Preview renames, copy nothing default: false
--backend ollama or lmstudio default: ollama
--api-url Override API base URL
--max-retries -r Retry failed files N times default: 3
norma achieves high throughput by batching filenames (100 per call) and submitting batches concurrently. This amortises the system-prompt cost across many files and keeps the GPU pipeline saturated.
| Config | Throughput |
|---|---|
qwen2.5:3b, batch 100, 16 workers |
~870 files/min |
qwen2.5:3b, batch 15, 8 workers (old default) |
~590 files/min |
qwen2.5:7b, batch 100, 16 workers |
~300β450 files/min |
Tested against 1 000 diverse files (multilingual, messy, numeric, research-paper-style). Results vary by hardware.
The 10-iteration autoresearch benchmark that produced these numbers is in
benchmark/.
norma-output/
βββ J.K. Rowling - Harry Potter.epub
βββ Lisa Kleypas - Scandal in primavara.pdf
βββ Isaac Asimov - Foundation.pdf
βββ ...
βββ _errors/ β files norma could not rename after all retries
βββ processed.log β tab-separated record of every rename
βββ norma.log β detailed run log
Files that could not be renamed (e.g. pure numeric IDs with no context) land in _errors/. Re-run them with a different model or format:
norma retry ./norma-output/_errors --format "{Author} - {Title}" --model qwen2.5:7bCLI (cli.py)
ββ builds Config dataclass
ββ calls run_pipeline()
pipeline.py
ββ auto-splits folders > 3 000 files
ββ collects all files
ββ chunks into batches of 100
ββ submits to ThreadPoolExecutor (16 workers)
β
βΌ
processor.py (per batch)
ββ sends numbered list of stems to LLM
ββ parses numbered response
ββ falls back to per-file calls on count mismatch
ββ validates output matches format (contains separator)
ββ copies file to output folder, handles collisions
dedup.py
ββ removes source files already in output (by filename match)
All configuration flows through a single Config dataclass β no global state, no hardcoded paths.