Grammar correction, translation styles, and tense practice in the browser — backed by 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 (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. Call Hugging Face or Groq from Settings; visitors can paste their own API keys; 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 (Qwen). Optional: DeepSeek-V4-Flash (same HF token) and Groq. A selected provider is exclusive (no silent vendor fallback). |
| Limits | Groq Free: RPM + RPD per selected model (docs), 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 --> 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)
- Docker Desktop optional (Redis + API)
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.
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 REDIS_URL=redis://127.0.0.1:6379/0
uvicorn app.main:app --reload --port 8000
# Windows PowerShell
$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 Free RPM/RPD apply (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 1000 (fallback Groq RPD). Live Groq caps come from the Free model catalog. |
HF_CHAT_MODEL / HF_DEEPSEEK_MODEL / HF_PROVIDER / GROQ_MODEL |
HF default Qwen/Qwen2.5-72B-Instruct; DeepSeek deepseek-ai/DeepSeek-V4-Flash. Default Groq Free model openai/gpt-oss-120b (Settings can pick any Free chat model). |
REQUEST_TIMEOUT_SECONDS |
HTTP timeout for provider calls (default 120). |
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 (Qwen), then DeepSeek, then Groq.
- Leftover
<think>blocks are stripped from replies. - An explicit Groq / HF 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 Groq Free RPM/RPD 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=
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.


