ZuZu Grammar — self-hosted LanguageTool
ZuZu Grammar uses LanguageTool for full sentence grammar, spelling, and punctuation. If LanguageTool is offline, the API falls back to basic local rules.
Hugging Face Spaces (recommended)
The Docker image embeds LanguageTool and starts it with the app (scripts/start.sh).
- Redeploy the Space (rebuild the image).
- First boot can take 1–3 minutes while Java + LT warm up.
- Check
GET /health→"languagetool": { "reachable": true }. - Space secrets (optional overrides):
LANGUAGE_TOOL_EMBEDDED=true
LANGUAGE_TOOL_URL=http://127.0.0.1:8010
LANGUAGE_TOOL_LANGUAGE=en-US
LANGUAGE_TOOL_ENABLED=true
LANGUAGE_TOOL_TIMEOUT=90
LANGUAGE_TOOL_CHUNK_CHARS=1800
GRAMMAR_MAX_CHARS=12000
LANGUAGETOOL_JAVA_OPTS=-Xms256m -Xmx1024m
Needs about 1–2 GB RAM. If the Space OOMs, raise Space hardware or lower -Xmx (Java 21 is used in the image).
Long drafts are split into ~1800-character chunks so one long paragraph does not OOM LanguageTool. Pastes over GRAMMAR_MAX_CHARS (default 12,000) return a clear 413 instead of a raw 500.
Do not set LANGUAGE_TOOL_URL to a host that isn’t running LT (that causes Connection refused and the “offline” fallback message).
Local: LanguageTool only + python app.py
docker compose up languagetool -d
Wait until healthy, then in .env:
LANGUAGE_TOOL_URL=http://127.0.0.1:8010
LANGUAGE_TOOL_LANGUAGE=en-US
LANGUAGE_TOOL_ENABLED=true
LANGUAGE_TOOL_LANGUAGE is the server default (en-US). The Grammar UI can override per check (en-GB, etc.).
curl -s http://127.0.0.1:8010/v2/languages | head
python app.py
Local: full Compose (app + LT sidecar)
docker compose up --build
App: http://127.0.0.1:7860
LanguageTool: http://127.0.0.1:8010
Compose sets LANGUAGE_TOOL_EMBEDDED=false and LANGUAGE_TOOL_URL=http://languagetool:8010.
Health
"languagetool": { "configured": true, "reachable": true, "default_language": "en-US" }
When reachable, grammar responses use "engine": "languagetool".
Troubleshooting Connection refused
| Cause | Fix |
|---|---|
| LT container not running | docker compose up languagetool -d |
| App URL wrong | Local app → http://127.0.0.1:8010; Compose app → http://languagetool:8010; Space embedded → http://127.0.0.1:8010 |
| Space still on old image | Rebuild/redeploy so embedded LT + start.sh are included |
| LT still starting | Wait 1–2 min; watch logs for LanguageTool ready |
| Long text → HTTP 500 | Redeploy latest build (sentence chunking + higher heap). Over GRAMMAR_MAX_CHARS now returns a clear 413. |
Resources
LanguageTool needs roughly 1–2 GB RAM.