feat(C1+C2): workflow system — WorkflowDefinition + Celery Canvas builder

Migrations 037 (workflow tables + 3 seed definitions) + 038 (output_types.workflow_definition_id).
WorkflowDefinition/Run/NodeResult SQLAlchemy models in domains/rendering/models.py.
workflow_builder.py: dispatch_workflow() with Celery Canvas for still/turntable/multi_angle.
workflow_router.py: CRUD endpoints at /api/workflows (admin/PM guards).
dispatch_service.py: dispatch_render_with_workflow() prefers workflow path when
  OutputType.workflow_definition_id is set, falls back to legacy dispatch otherwise.
main.py: registers workflows_router.
models/__init__.py: re-exports WorkflowDefinition, WorkflowRun, WorkflowNodeResult.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 17:07:21 +01:00
parent 217555025f
commit 7e47e4aca7
9 changed files with 512 additions and 1 deletions
+48
View File
@@ -89,3 +89,51 @@ class RenderPositionOut(BaseModel):
updated_at: datetime
model_config = {"from_attributes": True}
class WorkflowDefinitionCreate(BaseModel):
name: str
output_type_id: uuid.UUID | None = None
config: dict
is_active: bool = True
class WorkflowDefinitionUpdate(BaseModel):
name: str | None = None
config: dict | None = None
is_active: bool | None = None
class WorkflowDefinitionOut(BaseModel):
id: uuid.UUID
name: str
output_type_id: uuid.UUID | None
config: dict
is_active: bool
created_at: datetime
model_config = {"from_attributes": True}
class WorkflowNodeResultOut(BaseModel):
id: uuid.UUID
node_name: str
status: str
output: dict | None
log: str | None
duration_s: float | None
created_at: datetime
model_config = {"from_attributes": True}
class WorkflowRunOut(BaseModel):
id: uuid.UUID
workflow_def_id: uuid.UUID | None
order_line_id: uuid.UUID | None
celery_task_id: str | None
status: str
started_at: datetime | None
completed_at: datetime | None
error_message: str | None
created_at: datetime
node_results: list[WorkflowNodeResultOut] = []
model_config = {"from_attributes": True}