Skip to content

About

An AI-powered writing assistant that helps users rewrite, humanize, summarize, paraphrase, and improve text with context-aware, natural-sounding outputs using modern AI.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

33 Commits

Folders and files

Repository files navigation

WriteWise

WriteWise is an AI-powered writing platform built for job seekers and professionals. It combines general writing tools (like QuillBot) with job-focused intelligence, powered by the OpenAI API.

WriteWise is a standalone, self-hosted app: a React frontend talks to a Python FastAPI backend, and the backend calls OpenAI. There is no login and no database.

Browser → React frontend (localhost:3000) → FastAPI backend (localhost:8001) → OpenAI API

Fix grammar, rewrite content, generate emails, paraphrase text, summarize documents, and more — all in one place.


Features

10 Writing Modes

Mode What it does
Auto AI detects your intent and acts on it automatically
Grammar Fixes grammar and spelling without changing your voice
Email Drafts professional emails and personalized job application emails
Tone Changes how the text sounds: Professional, Casual, Friendly, Diplomatic, Formal, Confident, Persuasive, or Empathetic
Rewrite Improves how the text is structured and expressed, with an optional goal: Clear & Concise, More Direct, More Polished, More Persuasive, Simplify, or Keep My Style
Paraphrase Rewrites with the same meaning in 5 styles: Standard, Fluency, Formal, Simple, Creative
Summarize Extracts key points as a short summary or bullet points
Expand / Shorten Adds depth or trims text down to what matters
Humanize Removes AI-like phrasing and makes text sound naturally written
Prompt Turns a rough, natural-language message into a clear, LLM-ready request you can paste into ChatGPT, Claude or Gemini, keeping your meaning. See Prompt Mode

Smart Features

  • Personalization Panel — Enter your experience, target role, and skills. The AI uses this to personalize outputs, especially emails and rewrites. Saved in your browser (localStorage) only, and opened automatically in Email mode while empty
  • Output Variations — Generate 3 versions at once: Professional, Confident, and Friendly
  • Why You're a Good Fit — For job postings, 2-3 bullets that connect background you supplied to the role's requirements. Only shown when you have given your background
  • AI Self-Check — The AI's own scores for Clarity and Professionalism (1-10), plus Personalization when a profile was given, and one improvement suggestion. Scores the model does not return validly are left out, never filled in
  • Grounded job emails — The AI only states experience, skills and qualifications you supplied; a job posting's requirements are never presented as your background, and names or placeholders like [Your Name] are not invented
  • Facts kept on tone changes — Numbers, dates, deadlines, request counts and concrete requests are kept when the tone changes
  • Quick Templates — One-click starters for Job Application, Follow-up Email, Cold Outreach, and Referral Request
  • Regenerate — Re-run the same request for a fresh output
  • Copy to Clipboard — One-click copy with fallback support

UX Details

  • Dynamic placeholders and context hints per mode
  • Dynamic loading messages ("Paraphrasing...", "Summarizing...", "Humanizing..."...)
  • Button label changes contextually ("Expand" or "Shorten")
  • "Try with Example" CTA auto-fills a real job posting and switches to Email mode
  • No em dashes or double hyphens in any AI output — enforced at the system prompt level

Tech Stack

Layer Technology
Frontend React 18, Create React App 5 + CRACO, Tailwind CSS, Shadcn UI, Lucide React
Backend FastAPI, Python
AI OpenAI Responses API via the official openai Python SDK (model set by OPENAI_MODEL, default gpt-5.6-terra)
Storage None. Requests are stateless and nothing is persisted
Fonts IBM Plex Sans, JetBrains Mono

Project Structure

writewise/
├── api/
│   ├── index.py           # Vercel entrypoint; re-exports the FastAPI app from backend/server.py
│   └── requirements.txt   # Runtime Python dependencies (used by Vercel and by backend/requirements.txt)
├── backend/
│   ├── server.py          # FastAPI app, all writing modes, prompt construction, OpenAI call
│   ├── prompt_mode.py     # Prompt mode: its own instructions, URL protection, output cleanup
│   ├── secret_redaction.py # Removes obvious API keys, tokens and passwords (Prompt mode)
│   ├── requirements.txt   # Runtime deps (via api/requirements.txt) + uvicorn and test tools
│   ├── tests/             # Mocked API tests; tests/integration/ holds opt-in live OpenAI tests
│   ├── .env.example       # Template for backend/.env
│   └── .env               # OPENAI_API_KEY, OPENAI_MODEL, CORS_ORIGINS (not committed)
│
├── frontend/
│   ├── src/
│   │   ├── App.js                        # Main app, state, mode logic
│   │   ├── App.css                       # All styles (dark theme)
│   │   └── components/
│   │       ├── QuickTemplates.jsx        # 4 quick-start template chips
│   │       ├── PersonalizationPanel.jsx  # Collapsible experience/role/skills inputs
│   │       ├── TabOptions.jsx            # Mode-specific sub-options (pills)
│   │       └── OutputSection.jsx         # Output cards, scores, why-good-fit
│   ├── public/
│   │   └── images/bg-texture.png         # Background texture
│   ├── package.json
│   ├── package-lock.json
│   ├── .env.example       # Template for frontend/.env
│   └── .env               # REACT_APP_BACKEND_URL (not committed)
│
└── README.md

Getting Started

Prerequisites

  • Node.js 20.x (tested with 20.19.4) and npm 10.x (tested with 10.8.2)
  • Python 3.10+ (tested with 3.12)
  • An OpenAI API key

No database is required.

1. Clone the repository

git clone https://github.com/tymepas/WriteWise_Project.git
cd WriteWise_Project

2. Backend setup

cd backend
python -m venv .venv
# Windows:      .venv\Scripts\activate
# macOS/Linux:  source .venv/bin/activate
pip install -r requirements.txt

Copy backend/.env.example to backend/.env and fill in your key:

OPENAI_API_KEY=your_openai_api_key_here
OPENAI_MODEL=gpt-5.6-terra
CORS_ORIGINS=http://localhost:3000

Start the backend (from the backend directory):

uvicorn server:app --host 127.0.0.1 --port 8001 --reload

Check it is running: http://localhost:8001/api/ should return {"message": "WriteWise API"}.

3. Frontend setup

In a second terminal:

cd frontend
npm install

Copy frontend/.env.example to frontend/.env:

REACT_APP_BACKEND_URL=http://localhost:8001

Start the development server:

npm start

The app will be available at http://localhost:3000.

To create a production build in frontend/build/:

npm run build

REACT_APP_BACKEND_URL is baked into the bundle at build time. When it is not set, the frontend calls the API on its own origin (/api/...), which is how the Vercel deployment works.

4. Tests

With the backend virtual environment active, from the repository root:

pytest backend/tests

This runs the mocked tests (backend/tests/test_api_mocked.py and backend/tests/test_prompt_mode.py), which use a fake OpenAI client: no network calls and no API cost. The live tests in backend/tests/integration/ call a running backend and the real OpenAI API, so they are skipped unless you enable them:

WRITEWISE_LIVE_TESTS=1 REACT_APP_BACKEND_URL=http://127.0.0.1:8001 pytest backend/tests/integration

To run only the Prompt mode tests, use pytest backend/tests/test_prompt_mode.py (mocked) or add -s to the live command with backend/tests/integration/test_prompt_mode_live.py to print each fictional golden input and its output for review.


Deploying to Vercel

WriteWise deploys as a single Vercel project: the React build is served as static files and the FastAPI app runs as a Python function on the same domain.

/          → frontend/build (static React app)
/api/*     → api/index.py → FastAPI app from backend/server.py
  1. Import the GitHub repository into Vercel and keep the Root Directory as the repository root. vercel.json sets the install command, build command, output directory and the /api/* rewrite, so no build settings need to be changed in the dashboard.
  2. Add these Environment Variables in the Vercel project settings:
    • OPENAI_API_KEY: your OpenAI key (mark it as sensitive)
    • OPENAI_MODEL: gpt-5.6-terra
    • CORS_ORIGINS: your deployment URL, e.g. https://writewise.vercel.app (the app itself is same-origin and does not need CORS; this only limits other websites)
  3. Do not set REACT_APP_BACKEND_URL on Vercel, so the frontend calls /api on the same domain.
  4. Deploy.

The Python runtime version is pinned by api/.python-version (3.12), and the Python function installs only api/requirements.txt.

Abuse and cost protection

/api/generate is public and every request spends OpenAI credit. The app itself bounds the cost of a single request (the input limits above and the 6,000 output-token cap), but it does not rate limit: Vercel runs the API on many short-lived instances, so a counter in application memory would not be a real global limit. Use these two platform controls instead:

  1. Vercel Firewall rate limit (available on Hobby: 1 rate-limit rule per project). In the Vercel dashboard, open the project → Firewall → Configure → + New Rule:

    • Name: Limit /api/generate
    • If: Request Path equals /api/generate
    • Then: Rate Limit, Fixed Window, Time Window 60s, Request Limit 10, key IP, action Default (429)
    • Save Rule, then Review Changes → Publish

    You can start with the Log action to see real traffic first. Vercel counts rate limits per region, so a client spread across regions can exceed the limit somewhat. The frontend shows "Too many requests. Please wait a minute and try again." when this rule responds with 429.

  2. OpenAI spending limit. Use a dedicated OpenAI project for WriteWise and set a monthly budget and usage alerts on it in the OpenAI dashboard, so a traffic spike cannot run up an unlimited bill.


API Reference

POST /api/generate

Generate writing output based on the selected mode.

Request body:

{
  "mode": "email",
  "input": "Software Engineer role at TechCorp. Need 3 years React experience.",
  "context": "Friendly and direct tone",
  "experience": "4 years React developer",
  "target_role": "Frontend Engineer",
  "skills": "React, TypeScript, Node.js",
  "variations": false,
  "paraphrase_mode": "standard",
  "summary_type": "short"
}
Field Type Required Description
mode string Yes One of: auto, grammar, email, tone, rewrite, paraphrase, summarize, expand, shorten, humanize, prompt
input string Yes The text to process
context string No Extra instructions (e.g. "Keep it concise for a client email")
experience string No User's work experience (for personalization)
target_role string No Job role being targeted
skills string No Comma-separated skills
variations boolean No If true, returns 3 variations instead of 1
paraphrase_mode string No standard, fluency, formal, simple, creative
summary_type string No short or bullets
tone string No Tone mode only: professional, casual, friendly, diplomatic, formal, confident, persuasive, empathetic. If omitted, the tone is taken from context (default professional)
rewrite_goal string No Rewrite mode only: clear_concise, more_direct, more_polished, more_persuasive, simplify, keep_style. If omitted, the default rewrite is used

Limits: input 12,000 characters, context 2,000, experience 1,000, target_role 300, skills 1,000. Longer values are rejected, never truncated.

paraphrase_mode, summary_type, tone and rewrite_goal are case-insensitive. A missing or empty value uses the default; any other value not in the lists above is rejected with a 400.

Response:

{
  "output": "Subject: Frontend Engineer Application...",
  "variations": null,
  "mode": "email",
  "detected_mode": null,
  "why_good_fit": [
    "4 years of React experience directly matches the job requirement",
    "TypeScript proficiency aligns with their modern frontend stack"
  ],
  "evaluation": {
    "clarity": 9,
    "professionalism": 9,
    "personalization": 8,
    "suggestion": "Consider adding a specific project or achievement to stand out."
  }
}
  • why_good_fit is null unless the input is a job posting and the user supplied their own background.
  • evaluation is null if the model did not return valid 1-10 scores. evaluation.personalization is null when no experience, target_role or skills were sent. evaluation.suggestion may be null.
  • detected_mode is email or rewrite in Auto mode, otherwise null.
  • secrets_redacted (Prompt mode only) is the number of possible secrets (API keys, tokens, passwords) removed from the text before it was sent to OpenAI, or null when there were none.
  • In prompt mode, context, experience, target_role, skills and variations are ignored, and variations, why_good_fit and evaluation are always null.

Errors are returned as {"detail": "<message safe to show the user>"}; internal error text is only logged on the server.

Status When
400 Invalid request: empty or too-long text, unknown mode or option, malformed JSON
422 The result would be too long to generate in full (try shorter text or turn off variations), or the text was refused by the AI service
429 The OpenAI account is rate limited, or the Vercel Firewall rate limit was hit
500 The API key or model is not configured correctly, or an unexpected error
502 / 503 / 504 The AI service failed, could not be reached, or timed out

Each request to OpenAI is capped at 6,000 output tokens, with a 90 second timeout and one retry.

GET /api/

Health check. Returns { "message": "WriteWise API" }.


Environment Variables

Backend (/backend/.env)

Variable Description
OPENAI_API_KEY Your OpenAI API key. Required. Used only by the backend and never sent to the browser
OPENAI_MODEL OpenAI model to use. Optional, defaults to gpt-5.6-terra
CORS_ORIGINS Allowed frontend origins (comma-separated), e.g. http://localhost:3000

Frontend (/frontend/.env)

Variable Description
REACT_APP_BACKEND_URL Backend API base URL, e.g. http://localhost:8001. Do not put any API keys in the frontend .env: everything in it is visible in the browser

.env files are git-ignored; only the .env.example templates are committed.


Provider Migration Note

WriteWise was originally generated on the Emergent platform, where the backend called Anthropic's claude-sonnet-4-5-20250929 through Emergent's emergentintegrations wrapper and logged request metadata to MongoDB. The standalone version calls OpenAI directly with the official SDK, keeps the same prompts, request/response shape and output parsing, and stores nothing. The backend asks OpenAI for JSON-mode output so the model's response always parses into the structure the frontend expects. Output wording may differ from the original model.


Writing Mode Details

Auto Mode

Detects the best mode automatically from your input. Supports detection of: grammar errors, email intent, tone requests, paraphrase needs, summarization, and humanization.

Email Mode

If input is a job posting, generates a personalized job application email. If input is rough text, polishes it into a professional email. Always includes a subject line. Uses your experience, target role, and skills for personalization.

Paraphrase Styles

  • Standard — Natural rewrite with varied wording
  • Fluency — Smooth, easy-to-read version
  • Formal — Professional and academic register
  • Simple — Plain language, shorter sentences
  • Creative — Inventive rewriting with fresh phrasing

Summarize Formats

  • Short Summary — 2 to 3 sentence overview
  • Bullet Points — 4 to 6 key points

Prompt Mode

Write naturally. Keep your meaning. Prompt mode transforms a rough, natural-language message into a clear, concise, LLM-ready request while preserving your original intent. You type the way you think, copy the result, paste it into ChatGPT, Claude, Gemini or another assistant, and carry on the conversation. It is not a prompt-engineering tool: the result reads like a clear message from you, not a template.

Example (fictional):

You type: hey i have a python script which works fine when i test it with a csv around 5000 rows but when i use around 500000 rows it becomes really slow and sometimes memory usage goes very high i already tried removing some columns but it didnt make much difference please explain what could be causing this and what should i check first

WriteWise returns: I have a Python script that works fine with a CSV of around 5,000 rows, but with around 500,000 rows it becomes very slow and memory usage sometimes gets very high. I already tried removing some columns, but it did not make much difference. Please explain what could be causing this and what I should check first.

Core rule: clarify and organize, never reinterpret.

  • It fixes grammar, removes filler and repetition, and orders scattered thoughts. Several distinct requests can become a short list; a simple request stays one paragraph.
  • It keeps every question, fact, number, date, constraint, decision, exclusion ("don't include pricing"), what you already tried, your uncertainty, references to files, and links.
  • Each part stays the same kind of sentence: a statement is never turned into a question or a belief (no added "right?" or "I believe"), and a question stays a question.
  • Technically significant tokens are kept exactly as typed, character for character: code, commands, environment variables, file paths, identifiers, package names, versions, config keys and shorthand ("k8s", "gh actions", kubectl rollout restart deploy/api-gateway, MAX_RETRIES=5, config.yaml). They are never expanded or re-cased.
  • Ordinary words and obvious brand names get normal proofreading ("antropic" → "Anthropic", "vercel" → "Vercel", "claude code" → "Claude Code", "power bi" → "Power BI"), but an abbreviation is never expanded and a term is never swapped for a more formal one.
  • When you correct yourself ("no wait, first..."), only the final version is kept.
  • It keeps instructions to the assistant separate from content the assistant should write for someone else.
  • It never answers or performs the request, never claims to have opened a link or file, and never adds a persona ("You are an expert..."), template sections, requirements, questions or advice. Long messages are reorganized, not summarized.

How it differs from the other modes: Grammar only fixes errors, and Rewrite or Paraphrase polish text for a human reader. Prompt mode clarifies a request for an AI reader and is stricter about not adding or dropping anything. It has no style options, extra-context field or variations. In Prompt mode, Try an example fills the editor with a fictional rough message (a different one each click).

How it works:

  • It uses its own instructions (backend/prompt_mode.py) with the same OpenAI client, model and error handling as the other modes.
  • Links are replaced by placeholders such as ⟦URL_1: docs.example.com⟧ before the text is sent and put back where the model placed them, so every URL comes back byte for byte. The model sees only each link's website, never the path or query.
  • If the model drops, repeats or invents a link placeholder, WriteWise asks once more with a correction. If that also fails, it returns an error instead of guessing where a link belongs.
  • Wrappers the model sometimes adds (code fences, "Refined prompt:" labels, surrounding quotes) are removed, and an empty result is reported as an error.

Limitations:

  • Intent preservation depends on the model. The golden tests (backend/tests/prompt_mode_cases.py, all fictional) catch clear failures, but outputs deserve a quick read before you send them.
  • Links are validated deterministically (each one present once, none invented), but whether the sentence around a link describes it correctly depends on the model, which sees only the website.
  • A message whose links are mishandled twice in a row returns an error; trying again usually works.

Secret Redaction (Prompt mode)

Before a Prompt mode request reaches OpenAI, the backend replaces obvious credentials with [REDACTED SECRET]:

  • OpenAI-style sk- keys, AWS access key IDs, GitHub, Google, Slack and Stripe keys, JWTs and Bearer tokens
  • Private key blocks
  • Assignments such as OPENAI_API_KEY=... or password: ... whose value contains a digit or is 16+ characters long

The UI shows a warning when something was removed. Only the number and kind of secrets are logged, never the values. In this version the other modes are unchanged and do not redact. This is a basic safety net, not a complete secret scanner: unusual formats and secrets written as plain words are not detected, and the generic assignment rule skips text inside URLs so query strings are never altered.


Design

  • Dark theme (#09090B background)
  • IBM Plex Sans for body text
  • JetBrains Mono for labels, badges, and code-style UI
  • Scrollable tab row for all 10 modes
  • Minimal, distraction-free single-page layout
  • Fade-in animations on output render
  • Animated score bars on evaluation display

Roadmap

  • Generation history with saved sessions
  • Word and character count on the textarea
  • Download output as .txt
  • LinkedIn message quick template
  • Thank-you note template
  • Export as .docx
  • Share output via link

License

MIT

About

An AI-powered writing assistant that helps users rewrite, humanize, summarize, paraphrase, and improve text with context-aware, natural-sounding outputs using modern AI.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages