Skip to content

About

NAS AI File Sorter — organize NAS files with local LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

35 Commits

Folders and files

Repository files navigation

NAS AI File Sorter

Organize your NAS files using a local LLM (Ollama). Scans, classifies, reviews, and moves files with full undo capability.

Quick Start

# Prerequisites
ollama pull qwen2.5-coder:7b     # or use any model you prefer

DS=~/.venvs/data-sorter/bin/python   # venv lives on LOCAL disk (see Installation)

# Run the sorter (dry-run first!)
$DS sort.py --dry-run          # Preview changes without moving anything

# Full run (interactive review → execute)
$DS sort.py

# Undo the last sort
$DS sort.py --undo

How It Works

┌─────────┐    ┌──────────┐    ┌───────────┐    ┌────────┐    ┌─────────┐
│ Scanner │ →  │Extractor │ →  │Classifier │ →  │Reviewer│ →  │Executor │
│ (files) │    │(content) │    │  (Ollama) │    │  (TUI) │    │ (moves) │
└─────────┘    └──────────┘    └───────────┘    └────────┘    └─────────┘
                                                      ↑
                                               ┌──────────┐
                                               │  Deduper  │
                                               │(duplicates│
                                               └──────────┘
  1. Scanner — discovers all files on the NAS, respects .sortignore
  2. Extractor — extracts text (PDFs, Office docs) and metadata (EXIF, video/audio info)
  3. Classifier — sends file info to Ollama in batches → LLM suggests target category + confidence
  4. Deduper — detects duplicate files (SHA-256 exact + perceptual hash for images)
  5. Reviewer — Rich TUI shows colored preview tree; you accept/modify/reject per file
  6. Executor — moves files, logs everything to CSV for undo

Mental Model

The NAS is a physical place you walk through, not a database you query. Navigation is walking; search is the fallback. Six principles govern every destination:

  1. MM-1 — Filesystem is a place, not a database. Every destination is reachable by walking (folder browsing, not querying). Search stays a fallback.
  2. MM-2 — At most 3 levels. Destinations sit ≤ 3 folder levels below the NAS root (top/category/leaf); wide over deep. Projects/ and Zeno/Documents/Design/ are deep containers that keep their own internal trees.
  3. MM-3 — One clean subject per leaf. Context never adds a nesting level: a leaf is named for its owner, event, or subject — never A_B stitched names, never repeated generic words (Bilder, pics, screenshots, …).
  4. MM-4 — Structure matches content. Projects = tree, Media/doc areas = cluster, Archives = preserve. Dates are metadata, never folder chains.
  5. MM-5 — Consistency over cleverness. The LLM classifies, deterministic code normalizes.
  6. MM-6 — One deliberate pile. _Unsorted_Review/ holds everything uncertain — folders move there as units, never scattered file-by-file.

Enforcement — the policy is deterministic, not a suggestion:

Rule Where it lives
depth limit, deep containers, per-branch structures config.yaml → taxonomy.placement_max_depth: 3, deep_containers: [Projects, Zeno/Documents/Design], structures (Zeno/Family/Company/Media = cluster, Projects = tree, Archives = preserve)
depth fit, subject-leaf fold, container bypass sorter/placement.py → fit_to_depth(), is_under_container() (overflow → fold cat + leaf, note [depth-fit], review if unfittable)
LLM-facing rules generated {placement_rules} block (placement_rules_text()), injected into the batch prompt (sorter/classifier.py) and folder prompt (sort.py)

Behavioral notes: raw personal clips → Zeno/Videos, collected/finished media → Media/Videos, video-production work → Projects/Video_Projects; a proposal adds at most ONE new subfolder with category_path ≤ 3 segments total; generic-medium leaves are promoted to their owner/event/subject (a carve-out, never a translation of names).

Examples (before → after):

Before After Why
Diverse Daten/EVE/-Y-/old_G_files/bilder&screens Media/Photos/EVE generic-medium leaf promoted to owner EVE (junk segments don't disqualify)
Diverse Daten/Ausbildung/Schule/Projekt Müller Zeno/Documents/Projekt Müller folded [depth-fit] — ancestor chain dropped, one clean subject leaf kept
Zeno/Documents/Design/EVE/BO Logo unchanged deep container keeps its internal tree
Projects/Hypnose/Audio/Hypnose unchanged deep container — projects are trees, deep is fine
DELETE / uncertain-review actions untouched by fit no path rewrite on those actions

Rationale: Benn et al. 2015 (Scientific Reports) — browsing digital folders engages the same brain structures as spatial navigation (vs. search = linguistic resources); Kemp & Tenenbaum 2008 (PNAS) — no single best structure; it must match the data (trees for projects, clusters for topics). Machine-readable policy: .docs/mental-model.yaml.

Safety Features

  • Mandatory dry run — preview every change before anything moves
  • Confidence thresholds — 🟢 green (≥85%) auto-accepted, 🟡 yellow (50-85%) user reviews, 🔴 red (<50%) queued for manual review
  • CSV undo log — every move logged with SHA-256 hash; --undo reverses it
  • Staging trash — deleted duplicates go to Archives/_Duplicates_Delete/ never hard-deleted
  • Adobe Premiere temp files — flagged for cleanup (Auto-Save, Preview Files are regeneratable)

Installation

# Clone / enter the project directory
cd /mnt/NAS-Zeno/OpenCode/Data\ Sorter

# Use a venv on LOCAL disk — never inside this repo.
# This dir is a CIFS mount that rejects symlinks, so `python3 -m venv .venv`
# fails with Errno 95 on the lib64 -> lib link (even with --copies).
python3 -m venv ~/.venvs/data-sorter
~/.venvs/data-sorter/bin/pip install -r requirements.txt

# Verify (-p no:cacheprovider: this dir is a CIFS mount that rejects pytest's cache writes)
~/.venvs/data-sorter/bin/python -m pytest tests/ -q -p no:cacheprovider
~/.venvs/data-sorter/bin/python sort.py --help

Dependencies

  • Python 3.11+
  • Ollama running locally at http://localhost:11434
  • Model: qwen2.5-coder:7b (or any model you configure)
  • NAS mounted at /mnt/NAS-Zeno/ (configurable)
  • Optional: ffprobe (for video/audio metadata), markitdown auto-installed

CLI Usage

Command Description
$DS sort.py Dry run → interactive review → execute
$DS sort.py --dry-run Preview only, no changes
$DS sort.py --undo Revert the last sort run
$DS sort.py --execute Skip interactive review; derived/review moves are never auto-applied
$DS sort.py --path Subfolder Scan only a sub-path
$DS sort.py --config my.yaml Use alternate config

Configuration

Edit config.yaml to customize:

ollama:
  model: "qwen2.5-coder:7b"       # LLM model for classification
  batch_size: 8                    # files per request (KV cache reuse, lower latency)
  temperature: 0                   # deterministic classification
  seed: 42                         # reproducible LLM output
  timeout: 60                      # seconds

confidence:
  auto_accept: 85                  # 🟢 green threshold
  require_review: 50               # 🟡 yellow threshold

taxonomy:
  top_level:
    - Zeno
    - Family
    - Company
    - Projects
    - Media
    - Archives
    - _Unsorted_Review
  placement_max_depth: 3          # MM-2: destinations sit <= 3 levels below NAS root
  deep_containers:                # MM-2 exception: these keep their internal trees
    - Projects
    - Zeno/Documents/Design
  structures:                     # MM-4: branch structure by top level
    Zeno: cluster
    Family: cluster
    Company: cluster
    Projects: tree
    Media: cluster
    Archives: preserve
  categories:
    Zeno:
      - Documents, Downloads, Bilder, Videos, Finances, Career, ...

embeddings:
  enabled: false              # opt-in semantic pre-classification (nomic-embed-text)
  model: "nomic-embed-text"
  similarity_threshold: 0.85  # centroid cosine threshold; LLM only runs on unmatched files

Taxonomy (default)

Your NAS will be organized into:

/mnt/NAS-Zeno/
├── Zeno/              ← Your personal files
│   ├── Documents/     ← PDFs, Office docs, etc.
│   ├── Downloads/
│   ├── Bilder/
│   ├── Videos/
│   ├── Finances/
│   ├── Career/
│   └── Obsidian_Vault/
├── Family/
│   ├── Mom/
│   ├── Andreas/
│   └── Emily/
├── Company/
│   └── 5 Circles/     ← Your company
├── Projects/
│   ├── ERGO Paphos/
│   ├── FC Squad/
│   └── ... (all existing projects)
├── Media/
│   ├── DJ_Stuff/
│   ├── Music/
│   └── Photos/
├── Archives/
│   ├── Old_Projects/
│   ├── System/
│   └── _Duplicates_Delete/
└── _Unsorted_Review/  ← Low-confidence items

LLM can propose new subfolders under any top-level category if no existing one fits.

Testing

~/.venvs/data-sorter/bin/python -m pytest tests/ -v -p no:cacheprovider

591 tests, all passing.

Architecture

Module File Responsibility
Entry sort.py CLI, pipeline orchestration
Scanner sorter/scanner.py File discovery, MIME detection, ignore patterns
Extractor sorter/extractor.py Content/metadata extraction per file type
Classifier sorter/classifier.py Ollama REST client, batch inference (deterministic temp=0/seed=42)
Routing sorter/routing.py Deterministic extension→category pre-classifier with path-context guard + delete-protection for 3D models, design sources, media, documents
Taxonomy sorter/taxonomy.py Category tree, Adobe Premiere detection
Deduper sorter/deduper.py SHA-256 + perceptual hash + copy-suffix duplicate scanner
Reviewer sorter/reviewer.py Rich TUI interactive review
Executor sorter/executor.py File moves, CSV logging
Undo sorter/undo.py CSV reader, reverse file moves
Embedding sorter/embeddings.py nomic-embed-text embeddings, centroid matching, KMeans clustering (opt-in)

Design Doc

See .docs/2026-09-17-nas-file-sorter-design.yaml for full architecture and rationale.

Git and NAS

origin is GitHub (https://github.com/Ridoc/data-sorter.git). The NAS copy is the nas remote: ssh://bienenhaus/volume1/git/data-sorter.git on Bienenhaus (192.168.1.101), served by a key-only git user. The post-commit hook pushes nas and mirrors tracked files to /mnt/bienenhaus-workspace/backups/OpenCode/data-sorter/; full-history bundles live at /mnt/NAS-Zeno/backups/git/data-sorter.bundle. Restore with git clone ssh://bienenhaus/volume1/git/data-sorter.git or the bundle.

About

NAS AI File Sorter — organize NAS files with local LLM

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages