cc-codeconductor 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +2642 -226
- package/package.json +1 -1
- package/presets/agy/skills/evaluation/SKILL.md +6 -0
- package/presets/agy/skills/openspec/SKILL.md +32 -0
- package/presets/agy/workflows/cc-openspec.md +62 -0
- package/presets/agy/workflows/cc-scorecard.md +17 -0
- package/presets/claude/commands/cc/openspec.md +144 -0
- package/presets/claude/commands/cc/review.md +13 -2
- package/presets/claude/commands/cc/scorecard.md +65 -0
- package/presets/claude/skills/evaluation/SKILL.md +42 -0
- package/presets/claude/skills/openspec/SKILL.md +54 -0
- package/presets/codex/AGENTS.md +57 -0
- package/presets/cursor/.cursorignore +15 -0
- package/presets/cursor/AGENTS.md +504 -0
- package/presets/cursor/agents/architect.md +211 -0
- package/presets/cursor/agents/complexity-auditor.md +76 -0
- package/presets/cursor/agents/contract-builder.md +75 -0
- package/presets/cursor/agents/docs.md +180 -0
- package/presets/cursor/agents/goal-planner.md +71 -0
- package/presets/cursor/agents/implementer.md +161 -0
- package/presets/cursor/agents/orchestrator.md +377 -0
- package/presets/cursor/agents/repo-explorer.md +100 -0
- package/presets/cursor/agents/reviewer.md +237 -0
- package/presets/cursor/agents/security-reviewer.md +113 -0
- package/presets/cursor/agents/task-coach.md +145 -0
- package/presets/cursor/agents/tester.md +241 -0
- package/presets/cursor/commands/cc/api-contract.md +58 -0
- package/presets/cursor/commands/cc/db-migration.md +58 -0
- package/presets/cursor/commands/cc/feature.md +115 -0
- package/presets/cursor/commands/cc/fix.md +121 -0
- package/presets/cursor/commands/cc/openspec.md +144 -0
- package/presets/cursor/commands/cc/pagespeed.md +103 -0
- package/presets/cursor/commands/cc/refactor.md +148 -0
- package/presets/cursor/commands/cc/review.md +137 -0
- package/presets/cursor/commands/cc/scorecard.md +65 -0
- package/presets/cursor/commands/cc/tdd-cycle.md +226 -0
- package/presets/cursor/commands/cc/test-plan.md +138 -0
- package/presets/cursor/rules/behavioral-discipline.mdc +14 -0
- package/presets/cursor/rules/context-budget.mdc +12 -0
- package/presets/cursor/rules/orchestration.mdc +12 -0
- package/presets/cursor/rules/yagni-stdlib.mdc +11 -0
- package/presets/cursor/skills/android/SKILL.md +122 -0
- package/presets/cursor/skills/api-versioning/SKILL.md +394 -0
- package/presets/cursor/skills/astro/SKILL.md +322 -0
- package/presets/cursor/skills/auth-token-inspector/SKILL.md +33 -0
- package/presets/cursor/skills/code-review/SKILL.md +208 -0
- package/presets/cursor/skills/conductor-setup/SKILL.md +127 -0
- package/presets/cursor/skills/django-orm/SKILL.md +463 -0
- package/presets/cursor/skills/django-testing/SKILL.md +417 -0
- package/presets/cursor/skills/django-uv/SKILL.md +409 -0
- package/presets/cursor/skills/drizzle-schema-architect/SKILL.md +54 -0
- package/presets/cursor/skills/evaluation/SKILL.md +8 -0
- package/presets/cursor/skills/fastapi-pydantic-strict/SKILL.md +46 -0
- package/presets/cursor/skills/find-skills/SKILL.md +144 -0
- package/presets/cursor/skills/jpa-nplusone-detector/SKILL.md +49 -0
- package/presets/cursor/skills/jpa-postgres/SKILL.md +626 -0
- package/presets/cursor/skills/laravel-specialist/SKILL.md +267 -0
- package/presets/cursor/skills/laravel-specialist/references/eloquent.md +351 -0
- package/presets/cursor/skills/laravel-specialist/references/livewire.md +512 -0
- package/presets/cursor/skills/laravel-specialist/references/queues.md +423 -0
- package/presets/cursor/skills/laravel-specialist/references/routing.md +362 -0
- package/presets/cursor/skills/laravel-specialist/references/testing.md +522 -0
- package/presets/cursor/skills/livewire-alpine-bridge/SKILL.md +39 -0
- package/presets/cursor/skills/multi-agent-orchestration/README.md +144 -0
- package/presets/cursor/skills/multi-agent-orchestration/SKILL.md +579 -0
- package/presets/cursor/skills/multi-agent-orchestration/examples/framework_implementations.py +362 -0
- package/presets/cursor/skills/multi-agent-orchestration/examples/orchestration_patterns.py +411 -0
- package/presets/cursor/skills/multi-agent-orchestration/scripts/agent_communication.py +334 -0
- package/presets/cursor/skills/multi-agent-orchestration/scripts/benchmarking.py +341 -0
- package/presets/cursor/skills/multi-agent-orchestration/scripts/workflow_management.py +334 -0
- package/presets/cursor/skills/nextjs-typescript/SKILL.md +394 -0
- package/presets/cursor/skills/openspec/SKILL.md +52 -0
- package/presets/cursor/skills/pagespeed-insights/SKILL.md +445 -0
- package/presets/cursor/skills/pagespeed-insights/reference.md +50 -0
- package/presets/cursor/skills/pagespeed-perf/SKILL.md +281 -0
- package/presets/cursor/skills/php-pro/SKILL.md +210 -0
- package/presets/cursor/skills/php-pro/references/async-patterns.md +412 -0
- package/presets/cursor/skills/php-pro/references/laravel-patterns.md +377 -0
- package/presets/cursor/skills/php-pro/references/modern-php-features.md +323 -0
- package/presets/cursor/skills/php-pro/references/symfony-patterns.md +466 -0
- package/presets/cursor/skills/php-pro/references/testing-quality.md +466 -0
- package/presets/cursor/skills/python/SKILL.md +613 -0
- package/presets/cursor/skills/python-django-stack/SKILL.md +500 -0
- package/presets/cursor/skills/python-fastapi-stack/SKILL.md +464 -0
- package/presets/cursor/skills/security/SKILL.md +384 -0
- package/presets/cursor/skills/seo-analytics-injector/SKILL.md +44 -0
- package/presets/cursor/skills/spring-auth-auditor/SKILL.md +33 -0
- package/presets/cursor/skills/spring-boot-feature/SKILL.md +566 -0
- package/presets/cursor/skills/spring-boot-kotlin/SKILL.md +408 -0
- package/presets/cursor/skills/spring-boot-testing-strategy/SKILL.md +479 -0
- package/presets/cursor/skills/sqlalchemy/SKILL.md +473 -0
- package/presets/cursor/skills/tailwind-responsive-auditor/SKILL.md +32 -0
- package/presets/cursor/skills/tdd-mutation-tester/SKILL.md +28 -0
- package/presets/cursor/skills/testing-tdd/SKILL.md +592 -0
- package/presets/cursor/skills/workflow-orchestration-patterns/SKILL.md +98 -0
- package/presets/cursor/skills/workflow-orchestration-patterns/references/details.md +223 -0
- package/presets/opencode/agents/architect.md +1 -2
- package/presets/opencode/agents/complexity-auditor.md +1 -0
- package/presets/opencode/agents/contract-builder.md +93 -0
- package/presets/opencode/agents/docs.md +1 -2
- package/presets/opencode/agents/goal-planner.md +82 -0
- package/presets/opencode/agents/implementer.md +9 -2
- package/presets/opencode/agents/orchestrator.md +50 -12
- package/presets/opencode/agents/repo-explorer.md +0 -1
- package/presets/opencode/agents/reviewer.md +23 -2
- package/presets/opencode/agents/security-reviewer.md +129 -0
- package/presets/opencode/agents/task-coach.md +1 -2
- package/presets/opencode/agents/tester.md +1 -2
- package/presets/opencode/commands/cc-openspec.md +61 -0
- package/presets/opencode/commands/cc-scorecard.md +16 -0
- package/presets/opencode/prompts/v0.5.0/architect.md +222 -0
- package/presets/opencode/prompts/v0.5.0/complexity-auditor.md +91 -0
- package/presets/opencode/prompts/v0.5.0/contract-builder.md +84 -0
- package/presets/opencode/prompts/v0.5.0/docs.md +190 -0
- package/presets/opencode/prompts/v0.5.0/goal-planner.md +80 -0
- package/presets/opencode/prompts/v0.5.0/implementer.md +171 -0
- package/presets/opencode/prompts/v0.5.0/orchestrator.md +388 -0
- package/presets/opencode/prompts/v0.5.0/repo-explorer.md +111 -0
- package/presets/opencode/prompts/v0.5.0/reviewer.md +248 -0
- package/presets/opencode/prompts/v0.5.0/security-reviewer.md +123 -0
- package/presets/opencode/prompts/v0.5.0/task-coach.md +156 -0
- package/presets/opencode/prompts/v0.5.0/tester.md +252 -0
- package/presets/opencode/skills/evaluation/SKILL.md +6 -0
- package/presets/opencode/skills/openspec/SKILL.md +50 -0
- package/presets/templates/BACKLOG.md +33 -0
- package/presets/templates/execution-profile.yml +6 -0
- package/presets/templates/model-comparison.md +11 -0
- package/presets/templates/regression-checklist.yml +10 -0
- package/src/presets/manifests/agy.yml +2 -2
- package/src/presets/manifests/claude.yml +2 -2
- package/src/presets/manifests/codex.yml +2 -2
- package/src/presets/manifests/cursor.yml +19 -3
- package/src/presets/manifests/gemini.yml +2 -2
- package/src/presets/manifests/opencode.yml +2 -2
- package/src/presets/models/agy.yml +21 -0
- package/src/presets/models/claude.yml +18 -0
- package/src/presets/models/codex.yml +18 -0
- package/src/presets/models/cursor.yml +39 -9
- package/src/presets/models/gemini.yml +18 -0
- package/src/presets/models/opencode.yml +18 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Workflow Management for Multi-Agent Systems
|
|
3
|
+
|
|
4
|
+
Handle workflow execution, monitoring, and optimization.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Dict, List, Any, Optional, Callable
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class WorkflowStatus(Enum):
|
|
14
|
+
"""Workflow execution status."""
|
|
15
|
+
|
|
16
|
+
PENDING = "pending"
|
|
17
|
+
RUNNING = "running"
|
|
18
|
+
PAUSED = "paused"
|
|
19
|
+
COMPLETED = "completed"
|
|
20
|
+
FAILED = "failed"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TaskStatus(Enum):
|
|
24
|
+
"""Task execution status."""
|
|
25
|
+
|
|
26
|
+
PENDING = "pending"
|
|
27
|
+
RUNNING = "running"
|
|
28
|
+
COMPLETED = "completed"
|
|
29
|
+
FAILED = "failed"
|
|
30
|
+
SKIPPED = "skipped"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class Task:
|
|
35
|
+
"""Task to be executed by an agent."""
|
|
36
|
+
|
|
37
|
+
task_id: str
|
|
38
|
+
agent: str
|
|
39
|
+
description: str
|
|
40
|
+
status: TaskStatus = TaskStatus.PENDING
|
|
41
|
+
result: Optional[str] = None
|
|
42
|
+
error: Optional[str] = None
|
|
43
|
+
start_time: float = field(default_factory=time.time)
|
|
44
|
+
end_time: Optional[float] = None
|
|
45
|
+
dependencies: List[str] = field(default_factory=list)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class Workflow:
|
|
50
|
+
"""Workflow orchestration."""
|
|
51
|
+
|
|
52
|
+
workflow_id: str
|
|
53
|
+
name: str
|
|
54
|
+
tasks: Dict[str, Task] = field(default_factory=dict)
|
|
55
|
+
status: WorkflowStatus = WorkflowStatus.PENDING
|
|
56
|
+
start_time: Optional[float] = None
|
|
57
|
+
end_time: Optional[float] = None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class WorkflowExecutor:
|
|
61
|
+
"""Execute workflows with multiple agents."""
|
|
62
|
+
|
|
63
|
+
def __init__(self):
|
|
64
|
+
"""Initialize workflow executor."""
|
|
65
|
+
self.workflows: Dict[str, Workflow] = {}
|
|
66
|
+
self.execution_history: List[Dict] = []
|
|
67
|
+
|
|
68
|
+
def create_workflow(self, workflow_id: str, name: str) -> Workflow:
|
|
69
|
+
"""Create new workflow."""
|
|
70
|
+
workflow = Workflow(workflow_id=workflow_id, name=name)
|
|
71
|
+
self.workflows[workflow_id] = workflow
|
|
72
|
+
return workflow
|
|
73
|
+
|
|
74
|
+
def add_task(
|
|
75
|
+
self,
|
|
76
|
+
workflow_id: str,
|
|
77
|
+
task_id: str,
|
|
78
|
+
agent: str,
|
|
79
|
+
description: str,
|
|
80
|
+
dependencies: Optional[List[str]] = None,
|
|
81
|
+
) -> Task:
|
|
82
|
+
"""Add task to workflow."""
|
|
83
|
+
task = Task(
|
|
84
|
+
task_id=task_id,
|
|
85
|
+
agent=agent,
|
|
86
|
+
description=description,
|
|
87
|
+
dependencies=dependencies or [],
|
|
88
|
+
)
|
|
89
|
+
self.workflows[workflow_id].tasks[task_id] = task
|
|
90
|
+
return task
|
|
91
|
+
|
|
92
|
+
def execute_workflow(
|
|
93
|
+
self, workflow_id: str, executor_func: Callable
|
|
94
|
+
) -> Dict[str, Any]:
|
|
95
|
+
"""
|
|
96
|
+
Execute workflow.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
workflow_id: Workflow ID
|
|
100
|
+
executor_func: Function to execute tasks
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Execution results
|
|
104
|
+
"""
|
|
105
|
+
workflow = self.workflows[workflow_id]
|
|
106
|
+
workflow.status = WorkflowStatus.RUNNING
|
|
107
|
+
workflow.start_time = time.time()
|
|
108
|
+
|
|
109
|
+
executed_tasks = set()
|
|
110
|
+
results = {}
|
|
111
|
+
|
|
112
|
+
while len(executed_tasks) < len(workflow.tasks):
|
|
113
|
+
# Find ready tasks
|
|
114
|
+
ready_tasks = self._get_ready_tasks(workflow, executed_tasks)
|
|
115
|
+
|
|
116
|
+
if not ready_tasks:
|
|
117
|
+
# Check if workflow is stuck
|
|
118
|
+
if len(executed_tasks) > 0:
|
|
119
|
+
break
|
|
120
|
+
|
|
121
|
+
for task_id in ready_tasks:
|
|
122
|
+
task = workflow.tasks[task_id]
|
|
123
|
+
task.status = TaskStatus.RUNNING
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
# Execute task
|
|
127
|
+
result = executor_func(task)
|
|
128
|
+
task.result = result
|
|
129
|
+
task.status = TaskStatus.COMPLETED
|
|
130
|
+
results[task_id] = result
|
|
131
|
+
except Exception as e:
|
|
132
|
+
task.error = str(e)
|
|
133
|
+
task.status = TaskStatus.FAILED
|
|
134
|
+
results[task_id] = None
|
|
135
|
+
|
|
136
|
+
task.end_time = time.time()
|
|
137
|
+
executed_tasks.add(task_id)
|
|
138
|
+
|
|
139
|
+
# Finalize workflow
|
|
140
|
+
workflow.end_time = time.time()
|
|
141
|
+
if all(task.status == TaskStatus.COMPLETED for task in workflow.tasks.values()):
|
|
142
|
+
workflow.status = WorkflowStatus.COMPLETED
|
|
143
|
+
else:
|
|
144
|
+
workflow.status = WorkflowStatus.FAILED
|
|
145
|
+
|
|
146
|
+
execution_record = {
|
|
147
|
+
"workflow_id": workflow_id,
|
|
148
|
+
"status": workflow.status.value,
|
|
149
|
+
"duration": workflow.end_time - workflow.start_time,
|
|
150
|
+
"results": results,
|
|
151
|
+
}
|
|
152
|
+
self.execution_history.append(execution_record)
|
|
153
|
+
|
|
154
|
+
return results
|
|
155
|
+
|
|
156
|
+
def _get_ready_tasks(
|
|
157
|
+
self, workflow: Workflow, executed: set
|
|
158
|
+
) -> List[str]:
|
|
159
|
+
"""Get tasks ready to execute."""
|
|
160
|
+
ready = []
|
|
161
|
+
for task_id, task in workflow.tasks.items():
|
|
162
|
+
if task_id not in executed and task.status == TaskStatus.PENDING:
|
|
163
|
+
# Check if all dependencies are complete
|
|
164
|
+
deps_met = all(dep in executed for dep in task.dependencies)
|
|
165
|
+
if deps_met:
|
|
166
|
+
ready.append(task_id)
|
|
167
|
+
|
|
168
|
+
return ready
|
|
169
|
+
|
|
170
|
+
def get_workflow_status(self, workflow_id: str) -> Dict[str, Any]:
|
|
171
|
+
"""Get workflow status."""
|
|
172
|
+
workflow = self.workflows[workflow_id]
|
|
173
|
+
return {
|
|
174
|
+
"id": workflow.workflow_id,
|
|
175
|
+
"name": workflow.name,
|
|
176
|
+
"status": workflow.status.value,
|
|
177
|
+
"tasks": {
|
|
178
|
+
task_id: task.status.value
|
|
179
|
+
for task_id, task in workflow.tasks.items()
|
|
180
|
+
},
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class WorkflowOptimizer:
|
|
185
|
+
"""Optimize workflow execution."""
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def analyze_dependencies(workflow: Workflow) -> Dict[str, Any]:
|
|
189
|
+
"""Analyze task dependencies."""
|
|
190
|
+
dep_graph = {}
|
|
191
|
+
for task_id, task in workflow.tasks.items():
|
|
192
|
+
dep_graph[task_id] = task.dependencies
|
|
193
|
+
|
|
194
|
+
# Find critical path
|
|
195
|
+
critical_path = WorkflowOptimizer._find_critical_path(
|
|
196
|
+
dep_graph, workflow.tasks
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Find parallelizable tasks
|
|
200
|
+
parallel_groups = WorkflowOptimizer._find_parallel_groups(dep_graph)
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
"dependency_graph": dep_graph,
|
|
204
|
+
"critical_path": critical_path,
|
|
205
|
+
"parallelizable_groups": parallel_groups,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def _find_critical_path(dep_graph: Dict, tasks: Dict) -> List[str]:
|
|
210
|
+
"""Find tasks on critical path."""
|
|
211
|
+
# Simple implementation - actual critical path is more complex
|
|
212
|
+
return list(dep_graph.keys())
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def _find_parallel_groups(dep_graph: Dict) -> List[List[str]]:
|
|
216
|
+
"""Find groups of tasks that can execute in parallel."""
|
|
217
|
+
groups = []
|
|
218
|
+
remaining = set(dep_graph.keys())
|
|
219
|
+
|
|
220
|
+
while remaining:
|
|
221
|
+
# Find tasks with no dependencies in remaining set
|
|
222
|
+
independent = [
|
|
223
|
+
task
|
|
224
|
+
for task in remaining
|
|
225
|
+
if not any(dep in remaining for dep in dep_graph.get(task, []))
|
|
226
|
+
]
|
|
227
|
+
if independent:
|
|
228
|
+
groups.append(independent)
|
|
229
|
+
remaining -= set(independent)
|
|
230
|
+
else:
|
|
231
|
+
break
|
|
232
|
+
|
|
233
|
+
return groups
|
|
234
|
+
|
|
235
|
+
@staticmethod
|
|
236
|
+
def estimate_execution_time(
|
|
237
|
+
workflow: Workflow, task_times: Dict[str, float]
|
|
238
|
+
) -> float:
|
|
239
|
+
"""Estimate total execution time."""
|
|
240
|
+
parallel_groups = WorkflowOptimizer._find_parallel_groups({
|
|
241
|
+
task_id: task.dependencies
|
|
242
|
+
for task_id, task in workflow.tasks.items()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
total_time = 0
|
|
246
|
+
for group in parallel_groups:
|
|
247
|
+
group_time = max(
|
|
248
|
+
task_times.get(task_id, 1.0) for task_id in group
|
|
249
|
+
)
|
|
250
|
+
total_time += group_time
|
|
251
|
+
|
|
252
|
+
return total_time
|
|
253
|
+
|
|
254
|
+
@staticmethod
|
|
255
|
+
def suggest_parallelization(workflow: Workflow) -> List[Dict[str, Any]]:
|
|
256
|
+
"""Suggest ways to parallelize workflow."""
|
|
257
|
+
suggestions = []
|
|
258
|
+
|
|
259
|
+
dep_graph = {
|
|
260
|
+
task_id: task.dependencies
|
|
261
|
+
for task_id, task in workflow.tasks.items()
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
# Find sequential chains that could be parallelized
|
|
265
|
+
for task_id, dependencies in dep_graph.items():
|
|
266
|
+
if len(dependencies) == 0:
|
|
267
|
+
suggestions.append({
|
|
268
|
+
"task": task_id,
|
|
269
|
+
"suggestion": "Can be parallelized with other independent tasks",
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
return suggestions
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class WorkflowMonitor:
|
|
276
|
+
"""Monitor workflow execution."""
|
|
277
|
+
|
|
278
|
+
def __init__(self):
|
|
279
|
+
"""Initialize monitor."""
|
|
280
|
+
self.events: List[Dict] = []
|
|
281
|
+
|
|
282
|
+
def record_event(
|
|
283
|
+
self, workflow_id: str, task_id: str, event_type: str, data: Dict
|
|
284
|
+
) -> None:
|
|
285
|
+
"""Record workflow event."""
|
|
286
|
+
event = {
|
|
287
|
+
"workflow_id": workflow_id,
|
|
288
|
+
"task_id": task_id,
|
|
289
|
+
"event_type": event_type,
|
|
290
|
+
"timestamp": time.time(),
|
|
291
|
+
"data": data,
|
|
292
|
+
}
|
|
293
|
+
self.events.append(event)
|
|
294
|
+
|
|
295
|
+
def get_workflow_metrics(self, workflow_id: str) -> Dict[str, Any]:
|
|
296
|
+
"""Get workflow metrics."""
|
|
297
|
+
workflow_events = [e for e in self.events if e["workflow_id"] == workflow_id]
|
|
298
|
+
|
|
299
|
+
task_times = {}
|
|
300
|
+
for event in workflow_events:
|
|
301
|
+
task_id = event["task_id"]
|
|
302
|
+
if event["event_type"] == "start":
|
|
303
|
+
task_times[task_id] = {"start": event["timestamp"]}
|
|
304
|
+
elif event["event_type"] == "complete":
|
|
305
|
+
if task_id in task_times:
|
|
306
|
+
task_times[task_id]["end"] = event["timestamp"]
|
|
307
|
+
|
|
308
|
+
# Calculate durations
|
|
309
|
+
durations = {
|
|
310
|
+
task_id: times.get("end", time.time()) - times["start"]
|
|
311
|
+
for task_id, times in task_times.items()
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
"total_tasks": len(task_times),
|
|
316
|
+
"task_durations": durations,
|
|
317
|
+
"avg_duration": sum(durations.values()) / len(durations)
|
|
318
|
+
if durations
|
|
319
|
+
else 0,
|
|
320
|
+
"max_duration": max(durations.values()) if durations else 0,
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
def get_performance_report(self) -> Dict[str, Any]:
|
|
324
|
+
"""Get overall performance report."""
|
|
325
|
+
if not self.events:
|
|
326
|
+
return {"status": "no_events"}
|
|
327
|
+
|
|
328
|
+
workflow_ids = set(e["workflow_id"] for e in self.events)
|
|
329
|
+
|
|
330
|
+
report = {}
|
|
331
|
+
for workflow_id in workflow_ids:
|
|
332
|
+
report[workflow_id] = self.get_workflow_metrics(workflow_id)
|
|
333
|
+
|
|
334
|
+
return report
|