Inspiration
Every grammar checker worth using sends your text to someone else's servers. Grammarly, LanguageTool Premium — your messages, your code reviews, your half-written thoughts, shipped off for scoring. That's fine for a blog post. It's not fine for a private Discord DM or a work channel.
We wanted Premium-tier quality without the surveillance. The bet: a well-routed local pipeline can match cloud correction quality — cheap deterministic checks for the easy 90%, a real language model only for the hard cases — and nothing leaves the machine unless you say so.
What it does
GrammarForge checks your writing where you actually write it — the browser, Discord, and (soon) the OpenCode terminal — and suggests fixes inline. It's a drop-in replacement for LanguageTool Premium, so existing LanguageTool clients point at it unchanged.
It suggests; it never silently rewrites you. Corrections land in ~35 ms for the common case. And the only network traffic on any correction path is to the language-model endpoint you configure — which defaults to a model running on your own hardware.
How we built it
The core is a Go bridge running a two-tier pipeline:
- Fast path, in-process: Harper (Rust, via CGo, ~10 ms) for spelling and style, then GECToR — a RoBERTa grammar model as INT8 ONNX, ~25 ms — for structural errors. Most corrections never go further.
- Slow path, escalation-only: a language model handles the sentences the fast path can't. It speaks the OpenAI chat-completions API, so you point it at a local llama.cpp for full privacy (the default), or at OpenAI's models / open-weight gpt-oss when you want more headroom.
Around that: a per-sentence cache so unchanged text never re-runs the pipeline; deterministic repair chains that undo measured LLM over-edits before they reach you; and three thin clients (browser extension, Vencord plugin, OpenCode TUI). Every change to the correction logic has to clear an eval harness — a 125-case golden set plus CoNLL-14, BEA-19, JFLEG, and a clean-text false-positive corpus — before it merges.
Challenges we ran into
The language model is an over-eager editor. Left alone, it "corrects" things that were already right — expanding contractions, Americanizing British spelling, rewriting perfectly clean casual text. We measured it: turning on LLM escalation doubled the false-positive rate on clean input. Prompt-begging didn't fix it. Deterministic revert rules did — we catch the specific over-edit classes on the model's output and undo them before diffing.
We couldn't see the problem until we built the ruler. The standard GEC benchmarks are saturated — everything scores near 100% — so they're blind to false positives on clean text. We had to build a clean-text corpus and scorer from scratch just to measure the thing we most wanted to fix.
The elegant fix didn't work. A post-LLM semantic verifier — embed the original and the rewrite, reject edits that drift too far in meaning — sounded obviously correct. Calibration killed it: no threshold separates good corrections from over-edits, because meaning-preserving over-edits score higher than many legitimate rewrites. We recorded the negative result and moved on instead of shipping it on faith.
Accomplishments that we're proud of
- Quality per byte. The default 4B model hits ERRANT F0.5 0.906 and 125/125 on our golden set at ~3.3 GiB resident — beating larger, hungrier backends we tested on both accuracy and memory.
- Private by construction. The only outbound traffic on any correction path is to the model endpoint you chose, and by default that's a model on your own box. No telemetry, no analytics, nothing else.
- It actually shipped. Public repo, v0.2.0, a one-command install that a clean-room test passed cold, a multi-arch image that runs native on Apple Silicon, and all three clients built. The Discord one is in daily live use.
- The discipline held. Every risky idea got measured, and the ones that didn't earn their place got cut — not shipped and quietly regretted later.
What we learned
- Deterministic beat clever. A handful of hand-written revert rules outperformed the sophisticated semantic verifier. Boring and measured won.
- Believe the ruler over your instincts. Several improvements we were sure about got refuted by the eval and never shipped. Trusting the measurement was the whole discipline.
- A good local model is enough. We cleared the quality bar with a 4B model and no fine-tuning — the routing and the repair chains carried it.
- Negative results are results. "No safe threshold," "N-best majority-vote regresses," "iterative decoding costs latency for noise" — each one saved us from shipping a plausible mistake.
What's next for GrammarForge
- Learning from you. Deterministic reject-suppression is already live — tell it "no" enough times and it stops suggesting that edit. A personalization loop that fine-tunes a small adapter on your accepted corrections is next, gated on enough real signal to be worth it.
- OpenCode to parity. The terminal client is built; it goes fully live once an upstream TUI hook lands.
- Turning on what's waiting. Confidence calibration and trusted-category routing are built and dark, flipping on as live usage accumulates the signal to justify them.
- Wider reach. Store-published extensions, and a multi-sentence eval corpus to finally measure the cross-sentence context feature that single-sentence benchmarks can't see.
Log in or sign up for Devpost to join the conversation.