ai-coding-system / app /agent /executor.py
PYAE1994's picture
Update app/agent/executor.py
e36ea50 verified
from tools_runtime import execute_tool_call
from self_heal import SelfHealer
import shlex
class Executor:
def __init__(self, llm=None):
self.llm = llm
self.healer = SelfHealer(llm=llm)
# =====================================================
# MAIN PIPELINE
# =====================================================
def run_plan(self, plan):
if not isinstance(plan, dict):
return {"error": "Plan must be dict"}
steps = plan.get("steps", [])
if not isinstance(steps, list):
return {"error": "Invalid steps format"}
results = []
for step in steps:
print(f"🚀 Executing step: {step}")
# STEP 1: convert to action
action = self._interpret_step(step)
# STEP 2: execute tool
tool_result = self._execute_tool(action)
# STEP 3: self-heal (safe retry)
tool_result = self._safe_heal(step, tool_result)
# STEP 4: optional LLM analysis
llm_result = self._llm_analyze(step, action, tool_result)
# STEP 5: store
results.append({
"step": step,
"action": action,
"tool_result": tool_result,
"llm_result": llm_result
})
return {
"status": "completed",
"results": results
}
# =====================================================
# STEP INTERPRETER (FIXED - NO NOOP TRAP)
# =====================================================
def _interpret_step(self, step):
# already structured tool
if isinstance(step, dict):
return step
if not isinstance(step, str):
return {
"tool": "run_shell",
"args": {"cmd": "echo invalid_step"}
}
cmd = step.strip()
if not cmd:
return {
"tool": "run_shell",
"args": {"cmd": "echo empty_step"}
}
# 🔥 IMPORTANT FIX:
# DO NOT block natural language anymore
# Instead: ALWAYS convert to shell-safe fallback
shell_like = self._looks_like_shell(cmd)
if shell_like:
return {
"tool": "run_shell",
"args": {
"cmd": cmd
}
}
# fallback: convert to safe echo (never noop)
return {
"tool": "run_shell",
"args": {
"cmd": f"echo '[interpreted step] {shlex.quote(cmd)}'"
}
}
# =====================================================
# SIMPLE HEURISTIC (NOT BLOCKING)
# =====================================================
def _looks_like_shell(self, cmd: str):
shell_signals = [
"ls", "cd", "mkdir", "touch",
"python", "pip", "rm", "echo",
"./", "git", "npm", "curl"
]
first = cmd.split()[0].lower() if cmd.split() else ""
return first in shell_signals
# =====================================================
# TOOL EXECUTION
# =====================================================
def _execute_tool(self, action):
try:
if not isinstance(action, dict):
return {"error": "invalid_action"}
return execute_tool_call(action)
except Exception as e:
return {"error": str(e)}
# =====================================================
# SELF HEAL (SAFE RETRY ONLY)
# =====================================================
def _safe_heal(self, step, tool_result):
try:
fix = self.healer.heal(step, tool_result)
if isinstance(fix, dict) and "tool" in fix:
print("🛠️ Self-healing triggered")
return execute_tool_call(fix)
except Exception as e:
return {
"error": f"healer_error: {str(e)}"
}
return tool_result
# =====================================================
# LLM ANALYSIS (NO EXECUTION)
# =====================================================
def _llm_analyze(self, step, action, tool_result):
if not self.llm:
return None
try:
response = self.llm([
{
"role": "system",
"content": "Analyze only. NEVER execute tools."
},
{
"role": "user",
"content": f"""
Step: {step}
Action: {action}
Result: {tool_result}
"""
}
])
if isinstance(response, dict):
return (
response.get("choices", [{}])[0]
.get("message", {})
.get("content", "LLM_EMPTY")
)
return str(response)
except Exception as e:
return f"llm_error: {str(e)}"