feat: V1 - DAG编排/告警系统/Agent循环/知识库RAG/Webhook + 审核管理富上下文修复

This commit is contained in:
2026-08-13 00:46:47 +08:00
parent 13f5e5b5c4
commit 3937b4434d
+95
View File
@@ -0,0 +1,95 @@
"""DAG orchestration: execute tasks respecting dependencies."""
import json
from concurrent.futures import ThreadPoolExecutor, as_completed
from sqlalchemy.orm import Session
from typing import List, Dict
from app.models.task import Task
from app.services.execution_service import ExecutionService
from app.services.alert_service import AlertService
class DAGService:
@staticmethod
def execute_dag(db: Session, tenant_id: int, project_id: int, task_ids: List[int]) -> Dict:
"""Execute tasks respecting depends_on. Parallel for independent tasks."""
tasks = db.query(Task).filter(
Task.id.in_(task_ids), Task.tenant_id == tenant_id
).all()
if not tasks:
return {"success": 0, "failed": 0, "blocked": 0, "skipped": 0}
results = {"success": 0, "failed": 0, "blocked": 0, "skipped": 0}
completed = set()
failed = set()
remaining = {t.id: t for t in tasks}
while remaining:
# Find tasks whose deps are all completed
ready = []
for tid, task in list(remaining.items()):
deps = json.loads(task.depends_on or "[]")
deps = [d for d in deps if d in {t.id for t in tasks}] # only consider our set
if not deps:
ready.append(task)
elif all(d in completed for d in deps):
ready.append(task)
elif any(d in failed for d in deps):
# Dependency failed -> block this task
task.status = "cancelled"
results["blocked"] += 1
del remaining[tid]
if not ready:
break # no more executable tasks
# Execute ready tasks in parallel
for task in ready:
if task.id in remaining:
del remaining[task.id]
with ThreadPoolExecutor(max_workers=3) as pool:
futures = {}
for task in ready:
if task.status in ("done", "cancelled"):
results["skipped"] += 1
completed.add(task.id)
continue
fut = pool.submit(
DAGService._exec_one, db, tenant_id, task.id
)
futures[fut] = task.id
for fut in as_completed(futures):
tid = futures[fut]
try:
result = fut.result()
if result.get("status") in ("success", "needs_review"):
results["success"] += 1
completed.add(tid)
else:
results["failed"] += 1
failed.add(tid)
except Exception as e:
results["failed"] += 1
failed.add(tid)
# Check budget after each wave
try:
AlertService.check_and_alert(db, tenant_id, project_id)
except Exception:
pass
return results
@staticmethod
def _exec_one(db: Session, tenant_id: int, task_id: int):
"""Execute a single task in a thread-safe way."""
from app.database import SessionLocal
session = SessionLocal()
try:
result = ExecutionService.execute(session, tenant_id, task_id)
return {"status": result.status, "task_id": task_id}
except Exception as e:
return {"status": "error", "task_id": task_id, "error": str(e)}
finally:
session.close()