feat: 添加 Action 协议
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
"""动作类型统一入口,新增操作时在这里显式加入联合类型"""
|
||||
|
||||
from typing import Annotated
|
||||
|
||||
from pydantic import Field, TypeAdapter
|
||||
|
||||
from .base import ActionModel, BaseAction
|
||||
from .move import MoveAction
|
||||
from .resize import ResizeAction
|
||||
from .update import UpdateAction
|
||||
|
||||
# 按 type 选择对应模型,不维护重复的操作名称映射
|
||||
Action = Annotated[MoveAction | ResizeAction | UpdateAction, Field(discriminator="type")]
|
||||
action_adapter = TypeAdapter(Action)
|
||||
|
||||
|
||||
class ActionBatch(ActionModel):
|
||||
actions: list[Action] = Field(description="按数组顺序执行的动作列表,允许为空")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Action", "ActionBatch", "BaseAction", "MoveAction", "ResizeAction",
|
||||
"UpdateAction", "action_adapter",
|
||||
]
|
||||
@@ -0,0 +1,14 @@
|
||||
"""动作协议公共字段"""
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
from app.schemas.base import NonBlankString
|
||||
|
||||
|
||||
class ActionModel(BaseModel):
|
||||
# 操作字段显式声明,防止拼写错误被当作扩展字段忽略
|
||||
model_config = ConfigDict(extra="forbid", strict=True, allow_inf_nan=False)
|
||||
|
||||
|
||||
class BaseAction(ActionModel):
|
||||
target_id: NonBlankString = Field(description="已有组件的 ID")
|
||||
@@ -0,0 +1,19 @@
|
||||
"""按相对位移移动组件"""
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from app.schemas.screen import Number
|
||||
|
||||
from .base import BaseAction
|
||||
|
||||
|
||||
class MoveAction(BaseAction):
|
||||
type: Literal["move"] = "move"
|
||||
delta_x: Number = Field(
|
||||
default=0, description="水平位移,单位 px,正数向右,负数向左"
|
||||
)
|
||||
delta_y: Number = Field(
|
||||
default=0, description="垂直位移,单位 px,正数向下,负数向上"
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
"""设置组件的绝对尺寸"""
|
||||
|
||||
from typing import Literal, Self
|
||||
|
||||
from pydantic import Field, model_validator
|
||||
|
||||
from app.schemas.screen import PositiveNumber
|
||||
|
||||
from .base import BaseAction
|
||||
|
||||
|
||||
class ResizeAction(BaseAction):
|
||||
type: Literal["resize"] = "resize"
|
||||
width: PositiveNumber | None = Field(
|
||||
default=None, description="目标宽度,单位 px,省略或 null 表示保持原值"
|
||||
)
|
||||
height: PositiveNumber | None = Field(
|
||||
default=None, description="目标高度,单位 px,省略或 null 表示保持原值"
|
||||
)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def require_size(self) -> Self:
|
||||
if self.width is None and self.height is None:
|
||||
raise ValueError("resize 至少提供 width 或 height")
|
||||
return self
|
||||
@@ -0,0 +1,22 @@
|
||||
"""精确更新组件 props 或 style 下的一个字段"""
|
||||
|
||||
from typing import Annotated, Literal
|
||||
|
||||
from pydantic import Field, JsonValue
|
||||
|
||||
from app.schemas.base import NonBlankString
|
||||
|
||||
from .base import BaseAction
|
||||
|
||||
PathSegment = NonBlankString | Annotated[int, Field(ge=0)]
|
||||
|
||||
|
||||
class UpdateAction(BaseAction):
|
||||
type: Literal["update"] = "update"
|
||||
section: Literal["props", "style"] = Field(description="允许更新的组件属性区域")
|
||||
path: list[PathSegment] = Field(
|
||||
min_length=1, description="区域内的字段路径,字符串为对象键,整数为数组下标"
|
||||
)
|
||||
value: JsonValue = Field(
|
||||
description="替换该路径的值,支持任意 JSON;null 是赋值,不表示删除"
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
"""修改范围及后续能力需求"""
|
||||
|
||||
from typing import Literal, Self
|
||||
|
||||
from pydantic import Field, model_validator
|
||||
|
||||
from app.schemas.base import ExtensibleModel, NonBlankString
|
||||
|
||||
|
||||
class ChangeScope(ExtensibleModel):
|
||||
scope: Literal["local", "screen"] = Field(
|
||||
description="local=局部修改或新增;screen=整体设计或修改"
|
||||
)
|
||||
target_ids: list[NonBlankString] = Field(
|
||||
default_factory=list, description="涉及的已有组件 ID,新增内容和画布属性可为空"
|
||||
)
|
||||
needs_planning: bool = Field(
|
||||
description="是否需要布局、组件组合或设计规划,与范围大小分别判断"
|
||||
)
|
||||
resource_needs: list[NonBlankString] = Field(
|
||||
default_factory=list,
|
||||
description="需要补充查询的数据、组件、模板或素材及用途,已有信息足够则为空",
|
||||
)
|
||||
reason: NonBlankString = Field(description="简短说明范围、规划和资源判断依据")
|
||||
|
||||
@model_validator(mode="after")
|
||||
def unique_targets(self) -> Self:
|
||||
if len(self.target_ids) != len(set(self.target_ids)):
|
||||
raise ValueError("target_ids 不能包含重复 ID")
|
||||
return self
|
||||
@@ -50,13 +50,13 @@ class Requirement(ExtensibleModel):
|
||||
changes: list[RequirementChange] = Field(default_factory=list)
|
||||
needs_clarification: bool
|
||||
clarification_question: NonBlankString | None = None
|
||||
needs_planning: bool = Field(description="是否需要布局或设计规划,与修改范围分别判断")
|
||||
needs_resources: bool = Field(description="是否需要补充查询组件、数据、模板或素材")
|
||||
assumptions: list[NonBlankString] = Field(
|
||||
default_factory=list, description="非阻塞信息采用的默认假设,不可用来猜测删除目标"
|
||||
default_factory=list,
|
||||
description="非阻塞信息采用的默认假设,不可用来猜测删除目标",
|
||||
)
|
||||
constraints: list[NonBlankString] = Field(
|
||||
default_factory=list, description="用户明确提出的约束,例如只修改右侧、保留其他组件"
|
||||
default_factory=list,
|
||||
description="用户明确提出的约束,例如只修改右侧、保留其他组件",
|
||||
)
|
||||
|
||||
@model_validator(mode="after")
|
||||
|
||||
Reference in New Issue
Block a user