artifacts-dashboard/backend/app/schemas/pipeline.py
Paweł Orzech 75313b83c0
Add multi-user workflows/pipelines and error tracking
Add multi-user automation features and per-user error tracking.

- Database migrations: add workflow_configs/workflow_runs (004), app_errors (005), pipeline_configs/pipeline_runs (006), and add user_token_hash to app_errors (007).
- Backend: introduce per-request token handling (X-API-Token) via app.api.deps and update many API routes (auth, automations, bank, characters, dashboard, events, exchange, logs) to use user-scoped Artifacts client and character scoping. Auth endpoints no longer store tokens server-side (validate-only); clear is a no-op on server.
- New Errors API and services: endpoint to list, filter, resolve, and report errors scoped to the requesting user; add error models, schemas, middleware/error handler and error_service for recording/hashing tokens.
- Pipelines & Workflows: add API routers, models, schemas and engine modules (pipeline/worker/coordinator, workflow runner/conditions) and action_executor updates to support workflow/pipeline execution.
- Logs: logs endpoint now prefers fetching recent action logs from the game API (with fallback to local DB), supports paging and filtering, and scopes results to the user.
- Frontend: add pipeline/workflow builders, lists, progress components and hooks (use-errors, use-pipelines, use-workflows), sentry client config, and updates to API client/constants/types.
- Misc: add middleware error handler, various engine strategy tweaks, tests adjusted.

Overall this change enables per-user API tokens, scopes DB queries to each user, introduces pipelines/workflows runtime support, and centralizes application error tracking.
2026-03-01 23:02:34 +01:00

127 lines
3.9 KiB
Python

from datetime import datetime
from pydantic import BaseModel, Field
from app.schemas.workflow import TransitionConditionSchema
# ---------------------------------------------------------------------------
# Character step within a stage
# ---------------------------------------------------------------------------
class CharacterStepSchema(BaseModel):
"""A single character's work within a pipeline stage."""
id: str = Field(..., description="Unique step identifier (e.g. 'cs_1a')")
character_name: str = Field(..., min_length=1, max_length=100)
strategy_type: str = Field(
...,
description="Strategy type: combat, gathering, crafting, trading, task, leveling",
)
config: dict = Field(default_factory=dict, description="Strategy-specific configuration")
transition: TransitionConditionSchema | None = Field(
default=None,
description="Condition for this character-step to be considered done",
)
# ---------------------------------------------------------------------------
# Pipeline stage
# ---------------------------------------------------------------------------
class PipelineStageSchema(BaseModel):
"""A stage in the pipeline — character steps within it run in parallel."""
id: str = Field(..., description="Unique stage identifier (e.g. 'stage_1')")
name: str = Field(..., min_length=1, max_length=100)
character_steps: list[CharacterStepSchema] = Field(..., min_length=1)
# ---------------------------------------------------------------------------
# Request schemas
# ---------------------------------------------------------------------------
class PipelineConfigCreate(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
description: str = Field(default="")
stages: list[PipelineStageSchema] = Field(..., min_length=1)
loop: bool = Field(default=False)
max_loops: int = Field(default=0, ge=0)
class PipelineConfigUpdate(BaseModel):
name: str | None = Field(default=None, min_length=1, max_length=100)
description: str | None = None
stages: list[PipelineStageSchema] | None = Field(default=None, min_length=1)
loop: bool | None = None
max_loops: int | None = Field(default=None, ge=0)
enabled: bool | None = None
# ---------------------------------------------------------------------------
# Response schemas
# ---------------------------------------------------------------------------
class PipelineConfigResponse(BaseModel):
id: int
name: str
description: str
stages: list[dict]
loop: bool
max_loops: int
enabled: bool
created_at: datetime
updated_at: datetime
model_config = {"from_attributes": True}
class PipelineRunResponse(BaseModel):
id: int
pipeline_id: int
status: str
current_stage_index: int
current_stage_id: str
loop_count: int
total_actions_count: int
character_states: dict
stage_history: list[dict] = Field(default_factory=list)
started_at: datetime
stopped_at: datetime | None = None
error_message: str | None = None
model_config = {"from_attributes": True}
class CharacterStateResponse(BaseModel):
"""Status of a single character within an active pipeline."""
character_name: str
status: str # running, completed, error, idle
step_id: str = ""
actions_count: int = 0
strategy_state: str = ""
error: str | None = None
class PipelineStatusResponse(BaseModel):
pipeline_id: int
status: str
run_id: int | None = None
current_stage_index: int = 0
current_stage_id: str = ""
total_stages: int = 0
loop_count: int = 0
total_actions_count: int = 0
character_states: list[CharacterStateResponse] = Field(default_factory=list)
class PipelineConfigDetailResponse(BaseModel):
"""Pipeline config with its run history."""
config: PipelineConfigResponse
runs: list[PipelineRunResponse] = Field(default_factory=list)