The Problem: We all have folders full of randomly named files—downloads, screenshots, documents, videos—accumulating faster than we can organize them. Manual sorting is tedious, and simple rule-based tools can't understand context.
The Solution: SortAI uses Large Language Models to understand your files, not just match patterns. It reads filenames, extracts content from documents and media, and learns from your corrections to build a personalized organization system.
| Traditional Tools | SortAI |
|---|---|
Match file extensions (.pdf → Documents) |
Understands content ("invoice.pdf" → Documents/Financial) |
| Static rules | Learns from your corrections |
| Filename-only | Analyzes PDFs, transcribes audio/video, reads images |
| Flat categories | Dynamic hierarchies that emerge from your files |
SortAI is an intelligent macOS application that uses Large Language Models (LLMs) to automatically categorize and organize your files. It combines filename analysis, deep content extraction, and a learning knowledge graph to create a smart, adaptive file organization system.
- Filename-First Analysis: Uses LLMs to infer categories from filenames without reading content
- Deep Content Analysis: Extracts text from PDFs, transcribes audio/video, OCR for images
- GraphRAG Learning: Learns from your corrections to improve future categorization
- Confidence Scoring: Identifies files needing human review
- Emergent Categories: AI generates category hierarchy based on your files
- User Verification: Edit, rename, merge, or split categories before organizing
- Persistent Learning: Knowledge graph stores corrections (export/import UI planned)
- Wizard Flow: First-time user experience guides setup
- Tree View Editor: Visual hierarchy management
- QuickLook Integration: Component ready (UI integration planned)
- Conflict Resolution: Handle file conflicts elegantly
- FFmpeg Integration: Reliable audio extraction from video files
- Vision Framework: Image classification and object detection
- Speech Recognition: Transcribe audio content
- macOS 15.0+ (Tahoe)
- Xcode 17+
- Ollama (for LLM inference)
- Swift 6
-
Install Ollama (https://ollama.ai):
# Download from https://ollama.ai or: brew install ollama -
Pull a model (SortAI defaults to deepseek-r1:8b):
ollama pull deepseek-r1:8b # Or use any model you prefer - SortAI will auto-download if available -
Start Ollama:
ollama serve
# Clone the repository
git clone https://github.com/gilmanb1/SortAI.git
cd SortAI
# First time: Set up custom SQLite (required for GRDB snapshots)
./setup_sqlite.sh
# Build with the custom SQLite
./build.sh
# Run the application
.build/debug/SortAI
# Or create an app bundle
./build.sh --app
open .build/debug/SortAI.appNote: SortAI requires a custom SQLite build with
SQLITE_ENABLE_SNAPSHOT=1. The standard macOS SQLite doesn't have this feature. SeeBUILD_INSTRUCTIONS.mdandXCODE_BUILD_GUIDE.mdfor details.
Recommended: Use the build script
# Build and run
./build.sh && .build/debug/SortAI
# Or run tests
./test.shIf using Xcode:
# First time: Build custom SQLite
./build.sh
# Copy SQLite library for Xcode (after clean builds)
./copy_sqlite_for_xcode.sh
# Then run from Xcode: Cmd+RManual run:
# Development build
.build/debug/SortAI
# Release build
swift run -c releaseNote: SortAI requires custom SQLite with snapshot support. If you get "Library not loaded: libsqlite3.dylib", run
./copy_sqlite_for_xcode.sh. SeeXCODE_BUILD_GUIDE.mdfor details.
SortAI follows a modular, protocol-based architecture designed for extensibility and testability.
┌─────────────────────────────────────────────────────────────────┐
│ SortAI App │
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────────────┐ │
│ │ ContentView │ │ WizardView │ │ SettingsView │ │
│ └─────────────┘ └──────────────┘ └─────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│
┌────────────────────────────▼────────────────────────────────────┐
│ SortAIPipeline │
│ Orchestrates processing flow through injected components │
└────────────────────────────┬────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ MediaInspector │ │ Brain │ │ MemoryStore │
│ (Eye) │ │ (Categorizer) │ │ (Learning) │
│ - Vision │ │ - Ollama LLM │ │ - Patterns │
│ - Speech │ │ - Embeddings │ │ - Embeddings │
│ - OCR │ │ - Categories │ │ - History │
└────────────────┘ └────────────────┘ └────────────────┘
│ │ │
└───────────────────┼───────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ Unified Persistence Layer │
│ ┌─────────────────┐ ┌────────────────┐ ┌─────────────────┐ │
│ │ SortAIDatabase │ │ Repositories │ │ ConfigManager │ │
│ │ (GRDB) │ │ - Entity │ │ (JSON Config) │ │
│ │ │ │ - Relationship │ │ │ │
│ │ │ │ - Pattern │ │ │ │
│ │ │ │ - Record │ │ │ │
│ │ │ │ - Feedback │ │ │ │
│ └─────────────────┘ └────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| Component | Purpose | Location |
|---|---|---|
| SortAIPipeline | Main processing orchestrator | Core/Pipeline/ |
| MediaInspector | File content extraction (vision, audio, OCR) | Core/Eye/ |
| Brain | LLM-based categorization | Core/Brain/ |
| FastTaxonomyBuilder | Two-phase instant taxonomy creation | Core/Taxonomy/ |
| KeywordExtractor | Filename tokenization and keyword extraction | Core/Taxonomy/ |
| SimilarityClusterer | Jaccard + Levenshtein file clustering | Core/Taxonomy/ |
| MemoryStore | Learned patterns and embeddings | Core/Memory/ |
| KnowledgeGraphStore | GraphRAG entity/relationship storage | Core/Knowledge/ |
| FileOrganizer | File system operations | Core/Organizer/ |
| TaxonomyTree | Hierarchical category model | Core/Taxonomy/ |
| OllamaProvider | Ollama LLM integration | Core/LLM/ |
| FFmpegAudioExtractor | Audio/video extraction via FFmpeg CLI | Core/Audio/ |
| ConcurrencyThrottler | Rate limiting for LLM/IO | Core/Pipeline/ |
- Actor-based concurrency: All services are Swift actors for thread safety
- Protocol-based abstractions: Core interfaces defined in
SortAIProtocols.swift - Dependency injection: Components injected into Pipeline for testability
- Repository pattern: Database access via dedicated repository classes
- Singleton pattern:
SortAIDatabase.shared,ConfigurationManager.shared
SortAI uses a two-phase approach for ultra-fast initial file categorization:
Phase 1: Instant Rule-Based (<1 second)
- Keyword Extraction: Filenames are tokenized, split on delimiters and camelCase
- Stopword Filtering: Common words (the, and, file, download) are removed
- File Type Detection: Extension-based categorization (PDF, MP4, etc.)
- Similarity Clustering: Jaccard similarity + Levenshtein distance groups related files
- Cluster Naming: Auto-generated names from common keywords
Phase 2: Background LLM Refinement (async)
- LLM suggests better category names
- Proposes merges for small clusters
- User can proceed immediately while refinement continues
- User-edited categories are locked from LLM changes
Performance:
- 500 files: ~40ms
- 1000 files: ~80ms
- 5000 files: <1 second
This ensures users see results instantly while AI refinement improves quality in the background.
Configuration is managed via AppConfiguration and persisted as JSON.
Located at: ~/.sortai/config.json
{
"ollama": {
"host": "http://127.0.0.1:11434",
"defaultModel": "deepseek-r1:8b",
"embeddingModel": "nomic-embed-text",
"timeoutSeconds": 120,
"retryAttempts": 3
},
"memory": {
"embeddingDimension": 768,
"maxPatterns": 10000,
"similarityThreshold": 0.75
},
"processing": {
"maxConcurrentFiles": 5,
"useParallelProcessing": true,
"extractAudioFromVideo": true,
"confidenceThreshold": 0.75
},
"organization": {
"defaultMode": "copy",
"preserveFolderStructure": false,
"createBackup": true
}
}export SORTAI_OLLAMA_HOST="http://192.168.1.100:11434"
export SORTAI_CONFIG_FILE="~/.config/sortai/config.json"SortAI uses the FFmpeg command-line tools for robust audio/video processing. This enables:
- Audio extraction from any video format (MKV, AVI, WMV, FLV, WebM, etc.)
- Subtitle extraction from video files
- Media metadata inspection via ffprobe
- Speech-to-text transcription for video content
# Install via Homebrew (recommended)
brew install ffmpeg
# Verify installation
ffmpeg -version
ffprobe -versionSortAI auto-detects FFmpeg in these locations:
/opt/homebrew/bin/ffmpeg(Homebrew on Apple Silicon)/usr/local/bin/ffmpeg(Homebrew on Intel or manual install)/usr/bin/ffmpeg(System install)- Bundled
Contents/MacOS/ffmpeg(App bundle)
If FFmpeg is not found, SortAI falls back to AVFoundation (Apple's native framework), which has limited codec support.
| With FFmpeg | Without FFmpeg (AVFoundation only) |
|---|---|
| MKV, AVI, WMV, FLV, WebM | MP4, MOV, M4V |
| OGG, FLAC, WMA | MP3, M4A, WAV, AAC |
| All subtitle formats | None |
In the app, FFmpeg availability is logged at startup:
🎬 [FFmpeg] Found at: /opt/homebrew/bin/ffmpeg
Or if not found:
⚠️ [FFmpeg] Not found on system
# Use the test script - handles SQLite library setup automatically
./test.sh
# Or with Swift directly (requires SQLite in build dir)
swift test# Run specific test class
swift test --filter TaxonomyTests
# Run specific test method
swift test --filter testKeywordExtraction
# Skip tests that require Ollama
swift test --filter '!.*Embedding.*'xcodebuild test -scheme SortAI -destination 'platform=macOS'Tests/SortAITests/
├── TaxonomyTests.swift # Taxonomy node, tree, assignment tests
├── LLMProviderTests.swift # LLM abstraction layer tests
├── OrganizationTests.swift # File organization and throttling tests
├── DeepAnalyzerTests.swift # Deep content analysis tests
├── PersistenceTests.swift # Database and repository tests
├── ConfigurationTests.swift # Configuration system tests
├── ProtocolTests.swift # Protocol conformance and mock tests
└── SortAITests.swift # Integration and embedding tests
- Select Source Folder: Choose the folder containing files to organize
- Scanning: App recursively scans filenames (no content read yet)
- AI Inference: LLM analyzes filenames and suggests category hierarchy
- Verify Hierarchy: Review, edit, merge, split categories as needed
- Deep Analysis: Optionally analyze low-confidence files' content
- Resolve Conflicts: Handle any file naming conflicts
- Organize: Files are moved/copied to the organized structure
| Action | Shortcut |
|---|---|
| New Processing | ⌘N |
| Open Settings | ⌘, |
| Start Wizard | ⌘⇧W |
| Review Feedback | ⌘⇧R |
When you correct a categorization:
- The correction is stored in the knowledge graph
- Pattern embeddings are updated
- Future similar files will use the learned pattern
Note: Export/import is implemented at the API level but not yet exposed in the UI. A future update will add menu options to export and import your learned patterns.
// Programmatic export (no UI yet)
let exporter = GraphRAGExporter()
try await exporter.export(to: URL(fileURLWithPath: "~/patterns.sortai.json.gz"))| Package | Version | Purpose |
|---|---|---|
| GRDB.swift | 6.29.0+ | SQLite database |
Native Frameworks:
- SwiftUI - User interface
- Vision - Image analysis
- Speech - Audio transcription
- AVFoundation - Media handling
- CoreML - On-device ML
- NaturalLanguage - Text analysis
osx_cleanup_llm/
├── Package.swift
├── README.md
├── Sources/
│ └── SortAI/
│ ├── App/
│ │ ├── SortAIApp.swift # Entry point
│ │ ├── AppState.swift # Global state
│ │ ├── ContentView.swift # Main UI
│ │ ├── WizardView.swift # Setup wizard
│ │ ├── HierarchyEditorView.swift
│ │ ├── ConflictResolutionView.swift
│ │ ├── QuickLookPanel.swift
│ │ └── SettingsView.swift
│ └── Core/
│ ├── Brain/ # LLM categorization
│ ├── Configuration/ # Settings management
│ ├── Eye/ # Media inspection
│ ├── Knowledge/ # GraphRAG
│ ├── LLM/ # Provider abstraction
│ ├── Memory/ # Pattern learning
│ ├── Organizer/ # File operations
│ ├── Persistence/ # Database layer
│ ├── Pipeline/ # Processing flow
│ ├── Protocols/ # Interfaces
│ ├── Taxonomy/ # Category hierarchy
│ └── Audio/ # FFmpeg integration
└── Tests/
└── SortAITests/
# Check if Ollama is running
curl http://127.0.0.1:11434/api/tags
# Check available models
ollama list
# Restart Ollama
killall ollama && ollama serveIf you see "Smart audio extraction failed":
- Install FFmpeg:
brew install ffmpeg - Or enable FFmpeg-Kit in Package.swift (commented out by default)
Adjust concurrency settings:
{
"processing": {
"maxConcurrentFiles": 2
}
}Reset the database:
rm -rf ~/Library/Application\ Support/SortAI/sortai.db- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Run tests:
xcodebuild test -scheme SortAI -destination 'platform=macOS' - Commit changes:
git commit -m 'Add amazing feature' - Push:
git push origin feature/amazing-feature - Open a Pull Request
| Issue | Severity | Status | Workaround |
|---|---|---|---|
| TextField focus issues in Review modal | Medium | Investigating | Click outside the modal and try again |
| Change Category dialog sometimes auto-closes | Medium | Fixed in latest | Update to latest version |
| Zombie processes when running from terminal | Low | Known | Kill manually with pkill -9 SortAI |
| Audio extraction may fail for some MKV files | Low | Known | Install FFmpeg for better codec support |
- macOS 15+ only: Uses modern SwiftUI features not available on older versions
- Apple Silicon recommended: Some ML features may be slower on Intel Macs
- Ollama dependency: Requires local Ollama server for full LLM categorization
- Large video files: Processing videos >2GB may take significant time
- No cloud sync: Patterns and database are local only
- Wire up Watch Mode: Connect Settings toggle to
ContinuousWatchManager - Export/Import UI: Add File menu options to backup/restore learned patterns
- QuickLook integration: Add preview panel to feedback review workflow
- Apple Foundation Models support (macOS 26+): Zero-dependency LLM option
- Progressive degradation cascade: Apple LLM → Ollama → Local ML → Error
- Improved local-only mode: Better categorization without LLM using combined ML signals
- Batch operations: Select multiple files and apply bulk category changes
- Undo support: Revert file moves and category changes
- Cloud backup: Sync learned patterns across devices
- Watch folders: Backend implemented (
ContinuousWatchManager), needs UI wiring - Custom rules: User-defined regex → category mappings
- Duplicate detection: Identify and handle duplicate files
- Smart suggestions: Proactively suggest organization improvements
- Plugin system: Allow third-party categorization providers
- iOS companion app: Browse organized files from iPhone/iPad
- Network storage support: Organize files on NAS devices
- AI-powered deduplication: Semantic duplicate detection (similar content, different files)
- Time-based organization: Auto-archive old files
See LLM_Research.md for detailed analysis of:
- Apple Foundation Models integration plan
- Progressive degradation architecture
- Quality comparison between LLM providers
Version: 1.1.0
Stability: Beta (functional but actively developed)
Last Updated: January 2026
- ✅ Filename-based quick categorization
- ✅ Full content analysis with LLM
- ✅ PDF text extraction
- ✅ Image classification (Vision framework)
- ✅ Audio/video transcription
- ✅ Learning from user corrections
- ✅ Hierarchical category management
- 🔄 Progressive degradation (partially implemented)
- 🔄 Batch editing UI
- 🔄 Performance with >1000 files
- 🔄 Error recovery and retry logic
- ⚙️ Watch Mode:
ContinuousWatchManagerready, Settings toggle exists but does nothing - ⚙️ Export/Import Knowledge:
GraphRAGExporterhas methods, no menu/UI access - ⚙️ QuickLook Panel: Component built, not integrated into main workflow
This project is licensed under the MIT License - see the LICENSE file for details.
- Ollama - Local LLM inference
- GRDB.swift - SQLite database
- Apple's Vision, Speech, and NaturalLanguage frameworks
SortAI - Bringing intelligence to file organization