Skip to main content

Automatic repair for failing Python code, powered by any LLM.

Project description

self-heal

CI PyPI Python License: MIT

Automatic repair for failing Python code, powered by any LLM.

self-heal catches failures, proposes an LLM-guided fix with memory of prior attempts, verifies it, and retries. Works with Claude, OpenAI, Gemini, and 100+ other providers. Sync and async. One decorator.

from self_heal import repair

def test_dollars(fn): assert fn("$12.99") == 12.99
def test_rupees(fn):  assert fn("₹1,299") == 1299.0
def test_euros(fn):   assert fn("€5,49") == 5.49

@repair(tests=[test_dollars, test_rupees, test_euros])
def extract_price(text: str) -> float:
    # Naive: only handles "$X.YY" with no commas
    return float(text.replace("$", ""))

extract_price("$12.99")   # triggers repair loop until ALL tests pass

Benchmark

On 10 small Python tasks with plausible bugs (price parsing, palindrome, flatten, roman numerals, camelCase-to-snake_case, fizzbuzz, sum of digits, ...), each task repaired against a hand-written test suite:

Strategy Tasks passed Success rate LLM calls
Naive single-shot repair 7 / 10 70% 10
self-heal (multi-turn + memory) 10 / 10 100% 13

Gemini 2.5 Flash, max 3 attempts. Run it yourself: python benchmarks/run.py --proposer gemini --model gemini-2.5-flash. Full task list in benchmarks/tasks.py.

The 3 tasks where self-heal wins — extract_price, is_palindrome, count_vowels — all share a pattern: the first proposed fix handles one edge case but misses another. Memory of the failed attempt plus test feedback lets the second proposal cover both.

Install

self-heal ships with a Protocol and several optional adapters. Install the adapter(s) you want:

pip install 'self-heal-llm[claude]'    # Anthropic Claude (default)
pip install 'self-heal-llm[openai]'    # OpenAI + OpenAI-compatible endpoints
pip install 'self-heal-llm[gemini]'    # Google Gemini
pip install 'self-heal-llm[litellm]'   # 100+ providers via LiteLLM
pip install 'self-heal-llm[all]'       # everything

PyPI distribution name is self-heal-llm (the short name self-heal was blocked by PyPI's similarity check with an unrelated package). The Python import stays from self_heal import ....

Provider support

Adapter Covers
ClaudeProposer Anthropic Claude (native SDK)
OpenAIProposer OpenAI + any OpenAI-compatible endpoint (OpenRouter, Together, Groq, Fireworks, Anyscale, Perplexity, xAI, DeepSeek, Azure, Ollama, LM Studio, vLLM, llama.cpp server, ...)
GeminiProposer Google Gemini (native SDK)
LiteLLMProposer 100+ providers via LiteLLM (Bedrock, Vertex, Cohere, Mistral, ...)

Features

Multi-turn repair with memory

Every proposal sees the history of prior failed attempts so the LLM can't repeat the same mistake. This is the single biggest quality win over naive retry.

Verifiers — verify=callable

Catch bad return values, not just exceptions:

@repair(verify=lambda v: isinstance(v, float) and v > 0)
def extract_price(text): ...

If the predicate returns False or raises, self-heal treats it as a failure and repairs.

Test-driven repair — tests=[...]

Give self-heal a test suite; it repairs until every test passes:

def test_empty(fn):  assert fn("") is None
def test_dollar(fn): assert fn("$12.99") == 12.99

@repair(tests=[test_empty, test_dollar])
def extract_price(text): ...

Async-native

The decorator auto-detects async def and awaits correctly; the LLM call runs in a thread pool so your event loop stays free.

@repair()
async def fetch_and_parse(url: str) -> dict: ...

Prompt customization — prompt_extra="..."

Append domain-specific instructions to every repair prompt. Useful for "always handle None inputs" or "use only the standard library."

Bring your own LLM

Implement the LLMProposer Protocol (def propose(self, system: str, user: str) -> str) and pass it in.

Why this exists

AI coding agents fail on a lot of real tasks. The industry's current answer is "retry and hope." That's not a strategy.

self-heal treats repair as a first-class primitive: diagnose the failure, propose a targeted fix with memory of prior attempts, verify, retry. A thin library you can wrap around any Python function or agent tool.

How it works

  1. Catch the exception (or verifier/test failure) and capture inputs, traceback, failure type.
  2. Classify the failure (exception, verifier, test, assertion, validation).
  3. Propose a repaired function via an LLM with a failure-aware prompt that includes the full history of prior failed proposals.
  4. Recompile the proposed function into the running process.
  5. Verify with user-provided verifier + tests.
  6. Retry with the same inputs until success or max_attempts exhausted.

API

from self_heal import repair

@repair(
    max_attempts=3,
    model="claude-sonnet-4-6",
    proposer=None,               # or ClaudeProposer / OpenAIProposer / ...
    verbose=False,
    on_failure="raise",          # or "return_none"
    verify=None,                 # Callable[[Any], bool] — raise or False → repair
    tests=None,                  # list[Callable[[Callable], Any]]
    prompt_extra=None,           # str — extra user instructions in every prompt
)
def my_fn(...): ...

my_fn.last_repair   # RepairResult with full attempt history
my_fn.repair_loop   # the underlying RepairLoop

For advanced use:

from self_heal import RepairLoop

loop = RepairLoop(max_attempts=5, verbose=True)
result = loop.run(my_fn, args=(...), verify=..., tests=[...])

# Async:
result = await loop.arun(my_async_fn, args=(...))

Using different providers

Claude (default):

@repair()
def my_fn(...): ...

OpenAI:

from self_heal.llm import OpenAIProposer

@repair(proposer=OpenAIProposer(model="gpt-5"))
def my_fn(...): ...

Gemini:

from self_heal.llm import GeminiProposer

@repair(proposer=GeminiProposer(model="gemini-2.5-pro"))
def my_fn(...): ...

Any OpenAI-compatible endpoint (OpenRouter, Groq, Ollama, ...):

from self_heal.llm import OpenAIProposer

# OpenRouter — hundreds of models through one key
OpenAIProposer(
    model="google/gemini-2.5-pro",
    base_url="https://openrouter.ai/api/v1",
)

# Local Ollama
OpenAIProposer(
    model="llama3.3",
    base_url="http://localhost:11434/v1",
    api_key="ollama",
)

LiteLLM catch-all (100+ providers):

from self_heal.llm import LiteLLMProposer

LiteLLMProposer(model="bedrock/anthropic.claude-3-5-sonnet")
LiteLLMProposer(model="vertex_ai/gemini-2.5-pro")
LiteLLMProposer(model="cohere/command-r-plus")

Agent framework integration

self-heal composes with any Python agent framework. Wrap the tool's underlying callable with @repair and register the result as usual. Examples in examples/:

Safety

self-heal executes LLM-generated code via exec() in the same process. Same trust boundary as any LLM-in-the-loop system: do not run against untrusted inputs without a sandbox. Sandboxed execution is on the roadmap.

Roadmap

  • v0.0.1: core repair loop + decorator + Claude backend
  • v0.0.2: OpenAI, Gemini, LiteLLM adapters — works with any LLM
  • v0.1.0: multi-turn memory, verifiers, test-driven repair, async, benchmark harness
  • v0.2: telemetry + per-provider success metrics + CLI
  • v0.3: repair persistence (learn from past fixes, replay them)
  • v0.4: sandboxed execution
  • v1.0: stable API + extended benchmark suite

Development

git clone https://github.com/Johin2/self-heal.git
cd self-heal
python -m venv .venv
.venv/Scripts/pip install -e ".[dev]"   # Windows
# .venv/bin/pip install -e ".[dev]"     # macOS/Linux
pytest
ruff check .

Run the benchmark locally:

python benchmarks/run.py --proposer claude       # uses ANTHROPIC_API_KEY
python benchmarks/run.py --proposer openai       # uses OPENAI_API_KEY
python benchmarks/run.py --proposer gemini       # uses GEMINI_API_KEY

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

self_heal_llm-0.1.0.tar.gz (25.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

self_heal_llm-0.1.0-py3-none-any.whl (20.1 kB view details)

Uploaded Python 3

File details

Details for the file self_heal_llm-0.1.0.tar.gz.

File metadata

  • Download URL: self_heal_llm-0.1.0.tar.gz
  • Upload date:
  • Size: 25.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for self_heal_llm-0.1.0.tar.gz
Algorithm Hash digest
SHA256 014a436bcabd196bacf52b11e98f584a47f590339f28129ef456eb6ecce118e2
MD5 86879818cabd959e8e5e93f936b3dda1
BLAKE2b-256 6fa77f55eaf6ff61b37b3049487b18a9df824ab03dc802ea53755df9194572c7

See more details on using hashes here.

Provenance

The following attestation bundles were made for self_heal_llm-0.1.0.tar.gz:

Publisher: publish.yml on Johin2/self-heal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file self_heal_llm-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: self_heal_llm-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 20.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for self_heal_llm-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d2e06d82787147278888ed37d8aaed6ceb515a282c6883536a90fee5ea63681c
MD5 2e083f6ab04b2ffefbd3af6c209f238d
BLAKE2b-256 271413ccb34b69dc4d3cc596991a25b55ee4e19fb1cdc85c37cb6c0b83f82145

See more details on using hashes here.

Provenance

The following attestation bundles were made for self_heal_llm-0.1.0-py3-none-any.whl:

Publisher: publish.yml on Johin2/self-heal

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page