Skip to content

Repository files navigation

LeetCode Solver

Solve LeetCode problems hands-off with a local LLM driving a real Chrome.

A small web console: paste a problem URL (or hit Auto) and it opens the problem, writes a solution, runs the sample tests, repairs itself if they fail, submits, reads the verdict, and saves a Markdown record -- with no input from you.

navigate -> pick language -> read problem -> [LLM writes code] -> type into editor
        -> Run samples -> [LLM repairs on failure] -> Submit -> read verdict -> save

Everything runs on your machine. No API keys, no cloud, no Node, no browser extension.

The console, LM Studio and the Chrome window it drives, all running together

The whole stack: the console on the left, LM Studio serving the model at the bottom, and the Chrome window the solver is driving on the right -- mid-submission, samples already passed.


Table of contents


Why this works with small local models

Most "AI solves LeetCode" attempts hand the whole job to an agent: pick tools, emit tool-call JSON, hold a plan across dozens of rounds. Local models fall over doing that -- they malform tool calls or lose the plan and loop.

Here, code owns the control flow. The model is a worker with exactly two jobs:

Slot Input Output
Solve problem text + the editor's exact stub a solution
Repair the failing testcase (input / expected / actual) corrected solution

It is never asked to choose a tool or emit tool-call JSON -- only to return Python. That deletes both classic failure modes, which makes the whole thing model-agnostic: anything that can write a correct function works.


Requirements

Software

Python 3.10+ Runs the harness itself
Google Chrome (or Chromium) Driven over the DevTools Protocol -- it must be a real Chrome you can log into
LM Studio Serves the model. Any OpenAI-compatible server works (Ollama, vLLM, llama.cpp), but the context-length control is LM Studio-only
A LeetCode account You log in yourself, once, in the browser window the solver drives

The model

Any instruction-following model that writes decent Python. That's the whole requirement -- the harness sends plain text and asks for plain code back.

Things you might expect to need but don't:

  • Tool use / function calling -- never used. Code drives the browser; the model only writes code. That's the core design decision, so a model with no tool training is fine.
  • Vision -- never used. The problem description is scraped as text and sent as text. A problem with a diagram in it just loses the diagram, for a vision model and a text model alike.
  • Reasoning / thinking -- optional, and often counterproductive here. See Thinking & context length. A 7B coding model with thinking off will usually beat a big thinker that spends its whole token budget deliberating.

What actually matters, in order: Python quality -> speed -> context length. A coding-tuned model in the 7B-30B range is the sweet spot; larger helps on Hard problems and costs you wall-clock time.

Hardware

Whatever your model needs, plus headroom for the context you allocate. A 27B Q4 model at 8K context wants roughly 16 GB of VRAM/unified memory; the same model at 64K wants noticeably more, because KV cache scales with context. If loads start failing, lower the context length first.


Setup

1. Install the harness

git clone <your-repo-url> leetcode-solver
cd leetcode-solver
python -m venv .venv

Activate it -- Windows .venv\Scripts\activate, macOS/Linux source .venv/bin/activate -- then:

pip install -r requirements.txt

2. Start your model server

In LM Studio: load a model, open the Developer tab, and Start Server (default port 1234). The solver talks to http://127.0.0.1:1234/v1 unless you tell it otherwise.

Verify it from a shell:

curl http://127.0.0.1:1234/v1/models

3. Start the debug Chrome and log in

The solver needs a Chrome started with --remote-debugging-port. It uses a dedicated profile (chrome-debug-profile/, git-ignored) so your everyday browser and its cookies are untouched.

  • Windows: scripts\start-chrome-debug.bat
  • macOS/Linux: ./scripts/start-chrome-debug.sh
  • ...or just click Start debug Chrome in the console once it's running.

Log into LeetCode in that window. The solver never types credentials -- it drives the session you established. You only do this once; the profile persists.

4. Run the console

python -m leetcode_solver

...or run.bat / ./run.sh. Then open http://127.0.0.1:8765/.

Port already in use? Set LEETCODE_PORT=... in .env or the environment and restart.


Using the console

Pick a model from the dropdown (it lists whatever your server advertises), then either:

  • Solve queue -- paste one or more https://leetcode.com/problems/<slug>/ URLs, one per line. They're solved in order.
  • Auto-solve down the list -- walks the entire problem list in ID order, using LeetCode's own GraphQL through your logged-in session. Start anywhere with from #N, tick skip solved, and Stop ends it after the current problem. Premium-locked and non-Python problems are skipped automatically.

Two toggles worth knowing:

  • Max attempts -- how many solve/repair rounds before giving up on a problem (1-6).
  • Iterative repair -- off by default. On, every retry sees the full history of failed attempts and is told not to repeat an approach that already failed. Costs context; helps on Hard problems.

The log pane shows every step with timings, the verdict, runtime/memory/beats%, and where the record was saved:

A completed solve in the console log, from navigate through to Accepted and saved

Every line is a real state transition, not a status guess -- run-result is the sample run, verdict is the actual submission result read back off the page.


Thinking & context length

The Thinking & context button exposes two knobs that look similar and work completely differently.

The Thinking and context panel, showing the effort dropdown, max tokens, and the context length control

Thinking -- per request

Sent as the standard reasoning_effort field on every completion. LM Studio accepts none / minimal / low / medium / high / xhigh and maps those onto whatever the loaded model actually supports -- a plain on/off thinker reads none as off, while a graded one like gpt-oss uses low/medium/high directly. The panel shows each model's real capability underneath.

Leave it on server default to send nothing at all. A server that doesn't understand the field gets one automatic retry without it, so this stays safe on any backend.

WARNING: Not every model obeys it. Some ignore the API-level setting and think anyway -- the giveaway is a slow first attempt or code that arrives truncated. When that happens, turn thinking off in LM Studio itself (the model's settings -> Reasoning, or the chat sidebar's thinking toggle) and reload the model. That setting always wins over the request field.

Thinking tokens come out of the same budget as the answer. If you raise the effort, raise Max tokens too, or a long deliberation will cut the solution off mid-function. The harness detects that specific failure and tells you, rather than feeding you a truncated function.

For this harness, thinking off is a reasonable default: the model gets a complete problem statement and an exact stub, which is the easy case, and repair rounds already provide the "think again" loop.

Context length -- per model load

Context is allocated when a model enters memory, so there is no per-request setting. Changing it means reloading the model, which is exactly what Apply & reload does (via LM Studio's native /api/v1/models/load). The status line shows loaded at 8192 - max 262144 so you know where you stand.

Other backends don't expose this, and the control disables itself with an explanation -- set context wherever that server is started instead.

How much do you need? A solve prompt is the problem description (capped at 12,000 characters) plus the stub, so 8K is enough for a single-shot solve. Turn it up if you use Iterative repair on long problems, since every prior attempt is replayed into the prompt.


How it works

  1. Navigate to the problem and wait for the Monaco editor to exist.
  2. Pick a language -- prefer Python3; if unavailable, switch to Pandas; if neither, mark the job skipped and move on. (Monaco reports python3 for both Python3 and Pandas problems, so the language-button text is what actually distinguishes them.)
  3. Scrape the title, number, description and the exact function stub.
  4. Solve -- the model returns code; fences and stray prose are stripped.
  5. Inject the code into the editor.
  6. Run the sample tests. The verdict is read from [data-e2e-locator="console-result"] -- scoped, because a solved problem already displays "Accepted" in its status badge and a page-wide scan would false-positive.
  7. Repair on failure using the actual failing case, then retry (bounded by max attempts).
  8. Submit only after the samples pass; read the verdict + runtime/memory/beats%.
  9. Save a Markdown record under solutions/ and add it to a running INDEX.md.

Anything that isn't an explicit Accepted on the samples goes back for repair rather than being submitted -- a submission is never spent on code that hasn't already passed.

A Hard problem open in LeetCode while LM Studio generates tokens The submission result: Accepted, 2099 of 2099 testcases passed, 2ms runtime beating 51.24%
Step 4 -- the model writing a solution for #4 Median of Two Sorted Arrays (Hard). LM Studio shows the token counter ticking. Step 8 -- the verdict, read straight off the page: 2099/2099 testcases, 2 ms, beats 51.24%. That's what lands in the saved record.

Configuration

Everything is environment variables (or a .env -- see .env.example). All optional:

Variable Default Meaning
LEETCODE_LLM_BASE http://127.0.0.1:1234/v1 OpenAI-compatible base URL
LEETCODE_LLM_API_KEY (none) Only if your server needs one
LEETCODE_LLM_TIMEOUT 180 Seconds; covers a cold model load
LEETCODE_LLM_TEMPERATURE 0.2 Low = deterministic code
LEETCODE_LLM_MAX_TOKENS 2048 Per completion
LEETCODE_LLM_REASONING_EFFORT (none) none...xhigh; blank leaves the server's setting alone
LEETCODE_CDP_URL http://127.0.0.1:9222 Chrome DevTools endpoint
LEETCODE_CHROME_EXE (auto-detect) Chrome path if detection fails
LEETCODE_CHROME_PROFILE ./chrome-debug-profile Dedicated browser profile
LEETCODE_SOLUTIONS_DIR ./solutions Where records are written
LEETCODE_HOST / LEETCODE_PORT 127.0.0.1 / 8765 Console bind address

API

The console is just a client for these:

Method / Path Purpose
POST /api/solve {url, model?, max_attempts?, iterative?, reasoning_effort?, max_tokens?} -> {job_id, model}
GET /api/status/{job_id} {status, events[], result, error} -- poll ~1.5 s
GET /api/models Models advertised by your LLM server, plus the thinking levels it accepts
GET /api/llm/model?model= One model's context length (loaded vs max) and thinking capability
POST /api/llm/context {model, context_length} -> reloads that model at a new context length
GET /api/problems?offset&limit The LeetCode problem list (id order, with solved/Premium flags)
GET /api/chrome/status Is the debug Chrome reachable?
POST /api/chrome/launch Launch the debug Chrome

status is running / completed / failed / skipped.


Troubleshooting

"the code editor never loaded -- is this tab logged into LeetCode?" The debug Chrome isn't logged in, or the tab it attached to isn't a problem page. Open leetcode.com in that window and sign in.

"x not running -- click Start" and Start does nothing Chrome wasn't found. Set LEETCODE_CHROME_EXE to your Chrome binary. Note that a Chrome already running under your normal profile can swallow the launch -- close it, or rely on the dedicated profile that the scripts use.

"cannot reach the model server" LM Studio's server isn't started (Developer tab -> Start Server), or it's on a different port than LEETCODE_LLM_BASE.

The model returns prose instead of code It's ignoring the system prompt. Lower the temperature, or use a coding-tuned model. The harness strips fences and leading prose, but it can't rescue an essay.

Solutions arrive truncated / the log says the model spent its budget thinking Thinking is on and eating max_tokens. Lower the effort, raise Max tokens, or turn thinking off in LM Studio directly (see the warning above).

"a solve is already running" (HTTP 409) One solve at a time by design -- the browser is a shared resource. Wait, or stop Auto mode.

Selectors suddenly break after a LeetCode redesign Every selector lives in one file: leetcode_solver/leetcode.py. That's the only place to look.


Notes & limits

  • Keep it on loopback. The console drives your logged-in browser and spends local compute; there's no authentication by design.
  • It really submits. Every accepted solve is a real submission on your account. Auto mode over the whole list is thousands of problems -- that's what from #N and Stop are for.
  • One solve at a time -- a second request gets 409.
  • Language support is Python3 and Pandas. SQL/Bash/etc. are skipped, not failed.
  • Changing context length reloads the model, which is blocked while a solve is running.
  • LeetCode's DOM can change. See leetcode_solver/leetcode.py.

Project layout

leetcode_solver/
|-- config.py     env-driven settings (cross-platform)
|-- cdp.py        minimal Chrome DevTools client + Chrome launcher
|-- llm.py        OpenAI-compatible chat client + LM Studio model/context control
|-- leetcode.py   ALL page automation / selectors (change here if LeetCode moves)
|-- solver.py     the deterministic state machine + job store
|-- library.py    saves solution records + INDEX.md
|-- app.py        FastAPI routes
`-- static/       the console (index.html + app.js)
scripts/          start-chrome-debug (.bat / .sh)
solutions/        your saved records + INDEX.md
docs/
|-- ARCHITECTURE.md   internals: the model layer, the CDP layer, extending to other sites
`-- images/           README screenshots

For how any of it actually works under the hood -- the two-layer model API, the raw CDP client, the comparison with MCP browser agents, and what a "recipe" for another website would look like -- see docs/ARCHITECTURE.md.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages