Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HumanWrite

Local-first writing analysis with human-in-the-loop revision.

Paste text, get document statistics, sentence rhythm, vocabulary, repetition, transitions, punctuation and readability metrics. Build a personal style profile from your own writing. Detect measurable problem passages. Revise them in an editing workspace with undo/redo and diffs.

All of that runs locally — no LLM involved.

License: MIT Python React TypeScript


Why this exists

Most "writing improvement" tools send your document to a model and hand back a rewrite. That has two problems: your text leaves your machine, and you have no way to tell whether a number, a name or a quote survived the rewrite.

HumanWrite inverts the default. Measurement happens locally and is always available. If you choose to configure an AI provider, it is asked for alternatives on one passage at a time, and every replacement — including ones you edit yourself — is checked locally against the original before it can be applied. The human stays in control of every edit.

What it is not

Not an AI detector. Not a human-probability estimator. Not an authorship verifier. Not a detector-bypass tool. Not an automatic whole-document humanizer. It never rewrites a document by itself.

Features

Local analysis — document, sentence-rhythm and paragraph statistics; lexical diversity (TTR and Root TTR); repeated bigrams and trigrams; transition usage; repeated sentence openings; punctuation counts and per-100-word rates; contractions; Flesch readability. See METRICS.md.

Personal Style Profile — pool 2–12 of your own samples into a profile with a reliability level, per-feature stability across samples, and pooled style measurements. Compare any document against it.

Problem Map — an explainable list of measurable issues with UTF-16 offsets and verbatim excerpts. Eight issue types across sentence, paragraph and local-pattern scopes. Repetition detection works without a profile.

Human editing workspace — Working / Original / Changes / Analysis views, undo/redo, word-level diffs, and stale-map protection after edits. Direct manual editing is never restricted.

Optional targeted AI suggestions — 1–3 alternatives for one selected passage, reachable either from a Problem Map issue or from a manual selection in the editor. Analysis is optional for the manual path.

Protected content validation — a hard local filter. Numbers, percentages, dates, currency, URLs, emails, citations, quotes, technical identifiers, high-confidence names and your custom protected terms must survive exactly.

Semantic validation — advisory local meaning check (PASS / REVIEW / HIGH_RISK / UNAVAILABLE). Never calls a second model, never claims facts, never falsely reports PASS.

Style fit — an advisory normalized style-distance measurement that ranks and explains candidates. It never filters, and it is never presented as an authorship probability.

Architecture

Browser (React 19 + TS + Vite)
  └─ typed API client ──▶ FastAPI (Python 3.12)
                            ├─ services/       analysis · metrics · profiling · problem map
                            ├─ protection/     hard filter  (Sprint 6)
                            ├─ semantics/      advisory     (Sprint 7)
                            ├─ advanced_style/ advisory     (Sprint 8)
                            └─ providers/      the ONLY egress point

app/providers/llm.py is the single module in the codebase that makes an outbound network request. Everything else is local and deterministic.

The full module map, request flows and enforced design invariants are in ARCHITECTURE.md.

Quickstart

Requirements: Python 3.12 (e.g. via uv), Node.js 20+ and npm. No global Python packages are needed — the spaCy model is a pinned pip dependency.

Backend

cd backend
python3.12 -m venv .venv            # or: uv venv --python 3.12 .venv
source .venv/bin/activate
pip install -e ".[dev]"             # or: uv pip install -e ".[dev]"
uvicorn app.main:app --reload --port 8000
  • GET http://localhost:8000/api/health{"status":"ok"}
  • POST http://localhost:8000/api/analysis with {"text": "..."}

No API credentials are required. Local analysis, profiles, problem maps and the editor all start with no provider configured.

Frontend

cd frontend
npm install      # or: npm ci
npm run dev

Open http://localhost:5173. The Vite dev server proxies /api to http://localhost:8000.

Optional: local semantic validation

Meaning checks can use a local sentence-transformers embedding model:

cd backend && pip install -e ".[semantic]"

The model loads lazily on first use and is never downloaded at startup. If the library or model is unavailable, suggestions still work and meaning checks are marked UNAVAILABLE — never falsely PASS. Tests use a deterministic offline backend and never load a real model.

Configuration

Everything above works with nothing configured. To enable the optional AI-suggestions button, create a .env (git-ignored) from the template:

cp .env.example .env      # repository root or backend/ — both work
HUMANWRITE_LLM_PROVIDER=openai_compatible   # or "fake" for a deterministic offline demo
HUMANWRITE_LLM_API_KEY=your-key-here
HUMANWRITE_LLM_BASE_URL=https://api.openai.com/v1
HUMANWRITE_LLM_MODEL=gpt-4o-mini

The provider speaks an OpenAI-compatible chat/completions API, so any compatible provider works by changing the base URL and model. Official DeepSeek is a supported configuration:

HUMANWRITE_LLM_BASE_URL=https://api.deepseek.com
HUMANWRITE_LLM_MODEL=deepseek-v4-flash
HUMANWRITE_LLM_THINKING_MODE=disabled

With the DeepSeek base URL, HUMANWRITE_LLM_REASONING_EFFORT=none is automatically translated to thinking.type=disabled, because DeepSeek rejects reasoning.effort=none. Other effort values pass through unchanged.

Optional tuning: HUMANWRITE_LLM_TIMEOUT_SECONDS (default 120 — timeouts are never retried). No response_format is ever sent, since some gateways reject it; structured output is requested in the prompt and the response is parsed and validated locally, tolerating Markdown fences, content-part arrays and separate reasoning fields. Requests are bounded to 3 suggestions and 1600 output tokens, with one retry on transient 5xx only — never on auth failures, rate limits or malformed responses, and never a second "repair" call.

Safety and privacy

The design claim is simple: the machine never certifies meaning on a provider's word, and your text does not leave the machine unless you ask.

  • The local validator — not a provider's facts_preserved field — is the authority on whether a replacement is safe.
  • Every final replacement is validated against the original passage immediately before Apply, whether it was edited or not. Normal Apply never implicitly overrides protection; the override is a separate, clearly-labelled action after a visible rejection.
  • Guards read live state, never render closures: an apply is dropped unless the selected issue and snapshot version still match what the text was validated for.
  • Requesting a suggestion sends only the selected passage, at most its adjacent sentence, an optional short goal, a compact style context and compact protected-content constraints — never your corpus, embeddings, editor history or the full document.
  • Your API key never reaches the frontend, and logs never contain keys, headers, prompts or document text.

The full model is in SAFETY.md.

Testing

cd backend  && .venv/bin/pytest -q      # 675 passed
cd frontend && npm run typecheck        # clean
cd frontend && npm test                 # 195 passed across 13 files
cd frontend && npm run build            # typecheck + production build

Every test is deterministic and offline. Provider behaviour is mocked with httpx.MockTransport or replaced by a deterministic fake; semantic tests use a fake embedding backend; frontend tests run in jsdom and never touch the network. Tests never depend on a developer's local .env. New metrics require golden tests with manually verifiable expected values.

Documentation

Document Contents
ARCHITECTURE.md Module map, request flows, design invariants
API.md All 9 endpoints, error codes, limits
METRICS.md Every metric, formula and caveat
SAFETY.md Protection, semantic validation, style fit, privacy boundary
DEVELOPMENT.md Setup, conventions, and hard-won gotchas

Known limitations

  • Profiles live in frontend memory only — no persistence, no accounts.
  • The editor is a plain textarea: no rich text, no dark mode.
  • Stale problem-map offsets are never navigated against changed text; re-analyze after editing. This is by design.
  • Provider configuration guidance shows environment-variable names.
  • Semantic thresholds (0.80 / 0.55) and the protection detectors are heuristics, documented in backend/app/config.py and backend/app/protection/.

Roadmap

Sprints 1–10 are complete (local analysis → style profiles → problem map → editing workspace → optional AI suggestions → protected content → semantic validation → advanced style intelligence → UI polish → hardening and v1 readiness). Deliberately not started: whole-document rewrite, AI-detector integration, auth, database, Docker, payments, browser extension, rich-text editing.

License

MIT © 2026 Hrishikesh Das

About

HumanWrite — local-first writing analysis with protected-content validation

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages