self-heal-llm 0.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: Test on Python ${{ matrix.python-version }}
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
17
+
18
+ steps:
19
+ - name: Checkout
20
+ uses: actions/checkout@v4
21
+
22
+ - name: Set up Python ${{ matrix.python-version }}
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+ cache: pip
27
+
28
+ - name: Install package
29
+ run: |
30
+ python -m pip install --upgrade pip
31
+ pip install -e ".[dev]"
32
+
33
+ - name: Lint (ruff)
34
+ run: ruff check .
35
+
36
+ - name: Run tests
37
+ run: pytest -v
@@ -0,0 +1,40 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ name: Build and publish to PyPI
10
+ runs-on: ubuntu-latest
11
+
12
+ # OIDC trusted publishing — no API tokens required.
13
+ # Configure a pending publisher at:
14
+ # https://pypi.org/manage/account/publishing/
15
+ # Project: self-heal
16
+ # Owner: Johin2
17
+ # Repo: self-heal
18
+ # Workflow: publish.yml
19
+ permissions:
20
+ id-token: write
21
+
22
+ steps:
23
+ - name: Checkout
24
+ uses: actions/checkout@v4
25
+
26
+ - name: Set up Python
27
+ uses: actions/setup-python@v5
28
+ with:
29
+ python-version: "3.12"
30
+
31
+ - name: Install build tooling
32
+ run: |
33
+ python -m pip install --upgrade pip
34
+ python -m pip install build
35
+
36
+ - name: Build distribution
37
+ run: python -m build
38
+
39
+ - name: Publish to PyPI
40
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,79 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Distribution / packaging
7
+ .Python
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib/
15
+ lib64/
16
+ parts/
17
+ sdist/
18
+ var/
19
+ wheels/
20
+ share/python-wheels/
21
+ *.egg-info/
22
+ .installed.cfg
23
+ *.egg
24
+ MANIFEST
25
+
26
+ # PyInstaller
27
+ *.manifest
28
+ *.spec
29
+
30
+ # Installer logs
31
+ pip-log.txt
32
+ pip-delete-this-directory.txt
33
+
34
+ # Unit test / coverage reports
35
+ htmlcov/
36
+ .tox/
37
+ .nox/
38
+ .coverage
39
+ .coverage.*
40
+ .cache
41
+ nosetests.xml
42
+ coverage.xml
43
+ *.cover
44
+ *.py,cover
45
+ .hypothesis/
46
+ .pytest_cache/
47
+ cover/
48
+
49
+ # Environments
50
+ .env
51
+ .env.*
52
+ .venv
53
+ env/
54
+ venv/
55
+ ENV/
56
+
57
+ # IDE
58
+ .vscode/
59
+ .idea/
60
+ *.swp
61
+ *.swo
62
+ *~
63
+ .DS_Store
64
+ Thumbs.db
65
+
66
+ # uv
67
+ .uv/
68
+ uv.lock
69
+
70
+ # Ruff
71
+ .ruff_cache/
72
+
73
+ # Jupyter
74
+ .ipynb_checkpoints/
75
+
76
+ # Type checkers
77
+ .mypy_cache/
78
+ .pyright/
79
+ .pytype/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Johin Johny
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,257 @@
1
+ Metadata-Version: 2.4
2
+ Name: self-heal-llm
3
+ Version: 0.0.2
4
+ Summary: Automatic repair for failing Python code, powered by any LLM.
5
+ Project-URL: Homepage, https://github.com/Johin2/self-heal
6
+ Project-URL: Repository, https://github.com/Johin2/self-heal
7
+ Project-URL: Issues, https://github.com/Johin2/self-heal/issues
8
+ Author-email: Johin Johny <anandowais@gmail.com>
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 Johin Johny
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: agent,ai,claude,code-repair,gemini,litellm,llm,openai,repair,self-healing
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: OS Independent
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
41
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
42
+ Classifier: Topic :: Software Development :: Quality Assurance
43
+ Requires-Python: >=3.10
44
+ Requires-Dist: pydantic>=2.0.0
45
+ Provides-Extra: all
46
+ Requires-Dist: anthropic>=0.40.0; extra == 'all'
47
+ Requires-Dist: google-genai>=0.3.0; extra == 'all'
48
+ Requires-Dist: litellm>=1.0.0; extra == 'all'
49
+ Requires-Dist: openai>=1.40.0; extra == 'all'
50
+ Provides-Extra: claude
51
+ Requires-Dist: anthropic>=0.40.0; extra == 'claude'
52
+ Provides-Extra: dev
53
+ Requires-Dist: anthropic>=0.40.0; extra == 'dev'
54
+ Requires-Dist: google-genai>=0.3.0; extra == 'dev'
55
+ Requires-Dist: litellm>=1.0.0; extra == 'dev'
56
+ Requires-Dist: openai>=1.40.0; extra == 'dev'
57
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
58
+ Requires-Dist: ruff>=0.6.0; extra == 'dev'
59
+ Provides-Extra: gemini
60
+ Requires-Dist: google-genai>=0.3.0; extra == 'gemini'
61
+ Provides-Extra: litellm
62
+ Requires-Dist: litellm>=1.0.0; extra == 'litellm'
63
+ Provides-Extra: openai
64
+ Requires-Dist: openai>=1.40.0; extra == 'openai'
65
+ Description-Content-Type: text/markdown
66
+
67
+ # self-heal
68
+
69
+ [![CI](https://github.com/Johin2/self-heal/actions/workflows/ci.yml/badge.svg)](https://github.com/Johin2/self-heal/actions/workflows/ci.yml)
70
+ [![PyPI](https://img.shields.io/pypi/v/self-heal-llm.svg)](https://pypi.org/project/self-heal-llm/)
71
+ [![Python](https://img.shields.io/pypi/pyversions/self-heal-llm.svg)](https://pypi.org/project/self-heal-llm/)
72
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
73
+
74
+ > Automatic repair for failing Python code, powered by any LLM.
75
+
76
+ When a function fails, `self-heal` catches the exception, analyzes it with an LLM, proposes a repaired version, and retries. One decorator. Works with Claude, OpenAI, Gemini, and 100+ other providers.
77
+
78
+ ```python
79
+ from self_heal import repair
80
+
81
+ @repair(max_attempts=3)
82
+ def extract_price(text: str) -> float:
83
+ # Naive: only handles "$X.YY"
84
+ return float(text.replace("$", ""))
85
+
86
+ print(extract_price("$12.99")) # 12.99
87
+ print(extract_price("₹1,299")) # 1299.0 (repaired)
88
+ print(extract_price("€5,49")) # 5.49 (repaired)
89
+ ```
90
+
91
+ ## Install
92
+
93
+ `self-heal` ships with a Protocol and several optional adapters. Install the adapter(s) you want:
94
+
95
+ ```bash
96
+ pip install 'self-heal-llm[claude]' # Anthropic Claude (default)
97
+ pip install 'self-heal-llm[openai]' # OpenAI + OpenAI-compatible endpoints
98
+ pip install 'self-heal-llm[gemini]' # Google Gemini
99
+ pip install 'self-heal-llm[litellm]' # 100+ providers via LiteLLM
100
+ pip install 'self-heal-llm[all]' # everything
101
+ ```
102
+
103
+ > 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 ...`.
104
+
105
+ ## Provider support
106
+
107
+ | Adapter | Covers |
108
+ |---|---|
109
+ | `ClaudeProposer` | Anthropic Claude (native SDK) |
110
+ | `OpenAIProposer` | OpenAI + **any OpenAI-compatible endpoint** (OpenRouter, Together, Groq, Fireworks, Anyscale, Perplexity, xAI, DeepSeek, Azure, Ollama, LM Studio, vLLM, llama.cpp server, ...) |
111
+ | `GeminiProposer` | Google Gemini (native SDK) |
112
+ | `LiteLLMProposer` | 100+ providers via LiteLLM (Bedrock, Vertex, Cohere, Mistral, ...) |
113
+
114
+ ## Using different providers
115
+
116
+ **Claude (default):**
117
+ ```python
118
+ from self_heal import repair
119
+
120
+ @repair() # uses ClaudeProposer under the hood
121
+ def my_fn(...): ...
122
+ ```
123
+
124
+ **OpenAI:**
125
+ ```python
126
+ from self_heal import repair
127
+ from self_heal.llm import OpenAIProposer
128
+
129
+ @repair(proposer=OpenAIProposer(model="gpt-5"))
130
+ def my_fn(...): ...
131
+ ```
132
+
133
+ **Gemini:**
134
+ ```python
135
+ from self_heal import repair
136
+ from self_heal.llm import GeminiProposer
137
+
138
+ @repair(proposer=GeminiProposer(model="gemini-2.5-pro"))
139
+ def my_fn(...): ...
140
+ ```
141
+
142
+ **Any OpenAI-compatible endpoint (OpenRouter, Groq, Ollama, ...):**
143
+ ```python
144
+ from self_heal.llm import OpenAIProposer
145
+
146
+ # OpenRouter — hundreds of models through one key
147
+ proposer = OpenAIProposer(
148
+ model="google/gemini-2.5-pro",
149
+ base_url="https://openrouter.ai/api/v1",
150
+ )
151
+
152
+ # Groq — fast inference
153
+ proposer = OpenAIProposer(
154
+ model="llama-3.3-70b-versatile",
155
+ base_url="https://api.groq.com/openai/v1",
156
+ )
157
+
158
+ # Local Ollama
159
+ proposer = OpenAIProposer(
160
+ model="llama3.3",
161
+ base_url="http://localhost:11434/v1",
162
+ api_key="ollama",
163
+ )
164
+ ```
165
+
166
+ **LiteLLM catch-all (100+ providers):**
167
+ ```python
168
+ from self_heal.llm import LiteLLMProposer
169
+
170
+ proposer = LiteLLMProposer(model="anthropic/claude-sonnet-4-6")
171
+ proposer = LiteLLMProposer(model="bedrock/anthropic.claude-3-5-sonnet")
172
+ proposer = LiteLLMProposer(model="vertex_ai/gemini-2.5-pro")
173
+ proposer = LiteLLMProposer(model="cohere/command-r-plus")
174
+ ```
175
+
176
+ ## Why this exists
177
+
178
+ AI coding agents fail on a lot of real tasks. The industry's current answer is "retry and hope." That's not a strategy.
179
+
180
+ `self-heal` treats repair as a first-class primitive: diagnose the failure, propose a targeted fix, verify, retry. A thin library you can wrap around any Python function or agent tool.
181
+
182
+ Built on ongoing code-repair research (RepairBench, NeurIPS 2026).
183
+
184
+ ## How it works
185
+
186
+ 1. **Catch** the exception and capture inputs, traceback, and failure type.
187
+ 2. **Classify** the failure (validation, exception, assertion).
188
+ 3. **Propose** a repaired function via LLM with a failure-aware prompt.
189
+ 4. **Recompile** the proposed function into the running process.
190
+ 5. **Retry** with the same inputs.
191
+
192
+ All within a single decorator boundary.
193
+
194
+ ## API
195
+
196
+ **Decorator:**
197
+ ```python
198
+ from self_heal import repair
199
+
200
+ @repair(max_attempts=3, model="claude-sonnet-4-6", verbose=True)
201
+ def my_tool(x): ...
202
+
203
+ my_tool(42)
204
+ my_tool.last_repair # -> RepairResult with full attempt history
205
+ ```
206
+
207
+ **Loop (for advanced use):**
208
+ ```python
209
+ from self_heal import RepairLoop
210
+
211
+ loop = RepairLoop(max_attempts=5, verbose=True)
212
+ result = loop.run(my_tool, args=(42,))
213
+ if result.succeeded:
214
+ print(result.final_value)
215
+ else:
216
+ print(result.attempts[-1].failure.traceback)
217
+ ```
218
+
219
+ **Custom proposer:**
220
+ ```python
221
+ from self_heal.llm import LLMProposer
222
+
223
+ class MyProposer:
224
+ def propose(self, system: str, user: str) -> str:
225
+ # ... your logic ...
226
+ return "def my_tool(x): return x * 2"
227
+ ```
228
+
229
+ ## Safety
230
+
231
+ `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.
232
+
233
+ ## Roadmap
234
+
235
+ - [x] v0.0.1: core repair loop + decorator + Claude backend
236
+ - [x] v0.0.2: OpenAI, Gemini, LiteLLM adapters — works with any LLM
237
+ - [ ] v0.1: user-provided verifiers (beyond exception-catching)
238
+ - [ ] v0.2: telemetry + before/after success metrics
239
+ - [ ] v0.3: async support
240
+ - [ ] v0.4: sandboxed execution
241
+ - [ ] v0.5: repair persistence (learn from past fixes)
242
+ - [ ] v1.0: NeurIPS 2026 paper co-release
243
+
244
+ ## Development
245
+
246
+ ```bash
247
+ git clone https://github.com/Johin2/self-heal.git
248
+ cd self-heal
249
+ python -m venv .venv
250
+ .venv/Scripts/pip install -e ".[dev]" # Windows
251
+ # .venv/bin/pip install -e ".[dev]" # macOS/Linux
252
+ pytest
253
+ ```
254
+
255
+ ## License
256
+
257
+ MIT
@@ -0,0 +1,191 @@
1
+ # self-heal
2
+
3
+ [![CI](https://github.com/Johin2/self-heal/actions/workflows/ci.yml/badge.svg)](https://github.com/Johin2/self-heal/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/self-heal-llm.svg)](https://pypi.org/project/self-heal-llm/)
5
+ [![Python](https://img.shields.io/pypi/pyversions/self-heal-llm.svg)](https://pypi.org/project/self-heal-llm/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ > Automatic repair for failing Python code, powered by any LLM.
9
+
10
+ When a function fails, `self-heal` catches the exception, analyzes it with an LLM, proposes a repaired version, and retries. One decorator. Works with Claude, OpenAI, Gemini, and 100+ other providers.
11
+
12
+ ```python
13
+ from self_heal import repair
14
+
15
+ @repair(max_attempts=3)
16
+ def extract_price(text: str) -> float:
17
+ # Naive: only handles "$X.YY"
18
+ return float(text.replace("$", ""))
19
+
20
+ print(extract_price("$12.99")) # 12.99
21
+ print(extract_price("₹1,299")) # 1299.0 (repaired)
22
+ print(extract_price("€5,49")) # 5.49 (repaired)
23
+ ```
24
+
25
+ ## Install
26
+
27
+ `self-heal` ships with a Protocol and several optional adapters. Install the adapter(s) you want:
28
+
29
+ ```bash
30
+ pip install 'self-heal-llm[claude]' # Anthropic Claude (default)
31
+ pip install 'self-heal-llm[openai]' # OpenAI + OpenAI-compatible endpoints
32
+ pip install 'self-heal-llm[gemini]' # Google Gemini
33
+ pip install 'self-heal-llm[litellm]' # 100+ providers via LiteLLM
34
+ pip install 'self-heal-llm[all]' # everything
35
+ ```
36
+
37
+ > 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 ...`.
38
+
39
+ ## Provider support
40
+
41
+ | Adapter | Covers |
42
+ |---|---|
43
+ | `ClaudeProposer` | Anthropic Claude (native SDK) |
44
+ | `OpenAIProposer` | OpenAI + **any OpenAI-compatible endpoint** (OpenRouter, Together, Groq, Fireworks, Anyscale, Perplexity, xAI, DeepSeek, Azure, Ollama, LM Studio, vLLM, llama.cpp server, ...) |
45
+ | `GeminiProposer` | Google Gemini (native SDK) |
46
+ | `LiteLLMProposer` | 100+ providers via LiteLLM (Bedrock, Vertex, Cohere, Mistral, ...) |
47
+
48
+ ## Using different providers
49
+
50
+ **Claude (default):**
51
+ ```python
52
+ from self_heal import repair
53
+
54
+ @repair() # uses ClaudeProposer under the hood
55
+ def my_fn(...): ...
56
+ ```
57
+
58
+ **OpenAI:**
59
+ ```python
60
+ from self_heal import repair
61
+ from self_heal.llm import OpenAIProposer
62
+
63
+ @repair(proposer=OpenAIProposer(model="gpt-5"))
64
+ def my_fn(...): ...
65
+ ```
66
+
67
+ **Gemini:**
68
+ ```python
69
+ from self_heal import repair
70
+ from self_heal.llm import GeminiProposer
71
+
72
+ @repair(proposer=GeminiProposer(model="gemini-2.5-pro"))
73
+ def my_fn(...): ...
74
+ ```
75
+
76
+ **Any OpenAI-compatible endpoint (OpenRouter, Groq, Ollama, ...):**
77
+ ```python
78
+ from self_heal.llm import OpenAIProposer
79
+
80
+ # OpenRouter — hundreds of models through one key
81
+ proposer = OpenAIProposer(
82
+ model="google/gemini-2.5-pro",
83
+ base_url="https://openrouter.ai/api/v1",
84
+ )
85
+
86
+ # Groq — fast inference
87
+ proposer = OpenAIProposer(
88
+ model="llama-3.3-70b-versatile",
89
+ base_url="https://api.groq.com/openai/v1",
90
+ )
91
+
92
+ # Local Ollama
93
+ proposer = OpenAIProposer(
94
+ model="llama3.3",
95
+ base_url="http://localhost:11434/v1",
96
+ api_key="ollama",
97
+ )
98
+ ```
99
+
100
+ **LiteLLM catch-all (100+ providers):**
101
+ ```python
102
+ from self_heal.llm import LiteLLMProposer
103
+
104
+ proposer = LiteLLMProposer(model="anthropic/claude-sonnet-4-6")
105
+ proposer = LiteLLMProposer(model="bedrock/anthropic.claude-3-5-sonnet")
106
+ proposer = LiteLLMProposer(model="vertex_ai/gemini-2.5-pro")
107
+ proposer = LiteLLMProposer(model="cohere/command-r-plus")
108
+ ```
109
+
110
+ ## Why this exists
111
+
112
+ AI coding agents fail on a lot of real tasks. The industry's current answer is "retry and hope." That's not a strategy.
113
+
114
+ `self-heal` treats repair as a first-class primitive: diagnose the failure, propose a targeted fix, verify, retry. A thin library you can wrap around any Python function or agent tool.
115
+
116
+ Built on ongoing code-repair research (RepairBench, NeurIPS 2026).
117
+
118
+ ## How it works
119
+
120
+ 1. **Catch** the exception and capture inputs, traceback, and failure type.
121
+ 2. **Classify** the failure (validation, exception, assertion).
122
+ 3. **Propose** a repaired function via LLM with a failure-aware prompt.
123
+ 4. **Recompile** the proposed function into the running process.
124
+ 5. **Retry** with the same inputs.
125
+
126
+ All within a single decorator boundary.
127
+
128
+ ## API
129
+
130
+ **Decorator:**
131
+ ```python
132
+ from self_heal import repair
133
+
134
+ @repair(max_attempts=3, model="claude-sonnet-4-6", verbose=True)
135
+ def my_tool(x): ...
136
+
137
+ my_tool(42)
138
+ my_tool.last_repair # -> RepairResult with full attempt history
139
+ ```
140
+
141
+ **Loop (for advanced use):**
142
+ ```python
143
+ from self_heal import RepairLoop
144
+
145
+ loop = RepairLoop(max_attempts=5, verbose=True)
146
+ result = loop.run(my_tool, args=(42,))
147
+ if result.succeeded:
148
+ print(result.final_value)
149
+ else:
150
+ print(result.attempts[-1].failure.traceback)
151
+ ```
152
+
153
+ **Custom proposer:**
154
+ ```python
155
+ from self_heal.llm import LLMProposer
156
+
157
+ class MyProposer:
158
+ def propose(self, system: str, user: str) -> str:
159
+ # ... your logic ...
160
+ return "def my_tool(x): return x * 2"
161
+ ```
162
+
163
+ ## Safety
164
+
165
+ `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.
166
+
167
+ ## Roadmap
168
+
169
+ - [x] v0.0.1: core repair loop + decorator + Claude backend
170
+ - [x] v0.0.2: OpenAI, Gemini, LiteLLM adapters — works with any LLM
171
+ - [ ] v0.1: user-provided verifiers (beyond exception-catching)
172
+ - [ ] v0.2: telemetry + before/after success metrics
173
+ - [ ] v0.3: async support
174
+ - [ ] v0.4: sandboxed execution
175
+ - [ ] v0.5: repair persistence (learn from past fixes)
176
+ - [ ] v1.0: NeurIPS 2026 paper co-release
177
+
178
+ ## Development
179
+
180
+ ```bash
181
+ git clone https://github.com/Johin2/self-heal.git
182
+ cd self-heal
183
+ python -m venv .venv
184
+ .venv/Scripts/pip install -e ".[dev]" # Windows
185
+ # .venv/bin/pip install -e ".[dev]" # macOS/Linux
186
+ pytest
187
+ ```
188
+
189
+ ## License
190
+
191
+ MIT
@@ -0,0 +1,29 @@
1
+ """Minimal self-heal example.
2
+
3
+ Requires ANTHROPIC_API_KEY in the environment.
4
+
5
+ Run:
6
+ python examples/basic.py
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+
13
+ from self_heal import repair
14
+
15
+ logging.basicConfig(level=logging.INFO, format="%(message)s")
16
+
17
+
18
+ @repair(max_attempts=3, verbose=True)
19
+ def divide(a: float, b: float) -> float:
20
+ # Naive: crashes on b == 0.
21
+ return a / b
22
+
23
+
24
+ if __name__ == "__main__":
25
+ print("divide(10, 2) =", divide(10, 2))
26
+ print()
27
+ print("divide(10, 0) =", divide(10, 0))
28
+ print()
29
+ print("Attempts:", divide.last_repair.total_attempts)
@@ -0,0 +1,31 @@
1
+ """A realistic self-heal example: messy real-world input.
2
+
3
+ Requires ANTHROPIC_API_KEY in the environment.
4
+
5
+ Run:
6
+ python examples/extract_price.py
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+
13
+ from self_heal import repair
14
+
15
+ logging.basicConfig(level=logging.INFO, format="%(message)s")
16
+
17
+
18
+ @repair(max_attempts=3, verbose=True)
19
+ def extract_price(text: str) -> float:
20
+ # Naive: only handles "$X.YY" with no commas.
21
+ return float(text.replace("$", ""))
22
+
23
+
24
+ if __name__ == "__main__":
25
+ samples = ["$12.99", "₹1,299", "€5,49", "1000.50 USD", " $4,999.00 "]
26
+ for sample in samples:
27
+ try:
28
+ value = extract_price(sample)
29
+ print(f"{sample!r:>18} -> {value}")
30
+ except Exception as e:
31
+ print(f"{sample!r:>18} -> FAILED: {e}")