codeshield-runtime 0.1.1__tar.gz → 0.1.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.
Files changed (20) hide show
  1. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/PKG-INFO +8 -6
  2. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/README.md +7 -5
  3. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/pyproject.toml +1 -1
  4. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/loop.py +3 -1
  5. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/tools.py +8 -2
  6. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/tests/test_engine.py +36 -1
  7. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/.gitignore +0 -0
  8. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/LICENSE +0 -0
  9. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/__init__.py +0 -0
  10. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/__main__.py +0 -0
  11. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/analyzer.py +0 -0
  12. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/classifier.py +0 -0
  13. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/cli.py +0 -0
  14. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/environment.py +0 -0
  15. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/runner.py +0 -0
  16. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/src/codeshield/schemas.py +0 -0
  17. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/tests/__init__.py +0 -0
  18. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/tests/test_cli.py +0 -0
  19. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/tests/test_coverage.py +0 -0
  20. {codeshield_runtime-0.1.1 → codeshield_runtime-0.1.2}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: codeshield-runtime
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: A secure, isolated, and self-healing Python code execution engine powered by uv and AST analysis.
5
5
  Project-URL: Homepage, https://github.com/AlgorithmicMind/codeshield
6
6
  Project-URL: Repository, https://github.com/AlgorithmicMind/codeshield
@@ -158,10 +158,10 @@ CodeShield is not locked into a single LLM. Pass any Python callable as the `pat
158
158
  from codeshield.loop import SelfHealingEngine
159
159
 
160
160
 
161
- def custom_openai_patcher(code: str, diagnosis) -> str:
162
- # Any LLM call (OpenAI, Anthropic, DeepSeek, Ollama, LiteLLM)
161
+ def custom_llm_patcher(code: str, diagnosis) -> str:
162
+ # Compatible with any frontier provider: GPT-5.4, Claude Sonnet 5, DeepSeek V4, Ollama
163
163
  response = client.chat.completions.create(
164
- model="gpt-4o-mini",
164
+ model="gpt-5.4-mini", # or "claude-sonnet-5", "deepseek-v4-flash"
165
165
  messages=[
166
166
  {
167
167
  "role": "user",
@@ -172,7 +172,7 @@ def custom_openai_patcher(code: str, diagnosis) -> str:
172
172
  return response.choices[0].message.content
173
173
 
174
174
 
175
- engine = SelfHealingEngine(patch_generator=custom_openai_patcher)
175
+ engine = SelfHealingEngine(patch_generator=custom_llm_patcher)
176
176
  ```
177
177
 
178
178
  ### Zero-Config Self-Healing with Gemini Flash
@@ -234,17 +234,19 @@ The `examples/` folder contains ready-to-run recipes that have been executed and
234
234
  - `02_security_gatekeeper.py`: AST rejection of unsafe code.
235
235
  - `03_llm_healing_workflow.py`: self-healing workflow with an LLM or local fallback.
236
236
  - `04_agent_tool_dropin.py`: end-to-end agentic tool-calling workflow with dynamic code generation.
237
+ - `05_custom_llm_openai_compatible.py`: model-agnostic, API-key-free self-healing with a custom `patch_generator`.
237
238
 
238
239
  ```bash
239
240
  python examples/01_basic_sandboxing.py
240
241
  python examples/02_security_gatekeeper.py
241
242
  python examples/03_llm_healing_workflow.py
242
243
  python examples/04_agent_tool_dropin.py
244
+ python examples/05_custom_llm_openai_compatible.py
243
245
  ```
244
246
 
245
247
  ## Running Tests & Lint
246
248
 
247
- The suite currently has **50 tests** with **>82% code coverage** on `src/codeshield`.
249
+ The suite currently has **53 tests** with **>82% code coverage** on `src/codeshield`.
248
250
 
249
251
  ```bash
250
252
  ruff check src tests examples
@@ -122,10 +122,10 @@ CodeShield is not locked into a single LLM. Pass any Python callable as the `pat
122
122
  from codeshield.loop import SelfHealingEngine
123
123
 
124
124
 
125
- def custom_openai_patcher(code: str, diagnosis) -> str:
126
- # Any LLM call (OpenAI, Anthropic, DeepSeek, Ollama, LiteLLM)
125
+ def custom_llm_patcher(code: str, diagnosis) -> str:
126
+ # Compatible with any frontier provider: GPT-5.4, Claude Sonnet 5, DeepSeek V4, Ollama
127
127
  response = client.chat.completions.create(
128
- model="gpt-4o-mini",
128
+ model="gpt-5.4-mini", # or "claude-sonnet-5", "deepseek-v4-flash"
129
129
  messages=[
130
130
  {
131
131
  "role": "user",
@@ -136,7 +136,7 @@ def custom_openai_patcher(code: str, diagnosis) -> str:
136
136
  return response.choices[0].message.content
137
137
 
138
138
 
139
- engine = SelfHealingEngine(patch_generator=custom_openai_patcher)
139
+ engine = SelfHealingEngine(patch_generator=custom_llm_patcher)
140
140
  ```
141
141
 
142
142
  ### Zero-Config Self-Healing with Gemini Flash
@@ -198,17 +198,19 @@ The `examples/` folder contains ready-to-run recipes that have been executed and
198
198
  - `02_security_gatekeeper.py`: AST rejection of unsafe code.
199
199
  - `03_llm_healing_workflow.py`: self-healing workflow with an LLM or local fallback.
200
200
  - `04_agent_tool_dropin.py`: end-to-end agentic tool-calling workflow with dynamic code generation.
201
+ - `05_custom_llm_openai_compatible.py`: model-agnostic, API-key-free self-healing with a custom `patch_generator`.
201
202
 
202
203
  ```bash
203
204
  python examples/01_basic_sandboxing.py
204
205
  python examples/02_security_gatekeeper.py
205
206
  python examples/03_llm_healing_workflow.py
206
207
  python examples/04_agent_tool_dropin.py
208
+ python examples/05_custom_llm_openai_compatible.py
207
209
  ```
208
210
 
209
211
  ## Running Tests & Lint
210
212
 
211
- The suite currently has **50 tests** with **>82% code coverage** on `src/codeshield`.
213
+ The suite currently has **53 tests** with **>82% code coverage** on `src/codeshield`.
212
214
 
213
215
  ```bash
214
216
  ruff check src tests examples
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codeshield-runtime"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "A secure, isolated, and self-healing Python code execution engine powered by uv and AST analysis."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1,3 +1,4 @@
1
+ # ruff: noqa: UP045
1
2
  """Self-healing execution loop."""
2
3
 
3
4
  from __future__ import annotations
@@ -7,6 +8,7 @@ import os
7
8
  import re
8
9
  from collections.abc import Callable
9
10
  from pathlib import Path
11
+ from typing import Optional
10
12
 
11
13
  from codeshield.analyzer import validate_syntax_and_safety
12
14
  from codeshield.classifier import TracebackClassifier
@@ -173,7 +175,7 @@ class SelfHealingEngine:
173
175
  sandbox: SandboxManager | None = None,
174
176
  runner: SubprocessRunner | None = None,
175
177
  classifier: TracebackClassifier | None = None,
176
- patch_generator: Callable[[str, ErrorDiagnosis], str | None] | None = None,
178
+ patch_generator: Optional[Callable[[str, ErrorDiagnosis], Optional[str]]] = None,
177
179
  gemini_api_key: str | None = None,
178
180
  gemini_model: str | None = None,
179
181
  use_llm: bool = True,
@@ -1,3 +1,4 @@
1
+ # ruff: noqa: UP045
1
2
  """Universal agent tool wrapper for CodeShield.
2
3
 
3
4
  The function returned by ``create_code_execution_tool`` can be registered as a
@@ -9,20 +10,25 @@ stdout or a structured error report.
9
10
  from __future__ import annotations
10
11
 
11
12
  from collections.abc import Callable
13
+ from typing import Optional
12
14
 
13
15
  from codeshield.environment import SandboxError
14
16
  from codeshield.loop import SelfHealingEngine, SelfHealingError
15
17
  from codeshield.runner import SubprocessRunnerError
18
+ from codeshield.schemas import ErrorDiagnosis
16
19
 
17
20
 
18
21
  def create_code_execution_tool(
19
- engine: SelfHealingEngine | None = None,
22
+ engine: Optional[SelfHealingEngine] = None,
23
+ patch_generator: Optional[Callable[[str, ErrorDiagnosis], Optional[str]]] = None,
20
24
  ) -> Callable[[str], str]:
21
25
  """Return a drop-in ``execute_python_code(code: str) -> str`` tool.
22
26
 
23
27
  Args:
24
28
  engine: Optional ``SelfHealingEngine`` instance. When ``None``, a fresh
25
29
  engine is created for each tool call.
30
+ patch_generator: Optional custom patcher ``(code, diagnosis) -> patched``
31
+ forwarded to ``SelfHealingEngine`` when ``engine`` is not provided.
26
32
 
27
33
  Returns:
28
34
  A callable ready to be registered as an agent tool.
@@ -41,7 +47,7 @@ def create_code_execution_tool(
41
47
  The stdout of the script if execution succeeds, or a structured
42
48
  error report if it fails after all self-healing attempts.
43
49
  """
44
- _engine = engine or SelfHealingEngine()
50
+ _engine = engine or SelfHealingEngine(patch_generator=patch_generator)
45
51
  with _engine:
46
52
  try:
47
53
  result, diagnosis = _engine.run(code)
@@ -7,7 +7,7 @@ import pytest
7
7
  from codeshield.analyzer import validate_syntax_and_safety
8
8
  from codeshield.classifier import TracebackClassifier
9
9
  from codeshield.environment import SandboxManager
10
- from codeshield.loop import SelfHealingEngine
10
+ from codeshield.loop import SelfHealingEngine, SelfHealingError
11
11
  from codeshield.runner import SubprocessRunner
12
12
  from codeshield.schemas import (
13
13
  ExecutionResult,
@@ -244,3 +244,38 @@ def test_self_healing_engine_run_without_context_manager() -> None:
244
244
  assert result.stdout.strip() == "no context manager"
245
245
  assert diagnosis is None
246
246
  assert not engine._sandbox.workspace_path.exists()
247
+
248
+
249
+ def test_custom_patch_generator_success() -> None:
250
+ """A custom patch_generator can repair code that the local fallback cannot."""
251
+
252
+ def custom_patcher(code: str, diagnosis) -> str | None:
253
+ if diagnosis.error_type == "TypeError":
254
+ return code.replace('"Result: " + 42', '"Result: " + str(42)')
255
+ return None
256
+
257
+ engine = SelfHealingEngine(
258
+ patch_generator=custom_patcher,
259
+ use_llm=False,
260
+ )
261
+ with engine:
262
+ result, diagnosis = engine.run('print("Result: " + 42)')
263
+
264
+ assert result.exit_code == 0
265
+ assert result.stdout.strip() == "Result: 42"
266
+ assert diagnosis is None
267
+
268
+
269
+ def test_custom_patch_generator_ast_rejection() -> None:
270
+ """A custom patch that violates the AST security gate is rejected."""
271
+
272
+ def malicious_patcher(code: str, diagnosis) -> str | None:
273
+ return 'import os\nos.system("echo pwned")'
274
+
275
+ engine = SelfHealingEngine(
276
+ patch_generator=malicious_patcher,
277
+ use_llm=False,
278
+ )
279
+
280
+ with pytest.raises(SelfHealingError):
281
+ engine.run("print(undefined_value)")