Skip to main content

watfile

Classify files with a decision-making AI model and sort them into category folders.

watfile sends each document's text (title/abstract-grade extract) to a TypeSafe AI Jev (System One) Choice question, gets back a typed answer with a selected category, per-category probabilities and confidence, then moves the file into the matching folder. A Classifier abstraction keeps the backend pluggable — local MLX (laya) and other backends slot in later.

Install

Requires Python 3.12+ and uv.

From PyPI (once published)

# one-off run, no install
uvx watfile --help

# persistent CLI on your PATH
uv tool install watfile
watfile --help

Configuration

watfile resolves its TypeSafe API key (create one at https://console.typesafe.ai/) with this precedence — first match wins:

  1. TYPESAFE_API_KEY environment variable
  2. .env file in the current directory (gitignored; TYPESAFE_API_KEY=...)
  3. ~/.config/watfile/config.toml (api_key = "...", also base_url, model; $WATFILE_CONFIG or $XDG_CONFIG_HOME can relocate it)
export TYPESAFE_API_KEY=...        # option 1
echo 'TYPESAFE_API_KEY=...' > .env # option 2
cat > ~/.config/watfile/config.toml <<'EOF'   # option 3
api_key = "..."
EOF

Usage

Point watfile at files or folders, and either give a comma-separated category list (-c) or a target folder whose subfolders are the categories (-d):

# explicit categories, files moved into ./sorted/<category>/
uv run watfile ~/Downloads/invoice.pdf -c invoice,donation,apartment

# folder input, recursive; categories = existing subfolders of -d
mkdir -p ~/docs/{invoice,donation,apartment}
uv run watfile ~/Downloads -r -d ~/docs

# preview without touching anything
uv run watfile ~/Downloads -r -d ~/docs -n

# actually move the files (default is symlinking into the category folders)
uv run watfile ~/Downloads -r -d ~/docs -m

# copy instead
uv run watfile ~/Downloads -r -d ~/docs --copy

# custom output root with -c
uv run watfile *.pdf -c computerscience,biology -o ~/sorted

Output per file:

bill.pdf: invoice (conf 0.94) -> symlink to ~/docs/invoice/bill.pdf

Files that can't be classified (unsupported extension, no extractable text) are skipped with a warning; name collisions get a _1, _2… suffix.

Supported inputs

  • Text formats (read directly): .txt .md .markdown .rst .log .csv .json
  • PDF (via liteparse): only the first 2 pages are parsed, OCR disabled — enough for classification, ~1000x faster than a full parse. Scanned/image-only PDFs are skipped.

Backends

  • jev (default)TypeSafe AI Jev, cloud API. Needs TYPESAFE_API_KEY. Highest accuracy (4/4 on the arXiv fixtures). Batches automatically: documents are packed into one system_one call (~256 tokens each, up to ~100 files per call in the 30k-token window), so classifying a folder costs one API call, not one per file.
  • laya — local laya-mlx typed decision model on Apple Silicon (MLX, ~13ms/decision, fully offline after a one-time ~1GB checkpoint download). No API key needed. Same question shape as Jev. Because laya's context window is small (512–1024 tokens), the backend defaults to adaptive multi-chunk classification: the extract is split into ~200-token chunks; chunk 1 decides if its probability is decisive (≥0.5), otherwise further chunks are classified and probabilities aggregated until the decision is decisive (max 10). On the arXiv fixtures: 3/4 (Jev 4/4). Optional dependency — install with pip install watfile[laya] (or uv tool install 'watfile[laya]'); Apple Silicon only.
watfile ~/Downloads -r -d ~/docs --backend laya
# pick a checkpoint via config or env:
#   ~/.config/watfile/config.toml -> laya_model = "aac6fef/laya-multilingual-mlx"
#   or LAYA_MODEL=aac6fef/laya-mlx

Options (main)

watfile --help shows only these:

usage: watfile [-h] [-r] (-c CATEGORIES | -d DIRECTORY) [-o OUTPUT]
               [--backend {jev,laya}] [-n] [-m | --copy | --symlink]
               [--help-all]
               inputs [inputs ...]

main options:
  -h, --help            show this help message and exit
  --help-all            show advanced options too
  -r, --recursive       recurse into folder inputs
  -c CATEGORIES         comma-separated categories
  -d DIRECTORY          target folder whose existing subfolders are the categories
  -o OUTPUT             output root for sorted files (default: same as -d, or ./sorted with -c)
  --backend {jev,laya}  classifier backend (default: jev)
  -n, --dry-run         print decisions without placing files
  -m, --move            move files into the category folder (default: symlink)
  --copy                copy files instead of symlinking
  --symlink             create symlinks in category folders (default)

Advanced options

Shown by watfile --help-all:

  --batch N             cap files per API call (default: automatic — jev packs
                        everything that fits the 30k-token window, ~100 docs;
                        laya doesn't batch)
  --no-batch            disable batching, one API call per file (debugging)
  --chunk-tokens N      per-document token budget (default: backend-specific)
  --chunks N            split each document into N chunks, aggregate
                        probabilities; 0 = adaptive. Default: 0 for laya,
                        1 for jev

Batching example (jev batches by default; the flag just caps batch size):

# 100 files: ~2 API calls instead of 100 (256 tokens/doc, 30k window)
uv run watfile ~/Downloads -r -d ~/docs

# cap batch size, e.g. to keep batches small for debugging
uv run watfile ~/Downloads -r -d ~/docs --batch 25

Development

From source

git clone <repo> && cd watfile
uv sync            # create venv + install deps (typesafe-sdk, liteparse, laya-mlx)
uv run watfile --help

# or install the local checkout as a tool
uv tool install --from . watfile

Tests

uv sync
uv run pytest              # unit tests; live API tests skip without TYPESAFE_API_KEY

tests/fixture/ contains 4 real arXiv PDFs with ground-truth categories (derived from their arXiv subject tags) used by the integration tests.

Roadmap

  • laya local backend (MLX via OpenAI-compatible HTTP) done — native laya-mlx
  • batching: classify 25/50/100 files in a single API call done — jev batches automatically into the 30k-token window (default on, --no-batch to disable)

Release files for watfile 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for watfile 0.2.0
File Size Uploaded
watfile-0.2.0.tar.gz 15.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for watfile 0.2.0
File Interpreter ABI Platform
watfile-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 33.8 kB

Release files / watfile-0.2.0.tar.gz

Download URL watfile-0.2.0.tar.gz
Size 15.1 kB
Tags Source
SHA-256 checksum
How to use checksums
ff4533b69107fdb6c88fe1cce0e53d7b99fce64c6ea91f41b10313a4d31f7c07
BLAKE2b-256 checksum
How to use checksums
0af9e40d64243535d8b4a79e23602b62c3047296d527dc671e47c8c44b2526d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / watfile-0.2.0-py3-none-any.whl

Download URL watfile-0.2.0-py3-none-any.whl
Size 18.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2607a713e65aa2b5487659588ef4bc806c2cc7c99de0f764e66d0b2d1c012447
BLAKE2b-256 checksum
How to use checksums
50a8543d8a82ab78931842fec15796e5cd93683a8fb11d6d26055c1cc5e28b00
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

0.2.1

2 release files

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page