Grammar correction, translation styles, and tense practice in the browser — backed by Ollama, Hugging Face, or Groq.
Demo: deep-lm.vercel.app · API: deeplm.up.railway.app
Grammar / Spell Fixer — three style variants (via Groq)
Tenses — English 12-tense chart with Persian on every card
Settings — exclusive provider (Ollama, Hugging Face, or Groq)
DeepLM is a small, self-hostable language-learning tool: fix a sentence in several styles, generate tense charts with Persian glosses, and explain a tense without sending your text to a closed proprietary UI. You can run a local model with Ollama, or call Hugging Face / Groq. Visitors can paste their own API keys in Settings; shared server keys are rate-limited.
| Area | What you get |
|---|---|
| Grammar / spell fixer | Tab Translate. Infers meaning, then Native, Friendly / Casual, Professional, and Grammar Notes. Pick a locale (e.g. American vs British English). Default English → Persian. Max 1000 characters. |
| Tenses | English: 12 tenses. German: 6 (Präsens, Präteritum, Perfekt, Plusquamperfekt, Futur I, Futur II). Persian gloss on every card. Recent searches stay in this browser. |
| Tense explanation | Per-tense teaching notes and examples (cached in Redis like grammar/tenses). |
| Providers | Default is Hugging Face. Groq is optional. Ollama is disabled for now (OLLAMA_ENABLED=false). A selected provider is exclusive (no silent vendor fallback). |
| Limits | Groq: 30/UTC hour for grammar, tenses, and explain, with your key or the server key. Hugging Face: 50/day on the shared server token only; your own HF key is uncapped. Counted by browser id and IP. Cache hits do not count. |
| PWA | Installable on HTTPS (manifest, service worker, header Install button). |
| Changelog | Versions tab is generated from CHANGELOG.md. |
flowchart LR
Browser["Next.js PWA<br/>Vercel"] -->|JSON + X-Client-Id| API["FastAPI<br/>Railway"]
API --> Redis[(Redis<br/>cache + quotas)]
API --> Ollama["Ollama<br/>optional local"]
API --> HF["Hugging Face"]
API --> Groq["Groq"]
| Layer | Stack |
|---|---|
| Frontend | Next.js (App Router), React, TypeScript, Tailwind, Serwist |
| Backend | FastAPI, Pydantic Settings, httpx, Hugging Face Hub client |
| Data | Redis (response cache, 12h TTL; daily quotas) |
Monorepo layout:
.
├── backend/ FastAPI app (`app.main:app`)
├── frontend/ Next.js UI
├── docker-compose.yml
├── railway.toml API deploy (Railpack)
└── VERSION Semver source of truth
- Python 3.12+ (backend)
- Node.js 20+ (frontend)
- Redis (cache and default-key quotas)
- Ollama on the host if you use the local provider
- Docker Desktop optional (Redis + API)
ollama pull deepseek-r1
ollama serveOllama should listen on port 11434.
cp .env.example .env # Windows: copy .env.example .envNever commit .env. Hugging Face and Groq tokens are optional; they are only required when that provider is selected (or as a server default for visitors).
docker compose up --build| Service | URL |
|---|---|
| UI (run separately) | http://localhost:3000 |
| API | http://localhost:8000 |
| Health | http://localhost:8000/health |
Compose starts Redis and the backend. Ollama stays on the host; the API container uses http://host.docker.internal:11434.
cd frontend
npm install # or pnpm install
# Unix
export NEXT_PUBLIC_API_URL=http://localhost:8000
npm run dev
# Windows PowerShell
$env:NEXT_PUBLIC_API_URL="http://localhost:8000"
npm run devStart Redis first (docker compose up redis or a local Redis). Then:
cd backend
python -m venv .venv
# Unix: source .venv/bin/activate
# Windows: .venv\Scripts\activate
pip install -r requirements.txt
# Unix
export OLLAMA_BASE_URL=http://127.0.0.1:11434
export REDIS_URL=redis://127.0.0.1:6379/0
uvicorn app.main:app --reload --port 8000
# Windows PowerShell
$env:OLLAMA_BASE_URL="http://127.0.0.1:11434"
$env:REDIS_URL="redis://127.0.0.1:6379/0"
uvicorn app.main:app --reload --port 8000GET /health should include "redis": true before you rely on cache or shared HF/Groq keys.
Copy .env.example. Important variables:
| Variable | Purpose |
|---|---|
HF_TOKEN / GROQ_API_KEY |
Server default keys. Groq is always 30/hour (pasted or server). HF is 50/day only for the server token. |
HF_DEFAULT_DAILY_LIMIT / GROQ_DEFAULT_DAILY_LIMIT |
Defaults 50 (HF server token) and 30 (Groq). |
OLLAMA_BASE_URL / OLLAMA_MODEL |
Local model (deepseek-r1 by default). |
HF_CHAT_MODEL / HF_PROVIDER / GROQ_MODEL |
HF default Qwen/Qwen2.5-72B-Instruct via Inference Providers (HF_PROVIDER=auto lets Hugging Face pick a host). Groq: openai/gpt-oss-120b. |
REDIS_URL / REDIS_PRIVATE_URL |
Cache + quotas. Private URL is preferred on Railway. |
REDIS_TTL_SECONDS |
Cache TTL (default 43200 = 12 hours). |
CORS_ORIGINS |
Comma-separated browser origins. |
NEXT_PUBLIC_API_URL |
Frontend → API base URL (baked in at build time). |
API keys and Groq/HF tokens are never written to Redis. Cache keys hash text, languages, tense, and provider only.
Base URL in production: https://deeplm.up.railway.app.
| Method | Path | Notes |
|---|---|---|
GET |
/health |
Version, provider status, Redis reachability. |
GET |
/api/providers |
Same payload as health. |
GET |
/api/languages |
Grammar languages, tense counts, German tense labels. |
GET |
/api/limits |
Daily usage. Send X-Client-Id. Cache-Control: no-store. |
GET |
/api/changelog |
Parsed changelog for the Versions tab. |
POST |
/api/grammar |
Styled grammar / translation. |
POST |
/api/tenses |
Tense chart. |
POST |
/api/tenses/explain |
Tense explanation. |
Generation endpoints accept provider, optional hf_api_key / groq_api_key, and use Redis cache when Redis is up. Repeat requests with the same inputs return "cached": true and do not increment quotas.
- If Settings (or the request) names a provider, only that provider runs.
- If no provider is sent, try Hugging Face, then Groq. Ollama is skipped while
OLLAMA_ENABLEDis false. - Leftover
<think>blocks are stripped from replies. - An explicit Groq / HF / Ollama choice fails closed if that provider is missing or errors.
- Groq generations (your key or the server key) and default-key Hugging Face generations require Redis (503 if Redis is down) so the Groq 30/hour and HF 50/day caps can be enforced.
The repo root is a monorepo. Railpack builds the FastAPI service. Deploy the Next.js app separately (for example Vercel) with NEXT_PUBLIC_API_URL pointing at the API.
Do not set the Railway service root to frontend/. railpack.json starts Uvicorn from backend/.
Set at least:
CORS_ORIGINS=https://deep-lm.vercel.app,http://localhost:3000
HF_TOKEN=
GROQ_API_KEY=
OLLAMA_BASE_URL=http://127.0.0.1:11434
REDIS_TTL_SECONDS=43200Add a Redis plugin on the same project and environment. On the API service (not the Redis plugin), set:
REDIS_PRIVATE_URL=${{ Redis.REDIS_PRIVATE_URL }}
If the canvas service is not named Redis, use that name instead. Redeploy. Confirm GET /health → "redis": true.
REDIS_PRIVATE_URL is for Railway’s private network. It will not work from your laptop; local runs use REDIS_URL=redis://127.0.0.1:6379/0 (see railway.toml [environments.local.variables]).
- Root directory:
frontend - Install: pnpm or npm
- Env:
NEXT_PUBLIC_API_URL=https://deeplm.up.railway.app - Rebuild after changing
NEXT_PUBLIC_API_URL
Production PWA build uses webpack so Serwist can inject the worker: npm run build then npm run start.
| Piece | Location |
|---|---|
| Manifest | /manifest.webmanifest |
| Icons | frontend/public/icons/icon-192.png, icon-512.png |
| Service worker | /sw.js (build output; disabled in next dev) |
| Offline | /offline (shell only; API stays network-only) |
Chromium: header Install app. iOS: Share → Add to Home Screen.
cd backend
python -m unittest discover -s tests -vTargeted:
python -m unittest tests.test_config tests.test_quota tests.test_cache tests.test_translation_quality tests.test_tenses -vCanonical semver is VERSION. It is shown in the UI and on GET /health.
Each push to main runs .github/workflows/bump-version.yml, which increments the patch and tags vX.Y.Z. Commits whose message contains chore: bump version are skipped so the bot does not loop. For a minor or major release, bump VERSION (and the mirrored files) in the same PR before merge.
Log every change in CHANGELOG.md as major, minor, patch, or release.
Issues and pull requests are welcome at github.com/master2it/DeepLM.
- Fork and branch from
main. - Keep secrets out of git (
.env, tokens, Redis passwords). - Add or update a
CHANGELOG.mdentry in the same PR. - Prefer small, reviewable diffs. Match existing code style.
- If you change public API or provider behavior, update this README.
- Do not commit API keys, Redis URLs with passwords, or
.env. - Browser-pasted Groq/HF keys stay in
localStorageand are sent only to your configured API. - Default-key daily limits exist to protect shared tokens, not as a security boundary.
- Report vulnerabilities privately via GitHub Security advisories if available, otherwise open a private contact with the maintainer.
MIT. Copyright Master2iT.


