Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MathGPT Clone

Static demo · Getting started · Features · Documentation

A fully functional clone of the AI math solver math-gpt.org, built from scratch with Node.js and vanilla JavaScript. Type a problem, paste LaTeX or upload a photo and get an instant step-by-step solution with rendered math, interactive graphs and consistent, repeatable output.

Not affiliated with math-gpt.org. Built for educational purposes and containing no code or proprietary prompts from the original site.

Live static demo: https://outblade.github.io/mathgpt-clone/ (GitHub Pages, from docs/)

GitHub Pages only serves static files, so the hosted demo is a browser-only build: text/LaTeX problems are solved client-side by a mathjs port of the local solver, with history kept in this browser via localStorage. Image upload, AI solving, and accounts (email/password + Google/Apple) need the Express backend and only run when you clone the repo and npm start (see below).

Features

Feature How it works
Text input Free-form problems: solve x^2 - 5x + 6 = 0, derivative of sin(x)*x^2, 2^10 / 8
LaTeX input Paste LaTeX directly: \frac{3}{4} + \frac{1}{6}, \sqrt{18}
Image upload Photograph a handwritten or printed problem; a vision model reads and solves it
AI solving Provider-agnostic backend: OpenRouter (Qwen), Groq, Gemini or OpenAI
Offline fallback A built-in mathjs engine solves equations, derivatives, simplification, evaluation and plotting with no API key
Output consistency Identical or near-identical submissions return identical output (see below)
Step-by-step solutions Titled steps with plain-language detail and KaTeX-rendered math
Interactive graphs Functions are sampled and drawn with Chart.js (hover for values)
Accounts Email/password (bcrypt) plus Sign in with Google and Sign in with Apple
Sessions HttpOnly cookie sessions, persisted server-side, 30-day lifetime
History Every problem a logged-in user solves is stored, reopenable and deletable
Error handling Invalid input, unsupported problems, API failures and rate limits return clear messages
Security Rate limiting, input sanitization, OAuth state/CSRF cookie, JWKS id_token verification, CSP, upload type/size limits

Quick start

Requirements: Node.js 18+ (no native build tools; all dependencies are pure JavaScript, which matters on ARM64).

cd mathgpt-clone
npm install
copy .env.example .env      # macOS/Linux: cp .env.example .env
npm start

Open http://localhost:3000. The app works immediately: without any API key the built-in solver answers text problems (equations, derivatives, simplification, arithmetic, plotting). Image solving and AI text solving need a provider key.

1. API selection (free, math-capable LLM)

The backend is provider-agnostic; every supported provider speaks the OpenAI-compatible /chat/completions format, so switching is a config change, not a code change. Candidates were compared on the three axes that matter here:

Provider Model (default) Accuracy on math Latency Consistency support Free tier
OpenRouter (default) qwen/qwen-2.5-72b-instruct + qwen-2.5-vl-72b-instruct (vision) Strong; Qwen2.5 is a top open math model Medium seed + temperature honored Yes (free-tier keys and :free models)
Groq qwen-2.5-32b + llama-3.2-90b-vision Good Very low (fastest) seed supported Yes
Gemini gemini-2.0-flash Good Low temperature only (no seed) Yes
OpenAI gpt-4o Strong Medium seed supported No (paid)

Default: OpenRouter with Qwen2.5 / Qwen2.5-VL. Rationale: Qwen2.5 is one of the strongest openly available models on mathematical reasoning, OpenRouter exposes both a text and a vision variant through one key, and it honors seed + temperature=0, which is essential for the consistency requirement. Groq is the pick if latency dominates; Gemini is the simplest key to obtain. Any of them is selected automatically from whichever key you set.

To choose a provider, set its key in .env (get one from the URL in .env.example). If several are set, AI_PROVIDER decides; otherwise the first configured wins in the order openrouter → groq → gemini → openai. Restart after editing .env.

2. Output consistency (top priority)

The requirement: identical or highly similar problems/images must consistently produce identical or near-identical output. This is met with two layers.

Layer A - a hard cache guarantee (independent of the LLM).

  • Every text problem is canonicalized (whitespace/case/punctuation normalized) and hashed. The same problem returns the exact stored solution.
  • Every image is hashed two ways: a content hash (SHA-256 of the bytes) for exact duplicates, and a 64-bit perceptual hash (average hash) for near-duplicates. A re-encoded, rescaled or lightly cropped copy lands within a small Hamming distance (threshold 5/64) and maps to the same stored solution.
  • The cache is namespaced by provider:model:promptVersion:mode, so changing the model or prompt never serves a stale answer. It persists to data/cache.json, so consistency survives restarts.

This means the same image always yields the same output, and highly similar images yield the same output, regardless of any run-to-run LLM variation.

Layer B - deterministic model settings (for the first, uncached solve).

  • temperature: 0, top_p: 1, a fixed seed (default 42), and a strict response_format: json_object.
  • A rigid JSON output schema and explicit canonicalization rules in the prompt (simplest form, ascending solution order, one operation per step, fixed step wording), which remove the main sources of wording/structure drift.

Verify it:

npm start                 # in one terminal
node scripts/consistency-test.js            # in another
# or point at a custom host / your own image:
BASE=http://localhost:3000 node scripts/consistency-test.js path/to/problem.png

The harness checks the hash/cache machinery (no key needed), then submits the same text problem 6x and the same image Nx over HTTP and asserts every response is identical. With no AI key it still proves the exact/near-match cache guarantee.

3. Prompt architecture

The full prompt lives in lib/prompt.js and is versioned (PROMPT_VERSION). It is an original, engineered approximation of how a step-by-step tutor product prompts its model - not a copy of math-gpt.org's proprietary prompt (which is not public). It is organized around consistency and quality:

  • Role - expert math tutor/solver over text, LaTeX or image input.
  • Chain-of-thought - instruction to reason and verify internally, but keep reasoning out of the output.
  • Strict output schema - a single JSON object (interpreted, answer, answerLatex, steps[], graphExpression, graphRange); rigid structure is the biggest anti-drift lever.
  • Determinism/canonicalization rules - simplest canonical final form; ascending order for multiple solutions; one operation per step in textbook order; one canonical method per problem type; identical wording for identical operations; no conversational filler.
  • Notation guidelines - valid KaTeX in latex fields (no $ delimiters), plain language in detail/answer.
  • Graphing rule - emit a single-variable function in mathjs syntax when a plot helps.
  • Fallback - non-math or unreadable input returns a brief explanation and no steps.

The user-prompt structure is fixed in the same file (buildTextMessages, buildImageMessages) so the message sent to the model is byte-stable for a given input.

4. OAuth setup (Google and Apple)

Both are standard OpenID Connect authorization-code flows. The server verifies the provider's signed id_token against its published JWKS (via Node's native crypto - no extra dependency) and reads the email/name/subject. A CSRF state value is stored in an HttpOnly cookie and checked on callback. Accounts created via OAuth have no local password; if the email already exists, the identity is linked.

Google (https://console.cloud.google.com/apis/credentials): create an OAuth client ID (Web application), add redirect URI <BASE_URL>/api/auth/google/callback, put the client ID/secret in .env.

Apple (https://developer.apple.com/account/resources): create a Services ID and a "Sign in with Apple" key, download the .p8, register return URL <BASE_URL>/api/auth/apple/callback. Put the Services ID, Team ID, Key ID and the .p8 contents (newlines as \n) in .env. Apple posts the callback as a form (response_mode=form_post), which the server handles.

Buttons appear in the sign-in modal and are automatically disabled with a tooltip when a provider is not configured, so the app runs fine with no OAuth credentials. Discord is intentionally not included.

5. Static GitHub Pages build (docs/)

docs/ is a separate, self-contained browser-only build published to https://outblade.github.io/mathgpt-clone/ via Pages (source: master branch, /docs folder). It is not generated from public/ automatically; it's a manually maintained port:

  • docs/js/solver.js - the same logic as lib/solver.js, adapted to use a global math (mathjs loaded from jsdelivr's lib/browser/math.js UMD bundle) instead of require("mathjs"), exposing window.MathSolver instead of module.exports.
  • docs/js/app.js - a trimmed frontend: no fetch("/api/...") calls (there is no backend), problems are solved synchronously via window.MathSolver, history is kept in localStorage (mathgpt_demo_history, no login), and image upload / accounts show an explanatory message pointing back to this repo instead of attempting a network call.
  • docs/index.html / docs/css/style.css - same look as public/, with a top banner disclosing the static-demo scope and a "View on GitHub" link. Asset paths are relative (css/style.css, not /css/style.css), which matters because project Pages sites are served from a sub-path (/mathgpt-clone/), not domain root.

If you change public/ in a way that should also apply to the demo, port the change into docs/ by hand and push to master; Pages rebuilds automatically (check status with gh api repos/OutBlade/mathgpt-clone/pages/builds/latest). Preview it locally with the dependency-free static server: node scripts/static-server.js --port=5000.

Project structure

mathgpt-clone/
  server.js                Express app: routes, sessions, OAuth, rate limits, security headers
  lib/
    prompt.js              Versioned system + user prompt architecture
    ai.js                  Provider-agnostic AI backend (OpenRouter/Qwen, Groq, Gemini, OpenAI)
    solver.js              Local math engine (mathjs) fallback
    imagehash.js           SHA-256 content hash + perceptual (average) hash + text key
    cache.js               Consistency cache (exact + perceptual near-match), persisted
    oauth.js               Google/Apple OAuth: auth URLs, code exchange, JWKS id_token verify
    store.js               JSON persistence: users (local + OAuth), sessions, history
    openai.js              Backward-compat shim -> ai.js
  public/                  Full app frontend (index.html, css/style.css, js/app.js) - served by server.js
  docs/                    Static GitHub Pages demo (browser-only port, see section 5 above)
  scripts/
    consistency-test.js    Consistency test harness (unit + HTTP)
    static-server.js        Dependency-free local preview server for docs/
  data/                    Created at runtime: users/sessions/history/cache JSON
  .env.example             Environment template

API reference

Method Path Body Notes
POST /api/solve { "problem": "..." } Text/LaTeX; returns cached flag
POST /api/solve-image multipart: image, note Returns cached + match (exact/perceptual)
POST /api/auth/register { name, email, password } Password min 8 chars
POST /api/auth/login { email, password } Sets session cookie
POST /api/auth/logout - Clears session
GET /api/auth/me - Current user or null
GET /api/auth/google - Redirects to Google consent
GET /api/auth/google/callback ?code&state Verifies, signs in, redirects to /
GET /api/auth/apple - Redirects to Apple consent
POST /api/auth/apple/callback form_post Verifies, signs in, redirects to /
GET /api/history - Latest 50 entries (auth)
GET /api/history/:id - Full stored solution (auth)
DELETE /api/history/:id - Removes an entry (auth)
GET /api/config - OAuth + AI availability for the frontend
GET /api/health - { ok, aiConfigured, provider, oauth }

Feature mapping to the original site

math-gpt.org This clone
Hero with problem input box Centered hero, input card with image button and solve button
Example problems Clickable example chips under the input
AI step-by-step answers Steps with title, plain-language detail and KaTeX math per step
Graphing calculator Automatic Chart.js plot for single-variable functions
Photo solving Image upload to a vision model
Sign in with Google / Apple Full OIDC flows with JWKS verification (Discord omitted by request)
Account and saved history Register/login, per-user history drawer with reopen and delete

Deployment notes

  • Behind a reverse proxy, serve over HTTPS, add Secure to the session cookie in server.js, and set app.set("trust proxy", 1) so rate limits and OAuth redirects use the real client host. Set BASE_URL to the public HTTPS origin and register that redirect URI with Google/Apple.
  • The JSON store and cache are single-instance; for horizontal scaling, swap lib/store.js and lib/cache.js for a database/Redis implementation exposing the same functions.

Troubleshooting

  • "Image solving requires an AI backend": set a provider key in .env and restart (an already-cached identical image still works without a key).
  • "The API key was rejected": wrong key or no quota; check the provider dashboard.
  • Google/Apple button disabled: that provider is not configured; see OAuth setup.
  • Math or graphs not rendering: the page loads KaTeX and Chart.js from cdn.jsdelivr.net; check the network or vendor them into public/.
  • Port already in use: change PORT in .env or run node server.js --port=3001.

About

Full-stack AI math solver clone: text/LaTeX/image input, step-by-step solutions, Google/Apple sign-in, consistent output via image hashing

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages