nova-spike-hybrid / aether /code_generation.py
thefinalboss's picture
Initial release: NOVA + SPIKE + AETHER + HYBRID non-transformer AI stack
3636a69
Raw
History Blame Contribute Delete
11.7 kB
"""
code_generation.py — Code generation and execution.
PROBLEM
-------
GPT-4 can write and execute code. AETHER has no code capabilities beyond
the basic `python` tool (which only evaluates simple expressions).
SOLUTION
--------
CodeGenerator provides:
1. Code generation from natural language descriptions
2. Safe execution in a sandboxed environment
3. Code templates for common patterns (loops, functions, classes)
4. Error detection and correction
5. Multi-language support (Python, pseudocode)
Templates cover:
- Function definitions
- Loops (for, while)
- Conditionals (if/elif/else)
- List comprehensions
- Class definitions
- File I/O
- Sorting/searching algorithms
"""
from __future__ import annotations
import ast
import re
import textwrap
from typing import List, Tuple, Optional, Dict, Any
from dataclasses import dataclass, field
import logging
import traceback
log = logging.getLogger(__name__)
@dataclass
class CodeResult:
"""Result of code generation + execution."""
code: str
output: Optional[str]
error: Optional[str]
success: bool
language: str = "python"
# Code templates indexed by intent
CODE_TEMPLATES: Dict[str, str] = {
"function": """
def {name}({params}):
\"\"\"{docstring}\"\"\"
{body}
return {return_val}
""".strip(),
"for_loop": """
for {var} in {iterable}:
{body}
""".strip(),
"while_loop": """
while {condition}:
{body}
""".strip(),
"if_else": """
if {condition}:
{body_if}
else:
{body_else}
""".strip(),
"list_comprehension": "[{expr} for {var} in {iterable}]",
"class": """
class {name}:
def __init__(self, {params}):
{init_body}
def {method_name}(self):
{method_body}
""".strip(),
"sort": """
def {name}(arr):
return sorted(arr)
""".strip(),
"search": """
def {name}(arr, target):
for i, val in enumerate(arr):
if val == target:
return i
return -1
""".strip(),
"factorial": """
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
""".strip(),
"fibonacci": """
def fibonacci(n):
if n <= 1:
return n
a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return b
""".strip(),
"reverse_string": """
def reverse_string(s):
return s[::-1]
""".strip(),
"sum_list": """
def sum_list(lst):
return sum(lst)
""".strip(),
"max_list": """
def max_list(lst):
return max(lst)
""".strip(),
}
class CodeGenerator:
"""Generate and execute code from natural language."""
# Intent detection patterns
INTENT_PATTERNS = [
(r"function\s+(?:called\s+)?(\w+)", "function"),
(r"for\s+loop", "for_loop"),
(r"while\s+loop", "while_loop"),
(r"if\s+else|conditional", "if_else"),
(r"list\s+comprehension", "list_comprehension"),
(r"class\s+(?:called\s+)?(\w+)", "class"),
(r"sort", "sort"),
(r"search|find\s+in\s+list", "search"),
(r"factorial", "factorial"),
(r"fibonacci", "fibonacci"),
(r"reverse\s+string", "reverse_string"),
(r"sum\s+(?:of\s+)?(?:a\s+)?list", "sum_list"),
(r"max\s+(?:of\s+)?(?:a\s+)?list", "max_list"),
]
def __init__(self, agent):
self.agent = agent
# ------------------------------------------------------------------ #
# Code generation
# ------------------------------------------------------------------ #
def generate_code(self, description: str) -> CodeResult:
"""Generate code from a natural language description."""
desc_lower = description.lower()
# Detect intent
intent = None
match_groups = None
for pattern, intent_name in self.INTENT_PATTERNS:
m = re.search(pattern, desc_lower)
if m:
intent = intent_name
match_groups = m.groups()
break
if intent is None:
# Try to detect from keywords
if "factorial" in desc_lower:
intent = "factorial"
elif "fibonacci" in desc_lower:
intent = "fibonacci"
elif "reverse" in desc_lower and "string" in desc_lower:
intent = "reverse_string"
elif "sort" in desc_lower:
intent = "sort"
elif "search" in desc_lower:
intent = "search"
elif "sum" in desc_lower:
intent = "sum_list"
elif "max" in desc_lower:
intent = "max_list"
if intent and intent in CODE_TEMPLATES:
code = CODE_TEMPLATES[intent]
# Fill in template variables
if intent == "function" and match_groups:
code = code.format(
name=match_groups[0] if match_groups else "my_function",
params="x",
docstring=description,
body="pass",
return_val="x",
)
elif intent == "class" and match_groups:
code = code.format(
name=match_groups[0] if match_groups else "MyClass",
params="",
init_body="pass",
method_name="do_something",
method_body="pass",
)
elif intent == "sort":
code = code.format(name="sort_array")
elif intent == "search":
code = code.format(name="search_array")
else:
# Use template as-is (factorial, fibonacci, etc.)
pass
return CodeResult(code=code, output=None, error=None,
success=True, language="python")
# No template matched — generate pseudocode
pseudocode = self._generate_pseudocode(description)
return CodeResult(code=pseudocode, output=None, error=None,
success=True, language="pseudocode")
def _generate_pseudocode(self, description: str) -> str:
"""Generate pseudocode for a description."""
lines = [
f"# Pseudocode for: {description}",
"1. Initialize variables",
"2. Process input",
"3. Compute result",
"4. Return output",
]
return "\n".join(lines)
# ------------------------------------------------------------------ #
# Safe execution
# ------------------------------------------------------------------ #
def execute(self, code: str, timeout: int = 5) -> CodeResult:
"""Execute code safely in a restricted environment.
Only allows: arithmetic, string ops, list/tuple/dict ops,
basic control flow, function defs, print.
Blocks: imports, file I/O, exec, eval, open, system.
"""
# Validate: no dangerous constructs
dangerous = ["import ", "__", "exec(", "eval(", "open(", "system(",
"subprocess", "os.", "sys.", "globals(", "locals("]
for d in dangerous:
if d in code:
return CodeResult(code=code, output=None,
error=f"Blocked: contains '{d}'",
success=False)
# Try to parse the code
try:
tree = ast.parse(code)
except SyntaxError as e:
return CodeResult(code=code, output=None,
error=f"Syntax error: {e}",
success=False)
# Execute in a sandboxed namespace
namespace = {
"__builtins__": {
"print": print, "len": len, "range": range, "sum": sum,
"max": max, "min": min, "abs": abs, "round": round,
"sorted": sorted, "enumerate": enumerate, "zip": zip,
"int": int, "float": float, "str": str, "list": list,
"tuple": tuple, "dict": dict, "set": set, "bool": bool,
"True": True, "False": False, "None": None,
"TypeError": TypeError, "ValueError": ValueError,
"IndexError": IndexError, "KeyError": KeyError,
}
}
# Capture stdout
import io
import contextlib
stdout_capture = io.StringIO()
try:
with contextlib.redirect_stdout(stdout_capture):
exec(compile(tree, "<generated>", "exec"), namespace)
output = stdout_capture.getvalue()
return CodeResult(code=code, output=output, error=None,
success=True)
except Exception as e:
error = f"{type(e).__name__}: {e}"
return CodeResult(code=code, output=None, error=error,
success=False)
# ------------------------------------------------------------------ #
# Combined: generate + execute
# ------------------------------------------------------------------ #
def generate_and_execute(self, description: str) -> CodeResult:
"""Generate code from description and execute it."""
gen_result = self.generate_code(description)
if not gen_result.success:
return gen_result
if gen_result.language != "python":
return gen_result # can't execute pseudocode
return self.execute(gen_result.code)
# ------------------------------------------------------------------ #
# Error correction
# ------------------------------------------------------------------ #
def correct_code(self, code: str, error: str) -> str:
"""Try to correct code based on an error message."""
corrections = []
# Common errors and fixes
if "IndentationError" in error:
corrections.append("# Fix: ensure consistent indentation (4 spaces)")
# Try to fix indentation
lines = code.split("\n")
fixed_lines = []
indent_level = 0
for line in lines:
stripped = line.strip()
if stripped:
if stripped.startswith(("def ", "class ", "for ", "while ", "if ", "elif ", "else:", "try:", "except", "finally:")):
fixed_lines.append(" " * indent_level + stripped)
indent_level += 1
elif stripped.startswith(("return", "pass", "break", "continue")):
indent_level = max(0, indent_level - 1)
fixed_lines.append(" " * indent_level + stripped)
else:
fixed_lines.append(" " * indent_level + stripped)
else:
fixed_lines.append("")
return "\n".join(fixed_lines)
if "NameError" in error:
# Missing variable — add a placeholder
m = re.search(r"name '(\w+)' is not defined", error)
if m:
name = m.group(1)
return f"{name} = None # TODO: define {name}\n{code}"
if "SyntaxError" in error:
corrections.append("# Fix: check for missing colons, parentheses, or quotes")
return code + "\n" + "\n".join(corrections)
# ------------------------------------------------------------------ #
# Stats
# ------------------------------------------------------------------ #
def stats(self) -> Dict[str, Any]:
return {
"n_templates": len(CODE_TEMPLATES),
"n_intents": len(self.INTENT_PATTERNS),
"supported_intents": list(set(intent for _, intent in self.INTENT_PATTERNS)),
}