feat(workbench): persist operational workflows and events

This commit is contained in:
2026-09-24 04:19:02 +08:00
parent baff7dfe9e
commit 9335bdc1ea
66 changed files with 16580 additions and 238 deletions
+47 -5
View File
@@ -22,6 +22,10 @@ class ProblemDetails(BaseModel):
code: str
trace_id: str
errors: list[dict[str, Any]] = Field(default_factory=list)
retryable: bool = False
related_refs: list[Any] = Field(default_factory=list)
recovery_action: str = "none"
retry_after_seconds: int | None = None
def _trace_id(request: Request) -> str:
@@ -36,6 +40,10 @@ def _problem_response(
detail: str,
code: str,
errors: list[dict[str, Any]] | None = None,
retryable: bool = False,
related_refs: list[Any] | None = None,
recovery_action: str = "none",
retry_after_seconds: int | None = None,
) -> JSONResponse:
problem = ProblemDetails(
type=f"https://tjwater.example/problems/{code.replace('_', '-')}",
@@ -46,6 +54,10 @@ def _problem_response(
code=code,
trace_id=_trace_id(request),
errors=errors or [],
retryable=retryable,
related_refs=related_refs or [],
recovery_action=recovery_action,
retry_after_seconds=retry_after_seconds,
)
return JSONResponse(
status_code=status_code,
@@ -89,7 +101,6 @@ def install_problem_details_handlers(app: FastAPI) -> None:
@app.exception_handler(HTTPException)
async def http_error_handler(request: Request, exc: HTTPException) -> JSONResponse:
detail = exc.detail if isinstance(exc.detail, str) else str(exc.detail)
code_by_status = {
401: "unauthenticated",
403: "forbidden",
@@ -98,12 +109,43 @@ def install_problem_details_handlers(app: FastAPI) -> None:
422: "validation_error",
503: "dependency_unavailable",
}
fallback_code = code_by_status.get(exc.status_code, "request_error")
detail_payload = exc.detail if isinstance(exc.detail, dict) else None
code = (
detail_payload.get("code")
if detail_payload and isinstance(detail_payload.get("code"), str)
else fallback_code
)
detail = exc.detail if isinstance(exc.detail, str) else None
if detail_payload:
candidate = detail_payload.get("message") or detail_payload.get("detail")
if isinstance(candidate, str):
detail = candidate
if detail is None:
detail = code.replace("_", " ").title()
related_refs = detail_payload.get("related_refs", []) if detail_payload else []
if not isinstance(related_refs, list):
related_refs = []
recovery_action = (
detail_payload.get("recovery_action", "none") if detail_payload else "none"
)
if not isinstance(recovery_action, str):
recovery_action = "none"
retry_after_seconds = (
detail_payload.get("retry_after_seconds") if detail_payload else None
)
if not isinstance(retry_after_seconds, int):
retry_after_seconds = None
return _problem_response(
request,
status_code=exc.status_code,
title=code_by_status.get(exc.status_code, "request_error")
.replace("_", " ")
.title(),
title=fallback_code.replace("_", " ").title(),
detail=detail,
code=code_by_status.get(exc.status_code, "request_error"),
code=code,
retryable=(
bool(detail_payload.get("retryable", False)) if detail_payload else False
),
related_refs=related_refs,
recovery_action=recovery_action,
retry_after_seconds=retry_after_seconds,
)