"""通用 DTO:与 openapi/openapi.yaml 契约对齐的 Pydantic 类型。 对齐由 tests/test_dto_contract.py 用 bundled 契约校验关键字段; 禁止在此复制会漂移的完整值集(以契约为准)。 """ from typing import Annotated from pydantic import BaseModel, ConfigDict, Field def _id_pattern(prefix: str, version: int) -> str: ver = "7" if version == 7 else "4" tail = r"[0-9a-f]{12}-[0-9a-f]{4}-" + ver + r"[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" return f"^{prefix}" + tail UserId = Annotated[str, Field(pattern=_id_pattern("user_", 7))] PersonalDomainId = Annotated[str, Field(pattern=_id_pattern("domain_", 7))] ConversationId = Annotated[str, Field(pattern=_id_pattern("conversation_", 7))] BranchId = Annotated[str, Field(pattern=_id_pattern("branch_", 7))] MessageId = Annotated[str, Field(pattern=_id_pattern("message_", 7))] ContentBlockId = Annotated[str, Field(pattern=_id_pattern("block_", 7))] RunId = Annotated[str, Field(pattern=_id_pattern("run_", 7))] TurnId = Annotated[str, Field(pattern=_id_pattern("turn_", 7))] RequestId = Annotated[str, Field(pattern=_id_pattern("request_", 4))] class Money(BaseModel): """金额:最小货币单位整数 + ISO 4217 币种;禁止浮点(契约第 4 节)。""" model_config = ConfigDict(extra="forbid") amount_minor: int = Field(ge=0) currency: str = Field(pattern=r"^[A-Z]{3}$") class CursorPage[T](BaseModel): """opaque cursor 分页(契约第 4.1 节);客户端不得解析 cursor。""" data: list[T] next_cursor: str | None has_more: bool