AI Developer Agent
AI Developer Agent v1.0 backend
763ef0d
"""
Autonomous task planner.
Produces structured action plans (list of dicts):
{"type": "shell"|"python"|"browser"|"git"|"deploy"|"note", ...}
Strategy:
1. Try LLM-based planning with strict JSON output.
2. If LLM fails or returns invalid JSON, use heuristic fallback.
3. Always produces non-empty plan.
"""
from __future__ import annotations
import json
import logging
import re
from typing import Any, Dict, List
from .llm_router import get_router
logger = logging.getLogger("planner")
PLANNER_SYSTEM = (
"You are an autonomous AI Developer Agent's planner. "
"Decompose a user task into a JSON list of concrete actions. "
"Each action is an object with a 'type' field and supporting fields. "
"Supported types: shell (cmd), python (code), browser (action,url,...), "
"git (op,args), deploy (target), note (msg). "
"Return ONLY a JSON array, no commentary. Keep plan under 12 steps."
)
def plan_task(title: str, description: str, context: Dict[str, Any] | None = None) -> List[Dict[str, Any]]:
"""Generate a concrete action plan."""
router = get_router()
user_prompt = (
f"TASK TITLE: {title}\n"
f"DESCRIPTION:\n{description}\n\n"
f"CONTEXT:\n{json.dumps(context or {}, indent=2)[:2000]}\n\n"
"Output a JSON array of action objects. Example:\n"
'[{"type":"shell","cmd":"echo hi"},{"type":"note","msg":"done"}]'
)
messages = [
{"role": "system", "content": PLANNER_SYSTEM},
{"role": "user", "content": user_prompt},
]
try:
raw = router.chat(messages, temperature=0.1, max_tokens=1200, timeout=45.0)
except Exception as e:
logger.warning("Planner LLM call failed: %s", e)
raw = ""
plan = _parse_plan_json(raw)
if plan:
return plan
logger.info("Planner falling back to heuristic plan")
return heuristic_plan(title, description)
def _parse_plan_json(raw: str) -> List[Dict[str, Any]]:
if not raw:
return []
# Try direct
try:
obj = json.loads(raw)
if isinstance(obj, list):
return [a for a in obj if isinstance(a, dict) and "type" in a]
except Exception:
pass
# Find first JSON array in text
m = re.search(r"\[[\s\S]*\]", raw)
if m:
try:
obj = json.loads(m.group(0))
if isinstance(obj, list):
return [a for a in obj if isinstance(a, dict) and "type" in a]
except Exception:
return []
return []
def heuristic_plan(title: str, description: str) -> List[Dict[str, Any]]:
"""Always-valid fallback plan."""
text = (title + "\n" + description).lower()
plan: List[Dict[str, Any]] = []
if any(k in text for k in ["deploy", "deployment", "huggingface", "hf space", "vercel"]):
plan.append({"type": "note", "msg": "Deployment task detected"})
if "vercel" in text:
plan.append({"type": "deploy", "target": "vercel"})
if "huggingface" in text or "hf" in text:
plan.append({"type": "deploy", "target": "huggingface"})
return plan
if any(k in text for k in ["git", "github", "commit", "push", "pr ", "pull request"]):
plan.append({"type": "git", "op": "status"})
plan.append({"type": "note", "msg": "GitHub task detected"})
return plan
if any(k in text for k in ["browser", "scrape", "navigate", "click", "open url", "http://", "https://"]):
url_match = re.search(r"https?://[\w\.\-/?=&%#]+", description)
url = url_match.group(0) if url_match else "https://example.com"
plan.append({"type": "browser", "action": "navigate", "url": url})
plan.append({"type": "browser", "action": "screenshot"})
return plan
if any(k in text for k in ["run python", "python script", "execute python"]):
plan.append({"type": "python", "code": "print('Hello from AI Developer Agent')"})
return plan
if any(k in text for k in ["install", "pip ", "npm "]):
# Try to extract a package name
m = re.search(r"(?:install|add)\s+([\w\.\-]+)", text)
pkg = m.group(1) if m else ""
if "npm" in text:
plan.append({"type": "shell", "cmd": f"npm install {pkg}".strip()})
else:
plan.append({"type": "shell", "cmd": f"pip install {pkg}".strip()})
return plan
# Generic fallback: echo the task back as an inspection action.
plan.append({"type": "note", "msg": f"Plan-fallback for: {title}"})
plan.append({"type": "shell", "cmd": "uname -a && python3 --version && node --version 2>/dev/null || true"})
return plan
def repair_plan(error_category: str, detail: str = "") -> List[Dict[str, Any]]:
from .classifier import ErrorClass
from .repair import repair_actions
return repair_actions(ErrorClass(category=error_category, detail=detail, suggested_fix=""))