feat(tasks): add skill-templated task graph execution

This commit is contained in:
2026-06-23 10:22:58 +08:00
parent 6843d89b2c
commit 53b13e8eac
53 changed files with 4773 additions and 756 deletions

View File

@ -43,6 +43,7 @@ from beaver.services.user_files import (
UserFileNotFoundError,
UserFilePathError,
UserFileSizeError,
UserFileStorageError,
UserFileService,
)
from beaver.services.user_file_resolver import (
@ -644,6 +645,8 @@ def create_app(
return HTTPException(status_code=400, detail=str(exc) or "Invalid path")
if isinstance(exc, UserFileSizeError):
return HTTPException(status_code=413, detail=str(exc) or "File too large")
if isinstance(exc, UserFileStorageError):
return HTTPException(status_code=503, detail=str(exc) or "User file storage is unavailable")
if isinstance(exc, UserFileConfigurationError):
return HTTPException(status_code=503, detail=str(exc) or "User file storage is not configured")
return HTTPException(status_code=400, detail=str(exc) or "User file operation failed")
@ -1327,6 +1330,7 @@ def create_app(
"runs": runs,
}
)
sessions.sort(key=lambda item: item.get("updated_at") or item.get("created_at") or "", reverse=True)
return {"sessions": sessions}
@app.post("/api/sessions/{session_id:path}/archive")
@ -3166,6 +3170,11 @@ def _debug_runs_for_session(session_manager: Any, session_id: str) -> list[dict[
title = getattr(started, "title", None)
if title is None:
title = source or "run"
latency_ms = None
if completed is not None and isinstance(completed.event_payload, dict):
raw_latency = completed.event_payload.get("latency_ms")
latency_ms = raw_latency if isinstance(raw_latency, dict) else None
sorted_records = sorted(records, key=lambda item: item.timestamp or 0, reverse=True)
runs.append(
{
"run_id": run_id,
@ -3181,10 +3190,15 @@ def _debug_runs_for_session(session_manager: Any, session_id: str) -> list[dict[
"started_at": _iso_from_timestamp(started.timestamp if started is not None else None),
"ended_at": _iso_from_timestamp(completed.timestamp) if completed is not None else None,
"finish_reason": completed.finish_reason if completed is not None else None,
"events": [_debug_event_to_dict(item) for item in records],
"latency_ms": latency_ms or {},
"events": [_debug_event_to_dict(item) for item in sorted_records],
}
)
return runs
return sorted(
runs,
key=lambda item: item.get("ended_at") or item.get("started_at") or "",
reverse=True,
)
def _debug_event_to_dict(record: Any) -> dict[str, Any]: