File size: 8,759 Bytes
eca5751
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
"""Task Planner - Lập kế hoạch cho multi-step tasks."""
from __future__ import annotations

from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field
from enum import Enum


class TaskStatus(str, Enum):
    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"
    SKIPPED = "skipped"


@dataclass
class Task:
    """Một task trong plan."""
    id: int
    description: str
    skill: Optional[str] = None
    tools: List[str] = field(default_factory=list)
    depends_on: List[int] = field(default_factory=list)
    status: TaskStatus = TaskStatus.PENDING
    result: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class Plan:
    """Một execution plan."""
    id: str
    goal: str
    tasks: List[Task] = field(default_factory=list)
    created_at: str = ""
    status: TaskStatus = TaskStatus.PENDING
    
    def add_task(self, task: Task) -> None:
        self.tasks.append(task)
    
    def get_next_task(self) -> Optional[Task]:
        """Get next pending task whose dependencies are met.

        v0.4 fix: out-of-range dep IDs are treated as UNMET (not silently ignored).
        """
        for task in self.tasks:
            if task.status != TaskStatus.PENDING:
                continue
            # Check dependencies
            deps_met = True
            for dep_id in task.depends_on:
                if dep_id < 0 or dep_id >= len(self.tasks):
                    # Invalid dep ID → mark unmet, do NOT silently pass
                    deps_met = False
                    break
                if self.tasks[dep_id].status not in (TaskStatus.COMPLETED, TaskStatus.SKIPPED):
                    deps_met = False
                    break
            if deps_met:
                return task
        return None
    
    def is_complete(self) -> bool:
        return all(t.status in (TaskStatus.COMPLETED, TaskStatus.FAILED, TaskStatus.SKIPPED) for t in self.tasks)
    
    def summary(self) -> Dict[str, Any]:
        return {
            "id": self.id,
            "goal": self.goal,
            "total_tasks": len(self.tasks),
            "completed": sum(1 for t in self.tasks if t.status == TaskStatus.COMPLETED),
            "failed": sum(1 for t in self.tasks if t.status == TaskStatus.FAILED),
            "pending": sum(1 for t in self.tasks if t.status == TaskStatus.PENDING),
            "is_complete": self.is_complete(),
        }


class TaskPlanner:
    """Lập kế hoạch cho complex multi-step tasks.
    
    Features:
    - Decompose goal thành subtasks
    - Identify dependencies
    - Suggest skills/tools per task
    - Track execution status
    
    Usage:
        planner = TaskPlanner()
        plan = planner.create_plan("Build a REST API for todo app")
        for task in plan.tasks:
            print(f"Task {task.id}: {task.description}")
    """
    
    def __init__(self):
        self._plans: List[Plan] = []
        self._next_plan_id = 1
    
    def create_plan(self, goal: str) -> Plan:
        """Create an execution plan for a goal."""
        plan = Plan(
            id=f"plan_{self._next_plan_id}",
            goal=goal,
            created_at=__import__("datetime").datetime.now().isoformat(),
        )
        self._next_plan_id += 1
        
        # Decompose goal into tasks
        tasks = self._decompose(goal)
        for i, task_def in enumerate(tasks):
            task = Task(
                id=i,
                description=task_def["description"],
                skill=task_def.get("skill"),
                tools=task_def.get("tools", []),
                depends_on=task_def.get("depends_on", []),
            )
            plan.add_task(task)
        
        self._plans.append(plan)
        return plan
    
    def _decompose(self, goal: str) -> List[Dict[str, Any]]:
        """Decompose goal into subtasks.
        
        This is a heuristic-based decomposition.
        In production, this would use the LLM itself.
        """
        goal_lower = goal.lower()
        tasks = []
        
        # Common patterns
        if any(kw in goal_lower for kw in ["build", "create", "develop", "implement"]):
            tasks.extend([
                {
                    "description": f"Analyze requirements for: {goal}",
                    "skill": "reasoning",
                    "tools": [],
                },
                {
                    "description": "Design architecture and data models",
                    "skill": "algorithm_design",
                    "tools": [],
                    "depends_on": [0],
                },
                {
                    "description": "Implement core functionality",
                    "skill": "code_generation",
                    "tools": ["file_write", "python_exec"],
                    "depends_on": [1],
                },
                {
                    "description": "Write tests",
                    "skill": "testing",
                    "tools": ["python_exec", "shell_exec"],
                    "depends_on": [2],
                },
                {
                    "description": "Generate documentation",
                    "skill": "documentation",
                    "tools": ["file_write"],
                    "depends_on": [2],
                },
                {
                    "description": "Review and optimize code",
                    "skill": "code_review",
                    "tools": ["code_search", "code_lint"],
                    "depends_on": [3, 4],
                },
            ])
        elif any(kw in goal_lower for kw in ["debug", "fix", "repair"]):
            tasks.extend([
                {
                    "description": "Reproduce the issue",
                    "skill": "debugging",
                    "tools": ["shell_exec", "python_exec"],
                },
                {
                    "description": "Identify root cause",
                    "skill": "debugging",
                    "tools": ["code_search", "regex_search"],
                    "depends_on": [0],
                },
                {
                    "description": "Implement fix",
                    "skill": "code_generation",
                    "tools": ["file_write"],
                    "depends_on": [1],
                },
                {
                    "description": "Verify fix with tests",
                    "skill": "testing",
                    "tools": ["python_exec"],
                    "depends_on": [2],
                },
            ])
        elif any(kw in goal_lower for kw in ["analyze", "investigate", "understand"]):
            tasks.extend([
                {
                    "description": f"Gather information about: {goal}",
                    "skill": "reasoning",
                    "tools": ["web_search", "web_fetch", "file_read"],
                },
                {
                    "description": "Analyze and synthesize findings",
                    "skill": "data_analysis",
                    "tools": ["python_exec"],
                    "depends_on": [0],
                },
                {
                    "description": "Present insights and recommendations",
                    "skill": "summarization",
                    "tools": [],
                    "depends_on": [1],
                },
            ])
        else:
            # Default: single task
            tasks.append({
                "description": f"Handle: {goal}",
                "skill": None,
                "tools": [],
            })
        
        return tasks
    
    def execute_plan(
        self,
        plan: Plan,
        executor=None,
    ) -> Plan:
        """Execute a plan step by step.
        
        Args:
            plan: Plan to execute
            executor: Function(task) -> result (None = simulation)
        """
        while not plan.is_complete():
            task = plan.get_next_task()
            if task is None:
                break
            
            task.status = TaskStatus.IN_PROGRESS
            try:
                if executor:
                    result = executor(task)
                    task.result = result
                    task.status = TaskStatus.COMPLETED
                else:
                    task.status = TaskStatus.COMPLETED
                    task.result = "[simulated]"
            except Exception as e:
                task.status = TaskStatus.FAILED
                task.result = f"Error: {e}"
        
        return plan
    
    def list_plans(self) -> List[Dict[str, Any]]:
        """List all plans."""
        return [p.summary() for p in self._plans]