"""RAG 平台 Pydantic 数据模型。 这些是领域模型(非 ORM),ORM 模型定义在 store.py(U2)。 与 memory/knowledge_base.py 的 KnowledgeBase protocol 分离 — rag_platform 服务企业知识库场景,memory/ 服务 Agent 运行时记忆。 """ from __future__ import annotations import uuid from datetime import datetime, timezone from enum import Enum from pydantic import BaseModel, ConfigDict, Field def _utcnow() -> datetime: return datetime.now(timezone.utc) def _uuid() -> str: return str(uuid.uuid4()) class KBStatus(str, Enum): active = "active" archived = "archived" class DocumentStatus(str, Enum): """文档处理状态机:pending → parsing → segmenting → vectorizing → indexed | failed。""" pending = "pending" parsing = "parsing" segmenting = "segmenting" vectorizing = "vectorizing" indexed = "indexed" failed = "failed" class QueryMode(str, Enum): """检索模式:embedding 语义 / keywords 全文 / blend 双索引合并。""" embedding = "embedding" keywords = "keywords" blend = "blend" class KnowledgeBase(BaseModel): """知识库领域模型。""" model_config = ConfigDict(from_attributes=True) id: str = Field(default_factory=_uuid) name: str description: str = "" owner: str status: KBStatus = KBStatus.active # 检索与命中处理默认配置(Agent 运行时可覆盖) default_query_mode: QueryMode = QueryMode.blend default_hit_processing: str = "model_opt" # model_opt | direct caching_disabled: bool = False created_at: datetime = Field(default_factory=_utcnow) updated_at: datetime = Field(default_factory=_utcnow) class Document(BaseModel): """知识库文档领域模型。""" model_config = ConfigDict(from_attributes=True) id: str = Field(default_factory=_uuid) kb_id: str filename: str file_type: str file_size: int status: DocumentStatus = DocumentStatus.pending error_message: str | None = None created_at: datetime = Field(default_factory=_utcnow) updated_at: datetime = Field(default_factory=_utcnow) class Chunk(BaseModel): """文档分段后的文本块。""" model_config = ConfigDict(from_attributes=True) id: str = Field(default_factory=_uuid) document_id: str kb_id: str content: str metadata: dict = Field(default_factory=dict) embedding: list[float] | None = None class QueryResult(BaseModel): """检索结果条目。""" model_config = ConfigDict(from_attributes=True) chunk_id: str content: str score: float metadata: dict = Field(default_factory=dict) document_id: str kb_id: str class RetrievalRequest(BaseModel): """检索请求 — 支持覆盖 KB 默认配置。 retrieval_mode / hit_processing_mode 为 None 时使用 KB 默认值。 """ model_config = ConfigDict() query: str kb_ids: list[str] retrieval_mode: QueryMode | None = None # None = 使用 KB 默认 hit_processing_mode: str | None = None # None = 使用 KB 默认 top_k: int = 5 user_id: str | None = None # 用于 ACL 过滤 # --------------------------------------------------------------------------- # ORM Models (SQLAlchemy 2 DeclarativeBase + Mapped) # --------------------------------------------------------------------------- from sqlalchemy import DateTime, ForeignKey, String, Text, UniqueConstraint # noqa: E402 from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column # noqa: E402 class ORMBase(DeclarativeBase): """rag_platform ORM 基类 — 与 memory/models.py 的 declarative_base() 独立。""" pass class KBModel(ORMBase): """知识库 ORM 模型。""" __tablename__ = "rag_platform_kbs" id: Mapped[str] = mapped_column(String, primary_key=True, default=_uuid) name: Mapped[str] = mapped_column(String(255)) description: Mapped[str] = mapped_column(Text, default="") owner: Mapped[str] = mapped_column(String(255), index=True) status: Mapped[str] = mapped_column(String(32), default="active", index=True) default_query_mode: Mapped[str] = mapped_column(String(32), default="blend") default_hit_processing: Mapped[str] = mapped_column(String(32), default="model_opt") caching_disabled: Mapped[bool] = mapped_column(default=False) created_at: Mapped[datetime] = mapped_column(DateTime, default=_utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime, default=_utcnow) class DocumentModel(ORMBase): """知识库文档 ORM 模型。""" __tablename__ = "rag_platform_documents" id: Mapped[str] = mapped_column(String, primary_key=True, default=_uuid) kb_id: Mapped[str] = mapped_column( String, ForeignKey("rag_platform_kbs.id", ondelete="CASCADE"), index=True ) filename: Mapped[str] = mapped_column(String(512)) file_type: Mapped[str] = mapped_column(String(64)) file_size: Mapped[int] = mapped_column(default=0) status: Mapped[str] = mapped_column(String(32), default="pending", index=True) error_message: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=_utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime, default=_utcnow) class KBAclModel(ORMBase): """Per-KB 访问控制 ORM 模型。 kb_id FK → rag_platform_kbs.id ON DELETE CASCADE(KTD5 不变量)。 ACL 条目与 KB 元数据共享事务边界(同一 PG DB)。 """ __tablename__ = "rag_platform_kb_acl" __table_args__ = (UniqueConstraint("kb_id", "user_id", name="uq_kb_acl_kb_user"),) id: Mapped[str] = mapped_column(String, primary_key=True, default=_uuid) kb_id: Mapped[str] = mapped_column( String, ForeignKey("rag_platform_kbs.id", ondelete="CASCADE"), index=True ) user_id: Mapped[str] = mapped_column(String(255), index=True) role: Mapped[str] = mapped_column(String(32), default="viewer") # owner | viewer created_at: Mapped[datetime] = mapped_column(DateTime, default=_utcnow)