Files
zhuangxiu/services/api/app/domain/models.py
T

221 lines
6.2 KiB
Python

from datetime import UTC, datetime
from enum import StrEnum
from typing import Any
from pydantic import BaseModel, Field
class WorkflowStage(StrEnum):
UPLOADED = "uploaded"
REGION_SELECTION = "region_selection"
PLAN_REVIEW = "plan_review"
BLOCKOUT = "blockout"
STYLE_BRIEF = "style_brief"
DIRECTION_SELECTION = "direction_selection"
RENDER_REVIEW = "render_review"
EDITING = "editing"
COMPLETED = "completed"
FAILED = "failed"
class WorkflowCommand(StrEnum):
START_INGESTION = "start_ingestion"
SELECT_REGION = "select_region"
CONFIRM_PLAN = "confirm_plan"
BUILD_BLOCKOUT = "build_blockout"
SUBMIT_STYLE_BRIEF = "submit_style_brief"
SELECT_DIRECTION = "select_direction"
REQUEST_RENDER = "request_render"
APPLY_EDIT = "apply_edit"
COMPLETE_PROJECT = "complete_project"
RETRY = "retry"
class Point2D(BaseModel):
x: float
y: float
class PlanRegion(BaseModel):
id: str
name: str
bounds: list[float] = Field(min_length=4, max_length=4)
recommended: bool = False
confidence: float = Field(ge=0, le=1)
class PlanLayer(BaseModel):
id: str
label: str
category: str
visible: bool = True
source_names: list[str] = Field(default_factory=list)
element_count: int | None = None
class PlanIssue(BaseModel):
id: str
kind: str
message: str
confidence: float = Field(ge=0, le=1)
resolved: bool = False
class RoomProfile(BaseModel):
id: str
name: str
kind: str
area_sqm: float | None = None
confidence: float = Field(default=0.5, ge=0, le=1)
notes: str = ""
class StructureAnalysis(BaseModel):
status: str = "pending"
summary: str = ""
rooms: list[RoomProfile] = Field(default_factory=list)
circulation: str = ""
daylight: str = ""
layout_opportunities: list[str] = Field(default_factory=list)
risks: list[str] = Field(default_factory=list)
model_name: str = ""
degraded: bool = False
class PlanState(BaseModel):
source_name: str
source_kind: str
source_object_key: str | None = None
preview_object_key: str | None = None
preview_width: int | None = None
preview_height: int | None = None
page_count: int = 1
vector_based: bool = False
vector_element_count: int = 0
cad_layer_count: int = 0
ocr_used: bool = False
ocr_labels: list[str] = Field(default_factory=list)
ingestion_notes: list[str] = Field(default_factory=list)
regions: list[PlanRegion] = Field(default_factory=list)
selected_region_id: str | None = None
layers: list[PlanLayer] = Field(default_factory=list)
issues: list[PlanIssue] = Field(default_factory=list)
scale_mm_per_unit: float | None = None
ceiling_height_mm: int = 2800
gross_area_sqm: float | None = None
structure: StructureAnalysis = Field(default_factory=StructureAnalysis)
class CameraView(BaseModel):
id: str
label: str
room: str
status: str = "draft"
class SceneState(BaseModel):
blockout_asset_key: str | None = None
cameras: list[CameraView] = Field(default_factory=list)
locked_object_ids: list[str] = Field(default_factory=list)
furniture_mode: str = "preserve_reference"
class ColorToken(BaseModel):
name: str
hex: str
role: str
class StyleState(BaseModel):
concept: str = "克制、自然、明亮的现代住宅"
keywords: list[str] = Field(default_factory=lambda: ["轻盈", "自然材质", "留白"])
palette: list[ColorToken] = Field(default_factory=list)
materials: list[str] = Field(default_factory=list)
lighting: str = "低对比度的自然光与柔和间接照明"
forms: str = "低矮、水平延展、少量圆角"
density: str = "适度留白"
avoid: list[str] = Field(default_factory=list)
locked_decisions: list[str] = Field(default_factory=list)
selected_direction_id: str | None = None
class DesignBrief(BaseModel):
residents: str = ""
lifestyle: list[str] = Field(default_factory=list)
focus_rooms: list[str] = Field(default_factory=list)
preferred_styles: list[str] = Field(default_factory=list)
preferred_colors: list[str] = Field(default_factory=list)
disliked_elements: list[str] = Field(default_factory=list)
must_keep: list[str] = Field(default_factory=list)
budget_level: str = "适中"
additional_notes: str = ""
completed: bool = False
class StyleDirection(BaseModel):
id: str
name: str
thesis: str
keywords: list[str] = Field(default_factory=list)
palette: list[ColorToken] = Field(default_factory=list)
materials: list[str] = Field(default_factory=list)
lighting: str = ""
prompt: str = ""
model_name: str = ""
class RenderAsset(BaseModel):
id: str
direction_id: str
room: str
view: str
object_key: str
prompt: str
model_name: str
parent_id: str | None = None
edit_instruction: str = ""
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
class ProjectEvent(BaseModel):
id: str
kind: str
message: str
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
class ProjectSnapshot(BaseModel):
project_id: str
name: str
stage: WorkflowStage
revision: int = 1
plan: PlanState
scene: SceneState = Field(default_factory=SceneState)
style: StyleState = Field(default_factory=StyleState)
brief: DesignBrief = Field(default_factory=DesignBrief)
directions: list[StyleDirection] = Field(default_factory=list)
renders: list[RenderAsset] = Field(default_factory=list)
events: list[ProjectEvent] = Field(default_factory=list)
available_commands: list[WorkflowCommand] = Field(default_factory=list)
last_stable_stage: WorkflowStage | None = None
failure_reason: str | None = None
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
class CommandRequest(BaseModel):
command: WorkflowCommand
expected_revision: int = Field(ge=1)
payload: dict[str, Any] = Field(default_factory=dict)
class WorkflowStageDefinition(BaseModel):
id: WorkflowStage
label: str
purpose: str
human_confirmation: bool
class WorkflowDefinition(BaseModel):
stages: list[WorkflowStageDefinition]
transitions: dict[str, list[str]]