Copyright (C) 2025, Shyamal Suhana Chandra
Invented by Shyamal Chandra
Contact ssc56@duck.com to license code for commercial and non-commercial purposes.
Yankovinator is a Swift package that converts songs into parodies using Apple's NaturalLanguage framework and a local Ollama LLM (llama3.2:3b by default). It preserves syllable structure, rhyme scheme, capitalization, and punctuation while steering lyrics with theme keywords.
The yankovinator CLI (v1.06.0+) runs in batch mode only: put songs in --input-dir, one theme file per entry in --themes-dir, and read parodies from --output-dir as <theme>/<song>.parody.txt. Use benchmark for single-file timing, or the Swift library for one-off generation in code.
Website: https://shyamalschandra.github.io/Yankovinator/
- Syllable-accurate parody generation (word-by-word matching)
- Automatic rhyme detection and enforcement
- Semantic coherence across lines (context-aware generation)
- Theme advancement (develop themes, not only mention keywords)
- Capitalization and punctuation matching
- Theme-based keyword integration
- Automatic keyword generation from subjects via Ollama
- Oxford English Dictionary (1913 / Webster) word suggestions for richer substitutions
- Unsupervised NLP helpers: embedding lexical substitution, rhyme clustering, next-line coherence critic
- NaturalLanguage framework integration
- Local or cloud Ollama integration (
llama3.2:3bby default; any--ollama-url) - Word-by-word part-of-speech matching and OED-filtered substitutions (v1.04.9+)
- ParodyFitScorer global ranking; optional
--fit-optimizebatch hill-climbing - Parallel workers for batch jobs (
--workersup to 10 /--jobs) with a producer–consumer queue (license max 10 concurrent consumers; optional--consumerscap;:cloudsoft default 4) - Batch TUI progress on stderr: Rust
yankovinator-tui(ratatui/crossterm) with color boxes + emoji progress bars per worker thread; Swift ANSI fallback otherwise (--no-progressfor plain logs;YANKOVINATOR_RUST_TUI=0to force fallback) - Batch-only
yankovinatorCLI: required--input-dir,--themes-dir,--output-dir(no single-file lyrics arg or--keywords) - Combinatorial batch: every song × every theme via
--input-dir+--themes-dir - Multi-candidate ranking:
--candidatesup to 64 generates and scores variants per song×theme - CLI tools:
yankovinator,keyword-generator,benchmark - XCTest suite (unit + Ollama integration tests)
- LaTeX/Beamer docs and GitHub Pages site
- Swift 5.10 or later
- macOS 13.0+ (iOS 16.0+ for library use)
- Ollama installed and running
llama3.2:3bdownloaded in Ollama (ollama pull llama3.2:3b)- For
swift test: full Xcode app (Command Line Tools alone do not provide XCTest) - Homebrew (optional, for Homebrew install)
Download from GitHub Releases (current: v1.06.14). No Swift toolchain required.
curl -L -o yankovinator-universal.tar.gz \
https://github.com/shyamalschandra/Yankovinator/releases/download/v1.06.14/yankovinator-universal.tar.gz
tar -xzf yankovinator-universal.tar.gz
sudo mv yankovinator yankovinator-tui keyword-generator /usr/local/bin/
# Optional if present in the archive:
# sudo mv benchmark /usr/local/bin/
yankovinator --help
keyword-generator --helpSee docs/RELEASES.md for architecture-specific downloads and troubleshooting.
brew tap shyamalschandra/yankovinator
brew install yankovinator
yankovinator --version # → 1.06.14+
yankovinator --help
keyword-generator --helpInstalls pre-built binaries from GitHub Releases (yankovinator, yankovinator-tui, keyword-generator, benchmark). Tap: homebrew-yankovinator.
brew update && brew upgrade yankovinatorgit clone https://github.com/shyamalschandra/Yankovinator.git
cd Yankovinator
swift buildThen install and start Ollama (see Ollama Installation).
Yankovinator ships three CLI tools:
| Tool | Purpose |
|---|---|
yankovinator |
Batch parodies: all songs in --input-dir × all themes in --themes-dir |
keyword-generator |
Generate keyword: definition pairs from subjects |
benchmark |
Measure generation performance (single lyrics + keywords file) |
swift run keyword-generator <subject1> [subject2] ... [options]Options:
--count, -c <number>: Number of keyword pairs (default: 10)--ollama-url, -u <url>: Ollama API base URL (local or cloud; default:http://localhost:11434)--model, -m <name>: Ollama model (default:llama3.2:3b)--output, -o <file>: Output path (default: stdout)--workers, --jobs <n>: Max parallel subject jobs (1–10; license max)--verbose, -v: Verbose output
swift run keyword-generator "artificial intelligence" --output ai_keywords.txt
swift run keyword-generator "space exploration" "NASA" --count 15 --output space_keywords.txt
swift run keyword-generator "ai" "space" "music" --workers 10 \
--ollama-url https://ollama.example.com --output keywords.txtEvery run is songs × themes. Each file in --themes-dir is a theme (keyword: definition lines). Outputs land under --output-dir/<theme-stem>/<song-stem>.parody.txt.
Swift (development):
swift run yankovinator --input-dir <songs-dir> --themes-dir <themes-dir> --output-dir <out-dir> [options]Wrapper script (trims stray whitespace from arguments):
./yankovinator.sh --input-dir <songs-dir> --themes-dir <themes-dir> --output-dir <out-dir> [options]Required:
--input-dir <dir>: Directory of.txtlyrics files--themes-dir <dir>: Directory of theme keyword.txtfiles (keyword: definitionper line)--output-dir <dir>: Output root (<theme>/<song>.parody.txt)
Optional:
--ollama-url, -u <url>: Ollama API base URL (local or cloud)--model, -m <name>: Ollama model (default:llama3.2:3b)--workers, --jobs <n>: Parallel worker count (1–10; license max). Values above 10 are clamped with a stderr warning. Consumer pool = min(workers, 10,OLLAMA_NUM_PARALLELon localhost) unless--consumersis set.--consumers <n>: Cap in-flight consumer tasks (1–10; license max; default follows--workers). Values above 10 are clamped.--ollama-num-parallel <n>(alias--ollama-num-workers): Ollama serverOLLAMA_NUM_PARALLEL(or set env beforeollama serve; see Ollama FAQ). Consumer effect is also capped at 10 (license).--candidates <n>: Generate N ranked variants per song×theme (1–64)--fit-optimize: Extra Ollama passes to hill-climb syllable/POS/coherence fit in batch (slower, higher scores)--keep-candidates: Also write ranked variants under<song>.candidates/--force: Allow songs×themes×candidates totals larger than 100 generations--fresh-batch: Ignore/delete checkpoint in--output-dir/.yankovinatorand regenerate everything--ollama-timeout <sec>: Per-request Ollama HTTP timeout (30–900; heavy:cloudmodels default to 600s)--no-progress: Disable stderr progress bar for batch / multi-candidate runs--midi-progress: Lightweight MIDI cues per worker bar (macOS, interactive terminal only)--no-cloud-prescription: Disable auto tuning for:cloudmodels (worker cap ≤4, timeout, fast batch coherence)--analyze, -a: Show syllable analysis--verbose, -v: Verbose output
Quick start (local Ollama):
mkdir -p songs themes out
cp data/example_lyrics.txt songs/song.txt
cp data/example_keywords.txt themes/space.txt
swift run yankovinator --input-dir ./songs --themes-dir ./themes --output-dir ./out --verbose
# → out/space/song.parody.txtRank multiple candidates per song×theme:
swift run yankovinator --input-dir ./songs --themes-dir ./themes --output-dir ./out \
--candidates 10 --keep-candidates --verbose
# Best: out/<theme>/<song>.parody.txt
# All ranked: out/<theme>/<song>.candidates/
# If songs×themes×candidates > 100, add --forceStop and resume: Each finished song×theme×candidate is paged to disk under out/.yankovinator/ as it completes (append-only JSONL log + candidate files, fsynced). Re-run the same command after Ctrl+C to skip completed work — resume loads the completion/score index from JSONL and lazy-loads candidate text only when ranking or exporting winners (bounded LRU; not an all-or-nothing in-memory cache). Use --fresh-batch to wipe the checkpoint and start over. If you change songs, themes, --candidates, or --model, the fingerprint will not match—use --fresh-batch or a new --output-dir.
Batch TUI (fix garbled Unicode / segfaults): Release tarballs include yankovinator-tui (Rust + ratatui): ncurses-like color boxes and emoji progress bars for each threaded worker, plus an overall batch gauge and status feed. Install it next to yankovinator (same directory). The CLI auto-spawns it for multi-worker runs. From source: cd tui && cargo build --release (binary at tui/target/release/yankovinator-tui). Override path with YANKOVINATOR_TUI_PATH; disable with YANKOVINATOR_RUST_TUI=0. Use --no-progress for plain logs only.
Cloud Ollama (rate-limit safe):
Cloud models (*:cloud, including via local localhost:11434 proxy) get automatic retries on 429 / 502 / 503 (backoff + Retry-After), extra retries/backoff for DNS/connectivity to ollama.com, a default consumer cap of 4, and a cloud preflight probe before heavy batches. Prefer:
yankovinator --input-dir ./songs --themes-dir ./themes --output-dir ./out \
--model gemma4:31b-cloud --workers 4 --candidates 20 --force --verboseTip: If you see lookup ollama.com: no such host / i/o timeout, check VPN/DNS/firewall (the host running Ollama must resolve ollama.com), retry later, or switch to a local model (--model llama3.2:3b). Single candidate failures no longer abort the whole batch when checkpoints exist — re-run the same command to resume.
Raising --workers / --ollama-num-workers above ~4 against cloud often triggers 429 or ephemeral-port exhaustion (can't assign requested address). Use --no-cloud-prescription only if you accept that risk (still hard-capped at 10 by license terms). If a failed run left a bad checkpoint, add --fresh-batch.
Ollama server parallelism (local ollama serve):
Match Ollama’s OLLAMA_NUM_PARALLEL to your --workers / consumer pool (e.g. 10; license max):
export OLLAMA_NUM_PARALLEL=10 # set before starting the server; restart required
ollama serveThe CLI reads $OLLAMA_NUM_PARALLEL on localhost or you can pass --ollama-num-parallel 10. Local (non-:cloud) models are not capped by cloud prescription, but concurrent consumers never exceed 10 (license).
Heavy cloud batch (qwen3.5:397b-cloud, etc.): stderr prescription caps parallel HTTP workers at 4, may set 600s timeout if unset, and uses batch fast path with checkpoints. Use --no-cloud-prescription for a higher --workers count (up to the license max of 10) at your own rate-limit risk.
Higher cloud concurrency (opt-in, ≤10):
yankovinator --input-dir ./songs --themes-dir ./themes --output-dir ./out \
--workers 8 --no-cloud-prescription \
--candidates 20 --force --verbose --model gemma4:31b-cloudswift run benchmark \
--lyrics data/example_lyrics.txt \
--keywords data/example_keywords.txt \
--iterations 5swift run benchmark \
--lyrics data/example_lyrics.txt \
--keywords data/example_keywords.txt \
--iterations 10 \
--workers 10 \
--ollama-url https://ollama.example.comimport Yankovinator
let lyrics = [
"Twinkle twinkle little star",
"How I wonder what you are"
]
let keywords = [
"space": "the physical universe beyond Earth",
"stars": "luminous celestial bodies"
]
let parody = try await Yankovinator.generateParody(
originalLyrics: lyrics,
keywords: keywords,
ollamaURL: "http://localhost:11434",
ollamaModel: "llama3.2:3b"
)
for line in parody {
print(line)
}songs/ # --input-dir: one .txt file per song (stem = job id)
twinkle.txt
verse2.txt
themes/ # --themes-dir: one .txt file per theme
space.txt
science.txt
out/ # --output-dir
.yankovinator/ # resume checkpoint (manifest.json + completed.jsonl + candidates/)
space/
twinkle.parody.txt
science/
twinkle.parody.txt
One line per verse (empty lines are preserved):
Twinkle twinkle little star
How I wonder what you are
Up above the world so high
Like a diamond in the sky
Sample: data/example_lyrics.txt
Format: keyword: definition
science: the study of natural phenomena
space: the physical universe beyond Earth
exploration: the action of traveling to discover
Sample: data/example_keywords.txt
XCTest needs the Xcode developer directory:
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
swift testThe suite includes:
- Unit tests for syllable counting
- Keyword extraction tests
- Integration tests for Ollama connection and parody generation
Integration tests require Ollama with llama3.2:3b. If Ollama is unavailable, those tests skip gracefully.
Suggested local smoke checks:
swift build
swift run yankovinator --help
swift run keyword-generator --help
swift run benchmark --help
npm run build # GitHub Pages TypeScriptFull release gate (unit, regression, UX, A/B, blackbox, Ollama E2E, fit-optimize):
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
./scripts/certification-battery.shSee docs/CERTIFICATION.md. Latest run summary: docs/certification-latest.txt.
| Resource | Description |
|---|---|
| QUICK_START.md | Fast path from clone to first parody |
| RELEASE_NOTES_v1.06.14.md | Latest (cloud DNS resilience + batch isolation) |
| RELEASE_NOTES_v1.06.13.md | License max 10 concurrent consumers |
| RELEASE_NOTES_v1.06.12.md | Disk-paged batch checkpoints |
| RELEASE_NOTES_v1.06.10.md | Per-worker elapsed/remain on progress bars |
| RELEASE_NOTES_v1.06.9.md | TUI color boxes + emoji bars per worker |
| RELEASE_NOTES_v1.06.8.md | Expanded certification battery + docs/Pages |
| RELEASE_NOTES_v1.06.7.md | Fit-optimize parallel NL segfault fix |
| RELEASE_NOTES_v1.06.6.md | PATH TUI resolve + no Swift multiline segfault |
| RELEASE_NOTES_v1.06.5.md | Prior (MIDI prewarm / resume / Rust TUI) |
| docs/CERTIFICATION.md | Certification battery (unit, regression, UX, A/B, blackbox, E2E) |
| RELEASE_NOTES_v1.06.4.md | Resume checkpoint + Rust TUI |
| RELEASE_NOTES_v1.06.3.md | Prior release (ANSI alt-screen TUI fix) |
| RELEASE_NOTES_v1.06.2.md | Superseded ncurses experiment |
| RELEASE_NOTES_v1.06.1.md | Parallel batch crash fix |
| RELEASE_NOTES_v1.06.0.md | Batch-only CLI |
| docs/README.md | GitHub Pages site source |
| docs/RELEASES.md | Binary release install guide |
| docs/DEPLOYMENT.md | Pages deployment status |
docs/yankovinator.tex |
Technical paper (LaTeX) |
docs/presentation.tex |
Beamer slides |
docs/reference.tex |
API reference manual |
Generate PDFs:
cd docs
pdflatex yankovinator.tex
pdflatex presentation.tex
pdflatex reference.tex- SyllableCounter — syllable structure via NaturalLanguage + heuristics
- RhymeSchemeAnalyzer — baseline rhyme groups / schemes
- UnsupervisedRhymeClustering — phonetic + embedding rhyme discovery (unlabeled)
- LexicalSubstitutionEngine — syllable-matched NLEmbedding neighbors (MLM-style)
- CoherenceCritic — next-line surprise / coherence scoring (embedding + optional Ollama)
- OEDDictionary — dictionary-backed word suggestions
- OllamaClient — Ollama HTTP API (AsyncHTTPClient)
- ParodyGenerator — generation + refinement pipeline
- BenchmarkRunner — timing harness for CLI benchmarking
- Yankovinator — public library facade
- Swift 5.10+ / SwiftPM
- NaturalLanguage
- Ollama (
llama3.2:3bdefault) - AsyncHTTPClient
- ArgumentParser
- XCTest
- Analyze syllable structure (line totals and per-word patterns)
- Detect rhyme scheme from the original lyrics
- For each non-empty line:
- Apply syllable, rhyme, keyword, and context constraints
- Request a candidate from Ollama
- Refine word-by-word syllables and semantic coherence
- Match capitalization and punctuation to the original
- Preserve empty-line structure in the output
- Download from https://ollama.ai/download or
brew install --cask ollama-app - Launch Ollama from Applications
- Pull
llama3.2:3bin the UI or via CLI
brew install ollama
ollama serve
ollama pull llama3.2:3b
ollama listcurl http://localhost:11434/api/tags
ollama run llama3.2:3b "Hello, how are you?"curl http://localhost:11434/api/tags
# GUI: open Ollama from Applications
# CLI: ollama serveollama list
ollama pull llama3.2:3b
ollama show llama3.2:3bPoint at Xcode, not Command Line Tools:
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
# or for a single shell:
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
swift test- Confirm Ollama is running
- Check the port:
lsof -i :11434 - Override URL if needed:
--ollama-url http://localhost:11434
Syllable counting uses heuristics and may differ on unusual words; that is expected.
Copyright (C) 2025, Shyamal Suhana Chandra
Invented by Shyamal Chandra
Contact ssc56@duck.com to license code for commercial and non-commercial purposes.