"""本体提取API的请求和响应模型 本模块定义了本体提取系统的所有API请求和响应的Pydantic模型。 Classes: ExtractionRequest: 本体提取请求模型 ExtractionResponse: 本体提取响应模型 ExportRequest: OWL文件导出请求模型 ExportResponse: OWL文件导出响应模型 OntologyResultResponse: 本体提取结果响应模型(带毫秒时间戳) SceneCreateRequest: 场景创建请求模型 SceneUpdateRequest: 场景更新请求模型 SceneResponse: 场景响应模型 SceneListResponse: 场景列表响应模型 ClassCreateRequest: 类型创建请求模型 ClassUpdateRequest: 类型更新请求模型 ClassResponse: 类型响应模型 ClassListResponse: 类型列表响应模型 """ from typing import List, Optional import datetime from uuid import UUID from pydantic import BaseModel, Field, field_serializer, ConfigDict from app.core.memory.models.ontology_scenario_models import OntologyClass class ExtractionRequest(BaseModel): """本体提取请求模型 用于POST /api/ontology/extract端点的请求体。 Attributes: scenario: 场景描述文本,不能为空 domain: 可选的领域提示(如Healthcare, Education等) llm_id: LLM模型ID,必须提供 scene_id: 场景ID,必须提供,用于将提取的类保存到指定场景 Examples: >>> request = ExtractionRequest( ... scenario="医院管理患者记录...", ... domain="Healthcare", ... llm_id="550e8400-e29b-41d4-a716-446655440000", ... scene_id="660e8400-e29b-41d4-a716-446655440000" ... ) """ scenario: str = Field(..., description="场景描述文本", min_length=1) domain: Optional[str] = Field(None, description="可选的领域提示") llm_id: str = Field(..., description="LLM模型ID") scene_id: UUID = Field(..., description="场景ID,用于将提取的类保存到指定场景") class ExtractionResponse(BaseModel): """本体提取响应模型 用于POST /api/ontology/extract端点的响应体。 Attributes: classes: 提取的本体类列表 domain: 识别的领域 extracted_count: 提取的类数量 Examples: >>> response = ExtractionResponse( ... classes=[...], ... domain="Healthcare", ... extracted_count=7 ... ) """ classes: List[OntologyClass] = Field(default_factory=list, description="提取的本体类列表") domain: str = Field(..., description="识别的领域") extracted_count: int = Field(..., description="提取的类数量") class ExportBySceneRequest(BaseModel): """按场景导出OWL文件请求模型 用于POST /api/ontology/export端点的请求体。 根据scene_id从数据库查询该场景下的所有本体类型并导出为OWL文件。 Attributes: scene_id: 本体场景ID,必填,用于查询该场景下的所有类型 format: 导出格式,可选值:rdfxml(默认)、turtle Examples: >>> request = ExportBySceneRequest( ... scene_id=UUID("550e8400-e29b-41d4-a716-446655440000"), ... format="rdfxml" ... ) """ scene_id: UUID = Field(..., description="本体场景ID") format: str = Field("rdfxml", description="导出格式,可选值:rdfxml(默认)、turtle") class ExportBySceneResponse(BaseModel): """按场景导出OWL文件响应模型 用于POST /api/ontology/export端点的响应体。 Attributes: owl_content: OWL文件内容 filename: 导出文件名(含扩展名) scene_id: 场景ID scene_name: 场景名称 classes_count: 导出的类数量 Examples: >>> response = ExportBySceneResponse( ... owl_content="...", ... filename="medical_ontology.owl", ... scene_id=UUID("550e8400-e29b-41d4-a716-446655440000"), ... scene_name="医疗场景", ... classes_count=7 ... ) """ owl_content: str = Field(..., description="OWL文件内容") filename: str = Field(..., description="导出文件名(含扩展名)") scene_id: UUID = Field(..., description="场景ID") scene_name: str = Field(..., description="场景名称") classes_count: int = Field(..., description="导出的类数量") class OntologyResultResponse(BaseModel): """本体提取结果响应模型 用于返回数据库中存储的提取结果,时间戳为毫秒级。 Attributes: id: 结果ID (UUID) scenario: 场景描述文本 domain: 领域 classes_json: 提取的本体类数据(JSON格式) extracted_count: 提取的类数量 user_id: 用户ID created_at: 创建时间(毫秒时间戳) Examples: >>> response = OntologyResultResponse( ... id=uuid.uuid4(), ... scenario="医院管理患者记录...", ... domain="Healthcare", ... classes_json={"classes": [...]}, ... extracted_count=7, ... user_id=123, ... created_at=datetime.now() ... ) """ id: UUID = Field(..., description="结果ID") scenario: str = Field(..., description="场景描述文本") domain: Optional[str] = Field(None, description="领域") classes_json: dict = Field(..., description="提取的本体类数据(JSON格式)") extracted_count: int = Field(..., description="提取的类数量") user_id: Optional[int] = Field(None, description="用户ID") created_at: datetime.datetime = Field(..., description="创建时间") @field_serializer("created_at", when_used="json") def _serialize_created_at(self, dt: datetime.datetime): """将创建时间序列化为毫秒时间戳""" return int(dt.timestamp() * 1000) if dt else None class Config: from_attributes = True # ==================== 本体场景相关 Schema ==================== class SceneCreateRequest(BaseModel): """场景创建请求模型 用于创建新的本体场景。 Attributes: scene_name: 场景名称,必填,1-200字符 scene_description: 场景描述,可选 Examples: >>> request = SceneCreateRequest( ... scene_name="医疗场景", ... scene_description="用于医疗领域的本体建模" ... ) """ scene_name: str = Field(..., min_length=1, max_length=200, description="场景名称") scene_description: Optional[str] = Field(None, description="场景描述") class SceneUpdateRequest(BaseModel): """场景更新请求模型 用于更新已有本体场景信息。 Attributes: scene_name: 场景名称,可选,1-200字符 scene_description: 场景描述,可选 Examples: >>> request = SceneUpdateRequest( ... scene_name="更新后的场景名称", ... scene_description="更新后的描述" ... ) """ scene_name: Optional[str] = Field(None, min_length=1, max_length=200, description="场景名称") scene_description: Optional[str] = Field(None, description="场景描述") class SceneResponse(BaseModel): """场景响应模型 用于返回本体场景信息。 Attributes: scene_id: 场景ID scene_name: 场景名称 scene_description: 场景描述 type_num: 类型数量 workspace_id: 所属工作空间ID created_at: 创建时间(毫秒时间戳) updated_at: 更新时间(毫秒时间戳) classes_count: 类型数量 Examples: >>> response = SceneResponse( ... scene_id=uuid.uuid4(), ... scene_name="医疗场景", ... scene_description="用于医疗领域的本体建模", ... type_num=0, ... workspace_id=uuid.uuid4(), ... created_at=datetime.now(), ... updated_at=datetime.now(), ... classes_count=5 ... ) """ scene_id: UUID = Field(..., description="场景ID") scene_name: str = Field(..., description="场景名称") scene_description: Optional[str] = Field(None, description="场景描述") type_num: int = Field(..., description="类型数量") entity_type: Optional[List[str]] = Field(None, description="实体类型列表(最多3个class_name)") workspace_id: UUID = Field(..., description="所属工作空间ID") created_at: datetime.datetime = Field(..., description="创建时间(毫秒时间戳)") updated_at: datetime.datetime = Field(..., description="更新时间(毫秒时间戳)") classes_count: int = Field(0, description="类型数量") is_system_default: bool = Field(False, description="是否为系统默认场景") @field_serializer("created_at", when_used="json") def _serialize_created_at(self, dt: datetime.datetime): """将创建时间序列化为毫秒时间戳""" return int(dt.timestamp() * 1000) if dt else None @field_serializer("updated_at", when_used="json") def _serialize_updated_at(self, dt: datetime.datetime): """将更新时间序列化为毫秒时间戳""" return int(dt.timestamp() * 1000) if dt else None model_config = ConfigDict(from_attributes=True) class PaginationInfo(BaseModel): """分页信息模型 Attributes: page: 当前页码 pagesize: 每页数量 total: 总数量 hasnext: 是否有下一页 """ page: int = Field(..., description="当前页码") pagesize: int = Field(..., description="每页数量") total: int = Field(..., description="总数量") hasnext: bool = Field(..., description="是否有下一页") class SceneListResponse(BaseModel): """场景列表响应模型(支持分页) 用于返回本体场景列表。 Attributes: items: 场景列表 page: 分页信息(可选,分页时返回) Examples: >>> # 不分页 >>> response = SceneListResponse( ... items=[scene1, scene2] ... ) >>> # 分页 >>> response = SceneListResponse( ... items=[scene1, scene2, ...], ... page=PaginationInfo(page=1, pagesize=100, total=150, hasnext=True) ... ) """ items: List[SceneResponse] = Field(..., description="场景列表") page: Optional[PaginationInfo] = Field(None, description="分页信息") # ==================== 本体类型相关 Schema ==================== class ClassItem(BaseModel): """单个类型信息模型 Attributes: class_name: 类型名称,必填,1-200字符 class_description: 类型描述,可选 Examples: >>> item = ClassItem( ... class_name="患者", ... class_description="医院患者信息" ... ) """ class_name: str = Field(..., min_length=1, max_length=200, description="类型名称") class_description: Optional[str] = Field(None, description="类型描述") class ClassCreateRequest(BaseModel): """类型创建请求模型(统一使用列表形式) 通过列表中元素数量决定创建模式: - 列表包含 1 个元素:单个创建 - 列表包含多个元素:批量创建 Attributes: scene_id: 所属场景ID,必填 classes: 类型列表,必填,至少包含 1 个元素 Examples: # 单个创建(列表中 1 个元素) >>> request = ClassCreateRequest( ... scene_id=uuid.uuid4(), ... classes=[ ... ClassItem(class_name="患者", class_description="医院患者信息") ... ] ... ) # 批量创建(列表中多个元素) >>> request = ClassCreateRequest( ... scene_id=uuid.uuid4(), ... classes=[ ... ClassItem(class_name="患者", class_description="医院患者信息"), ... ClassItem(class_name="医生", class_description="医院医生信息"), ... ClassItem(class_name="药品", class_description="医院药品信息") ... ] ... ) """ scene_id: UUID = Field(..., description="所属场景ID") classes: List[ClassItem] = Field(..., min_length=1, description="类型列表,至少包含 1 个元素") class ClassUpdateRequest(BaseModel): """类型更新请求模型 用于更新已有本体类型信息。 Attributes: class_name: 类型名称,可选,1-200字符 class_description: 类型描述,可选 Examples: >>> request = ClassUpdateRequest( ... class_name="更新后的类型名称", ... class_description="更新后的描述" ... ) """ class_name: Optional[str] = Field(None, min_length=1, max_length=200, description="类型名称") class_description: Optional[str] = Field(None, description="类型描述") class ClassResponse(BaseModel): """类型响应模型 用于返回本体类型信息。 Attributes: class_id: 类型ID class_name: 类型名称 class_description: 类型描述 scene_id: 所属场景ID created_at: 创建时间(毫秒时间戳) updated_at: 更新时间(毫秒时间戳) Examples: >>> response = ClassResponse( ... class_id=uuid.uuid4(), ... class_name="患者", ... class_description="医院患者信息", ... scene_id=uuid.uuid4(), ... created_at=datetime.now(), ... updated_at=datetime.now() ... ) """ class_id: UUID = Field(..., description="类型ID") class_name: str = Field(..., description="类型名称") class_description: Optional[str] = Field(None, description="类型描述") scene_id: UUID = Field(..., description="所属场景ID") created_at: datetime.datetime = Field(..., description="创建时间(毫秒时间戳)") updated_at: datetime.datetime = Field(..., description="更新时间(毫秒时间戳)") @field_serializer("created_at", when_used="json") def _serialize_created_at(self, dt: datetime.datetime): """将创建时间序列化为毫秒时间戳""" return int(dt.timestamp() * 1000) if dt else None @field_serializer("updated_at", when_used="json") def _serialize_updated_at(self, dt: datetime.datetime): """将更新时间序列化为毫秒时间戳""" return int(dt.timestamp() * 1000) if dt else None model_config = ConfigDict(from_attributes=True) class ClassBatchCreateResponse(BaseModel): """批量创建类型响应模型 用于返回批量创建的结果统计和详情。 Attributes: total: 总共尝试创建的数量 success_count: 成功创建的数量 failed_count: 失败的数量 items: 成功创建的类型列表 errors: 失败的错误信息列表(可选) Examples: >>> response = ClassBatchCreateResponse( ... total=3, ... success_count=2, ... failed_count=1, ... items=[class1, class2], ... errors=["创建类型 '药品' 失败: 类型名称已存在"] ... ) """ total: int = Field(..., description="总共尝试创建的数量") success_count: int = Field(..., description="成功创建的数量") failed_count: int = Field(0, description="失败的数量") items: List[ClassResponse] = Field(..., description="成功创建的类型列表") errors: Optional[List[str]] = Field(None, description="失败的错误信息列表") class ClassListResponse(BaseModel): """类型列表响应模型 用于返回本体类型列表。 Attributes: total: 总数量 scene_id: 所属场景ID scene_name: 场景名称 scene_description: 场景描述 items: 类型列表 Examples: >>> response = ClassListResponse( ... total=3, ... scene_id=uuid.uuid4(), ... scene_name="医疗场景", ... scene_description="用于医疗领域的本体建模", ... items=[class1, class2, class3] ... ) """ total: int = Field(..., description="总数量") scene_id: UUID = Field(..., description="所属场景ID") scene_name: str = Field(..., description="场景名称") scene_description: Optional[str] = Field(None, description="场景描述") is_system_default: bool = Field(False, description="是否为系统默认场景") items: List[ClassResponse] = Field(..., description="类型列表") # ==================== OWL 导入相关 Schema ==================== class ImportOwlRequest(BaseModel): """OWL 文件导入请求 用于 POST /api/ontology/import 端点的请求体。 解析 OWL 文件并将类型直接导入到指定场景。 Attributes: scene_id: 目标场景ID,必填 owl_content: OWL 文件内容(字符串形式) format: 文件格式,可选值:rdfxml(默认)、turtle Examples: >>> request = ImportOwlRequest( ... scene_id=UUID("550e8400-e29b-41d4-a716-446655440000"), ... owl_content="...", ... format="rdfxml" ... ) """ scene_id: UUID = Field(..., description="目标场景ID") owl_content: str = Field(..., min_length=1, description="OWL 文件内容") format: str = Field("rdfxml", description="文件格式,可选值:rdfxml(默认)、turtle") class ImportOwlResponse(BaseModel): """OWL 文件导入响应 用于返回导入结果。 Attributes: scene_id: 场景ID scene_name: 场景名称 imported_count: 成功导入的类型数量 skipped_count: 跳过的数量(重复) items: 导入的类型列表 Examples: >>> response = ImportOwlResponse( ... scene_id=UUID("..."), ... scene_name="智能制造场景", ... imported_count=4, ... skipped_count=0, ... items=[...] ... ) """ scene_id: UUID = Field(..., description="场景ID") scene_name: str = Field(..., description="场景名称") imported_count: int = Field(..., description="成功导入的类型数量") skipped_count: int = Field(0, description="跳过的数量(重复)") items: List[ClassResponse] = Field(..., description="导入的类型列表")