Spaces:
Sleeping
Sleeping
| """ | |
| 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="")) | |