Natural-language code transformation as a library: generate unified diffs with LLMs and apply them resiliently with smartapply. Formerly gptdiff.
Project description
Patchling
Formerly gptdiff. Same library, same API —
pip install patchling(thegptdiffpackage still resolves during the transition).
Natural-language code transformation, as a library. Hand Patchling a dict of files and a plain-English goal; get back a unified diff — and, via smartapply, the transformed files. It's a bounded primitive you embed inside your own software systems, not an open-ended coding agent.
from patchling import generate_diff, smartapply, build_environment
files = {"main.py": "def old_name():\n print('Need renaming')\n"}
diff = generate_diff(build_environment(files), "Rename old_name to new_name")
updated = smartapply(diff, files)
print(updated["main.py"])
Files in, files out. No filesystem access required, no agent harness. The hard part — applying an LLM-generated diff that git apply would reject — is what smartapply solves: per-file, AI-assisted patch resolution that survives fuzzy hunks, renames, new files, and deletions.
📚 Full documentation at 255bits.github.io/patchling-py
Prefer the browser? patchling is a JavaScript port of
generateDiff + smartapply — try the live demos →.
The patchling family — patchling (you are here) · patchling (browser-first JS port) · patchling-examples (live browser demos)
The Patchling family
The same primitive exists for every runtime, and it powers a real product:
| Project | What it is |
|---|---|
| patchling (this repo) | Python library + CLI tools — PyPI |
| patchling | Zero-dependency ESM port for browser and Node — generateDiff + smartapply on in-memory file maps |
| nanoodle.com | Visual AI workflow editor built on patchling — no server, no signup, bring your own key. See the primitive working in production |
| Live demos | Browser examples: LLM-edited games, 3D scenes, stream overlays, AI characters |
Building for the browser? Start with patchling. Building a Python backend, pipeline, or your own agent? You're in the right repo.
Quick Start
1. Install
pip install patchling
2. Set your API key
Works with any OpenAI-compatible endpoint. Get a key at nano-gpt.com/api, or point GPTDIFF_LLM_BASE_URL at your own provider.
# Linux/macOS
export GPTDIFF_LLM_API_KEY='your-api-key'
# Windows
set GPTDIFF_LLM_API_KEY=your-api-key
3. Transform files in your code
from patchling import generate_diff, smartapply, build_environment
files = {
"models.py": "class User:\n name = CharField()",
"tests/test_models.py": "def test_user():\n User(name='Test').save()",
}
diff = generate_diff(
build_environment(files),
"Rename the 'name' field to 'username' across all layers",
)
files = smartapply(diff, files)
The diff is plain unified-diff text — log it, review it, gate it behind approval, or apply it immediately. That's the point: your system stays in control of what changes and when.
See examples/usage_example.py for a runnable version.
Core API
generate_diff(environment: str, goal: str, model: str = ...) -> str— generates a unified diff implementing the goal.modeldefaults to theGPTDIFF_MODELenv var.smartapply(diff_text: str, files: dict[str, str], model: str = ...) -> dict[str, str]— applies a diff with AI-powered conflict resolution. Handles new files, deletions, and hunks that standard patching rejects. Returns a new dict; input is not mutated.build_environment(files: dict[str, str]) -> str— serializes a files dict into the environment stringgenerate_diffexpects.load_project_files(path, cwd) -> dict/save_files(files, base_dir)— optional filesystem helpers for when you do want to read/write a real project (respects.gitignoreand.gptignore).
Full signatures, error handling, and edge cases: API Reference.
Pipeline example — sequential transformations over an in-memory codebase:
from patchling import generate_diff, smartapply, build_environment
files = load_your_codebase() # dict of {path: content}
for task in [
"Add python type annotations",
"Convert string formatting to f-strings",
"Update deprecated API calls",
]:
files = smartapply(generate_diff(build_environment(files), task), files)
This is the pattern nanoodle.com runs in the browser (via patchling): each workflow node is a bounded diff→apply step over an in-memory file map, and the app never touches a server.
Choosing a Model
Reasoning models produce more accurate diffs for complex changes; fast models win for applying diffs and simple edits.
| Model | Best for | Notes |
|---|---|---|
gemini-3-pro-preview |
Generating diffs | Recommended default |
gpt-4o / claude-sonnet-4-20250514 |
Complex or context-sensitive changes | Slower, more careful |
gpt5-mini |
Applying diffs (smartapply) |
Fast and reliable — best GPTDIFF_SMARTAPPLY_MODEL |
gemini-2.0-flash |
Simple text changes | Most cost-effective |
export GPTDIFF_MODEL='gemini-3-pro-preview'
export GPTDIFF_SMARTAPPLY_MODEL='gpt5-mini'
Environment variables
| Variable | Purpose | Default |
|---|---|---|
GPTDIFF_LLM_API_KEY |
API key (required) | — |
GPTDIFF_MODEL |
Model for diff generation | gemini-3-pro-preview |
GPTDIFF_SMARTAPPLY_MODEL |
Model for applying diffs | GPTDIFF_MODEL |
GPTDIFF_LLM_BASE_URL |
OpenAI-compatible endpoint | https://nano-gpt.com/api/v1/ |
Command-Line Tools
The library also ships two CLIs for working on a real project directory.
The former command names
gptdiffandgptpatchstill work as aliases forpatchlingandpatchling-apply, so existing scripts don't break.
patchling
Describe a change; Patchling scans the project (respecting .gitignore/.gptignore), generates a diff, and optionally applies it:
| Command | What it does |
|---|---|
patchling "prompt" |
Writes prompt.txt only — preview what would be sent |
patchling "prompt" --call |
Generates the diff into diff.patch for review |
patchling "prompt" --apply |
Generates and applies in one step |
cd your-project
patchling "Add type hints to all functions" --apply
# Target specific paths
patchling "Add logging" src/api/ src/utils/helpers.py
Useful flags: --model, --temperature, --prepend <file> (custom instructions), --image <path> (visual context), --nobeep. Full list: CLI Reference.
Because changes arrive as diffs, the CLI is git-native: review with git diff, keep with git add -p, discard with git checkout ..
patchling-apply
Applies an existing unified diff to a project — standard patch logic first, smartapply fallback when that fails:
patchling-apply path/to/diff.patch
patchling-apply --diff "<diff text>"
Options: --project-dir, --model, --max_tokens, --nobeep. Details: patchling-apply docs.
Agent loops
Because each invocation is bounded (one goal → one diff), the CLI composes into loops:
while true; do
patchling "Add missing test cases for edge conditions" --apply
git add -A && git commit -m "Auto-improvement $(date +%H:%M)" 2>/dev/null
sleep 30
done
One overnight test-coverage loop took a project from 18 to 127 test cases. Recipes and guardrails: Automation Guide.
Testing
pip install -e .[test]
pytest tests/
Documentation
Docs live at 255bits.github.io/patchling-py. To preview locally:
pip install .[docs]
mkdocs serve
Related projects
- patchling — the browser/Node port
- nanoodle.com — visual AI workflow editor built on patchling
- patchling live demos
- AI Agent Toolbox — powers Patchling's tool-call parsing across models
MIT licensed. Built by 255labs.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file patchling-0.8.1.tar.gz.
File metadata
- Download URL: patchling-0.8.1.tar.gz
- Upload date:
- Size: 36.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce7c2e565330534e3294a4f9e0a7fdef24ca850674eb6fdf0b58bb00dd87818c
|
|
| MD5 |
c06a1570f098e239d1baa687f80f198b
|
|
| BLAKE2b-256 |
9c88ee05752c53616cd7c60e4b990efb7b013d0f80c33c8664d40c9c8a396d6f
|
File details
Details for the file patchling-0.8.1-py3-none-any.whl.
File metadata
- Download URL: patchling-0.8.1-py3-none-any.whl
- Upload date:
- Size: 25.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d5bdc5b20d562f6084e2bff996848955258ce5f418dc9f74bdeaf8758afc650a
|
|
| MD5 |
1564fac4a32c5e046cba7ba82f4602aa
|
|
| BLAKE2b-256 |
1d05f1f949a4833e4f7140011aa21ac7562e85de46ca11bbd3bad2171f5690ee
|