"""Autonomous planning heuristics used by the Python runtime.""" from __future__ import annotations from typing import Any from maris_core.memory_context import MemoryMatch _CODE_KEYWORDS = ("kod", "api", "script", "python", "rust", "refactor", "fix") _BROWSER_KEYWORDS = ("browser", "web", "pārlūk", "klikš", "scrape", "form", "http://", "https://") _VALIDATION_KEYWORDS = ("test", "verify", "pārbaud", "validate", "review") _RESEARCH_KEYWORDS = ("research", "meklē", "salīdzini", "izpēti") _PUNCTUATION_SEPARATORS = {",", ";", "->"} _WORD_SEPARATORS = {"and", "un", "then"} class Planner: """Produces structured task graphs with lightweight policy hints.""" def describe(self, goal: str, max_steps: int = 10) -> list[str]: structured = self.decompose(goal, max_steps=max_steps) if structured: return [str(step["action"]) for step in structured] return [ f"Izanalizēt mērķi un izpildes robežas: {goal}", "Izpildīt galveno darba soli ar atbilstošu rīku", "Validēt rezultātu un sagatavot nākamos soļus", ][: max(1, max_steps)] def decompose( self, goal: str, max_steps: int = 10, memory_context: list[MemoryMatch] | None = None, ) -> list[dict[str, Any]]: normalized_goal = goal.strip() if not normalized_goal: return [] chunks = _split_goal_chunks(normalized_goal) candidate_steps = chunks[: max_steps - 1] if chunks else [] if not candidate_steps: candidate_steps.append(normalized_goal) actions: list[str] = [f"Izanalizēt mērķi un atkarības: {normalized_goal}"] actions.extend(candidate_steps[: max(0, max_steps - 2)]) if len(actions) < max_steps: actions.append("Pārbaudīt rezultātu, riskus un resumējamu stāvokli") if memory_context: actions.insert( 1, "Ielādēt un izmantot atbilstošo sesijas/memory kontekstu pirms galvenās izpildes", ) plan: list[dict[str, Any]] = [] for index, action in enumerate(actions[:max_steps], start=1): tool = self._infer_tool(action) risk_level = "high" if tool in {"browser_automation", "code_generation"} else "medium" plan.append( { "step": index, "action": action, "tool": tool, "depends_on_steps": [index - 1] if index > 1 else [], "execution_policy": "sequential", "risk_level": risk_level, "approval_required": tool in {"browser_automation", "code_generation", "validation"}, "max_attempts": 1 if tool == "validation" else 2, "observability_tags": ["autonomous", tool, f"risk:{risk_level}"], } ) return plan def _infer_tool(self, action: str) -> str: lowered = action.lower() if any(keyword in lowered for keyword in _BROWSER_KEYWORDS): return "browser_automation" if any(keyword in lowered for keyword in _CODE_KEYWORDS): return "code_generation" if any(keyword in lowered for keyword in _VALIDATION_KEYWORDS): return "validation" if any(keyword in lowered for keyword in _RESEARCH_KEYWORDS): return "web_research" return "reasoning" def _split_goal_chunks(goal: str) -> list[str]: normalized = goal.replace("->", " -> ").replace(",", " , ").replace(";", " ; ") tokens = normalized.split() chunks: list[str] = [] current_tokens: list[str] = [] index = 0 while index < len(tokens): token = tokens[index] lowered = token.lower() next_lowered = tokens[index + 1].lower() if index + 1 < len(tokens) else "" is_separator = ( token in _PUNCTUATION_SEPARATORS or lowered in _WORD_SEPARATORS or (lowered == "un" and next_lowered == "tad") ) if is_separator: if current_tokens: chunks.append(" ".join(current_tokens).strip(" -")) current_tokens = [] index += 2 if lowered == "un" and next_lowered == "tad" else 1 continue current_tokens.append(token) index += 1 if current_tokens: chunks.append(" ".join(current_tokens).strip(" -")) return [chunk for chunk in chunks if chunk]