Refactor backend to support interface-definition driven model resources
- Introduce a new model structure for managing interface definitions and model resources, enhancing the backend's capability to handle various service integrations. - Update the Makefile to reflect changes in database seeding and resource management commands. - Remove the deprecated credentials management routes and replace them with a unified model registry API. - Modify existing routes and schemas to align with the new model structure, ensuring seamless integration with the frontend. - Enhance database seeding scripts to populate new model resources and their configurations. - Update README documentation to reflect the new architecture and usage instructions for model resources and interface definitions.
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
"""数据表定义(SQLAlchemy 2.0)。
|
||||
|
||||
两张表,职责分离(见设计):
|
||||
- ProviderCredential:模型凭证(key 明文存,同 dograh,靠 DB 访问控制兜底;读时打码)
|
||||
- Assistant:助手配置,**只存模型/音色的"选项名",不嵌 key**
|
||||
模型注册表由接口定义驱动:
|
||||
- InterfaceDefinition:具体接入协议及其动态表单字段
|
||||
- ModelResource:模型配置与鉴权值
|
||||
- AssistantModelBinding:助手按能力选择模型资源
|
||||
|
||||
助手运行时再用 kind 去 ProviderCredential 取真 key(services/config_resolver.py)。
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import JSON, Boolean, DateTime, Float, ForeignKey, String, func
|
||||
from sqlalchemy import JSON, Boolean, DateTime, ForeignKey, String, func
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
|
||||
|
||||
|
||||
@@ -17,22 +18,39 @@ class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class ProviderCredential(Base):
|
||||
"""模型资源凭证。字段对齐前端 ComponentsModelsPage 的 ModelResource。"""
|
||||
class InterfaceDefinition(Base):
|
||||
"""具体接入协议,例如 xfyun-tts 与 xfyun-super-tts。"""
|
||||
|
||||
__tablename__ = "provider_credentials"
|
||||
__tablename__ = "interface_definitions"
|
||||
|
||||
id: Mapped[str] = mapped_column(String(40), primary_key=True) # model_xxx
|
||||
name: Mapped[str] = mapped_column(String(128), default="") # 资源名称,如 "DeepSeek-V3"
|
||||
model_id: Mapped[str] = mapped_column(String(128), default="") # 模型ID,如 "deepseek-chat"
|
||||
type: Mapped[str] = mapped_column(String(16), index=True) # LLM|ASR|TTS|Realtime|Embedding
|
||||
interface_type: Mapped[str] = mapped_column(String(32), default="openai") # openai|xfyun|dashscope|gemini
|
||||
api_url: Mapped[str] = mapped_column(String(512), default="")
|
||||
api_key: Mapped[str] = mapped_column(String(512), default="") # 明文
|
||||
voice: Mapped[str] = mapped_column(String(128), default="") # TTS 音色
|
||||
speed: Mapped[float] = mapped_column(Float, default=1.0) # TTS 语速
|
||||
language: Mapped[str] = mapped_column(String(32), default="") # ASR 语言
|
||||
# 同一 type 下的默认凭证(后端解析用;前端 ModelResource 无此字段,留作可选)
|
||||
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)
|
||||
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
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(
|
||||
@@ -41,7 +59,7 @@ class ProviderCredential(Base):
|
||||
|
||||
|
||||
class KnowledgeBase(Base):
|
||||
"""知识库注册表。本身引用一个 Embedding 凭证(用哪个向量模型)。
|
||||
"""知识库注册表。本身引用一个 Embedding 模型资源。
|
||||
|
||||
文档/分块(pgvector)是 KB 内部实现,这里先不展开;助手侧只认 knowledge_base_id。
|
||||
"""
|
||||
@@ -51,10 +69,9 @@ class KnowledgeBase(Base):
|
||||
id: Mapped[str] = mapped_column(String(40), primary_key=True) # kb_xxx
|
||||
name: Mapped[str] = mapped_column(String(128))
|
||||
description: Mapped[str] = mapped_column(String(2048), default="")
|
||||
# 该 KB 用哪个向量模型;凭证被删则置空
|
||||
embedding_credential_id: Mapped[str | None] = mapped_column(
|
||||
embedding_model_resource_id: Mapped[str | None] = mapped_column(
|
||||
String(40),
|
||||
ForeignKey("provider_credentials.id", ondelete="SET NULL"),
|
||||
ForeignKey("model_resources.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
status: Mapped[str] = mapped_column(String(16), default="active") # active|archived
|
||||
@@ -80,19 +97,6 @@ class Assistant(Base):
|
||||
greeting: Mapped[str] = mapped_column(String(2048), default="")
|
||||
enable_interrupt: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
|
||||
# ---- 引用"注册好的资源":凭证被删 → SET NULL(resolver 有默认/.env 兜底) ----
|
||||
llm_credential_id: Mapped[str | None] = mapped_column(
|
||||
String(40), ForeignKey("provider_credentials.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
asr_credential_id: Mapped[str | None] = mapped_column(
|
||||
String(40), ForeignKey("provider_credentials.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
tts_credential_id: Mapped[str | None] = mapped_column(
|
||||
String(40), ForeignKey("provider_credentials.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
realtime_credential_id: Mapped[str | None] = mapped_column(
|
||||
String(40), ForeignKey("provider_credentials.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
# KB 引用:被引用时禁止删 KB(RESTRICT),无默认兜底
|
||||
knowledge_base_id: Mapped[str | None] = mapped_column(
|
||||
String(40), ForeignKey("knowledge_bases.id", ondelete="RESTRICT"), nullable=True
|
||||
@@ -101,7 +105,7 @@ class Assistant(Base):
|
||||
# ---- 瘦类型专属字段(真列,稀疏:按 type 用其中几列) ----
|
||||
prompt: Mapped[str] = mapped_column(String(8192), default="") # prompt / opencode
|
||||
api_url: Mapped[str] = mapped_column(String(512), default="") # dify / fastgpt / opencode
|
||||
api_key: Mapped[str] = mapped_column(String(512), default="") # dify / fastgpt / opencode(打码/哨兵,同凭证)
|
||||
api_key: Mapped[str] = mapped_column(String(512), default="") # dify / fastgpt / opencode(打码/哨兵)
|
||||
app_id: Mapped[str] = mapped_column(String(128), default="") # fastgpt
|
||||
# workflow 专属:图(nodes/edges)。要版本化时再迁出到 assistant_workflow 表
|
||||
graph: Mapped[dict] = mapped_column(JSON, default=dict)
|
||||
@@ -110,3 +114,26 @@ class Assistant(Base):
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
|
||||
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()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user