From 73fdfed0e5649a9f27c54969db8b9ede79f5bede Mon Sep 17 00:00:00 2001 From: Nelson <1475262689@qq.com> Date: Sat, 11 Jul 2026 18:59:29 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BB=BA=E7=AB=8B=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E4=B8=8E=E6=A3=80=E7=B4=A2=E8=BF=BD=E8=B8=AA?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../versions/0007_knowledge_agent_rebuild.py | 319 ++++++++++++++++++ .../apps/backend/app/models/__init__.py | 31 +- .../apps/backend/app/models/knowledge.py | 238 ++++++++++++- 3 files changed, 586 insertions(+), 2 deletions(-) create mode 100644 ai_knowledge_base_v2/apps/backend/alembic/versions/0007_knowledge_agent_rebuild.py diff --git a/ai_knowledge_base_v2/apps/backend/alembic/versions/0007_knowledge_agent_rebuild.py b/ai_knowledge_base_v2/apps/backend/alembic/versions/0007_knowledge_agent_rebuild.py new file mode 100644 index 0000000..3e9ad9e --- /dev/null +++ b/ai_knowledge_base_v2/apps/backend/alembic/versions/0007_knowledge_agent_rebuild.py @@ -0,0 +1,319 @@ +from __future__ import annotations + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +revision = "0007_knowledge_agent_rebuild" +down_revision = "0006_expand_ai_request_log_text" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + for column in [ + sa.Column("source_title", sa.String(255), nullable=True), + sa.Column("knowledge_type", sa.String(30), nullable=False, server_default="general"), + sa.Column("review_mode", sa.String(30), nullable=False, server_default="manual"), + sa.Column("lifecycle_status", sa.String(30), nullable=False, server_default="active"), + sa.Column("source_status", sa.String(30), nullable=False, server_default="normal"), + sa.Column("source_error", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("manifest_confirmed", sa.Integer(), nullable=False, server_default="0"), + sa.Column("current_version_id", sa.BigInteger(), nullable=True), + sa.Column("pending_version_id", sa.BigInteger(), nullable=True), + sa.Column("last_synced_at", sa.DateTime(), nullable=True), + sa.Column("last_published_at", sa.DateTime(), nullable=True), + sa.Column("migration_status", sa.String(30), nullable=False, server_default="pending"), + ]: + op.add_column("sys_knowledge", column) + + op.create_table( + "sys_knowledge_source_snapshot", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("sync_job_id", sa.BigInteger(), nullable=True), + sa.Column("source_title", sa.String(255), nullable=False), + sa.Column("source_content", mysql.LONGTEXT(), nullable=False), + sa.Column("content_hash", sa.String(64), nullable=False), + sa.Column("source_identifier", sa.String(255), nullable=False), + sa.Column("fetched_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), + sa.Column("created_by", sa.BigInteger(), nullable=True), + sa.ForeignKeyConstraint(["knowledge_id"], ["sys_knowledge.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_source_snapshot", "knowledge_id", "sync_job_id", "content_hash") + + op.create_table( + "sys_knowledge_version", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("snapshot_id", sa.BigInteger(), nullable=False), + sa.Column("version_no", sa.Integer(), nullable=False), + sa.Column("status", sa.String(30), nullable=False), + sa.Column("quality_status", sa.String(30), nullable=False, server_default="pending"), + sa.Column("change_summary", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("quality_report", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("processing_rule_version", sa.String(50), nullable=False), + sa.Column("generation_model", sa.String(100), nullable=True), + sa.Column("created_by", sa.BigInteger(), nullable=True), + sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), + sa.Column("published_by", sa.BigInteger(), nullable=True), + sa.Column("published_at", sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(["knowledge_id"], ["sys_knowledge.id"]), + sa.ForeignKeyConstraint(["snapshot_id"], ["sys_knowledge_source_snapshot.id"]), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint("knowledge_id", "version_no", name="uq_kb_version_no"), + ) + _indexes("sys_knowledge_version", "knowledge_id", "status") + + op.create_table( + "sys_knowledge_manifest", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("version_id", sa.BigInteger(), nullable=False), + sa.Column("purpose", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("applicable_questions", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("inapplicable_questions", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("core_topics", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("entities", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("boundaries", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("content_hash", sa.String(64), nullable=False), + sa.Column("change_level", sa.String(20), nullable=False, server_default="major"), + sa.Column("confirmed", sa.Integer(), nullable=False, server_default="0"), + sa.Column("confirmed_by", sa.BigInteger(), nullable=True), + sa.Column("confirmed_at", sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(["knowledge_id"], ["sys_knowledge.id"]), + sa.ForeignKeyConstraint(["version_id"], ["sys_knowledge_version.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_manifest", "knowledge_id", "version_id") + + op.create_table( + "sys_knowledge_section", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("version_id", sa.BigInteger(), nullable=False), + sa.Column("section_key", sa.String(64), nullable=False), + sa.Column("title", sa.String(255), nullable=False), + sa.Column("content", mysql.LONGTEXT(), nullable=False), + sa.Column("source_start", sa.Integer(), nullable=False), + sa.Column("source_end", sa.Integer(), nullable=False), + sa.Column("sort_order", sa.Integer(), nullable=False), + sa.Column("content_hash", sa.String(64), nullable=False), + sa.ForeignKeyConstraint(["knowledge_id"], ["sys_knowledge.id"]), + sa.ForeignKeyConstraint(["version_id"], ["sys_knowledge_version.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_section", "knowledge_id", "version_id") + + op.create_table( + "sys_knowledge_chunk", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("version_id", sa.BigInteger(), nullable=False), + sa.Column("section_id", sa.BigInteger(), nullable=False), + sa.Column("title", sa.String(255), nullable=False), + sa.Column("content", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("normalized_text", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("keywords", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("synonyms", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("source_start", sa.Integer(), nullable=False), + sa.Column("source_end", sa.Integer(), nullable=False), + sa.Column("sort_order", sa.Integer(), nullable=False), + sa.Column("content_hash", sa.String(64), nullable=False), + sa.ForeignKeyConstraint(["knowledge_id"], ["sys_knowledge.id"]), + sa.ForeignKeyConstraint(["version_id"], ["sys_knowledge_version.id"]), + sa.ForeignKeyConstraint(["section_id"], ["sys_knowledge_section.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_chunk", "knowledge_id", "version_id", "section_id") + + op.create_table( + "sys_knowledge_card", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("version_id", sa.BigInteger(), nullable=False), + sa.Column("section_id", sa.BigInteger(), nullable=False), + sa.Column("title", sa.String(255), nullable=False), + sa.Column("summary", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("core_conclusion", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("applicable_questions", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("inapplicable_questions", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("keywords", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("synonyms", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("entities", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("risk_level", sa.String(20), nullable=False, server_default="normal"), + sa.Column("source_range", sa.String(100), nullable=False), + sa.Column("content_hash", sa.String(64), nullable=False), + sa.Column("generation_model", sa.String(100), nullable=True), + sa.Column("generation_rule_version", sa.String(50), nullable=False), + sa.Column("review_status", sa.String(20), nullable=False, server_default="pending"), + sa.ForeignKeyConstraint(["knowledge_id"], ["sys_knowledge.id"]), + sa.ForeignKeyConstraint(["version_id"], ["sys_knowledge_version.id"]), + sa.ForeignKeyConstraint(["section_id"], ["sys_knowledge_section.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_card", "knowledge_id", "version_id", "section_id") + + _create_job_and_log_tables() + + +def _create_job_and_log_tables() -> None: + op.create_table( + "sys_knowledge_sync_job", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("mode", sa.String(30), nullable=False), + sa.Column("status", sa.String(30), nullable=False), + sa.Column("stage", sa.String(50), nullable=False), + sa.Column("progress", sa.Integer(), nullable=False, server_default="0"), + sa.Column("source_error", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("result_summary", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("requested_by", sa.BigInteger(), nullable=False), + sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), + sa.Column("started_at", sa.DateTime(), nullable=True), + sa.Column("finished_at", sa.DateTime(), nullable=True), + sa.ForeignKeyConstraint(["knowledge_id"], ["sys_knowledge.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_sync_job", "knowledge_id", "status") + + op.create_table( + "sys_knowledge_review_record", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("version_id", sa.BigInteger(), nullable=False), + sa.Column("card_id", sa.BigInteger(), nullable=True), + sa.Column("action", sa.String(30), nullable=False), + sa.Column("reason", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("reviewed_by", sa.BigInteger(), nullable=False), + sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), + sa.ForeignKeyConstraint(["knowledge_id"], ["sys_knowledge.id"]), + sa.ForeignKeyConstraint(["version_id"], ["sys_knowledge_version.id"]), + sa.ForeignKeyConstraint(["card_id"], ["sys_knowledge_card.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_review_record", "knowledge_id", "version_id") + + op.create_table( + "sys_knowledge_publish_log", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("from_version_id", sa.BigInteger(), nullable=True), + sa.Column("to_version_id", sa.BigInteger(), nullable=True), + sa.Column("action", sa.String(30), nullable=False), + sa.Column("keep_open", sa.Integer(), nullable=False, server_default="0"), + sa.Column("detail_json", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("operated_by", sa.BigInteger(), nullable=False), + sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), + sa.ForeignKeyConstraint(["knowledge_id"], ["sys_knowledge.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_publish_log", "knowledge_id") + + op.create_table( + "sys_knowledge_retrieval_log", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("session_id", sa.BigInteger(), nullable=True), + sa.Column("message_id", sa.BigInteger(), nullable=True), + sa.Column("user_id", sa.BigInteger(), nullable=True), + sa.Column("question", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("catalog_snapshot", mysql.LONGTEXT(), nullable=False), + sa.Column("catalog_hash", sa.String(64), nullable=False), + sa.Column("safety_rule_version", sa.String(50), nullable=False), + sa.Column("knowledge_called", sa.Integer(), nullable=False, server_default="0"), + sa.Column("selected_knowledge_ids", sa.String(500), nullable=True), + sa.Column("query_terms", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("tool_trace", mysql.LONGTEXT(), nullable=True), + sa.Column("final_section_ids", sa.String(500), nullable=True), + sa.Column("discarded_conflicts", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("final_answer", mysql.LONGTEXT(), nullable=True), + sa.Column("status", sa.String(20), nullable=False), + sa.Column("tool_cost_ms", sa.Integer(), nullable=True), + sa.Column("total_cost_ms", sa.Integer(), nullable=True), + sa.Column("error_message", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("attention_created", sa.Integer(), nullable=False, server_default="0"), + sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_retrieval_log", "session_id", "message_id", "user_id") + + op.create_table( + "sys_knowledge_retrieval_candidate", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("retrieval_log_id", sa.BigInteger(), nullable=False), + sa.Column("knowledge_id", sa.BigInteger(), nullable=False), + sa.Column("version_id", sa.BigInteger(), nullable=False), + sa.Column("chunk_id", sa.BigInteger(), nullable=False), + sa.Column("section_id", sa.BigInteger(), nullable=False), + sa.Column("lexical_score", sa.String(30), nullable=False), + sa.Column("rerank_score", sa.String(30), nullable=True), + sa.Column("selected", sa.Integer(), nullable=False, server_default="0"), + sa.Column("discard_reason", sa.String(255), nullable=True), + sa.ForeignKeyConstraint(["retrieval_log_id"], ["sys_knowledge_retrieval_log.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_knowledge_retrieval_candidate", "retrieval_log_id", "knowledge_id", "version_id") + + op.create_table( + "sys_human_attention_record", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("session_id", sa.BigInteger(), nullable=False), + sa.Column("message_id", sa.BigInteger(), nullable=False), + sa.Column("user_id", sa.BigInteger(), nullable=False), + sa.Column("trigger_message", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("problem_summary", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("trigger_reason", mysql.MEDIUMTEXT(), nullable=False), + sa.Column("priority", sa.String(20), nullable=False), + sa.Column("status", sa.String(20), nullable=False, server_default="pending"), + sa.Column("handler_id", sa.BigInteger(), nullable=True), + sa.Column("handler_note", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), + sa.Column("updated_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_human_attention_record", "session_id", "message_id", "user_id", "priority", "status") + + op.create_table( + "sys_human_attention_history", + sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False), + sa.Column("attention_id", sa.BigInteger(), nullable=False), + sa.Column("from_status", sa.String(20), nullable=True), + sa.Column("to_status", sa.String(20), nullable=False), + sa.Column("note", mysql.MEDIUMTEXT(), nullable=True), + sa.Column("operated_by", sa.BigInteger(), nullable=False), + sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False), + sa.ForeignKeyConstraint(["attention_id"], ["sys_human_attention_record.id"]), + sa.PrimaryKeyConstraint("id"), + ) + _indexes("sys_human_attention_history", "attention_id") + + +def _indexes(table: str, *columns: str) -> None: + for column in columns: + op.create_index(f"ix_{table}_{column}", table, [column]) + + +def downgrade() -> None: + for table in [ + "sys_human_attention_history", + "sys_human_attention_record", + "sys_knowledge_retrieval_candidate", + "sys_knowledge_retrieval_log", + "sys_knowledge_publish_log", + "sys_knowledge_review_record", + "sys_knowledge_sync_job", + "sys_knowledge_card", + "sys_knowledge_chunk", + "sys_knowledge_section", + "sys_knowledge_manifest", + "sys_knowledge_version", + "sys_knowledge_source_snapshot", + ]: + op.drop_table(table) + for column in [ + "migration_status", "last_published_at", "last_synced_at", "pending_version_id", + "current_version_id", "manifest_confirmed", "source_error", "source_status", + "lifecycle_status", "review_mode", "knowledge_type", "source_title", + ]: + op.drop_column("sys_knowledge", column) diff --git a/ai_knowledge_base_v2/apps/backend/app/models/__init__.py b/ai_knowledge_base_v2/apps/backend/app/models/__init__.py index cce13e5..9d6e077 100644 --- a/ai_knowledge_base_v2/apps/backend/app/models/__init__.py +++ b/ai_knowledge_base_v2/apps/backend/app/models/__init__.py @@ -2,7 +2,23 @@ from app.models.admin import Admin, Role from app.models.ai_config import ModelConfig, Prompt, SystemConfig from app.models.base import Base from app.models.chat import ChatMessage, ChatSession -from app.models.knowledge import Knowledge, UserKnowledgePermission +from app.models.knowledge import ( + HumanAttentionHistory, + HumanAttentionRecord, + Knowledge, + KnowledgeCard, + KnowledgeChunk, + KnowledgeManifest, + KnowledgePublishLog, + KnowledgeRetrievalCandidate, + KnowledgeRetrievalLog, + KnowledgeReviewRecord, + KnowledgeSection, + KnowledgeSourceSnapshot, + KnowledgeSyncJob, + KnowledgeVersion, + UserKnowledgePermission, +) from app.models.logs import AiRequestLog, OperationLog from app.models.user import User @@ -13,6 +29,19 @@ __all__ = [ "ChatMessage", "ChatSession", "Knowledge", + "KnowledgeCard", + "KnowledgeChunk", + "KnowledgeManifest", + "KnowledgePublishLog", + "KnowledgeRetrievalCandidate", + "KnowledgeRetrievalLog", + "KnowledgeReviewRecord", + "KnowledgeSection", + "KnowledgeSourceSnapshot", + "KnowledgeSyncJob", + "KnowledgeVersion", + "HumanAttentionHistory", + "HumanAttentionRecord", "ModelConfig", "OperationLog", "Prompt", diff --git a/ai_knowledge_base_v2/apps/backend/app/models/knowledge.py b/ai_knowledge_base_v2/apps/backend/app/models/knowledge.py index 2d68930..7f93f9c 100644 --- a/ai_knowledge_base_v2/apps/backend/app/models/knowledge.py +++ b/ai_knowledge_base_v2/apps/backend/app/models/knowledge.py @@ -2,7 +2,7 @@ from __future__ import annotations from datetime import datetime -from sqlalchemy import BigInteger, DateTime, ForeignKey, String +from sqlalchemy import BigInteger, DateTime, ForeignKey, Integer, String, Text, UniqueConstraint, func from sqlalchemy.orm import Mapped, mapped_column, relationship from app.models.base import Base, TimestampMixin @@ -17,6 +17,18 @@ class Knowledge(Base, TimestampMixin): feishu_node_id: Mapped[str] = mapped_column(String(100), nullable=False) status: Mapped[int] = mapped_column(default=1, nullable=False) remark: Mapped[str | None] = mapped_column(String(255), nullable=True) + source_title: Mapped[str | None] = mapped_column(String(255), nullable=True) + knowledge_type: Mapped[str] = mapped_column(String(30), default="general", nullable=False) + review_mode: Mapped[str] = mapped_column(String(30), default="manual", nullable=False) + lifecycle_status: Mapped[str] = mapped_column(String(30), default="active", nullable=False) + source_status: Mapped[str] = mapped_column(String(30), default="normal", nullable=False) + source_error: Mapped[str | None] = mapped_column(Text, nullable=True) + manifest_confirmed: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + current_version_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) + pending_version_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) + last_synced_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) + last_published_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) + migration_status: Mapped[str] = mapped_column(String(30), default="pending", nullable=False) user_permissions: Mapped[list["UserKnowledgePermission"]] = relationship( "UserKnowledgePermission", back_populates="knowledge" @@ -35,3 +47,227 @@ class UserKnowledgePermission(Base, TimestampMixin): user: Mapped["User"] = relationship("User", back_populates="knowledge_permissions") knowledge: Mapped[Knowledge] = relationship("Knowledge", back_populates="user_permissions") + + +class KnowledgeSourceSnapshot(Base): + __tablename__ = "sys_knowledge_source_snapshot" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False) + sync_job_id: Mapped[int | None] = mapped_column(BigInteger, index=True, nullable=True) + source_title: Mapped[str] = mapped_column(String(255), nullable=False) + source_content: Mapped[str] = mapped_column(Text, nullable=False) + content_hash: Mapped[str] = mapped_column(String(64), index=True, nullable=False) + source_identifier: Mapped[str] = mapped_column(String(255), nullable=False) + fetched_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) + created_by: Mapped[int | None] = mapped_column(BigInteger, nullable=True) + + +class KnowledgeVersion(Base): + __tablename__ = "sys_knowledge_version" + __table_args__ = (UniqueConstraint("knowledge_id", "version_no", name="uq_kb_version_no"),) + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False) + snapshot_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge_source_snapshot.id"), nullable=False) + version_no: Mapped[int] = mapped_column(Integer, nullable=False) + status: Mapped[str] = mapped_column(String(30), index=True, nullable=False) + quality_status: Mapped[str] = mapped_column(String(30), default="pending", nullable=False) + change_summary: Mapped[str | None] = mapped_column(Text, nullable=True) + quality_report: Mapped[str | None] = mapped_column(Text, nullable=True) + processing_rule_version: Mapped[str] = mapped_column(String(50), nullable=False) + generation_model: Mapped[str | None] = mapped_column(String(100), nullable=True) + created_by: Mapped[int | None] = mapped_column(BigInteger, nullable=True) + created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) + published_by: Mapped[int | None] = mapped_column(BigInteger, nullable=True) + published_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) + + +class KnowledgeManifest(Base): + __tablename__ = "sys_knowledge_manifest" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False) + version_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge_version.id"), index=True, nullable=False) + purpose: Mapped[str] = mapped_column(Text, nullable=False) + applicable_questions: Mapped[str] = mapped_column(Text, nullable=False) + inapplicable_questions: Mapped[str] = mapped_column(Text, nullable=False) + core_topics: Mapped[str] = mapped_column(Text, nullable=False) + entities: Mapped[str | None] = mapped_column(Text, nullable=True) + boundaries: Mapped[str | None] = mapped_column(Text, nullable=True) + content_hash: Mapped[str] = mapped_column(String(64), nullable=False) + change_level: Mapped[str] = mapped_column(String(20), default="major", nullable=False) + confirmed: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + confirmed_by: Mapped[int | None] = mapped_column(BigInteger, nullable=True) + confirmed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) + + +class KnowledgeSection(Base): + __tablename__ = "sys_knowledge_section" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False) + version_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge_version.id"), index=True, nullable=False) + section_key: Mapped[str] = mapped_column(String(64), nullable=False) + title: Mapped[str] = mapped_column(String(255), nullable=False) + content: Mapped[str] = mapped_column(Text, nullable=False) + source_start: Mapped[int] = mapped_column(Integer, nullable=False) + source_end: Mapped[int] = mapped_column(Integer, nullable=False) + sort_order: Mapped[int] = mapped_column(Integer, nullable=False) + content_hash: Mapped[str] = mapped_column(String(64), nullable=False) + + +class KnowledgeChunk(Base): + __tablename__ = "sys_knowledge_chunk" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False) + version_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge_version.id"), index=True, nullable=False) + section_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge_section.id"), index=True, nullable=False) + title: Mapped[str] = mapped_column(String(255), nullable=False) + content: Mapped[str] = mapped_column(Text, nullable=False) + normalized_text: Mapped[str] = mapped_column(Text, nullable=False) + keywords: Mapped[str | None] = mapped_column(Text, nullable=True) + synonyms: Mapped[str | None] = mapped_column(Text, nullable=True) + source_start: Mapped[int] = mapped_column(Integer, nullable=False) + source_end: Mapped[int] = mapped_column(Integer, nullable=False) + sort_order: Mapped[int] = mapped_column(Integer, nullable=False) + content_hash: Mapped[str] = mapped_column(String(64), nullable=False) + + +class KnowledgeCard(Base): + __tablename__ = "sys_knowledge_card" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False) + version_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge_version.id"), index=True, nullable=False) + section_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge_section.id"), index=True, nullable=False) + title: Mapped[str] = mapped_column(String(255), nullable=False) + summary: Mapped[str] = mapped_column(Text, nullable=False) + core_conclusion: Mapped[str] = mapped_column(Text, nullable=False) + applicable_questions: Mapped[str] = mapped_column(Text, nullable=False) + inapplicable_questions: Mapped[str] = mapped_column(Text, nullable=False) + keywords: Mapped[str] = mapped_column(Text, nullable=False) + synonyms: Mapped[str | None] = mapped_column(Text, nullable=True) + entities: Mapped[str | None] = mapped_column(Text, nullable=True) + risk_level: Mapped[str] = mapped_column(String(20), default="normal", nullable=False) + source_range: Mapped[str] = mapped_column(String(100), nullable=False) + content_hash: Mapped[str] = mapped_column(String(64), nullable=False) + generation_model: Mapped[str | None] = mapped_column(String(100), nullable=True) + generation_rule_version: Mapped[str] = mapped_column(String(50), nullable=False) + review_status: Mapped[str] = mapped_column(String(20), default="pending", nullable=False) + + +class KnowledgeSyncJob(Base): + __tablename__ = "sys_knowledge_sync_job" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False) + mode: Mapped[str] = mapped_column(String(30), nullable=False) + status: Mapped[str] = mapped_column(String(30), index=True, nullable=False) + stage: Mapped[str] = mapped_column(String(50), nullable=False) + progress: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + source_error: Mapped[str | None] = mapped_column(Text, nullable=True) + result_summary: Mapped[str | None] = mapped_column(Text, nullable=True) + requested_by: Mapped[int] = mapped_column(BigInteger, nullable=False) + created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) + started_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) + finished_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) + + +class KnowledgeReviewRecord(Base): + __tablename__ = "sys_knowledge_review_record" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False) + version_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge_version.id"), index=True, nullable=False) + card_id: Mapped[int | None] = mapped_column(ForeignKey("sys_knowledge_card.id"), nullable=True) + action: Mapped[str] = mapped_column(String(30), nullable=False) + reason: Mapped[str | None] = mapped_column(Text, nullable=True) + reviewed_by: Mapped[int] = mapped_column(BigInteger, nullable=False) + created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) + + +class KnowledgePublishLog(Base): + __tablename__ = "sys_knowledge_publish_log" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + knowledge_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge.id"), index=True, nullable=False) + from_version_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) + to_version_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) + action: Mapped[str] = mapped_column(String(30), nullable=False) + keep_open: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + detail_json: Mapped[str | None] = mapped_column(Text, nullable=True) + operated_by: Mapped[int] = mapped_column(BigInteger, nullable=False) + created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) + + +class KnowledgeRetrievalLog(Base): + __tablename__ = "sys_knowledge_retrieval_log" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + session_id: Mapped[int | None] = mapped_column(BigInteger, index=True, nullable=True) + message_id: Mapped[int | None] = mapped_column(BigInteger, index=True, nullable=True) + user_id: Mapped[int | None] = mapped_column(BigInteger, index=True, nullable=True) + question: Mapped[str] = mapped_column(Text, nullable=False) + catalog_snapshot: Mapped[str] = mapped_column(Text, nullable=False) + catalog_hash: Mapped[str] = mapped_column(String(64), nullable=False) + safety_rule_version: Mapped[str] = mapped_column(String(50), nullable=False) + knowledge_called: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + selected_knowledge_ids: Mapped[str | None] = mapped_column(String(500), nullable=True) + query_terms: Mapped[str | None] = mapped_column(Text, nullable=True) + tool_trace: Mapped[str | None] = mapped_column(Text, nullable=True) + final_section_ids: Mapped[str | None] = mapped_column(String(500), nullable=True) + discarded_conflicts: Mapped[str | None] = mapped_column(Text, nullable=True) + final_answer: Mapped[str | None] = mapped_column(Text, nullable=True) + status: Mapped[str] = mapped_column(String(20), nullable=False) + tool_cost_ms: Mapped[int | None] = mapped_column(Integer, nullable=True) + total_cost_ms: Mapped[int | None] = mapped_column(Integer, nullable=True) + error_message: Mapped[str | None] = mapped_column(Text, nullable=True) + attention_created: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) + + +class KnowledgeRetrievalCandidate(Base): + __tablename__ = "sys_knowledge_retrieval_candidate" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + retrieval_log_id: Mapped[int] = mapped_column(ForeignKey("sys_knowledge_retrieval_log.id"), index=True, nullable=False) + knowledge_id: Mapped[int] = mapped_column(BigInteger, index=True, nullable=False) + version_id: Mapped[int] = mapped_column(BigInteger, index=True, nullable=False) + chunk_id: Mapped[int] = mapped_column(BigInteger, nullable=False) + section_id: Mapped[int] = mapped_column(BigInteger, nullable=False) + lexical_score: Mapped[str] = mapped_column(String(30), nullable=False) + rerank_score: Mapped[str | None] = mapped_column(String(30), nullable=True) + selected: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + discard_reason: Mapped[str | None] = mapped_column(String(255), nullable=True) + + +class HumanAttentionRecord(Base): + __tablename__ = "sys_human_attention_record" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + session_id: Mapped[int] = mapped_column(BigInteger, index=True, nullable=False) + message_id: Mapped[int] = mapped_column(BigInteger, index=True, nullable=False) + user_id: Mapped[int] = mapped_column(BigInteger, index=True, nullable=False) + trigger_message: Mapped[str] = mapped_column(Text, nullable=False) + problem_summary: Mapped[str] = mapped_column(Text, nullable=False) + trigger_reason: Mapped[str] = mapped_column(Text, nullable=False) + priority: Mapped[str] = mapped_column(String(20), index=True, nullable=False) + status: Mapped[str] = mapped_column(String(20), index=True, default="pending", nullable=False) + handler_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) + handler_note: Mapped[str | None] = mapped_column(Text, nullable=True) + created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) + updated_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), onupdate=func.now(), nullable=False) + + +class HumanAttentionHistory(Base): + __tablename__ = "sys_human_attention_history" + + id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) + attention_id: Mapped[int] = mapped_column(ForeignKey("sys_human_attention_record.id"), index=True, nullable=False) + from_status: Mapped[str | None] = mapped_column(String(20), nullable=True) + to_status: Mapped[str] = mapped_column(String(20), nullable=False) + note: Mapped[str | None] = mapped_column(Text, nullable=True) + operated_by: Mapped[int] = mapped_column(BigInteger, nullable=False) + created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False)