PlanOpticon

planopticon / video_processor / agent / skills / task_breakdown.py
Source Blame History 76 lines
0981a08… noreply 1 """Skill: Break down goals into tasks with dependencies."""
0981a08… noreply 2
0981a08… noreply 3 from video_processor.agent.skills.base import (
0981a08… noreply 4 AgentContext,
0981a08… noreply 5 Artifact,
0981a08… noreply 6 Skill,
0981a08… noreply 7 register_skill,
0981a08… noreply 8 )
0981a08… noreply 9 from video_processor.utils.json_parsing import parse_json_from_response
0981a08… noreply 10
0981a08… noreply 11
0981a08… noreply 12 class TaskBreakdownSkill(Skill):
0981a08… noreply 13 name = "task_breakdown"
0981a08… noreply 14 description = "Break down goals into tasks with dependencies"
0981a08… noreply 15
0981a08… noreply 16 def execute(self, context: AgentContext, **kwargs) -> Artifact:
0981a08… noreply 17 stats = context.query_engine.stats()
0981a08… noreply 18 entities = context.query_engine.entities()
0981a08… noreply 19 relationships = context.query_engine.relationships()
0981a08… noreply 20
0981a08… noreply 21 task_types = {"goal", "feature", "milestone"}
0981a08… noreply 22 relevant = [
0981a08… noreply 23 e for e in context.planning_entities if getattr(e, "type", "").lower() in task_types
0981a08… noreply 24 ]
0981a08… noreply 25
0981a08… noreply 26 parts = [
0981a08… noreply 27 "You are a project manager. Using the following knowledge "
0981a08… noreply 28 "graph context, decompose goals and features into tasks.",
0981a08… noreply 29 "",
0981a08… noreply 30 "## Knowledge Graph Overview",
0981a08… noreply 31 stats.to_text(),
0981a08… noreply 32 "",
0981a08… noreply 33 "## Entities",
0981a08… noreply 34 entities.to_text(),
0981a08… noreply 35 "",
0981a08… noreply 36 "## Relationships",
0981a08… noreply 37 relationships.to_text(),
0981a08… noreply 38 "",
0981a08… noreply 39 "## Goals, Features & Milestones",
0981a08… noreply 40 ]
0981a08… noreply 41 for e in relevant:
0981a08… noreply 42 parts.append(f"- [{getattr(e, 'type', 'unknown')}] {e}")
0981a08… noreply 43
0981a08… noreply 44 if not relevant:
0981a08… noreply 45 parts.append("(No pre-filtered entities; derive tasks from the full context above.)")
0981a08… noreply 46
0981a08… noreply 47 parts.append(
0981a08… noreply 48 "\nReturn a JSON array of task objects with:\n"
0981a08… noreply 49 '- "id": string (e.g. "T1", "T2")\n'
0981a08… noreply 50 '- "title": string\n'
0981a08… noreply 51 '- "description": string\n'
0981a08… noreply 52 '- "depends_on": list of task id strings\n'
0981a08… noreply 53 '- "priority": "high" | "medium" | "low"\n'
0981a08… noreply 54 '- "estimate": string (e.g. "2d", "1w")\n'
0981a08… noreply 55 '- "assignee_role": string\n\n'
0981a08… noreply 56 "Return ONLY the JSON."
0981a08… noreply 57 )
0981a08… noreply 58
0981a08… noreply 59 prompt = "\n".join(parts)
0981a08… noreply 60 response = context.provider_manager.chat(messages=[{"role": "user", "content": prompt}])
0981a08… noreply 61 parsed = parse_json_from_response(response)
0981a08… noreply 62
0981a08… noreply 63 import json
0981a08… noreply 64
0981a08… noreply 65 content = json.dumps(parsed, indent=2) if isinstance(parsed, list) else response
0981a08… noreply 66
0981a08… noreply 67 return Artifact(
0981a08… noreply 68 name="Task Breakdown",
0981a08… noreply 69 content=content,
0981a08… noreply 70 artifact_type="task_list",
0981a08… noreply 71 format="json",
0981a08… noreply 72 metadata={"tasks": parsed if isinstance(parsed, list) else []},
0981a08… noreply 73 )
0981a08… noreply 74
0981a08… noreply 75
0981a08… noreply 76 register_skill(TaskBreakdownSkill())

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button