mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-07-12 13:29:04 +00:00
73 lines
2.5 KiB
Python
73 lines
2.5 KiB
Python
from dataclasses import dataclass
|
|
from datetime import datetime, timezone
|
|
from enum import Enum
|
|
from typing import Any, Optional
|
|
from uuid import UUID, uuid4
|
|
|
|
|
|
class SubTaskFailure(Exception):
|
|
"""A *recorded* failure: the work ran, its outcome belongs on the SubTask
|
|
record, and the message must NOT be retried (ADR-0055 — status is a
|
|
record, not retry fuel). Infra crashes raise anything else and keep their
|
|
retry semantics. ``details`` lands on the failed SubTask's outputs."""
|
|
|
|
def __init__(
|
|
self, message: str, details: Optional[dict[str, Any]] = None
|
|
) -> None:
|
|
super().__init__(message)
|
|
self.details = details
|
|
|
|
|
|
class SubTaskStatus(str, Enum):
|
|
WAITING = "waiting"
|
|
IN_PROGRESS = "in progress"
|
|
COMPLETE = "complete"
|
|
FAILED = "failed"
|
|
|
|
|
|
@dataclass
|
|
class SubTask:
|
|
id: UUID
|
|
task_id: UUID
|
|
status: SubTaskStatus = SubTaskStatus.WAITING
|
|
inputs: Optional[dict[str, Any]] = None
|
|
outputs: Optional[dict[str, Any]] = None
|
|
cloud_logs_url: Optional[str] = None
|
|
job_started: Optional[datetime] = None
|
|
job_completed: Optional[datetime] = None
|
|
|
|
@classmethod
|
|
def create(
|
|
cls, *, task_id: UUID, inputs: Optional[dict[str, Any]] = None
|
|
) -> "SubTask":
|
|
return cls(
|
|
id=uuid4(),
|
|
task_id=task_id,
|
|
status=SubTaskStatus.WAITING,
|
|
inputs=inputs,
|
|
)
|
|
|
|
def start(self, cloud_logs_url: Optional[str] = None) -> None:
|
|
# FAILED may restart: a failed batch is re-run by re-sending its own
|
|
# inputs, and its completion un-fails the parent Task (ADR-0055).
|
|
if self.status is SubTaskStatus.COMPLETE:
|
|
raise ValueError(f"cannot start subtask in status {self.status}")
|
|
if self.job_started is None:
|
|
self.job_started = datetime.now(timezone.utc)
|
|
self.status = SubTaskStatus.IN_PROGRESS
|
|
self.job_completed = None
|
|
if cloud_logs_url is not None:
|
|
self.cloud_logs_url = cloud_logs_url
|
|
|
|
def complete(self, result: Any = None) -> None:
|
|
self.status = SubTaskStatus.COMPLETE
|
|
self.job_completed = datetime.now(timezone.utc)
|
|
if result is not None:
|
|
self.outputs = {"result": result}
|
|
|
|
def fail(self, error: BaseException) -> None:
|
|
self.status = SubTaskStatus.FAILED
|
|
self.job_completed = datetime.now(timezone.utc)
|
|
self.outputs = {"error": str(error)}
|
|
if isinstance(error, SubTaskFailure) and error.details is not None:
|
|
self.outputs.update(error.details)
|