Johannes Kaindl90 downloadsRetune the communication style of a text with four dials and a local LLM — direct or diplomatic, explicit or implicit, factual or social, literal or figurative.
🇬🇧 English · 🇩🇪 Deutsch
Retune the communication style of a text with four dials and a local LLM — direct or diplomatic, explicit or implicit, factual or social, literal or figurative. Nothing leaves your machine.
Neurodivergent and neurotypical communication styles differ along a few well-described dimensions: how direct a request is, how much context is spelled out, how much social framing surrounds the content, and how literal the language is. LingoTuner is an interpreter between those styles. Select a text, set four dials, add a note if you like, and a local model rewrites the text — same information, different style. A sidebar panel next to your note holds the four sliders, the note field, the streamed result and the four ways out of it.

system.md) or single example pairs (<dimension>_<level>.md). Empty means the shipped text — so improvements still reach you. One button writes the shipped texts into the folder as a starting point; existing files are never overwritten.GET /v1/models; leaving the setting empty lets the server use whatever it has loaded. Reasoning models are handled: <think> output is separated from the result, and reasoning is suppressed where the server supports it.app.plugins.plugins["lingotuner"].api.tune(text, "clarity") runs the same path as the panel and returns { ok: true, text, truncated } or a named reason. Every call is also reported to LLM Lab if that plugin is installed.app://obsidian.md, and a server that answers a plain request may still refuse that. In LM Studio enable CORS in the server settings (lms server start --bind 0.0.0.0 --cors sets both at once — a missing flag is reset to its default); for Ollama set OLLAMA_ORIGINS=app://obsidian.md. If the server refuses the stream, the plugin retries once without streaming, so the answer still arrives — all at once instead of token by token. Only when that fails too does it name the case instead of reporting a generic network error.context:-2, social:2) hit the intended tone but not the concreteness of the reference text. A very small model tends to shorten instead of rephrase.Via AnySource Sideloader, which installs and updates plugins from any git forge. Subscribe to the Order from Traces catalog once under Settings → AnySource Sideloader → Catalogs → Add:
https://git.jkaindl.de/jkaindl/obsidian-catalog/raw/branch/main/catalog.json
LingoTuner then appears in the sideloader's plugin list and updates like any other plugin; every download is checksum-verified. To install just this one plugin without the catalog, add its repository URL as a source instead: https://github.com/johannes-kaindl/lingotuner.
Download main.js, manifest.json and styles.css from the latest release (or the lingotuner.zip bundle, which contains exactly those three files) into <vault>/.obsidian/plugins/lingotuner/, then enable the plugin under Settings → Community plugins.
git clone https://github.com/johannes-kaindl/lingotuner
cd lingotuner
npm install
npm run build # produces main.js
Copy main.js, manifest.json and styles.css into <vault>/.obsidian/plugins/lingotuner/.
The replace buttons stay disabled unless the source is still live — same note, still in editing mode, text unchanged since it was read. Copy and New note always work.

Click the preview for full size — the four dials with their step names
Settings → Community plugins → LingoTuner, grouped into connection, style and output:
| Setting | What it does |
|---|---|
| Endpoints | An ordered list of OpenAI-compatible servers, each with an optional API key and model override. The first reachable one is used; every row shows its own reachability status. API keys are kept in Obsidian's keychain, not in the plugin's data file. |
| Request | Shows the sampling values, thinking level and token budget actually sent for the current model family and backend, with per-family overrides, the last request and any deviations for the running session. The thinking control in the panel switches between off and the model's preferred level; a per-family toggle in this section turns it into a four-level dropdown. |
| Override folder | Optional vault folder with system.md and <dimension>_<level>.md files that replace shipped prompt text. Empty means shipped text. |
| Saved presets | Your own dial combinations, saved from the panel. |
| Logbook | Off by default. When on, every run is appended to a monthly note (time, model, dial values, note, original, result). |
| Folder for new notes | Where New note puts the tuned text. Empty means the vault root. |

The prompt is assembled, not templated. A system part states the invariants — keep the language of the text, add nothing, drop nothing, keep the Markdown structure, return only the rewritten text. Each dial that is not in the middle adds one instruction block with the name of its step plus one before/after example pair for exactly that step; a dial in the middle adds nothing at all, which is why "all dials centred and no note" is reported as nothing to do rather than sent to a model. The note, if present, is appended last with an explicit precedence over everything above it.
The answer is streamed over XHR (server-sent events), reasoning output is separated from the result before it is displayed, and a stream cut short by the token limit is shown with a warning rather than silently kept. The whole path — prompt building, dial model, session history, source guards — lives in src/core/ without importing obsidian, and a gate keeps it that way, which is why it can be tested in plain Node.
AGENTS.md — architecture, conventions and gotchas for contributors and AI agents.docs/internal/SMOKE.md — the GUI smoke test: what is checked against a running Obsidian, and how to reproduce the run.docs/internal/LAB.md — the measured run of all 16 example pairs against a local model, with the findings that shaped the shipped prompt text.CHANGELOG.md — what changed per release.Code AGPL-3.0-or-later (LICENSE), documentation CC BY-SA 4.0 (LICENSE-DOCS).