2026-06-08 13:51:28 +08:00
|
|
|
"""数据表定义(SQLAlchemy 2.0)。
|
|
|
|
|
|
2026-06-14 19:36:12 +08:00
|
|
|
模型注册表由接口定义驱动:
|
|
|
|
|
- InterfaceDefinition:具体接入协议及其动态表单字段
|
|
|
|
|
- ModelResource:模型配置与鉴权值
|
|
|
|
|
- AssistantModelBinding:助手按能力选择模型资源
|
2026-06-08 13:51:28 +08:00
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
2026-07-10 16:25:06 +08:00
|
|
|
from sqlalchemy import (
|
|
|
|
|
JSON,
|
|
|
|
|
Boolean,
|
|
|
|
|
DateTime,
|
|
|
|
|
ForeignKey,
|
|
|
|
|
Integer,
|
|
|
|
|
String,
|
|
|
|
|
Text,
|
|
|
|
|
UniqueConstraint,
|
|
|
|
|
func,
|
|
|
|
|
)
|
2026-06-14 19:36:12 +08:00
|
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
2026-06-08 13:51:28 +08:00
|
|
|
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Base(DeclarativeBase):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
2026-06-14 19:36:12 +08:00
|
|
|
class InterfaceDefinition(Base):
|
|
|
|
|
"""具体接入协议,例如 xfyun-tts 与 xfyun-super-tts。"""
|
2026-06-08 13:51:28 +08:00
|
|
|
|
2026-06-14 19:36:12 +08:00
|
|
|
__tablename__ = "interface_definitions"
|
2026-06-08 13:51:28 +08:00
|
|
|
|
2026-06-14 19:36:12 +08:00
|
|
|
interface_type: Mapped[str] = mapped_column(String(64), primary_key=True)
|
|
|
|
|
name: Mapped[str] = mapped_column(String(128))
|
|
|
|
|
capability: Mapped[str] = mapped_column(String(16), index=True)
|
|
|
|
|
field_schema: Mapped[dict] = mapped_column(JSONB, default=dict)
|
|
|
|
|
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
|
|
|
version: Mapped[int] = mapped_column(default=1)
|
|
|
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ModelResource(Base):
|
|
|
|
|
"""统一模型资源:接口类型决定能力、鉴权字段和调用参数。"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "model_resources"
|
|
|
|
|
|
|
|
|
|
id: Mapped[str] = mapped_column(String(40), primary_key=True)
|
|
|
|
|
name: Mapped[str] = mapped_column(String(128), default="")
|
|
|
|
|
capability: Mapped[str] = mapped_column(String(16), index=True)
|
|
|
|
|
interface_type: Mapped[str] = mapped_column(
|
|
|
|
|
String(64),
|
|
|
|
|
ForeignKey("interface_definitions.interface_type", ondelete="RESTRICT"),
|
|
|
|
|
index=True,
|
|
|
|
|
)
|
|
|
|
|
values: Mapped[dict] = mapped_column(JSONB, default=dict)
|
|
|
|
|
secrets: Mapped[dict] = mapped_column(JSONB, default=dict)
|
2026-07-07 19:41:45 +08:00
|
|
|
support_image_input: Mapped[bool] = mapped_column(Boolean, default=False)
|
2026-06-14 19:36:12 +08:00
|
|
|
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
|
2026-06-08 13:51:28 +08:00
|
|
|
is_default: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
|
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2026-06-09 08:31:39 +08:00
|
|
|
class KnowledgeBase(Base):
|
2026-06-14 19:36:12 +08:00
|
|
|
"""知识库注册表。本身引用一个 Embedding 模型资源。
|
2026-06-08 13:51:28 +08:00
|
|
|
|
2026-06-09 08:31:39 +08:00
|
|
|
文档/分块(pgvector)是 KB 内部实现,这里先不展开;助手侧只认 knowledge_base_id。
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "knowledge_bases"
|
|
|
|
|
|
|
|
|
|
id: Mapped[str] = mapped_column(String(40), primary_key=True) # kb_xxx
|
2026-06-08 13:51:28 +08:00
|
|
|
name: Mapped[str] = mapped_column(String(128))
|
2026-06-09 08:31:39 +08:00
|
|
|
description: Mapped[str] = mapped_column(String(2048), default="")
|
2026-06-14 19:36:12 +08:00
|
|
|
embedding_model_resource_id: Mapped[str | None] = mapped_column(
|
2026-06-09 08:31:39 +08:00
|
|
|
String(40),
|
2026-06-14 19:36:12 +08:00
|
|
|
ForeignKey("model_resources.id", ondelete="SET NULL"),
|
2026-07-09 16:29:10 +08:00
|
|
|
index=True,
|
2026-06-09 08:31:39 +08:00
|
|
|
nullable=True,
|
|
|
|
|
)
|
|
|
|
|
status: Mapped[str] = mapped_column(String(16), default="active") # active|archived
|
|
|
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Assistant(Base):
|
|
|
|
|
"""助手(单表,无版本化)。type 为可变普通列,5 种类型共用此表。
|
|
|
|
|
|
|
|
|
|
模型/KB 以 FK 引用注册表;类型专属字段塞进 config(JSON)。
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "assistants"
|
|
|
|
|
|
|
|
|
|
id: Mapped[str] = mapped_column(String(40), primary_key=True) # asst_xxx
|
|
|
|
|
name: Mapped[str] = mapped_column(String(128))
|
|
|
|
|
# prompt|workflow|dify|fastgpt|opencode;创建后可改
|
|
|
|
|
type: Mapped[str] = mapped_column(String(16), index=True, default="prompt")
|
|
|
|
|
runtime_mode: Mapped[str] = mapped_column(String(16), default="pipeline")
|
2026-06-08 13:51:28 +08:00
|
|
|
greeting: Mapped[str] = mapped_column(String(2048), default="")
|
|
|
|
|
enable_interrupt: Mapped[bool] = mapped_column(Boolean, default=True)
|
2026-07-12 11:08:19 +08:00
|
|
|
turn_config: Mapped[dict] = mapped_column(JSON, default=dict)
|
2026-07-07 21:50:15 +08:00
|
|
|
vision_enabled: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
|
|
|
vision_model_resource_id: Mapped[str | None] = mapped_column(
|
|
|
|
|
String(40),
|
|
|
|
|
ForeignKey("model_resources.id", ondelete="SET NULL"),
|
|
|
|
|
nullable=True,
|
|
|
|
|
)
|
2026-06-09 08:31:39 +08:00
|
|
|
|
|
|
|
|
# KB 引用:被引用时禁止删 KB(RESTRICT),无默认兜底
|
|
|
|
|
knowledge_base_id: Mapped[str | None] = mapped_column(
|
|
|
|
|
String(40), ForeignKey("knowledge_bases.id", ondelete="RESTRICT"), nullable=True
|
|
|
|
|
)
|
|
|
|
|
|
2026-06-09 10:37:29 +08:00
|
|
|
# ---- 瘦类型专属字段(真列,稀疏:按 type 用其中几列) ----
|
|
|
|
|
prompt: Mapped[str] = mapped_column(String(8192), default="") # prompt / opencode
|
|
|
|
|
api_url: Mapped[str] = mapped_column(String(512), default="") # dify / fastgpt / opencode
|
2026-06-14 19:36:12 +08:00
|
|
|
api_key: Mapped[str] = mapped_column(String(512), default="") # dify / fastgpt / opencode(打码/哨兵)
|
2026-06-09 10:37:29 +08:00
|
|
|
app_id: Mapped[str] = mapped_column(String(128), default="") # fastgpt
|
|
|
|
|
# workflow 专属:图(nodes/edges)。要版本化时再迁出到 assistant_workflow 表
|
|
|
|
|
graph: Mapped[dict] = mapped_column(JSON, default=dict)
|
2026-06-09 08:31:39 +08:00
|
|
|
|
2026-06-08 13:51:28 +08:00
|
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
|
|
|
)
|
2026-06-14 19:36:12 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class AssistantModelBinding(Base):
|
|
|
|
|
"""助手按能力绑定统一模型资源;config 可覆盖资源默认 options。"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "assistant_model_bindings"
|
|
|
|
|
|
|
|
|
|
assistant_id: Mapped[str] = mapped_column(
|
|
|
|
|
String(40),
|
|
|
|
|
ForeignKey("assistants.id", ondelete="CASCADE"),
|
|
|
|
|
primary_key=True,
|
|
|
|
|
)
|
|
|
|
|
capability: Mapped[str] = mapped_column(String(16), primary_key=True)
|
|
|
|
|
model_resource_id: Mapped[str] = mapped_column(
|
|
|
|
|
String(40),
|
|
|
|
|
ForeignKey("model_resources.id", ondelete="RESTRICT"),
|
|
|
|
|
index=True,
|
|
|
|
|
)
|
|
|
|
|
config: Mapped[dict] = mapped_column(JSONB, default=dict)
|
|
|
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
|
|
|
)
|
2026-07-10 10:05:41 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class Tool(Base):
|
2026-07-10 14:32:10 +08:00
|
|
|
"""Reusable LLM tool definition; supported types are executed at runtime."""
|
2026-07-10 10:05:41 +08:00
|
|
|
|
|
|
|
|
__tablename__ = "tools"
|
|
|
|
|
|
|
|
|
|
id: Mapped[str] = mapped_column(String(40), primary_key=True)
|
|
|
|
|
name: Mapped[str] = mapped_column(String(128))
|
|
|
|
|
function_name: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
|
|
|
|
type: Mapped[str] = mapped_column(String(24), index=True)
|
|
|
|
|
description: Mapped[str] = mapped_column(String(2048), default="")
|
|
|
|
|
definition: Mapped[dict] = mapped_column(JSONB, default=dict)
|
|
|
|
|
secrets: Mapped[dict] = mapped_column(JSONB, default=dict)
|
|
|
|
|
status: Mapped[str] = mapped_column(String(16), index=True, default="active")
|
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now()
|
|
|
|
|
)
|
|
|
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AssistantToolBinding(Base):
|
|
|
|
|
"""Prompt assistant to reusable tool many-to-many binding."""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "assistant_tool_bindings"
|
|
|
|
|
|
|
|
|
|
assistant_id: Mapped[str] = mapped_column(
|
|
|
|
|
String(40),
|
|
|
|
|
ForeignKey("assistants.id", ondelete="CASCADE"),
|
|
|
|
|
primary_key=True,
|
|
|
|
|
)
|
|
|
|
|
tool_id: Mapped[str] = mapped_column(
|
|
|
|
|
String(40),
|
|
|
|
|
ForeignKey("tools.id", ondelete="RESTRICT"),
|
|
|
|
|
primary_key=True,
|
|
|
|
|
index=True,
|
|
|
|
|
)
|
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now()
|
|
|
|
|
)
|
2026-07-10 16:25:06 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConversationSession(Base):
|
|
|
|
|
"""一次完整连接对应的对话会话;媒体文件以后作为 artifact 关联进来。"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "conversation_sessions"
|
|
|
|
|
|
|
|
|
|
id: Mapped[str] = mapped_column(String(40), primary_key=True)
|
|
|
|
|
assistant_id: Mapped[str | None] = mapped_column(
|
|
|
|
|
String(40),
|
|
|
|
|
ForeignKey("assistants.id", ondelete="SET NULL"),
|
|
|
|
|
nullable=True,
|
|
|
|
|
index=True,
|
|
|
|
|
)
|
|
|
|
|
assistant_name: Mapped[str] = mapped_column(String(128), default="")
|
|
|
|
|
channel: Mapped[str] = mapped_column(String(24), index=True)
|
|
|
|
|
runtime_mode: Mapped[str] = mapped_column(String(16), default="pipeline")
|
|
|
|
|
status: Mapped[str] = mapped_column(String(16), index=True, default="active")
|
|
|
|
|
message_count: Mapped[int] = mapped_column(Integer, default=0)
|
|
|
|
|
started_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now(), index=True
|
|
|
|
|
)
|
|
|
|
|
ended_at: Mapped[datetime | None] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), nullable=True
|
|
|
|
|
)
|
|
|
|
|
extra: Mapped[dict] = mapped_column(JSONB, default=dict)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConversationMessage(Base):
|
|
|
|
|
"""会话中的有序消息;当前只写 text,结构预留其他内容类型。"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "conversation_messages"
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
UniqueConstraint(
|
|
|
|
|
"session_id", "sequence", name="uq_conversation_message_sequence"
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
id: Mapped[str] = mapped_column(String(40), primary_key=True)
|
|
|
|
|
session_id: Mapped[str] = mapped_column(
|
|
|
|
|
String(40),
|
|
|
|
|
ForeignKey("conversation_sessions.id", ondelete="CASCADE"),
|
|
|
|
|
index=True,
|
|
|
|
|
)
|
|
|
|
|
sequence: Mapped[int] = mapped_column(Integer)
|
|
|
|
|
role: Mapped[str] = mapped_column(String(16))
|
|
|
|
|
content_type: Mapped[str] = mapped_column(String(16), default="text")
|
|
|
|
|
content: Mapped[str] = mapped_column(Text, default="")
|
|
|
|
|
occurred_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now()
|
|
|
|
|
)
|
|
|
|
|
extra: Mapped[dict] = mapped_column(JSONB, default=dict)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConversationArtifact(Base):
|
|
|
|
|
"""未来的音频、视频、图片等大对象索引;二进制本体不进入数据库。"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "conversation_artifacts"
|
|
|
|
|
|
|
|
|
|
id: Mapped[str] = mapped_column(String(40), primary_key=True)
|
|
|
|
|
session_id: Mapped[str] = mapped_column(
|
|
|
|
|
String(40),
|
|
|
|
|
ForeignKey("conversation_sessions.id", ondelete="CASCADE"),
|
|
|
|
|
index=True,
|
|
|
|
|
)
|
|
|
|
|
message_id: Mapped[str | None] = mapped_column(
|
|
|
|
|
String(40),
|
|
|
|
|
ForeignKey("conversation_messages.id", ondelete="SET NULL"),
|
|
|
|
|
nullable=True,
|
|
|
|
|
index=True,
|
|
|
|
|
)
|
|
|
|
|
kind: Mapped[str] = mapped_column(String(16), index=True)
|
|
|
|
|
storage_uri: Mapped[str] = mapped_column(String(1024))
|
|
|
|
|
mime_type: Mapped[str] = mapped_column(String(128), default="")
|
|
|
|
|
size_bytes: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
|
|
|
duration_ms: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
|
|
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
|
|
|
DateTime(timezone=True), server_default=func.now()
|
|
|
|
|
)
|
|
|
|
|
extra: Mapped[dict] = mapped_column(JSONB, default=dict)
|