feat: Add base project structure with API and web components
This commit is contained in:
52
api/app/models/__init__.py
Normal file
52
api/app/models/__init__.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from .tenant_model import Tenants
|
||||
from .user_model import User
|
||||
from .workspace_model import Workspace, WorkspaceMember, WorkspaceRole
|
||||
from .knowledge_model import Knowledge
|
||||
from .document_model import Document
|
||||
from .file_model import File
|
||||
from .generic_file_model import GenericFile
|
||||
from .models_model import ModelConfig, ModelProvider, ModelType, ModelApiKey
|
||||
from .knowledgeshare_model import KnowledgeShare
|
||||
from .app_model import App
|
||||
from .agent_app_config_model import AgentConfig
|
||||
from .app_release_model import AppRelease
|
||||
from .memory_increment_model import MemoryIncrement
|
||||
from .end_user_model import EndUser
|
||||
from .appshare_model import AppShare
|
||||
from .release_share_model import ReleaseShare
|
||||
from .conversation_model import Conversation, Message
|
||||
from .api_key_model import ApiKey, ApiKeyLog, ApiKeyType
|
||||
from .data_config_model import DataConfig
|
||||
from .multi_agent_model import MultiAgentConfig, AgentInvocation
|
||||
|
||||
__all__ = [
|
||||
"Tenants",
|
||||
"User",
|
||||
"Workspace",
|
||||
"WorkspaceMember",
|
||||
"WorkspaceRole",
|
||||
"Knowledge",
|
||||
"Document",
|
||||
"File",
|
||||
"GenericFile",
|
||||
"ModelConfig",
|
||||
"ModelProvider",
|
||||
"ModelType",
|
||||
"ModelApiKey",
|
||||
"KnowledgeShare",
|
||||
"App",
|
||||
"AgentConfig",
|
||||
"AppRelease",
|
||||
"MemoryIncrement",
|
||||
"EndUser",
|
||||
"AppShare",
|
||||
"ReleaseShare",
|
||||
"Conversation",
|
||||
"Message",
|
||||
"ApiKey",
|
||||
"ApiKeyLog",
|
||||
"ApiKeyType",
|
||||
"DataConfig",
|
||||
"MultiAgentConfig",
|
||||
"AgentInvocation"
|
||||
]
|
||||
44
api/app/models/agent_app_config_model.py
Normal file
44
api/app/models/agent_app_config_model.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, Text, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class AgentConfig(Base):
|
||||
__tablename__ = "agent_configs"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
|
||||
# 一对一关联到 App
|
||||
app_id = Column(UUID(as_uuid=True), ForeignKey("apps.id"), nullable=False, unique=True, index=True)
|
||||
|
||||
# Agent 行为配置
|
||||
system_prompt = Column(Text, nullable=True, comment="系统提示词")
|
||||
default_model_config_id = Column(UUID(as_uuid=True), ForeignKey("model_configs.id"), nullable=True, index=True, comment="默认模型配置ID")
|
||||
|
||||
# 结构化配置(直接存储 JSON)
|
||||
model_parameters = Column(JSON, nullable=True, comment="模型参数配置(temperature、max_tokens等)")
|
||||
knowledge_retrieval = Column(JSON, nullable=True, comment="知识库检索配置")
|
||||
memory = Column(JSON, nullable=True, comment="记忆配置")
|
||||
variables = Column(JSON, default=list, nullable=True, comment="变量配置")
|
||||
tools = Column(JSON, default=dict, nullable=True, comment="工具配置")
|
||||
|
||||
# 多 Agent 相关字段
|
||||
agent_role = Column(String(20), comment="Agent 角色: master|sub|standalone")
|
||||
agent_domain = Column(String(50), comment="专业领域: customer_service|technical_support|sales 等")
|
||||
parent_agent_id = Column(UUID(as_uuid=True), ForeignKey("agent_configs.id"), comment="父 Agent ID")
|
||||
capabilities = Column(JSON, default=list, comment="Agent 能力列表")
|
||||
|
||||
# 状态与时间戳
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
|
||||
# 关系
|
||||
app = relationship("App", back_populates="agent_config")
|
||||
parent_agent = relationship("AgentConfig", remote_side=[id], backref="sub_agents")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<AgentConfig(id={self.id}, app_id={self.app_id})>"
|
||||
90
api/app/models/api_key_model.py
Normal file
90
api/app/models/api_key_model.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""API Key 数据模型"""
|
||||
import datetime
|
||||
import uuid
|
||||
from enum import StrEnum
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, Integer, ForeignKey, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class ApiKeyType(StrEnum):
|
||||
"""API Key 类型"""
|
||||
APP = "app" # 应用 API Key
|
||||
RAG = "rag" # RAG API Key
|
||||
MEMORY = "memory" # Memory API Key
|
||||
GENERAL = "general" # 通用 API Key
|
||||
|
||||
|
||||
class ApiKey(Base):
|
||||
"""API Key 表"""
|
||||
__tablename__ = "api_keys"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
|
||||
# 基本信息
|
||||
name = Column(String(255), nullable=False, comment="API Key 名称")
|
||||
description = Column(Text, comment="描述")
|
||||
key_prefix = Column(String(20), nullable=False, comment="Key 前缀")
|
||||
key_hash = Column(String(255), nullable=False, unique=True, index=True, comment="Key 哈希值")
|
||||
|
||||
# 类型和权限
|
||||
type = Column(String(50), nullable=False, index=True, comment="API Key 类型")
|
||||
scopes = Column(JSONB, nullable=False, default=list, comment="权限范围列表")
|
||||
|
||||
# 关联资源
|
||||
workspace_id = Column(UUID(as_uuid=True), ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False, index=True, comment="所属工作空间")
|
||||
resource_id = Column(UUID(as_uuid=True), index=True, comment="关联资源ID")
|
||||
resource_type = Column(String(50), comment="资源类型")
|
||||
|
||||
# 限制和配额
|
||||
rate_limit = Column(Integer, default=100, comment="速率限制(请求/分钟)")
|
||||
quota_limit = Column(Integer, comment="配额限制(总请求数)")
|
||||
quota_used = Column(Integer, default=0, comment="已使用配额")
|
||||
|
||||
# 有效期
|
||||
expires_at = Column(DateTime, comment="过期时间")
|
||||
|
||||
# 状态
|
||||
is_active = Column(Boolean, default=True, nullable=False, comment="是否激活")
|
||||
last_used_at = Column(DateTime, comment="最后使用时间")
|
||||
usage_count = Column(Integer, default=0, comment="使用次数")
|
||||
|
||||
# 审计
|
||||
created_by = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False, comment="创建者")
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, nullable=False, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment="更新时间")
|
||||
|
||||
# 关系
|
||||
workspace = relationship("Workspace", back_populates="api_keys")
|
||||
creator = relationship("User", foreign_keys=[created_by])
|
||||
logs = relationship("ApiKeyLog", back_populates="api_key", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class ApiKeyLog(Base):
|
||||
"""API Key 使用日志表"""
|
||||
__tablename__ = "api_key_logs"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
|
||||
api_key_id = Column(UUID(as_uuid=True), ForeignKey("api_keys.id", ondelete="CASCADE"), nullable=False, index=True, comment="API Key ID")
|
||||
|
||||
# 请求信息
|
||||
endpoint = Column(String(255), nullable=False, comment="请求端点")
|
||||
method = Column(String(10), nullable=False, comment="HTTP 方法")
|
||||
ip_address = Column(String(50), comment="IP 地址")
|
||||
user_agent = Column(Text, comment="User Agent")
|
||||
|
||||
# 响应信息
|
||||
status_code = Column(Integer, comment="响应状态码")
|
||||
response_time = Column(Integer, comment="响应时间(毫秒)")
|
||||
|
||||
# Token 使用
|
||||
tokens_used = Column(Integer, comment="使用的 Token 数")
|
||||
|
||||
# 时间
|
||||
created_at = Column(DateTime, nullable=False, default=datetime.datetime.now, index=True, comment="创建时间")
|
||||
|
||||
# 关系
|
||||
api_key = relationship("ApiKey", back_populates="logs")
|
||||
115
api/app/models/app_model.py
Normal file
115
api/app/models/app_model.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import datetime
|
||||
from enum import StrEnum
|
||||
from re import LOCALE
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
|
||||
class IconType(StrEnum):
|
||||
"""图标类型枚举"""
|
||||
LOCALE = "locale"
|
||||
REMOTE = "remote"
|
||||
|
||||
# 可见性:private | workspace | public
|
||||
class AppVisibility(StrEnum):
|
||||
"""可见性枚举"""
|
||||
PRIVATE = "private"
|
||||
WORKSPACE = "workspace"
|
||||
PUBLIC = "public"
|
||||
|
||||
# 应用类型:agent | workflow | multi_agent
|
||||
class AppType(StrEnum):
|
||||
"""应用类型枚举"""
|
||||
AGENT = "agent"
|
||||
WORKFLOW = "workflow"
|
||||
MULTI_AGENT = "multi_agent"
|
||||
|
||||
|
||||
# 应用状态:draft | active | archived
|
||||
class AppStatus(StrEnum):
|
||||
"""应用状态枚举"""
|
||||
DRAFT = "draft"
|
||||
ACTIVE = "active"
|
||||
ARCHIVED = "archived"
|
||||
|
||||
|
||||
class App(Base):
|
||||
__tablename__ = "apps"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
workspace_id = Column(UUID(as_uuid=True), nullable=False, comment="workspaces.id")
|
||||
created_by = Column(UUID(as_uuid=True), nullable=False, comment="users.id")
|
||||
|
||||
name = Column(String, index=True, nullable=False)
|
||||
description = Column(String, nullable=True)
|
||||
icon = Column(String, nullable=True)
|
||||
icon_type = Column(String, nullable=True)
|
||||
|
||||
# 应用类型:agent | workflow 等
|
||||
type = Column(String, index=True, nullable=False)
|
||||
|
||||
# 可见性:private | workspace | public
|
||||
visibility = Column(String, default="workspace")
|
||||
|
||||
# 状态:draft | active | archived
|
||||
status = Column(String, default="draft")
|
||||
|
||||
# 标签或扩展元数据
|
||||
tags = Column(JSON, default=list)
|
||||
|
||||
# 当前已发布版本指针(发布后指向快照,不受编辑影响)
|
||||
current_release_id = Column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("app_releases.id", use_alter=True, name="fk_apps_current_release_id"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
|
||||
# 一对一:Agent 配置(仅当 type=agent 时有效)
|
||||
agent_config = relationship(
|
||||
"AgentConfig",
|
||||
back_populates="app",
|
||||
uselist=False,
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
|
||||
# 一对一:多 Agent 配置(仅当 type=multi_agent 时有效)
|
||||
multi_agent_config = relationship(
|
||||
"MultiAgentConfig",
|
||||
back_populates="app",
|
||||
uselist=False,
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
|
||||
# 发布版本关联
|
||||
current_release = relationship("AppRelease", foreign_keys=[current_release_id])
|
||||
# 指定外键以避免与 current_release_id 造成歧义
|
||||
releases = relationship(
|
||||
"AppRelease",
|
||||
back_populates="app",
|
||||
cascade="all, delete-orphan",
|
||||
foreign_keys="AppRelease.app_id",
|
||||
)
|
||||
|
||||
# 会话关联
|
||||
conversations = relationship(
|
||||
"Conversation",
|
||||
back_populates="app",
|
||||
cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
# 与 EndUser 的反向关系
|
||||
end_users = relationship(
|
||||
"EndUser",
|
||||
back_populates="app",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<App(id={self.id}, name={self.name}, type={self.type})>"
|
||||
68
api/app/models/app_release_model.py
Normal file
68
api/app/models/app_release_model.py
Normal file
@@ -0,0 +1,68 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, Integer, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
from app.models.app_model import IconType
|
||||
|
||||
|
||||
class AppRelease(Base):
|
||||
__tablename__ = "app_releases"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("app_id", "version", name="uq_app_release_app_version"),
|
||||
)
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
app_id = Column(UUID(as_uuid=True), ForeignKey("apps.id"), nullable=False, index=True)
|
||||
|
||||
# 版本号(按应用内递增)
|
||||
version = Column(Integer, nullable=False, default=1, index=True)
|
||||
# 版本号,显示用
|
||||
version_name = Column(String, nullable=False)
|
||||
# 版本说明
|
||||
release_notes = Column(String, nullable=True, comment="版本说明")
|
||||
|
||||
# 基础信息快照(发布时冻结)
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(String, nullable=True)
|
||||
icon = Column(String, nullable=True)
|
||||
icon_type = Column(String, nullable=True)
|
||||
type = Column(String, nullable=False)
|
||||
visibility = Column(String, default="private")
|
||||
|
||||
# 类型特定配置快照(针对 agent/workflow 等统一存放)
|
||||
config = Column(JSON, default=dict)
|
||||
|
||||
# 便于查询的索引字段(例如 agent 的默认模型)
|
||||
default_model_config_id = Column(UUID(as_uuid=True), ForeignKey("model_configs.id"), nullable=True, index=True)
|
||||
|
||||
# 发布信息
|
||||
published_by = Column(UUID(as_uuid=True), nullable=False, comment="users.id")
|
||||
published_at = Column(DateTime, default=datetime.datetime.now)
|
||||
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
|
||||
# 关系: 指定外键,避免与 App.current_release_id 引起歧义
|
||||
app = relationship("App", back_populates="releases", foreign_keys=[app_id])
|
||||
|
||||
# 发布人关系 - 使用 primaryjoin 明确指定关联条件
|
||||
publisher = relationship(
|
||||
"User",
|
||||
primaryjoin="AppRelease.published_by == User.id",
|
||||
foreign_keys=[published_by],
|
||||
lazy="joined",
|
||||
viewonly=True # 只读关系,不会尝试更新
|
||||
)
|
||||
|
||||
@property
|
||||
def publisher_name(self) -> str:
|
||||
"""发布人名称"""
|
||||
if self.publisher:
|
||||
return self.publisher.username or self.publisher.email or "未知用户"
|
||||
return "未知用户"
|
||||
|
||||
def __repr__(self):
|
||||
return f"<AppRelease(id={self.id}, app_id={self.app_id}, version={self.version})>"
|
||||
28
api/app/models/appshare_model.py
Normal file
28
api/app/models/appshare_model.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, DateTime, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from app.db import Base
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
|
||||
class AppShare(Base):
|
||||
"""应用分享模型
|
||||
|
||||
记录应用从一个工作空间分享到另一个工作空间的关系
|
||||
"""
|
||||
__tablename__ = "app_shares"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
source_app_id = Column(UUID(as_uuid=True), ForeignKey('apps.id', ondelete='CASCADE'), nullable=False, comment="源应用ID")
|
||||
source_workspace_id = Column(UUID(as_uuid=True), ForeignKey('workspaces.id'), nullable=False, comment="源工作空间ID")
|
||||
target_workspace_id = Column(UUID(as_uuid=True), ForeignKey('workspaces.id'), nullable=False, comment="目标工作空间ID")
|
||||
shared_by = Column(UUID(as_uuid=True), ForeignKey('users.id'), nullable=False, comment="分享者用户ID")
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now)
|
||||
|
||||
# Relationships
|
||||
source_app = relationship("App", foreign_keys=[source_app_id], backref="shares")
|
||||
source_workspace = relationship("Workspace", foreign_keys=[source_workspace_id])
|
||||
target_workspace = relationship("Workspace", foreign_keys=[target_workspace_id])
|
||||
shared_user = relationship("User", backref="app_shares")
|
||||
80
api/app/models/conversation_model.py
Normal file
80
api/app/models/conversation_model.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
会话和消息模型
|
||||
"""
|
||||
import uuid
|
||||
import datetime
|
||||
from sqlalchemy import Column, String, DateTime, ForeignKey, Boolean, Integer, Text, JSON
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class Conversation(Base):
|
||||
"""会话表
|
||||
|
||||
会话类型说明:
|
||||
- 草稿会话 (is_draft=True): 使用应用的当前草稿配置,用于开发和测试
|
||||
- 发布会话 (is_draft=False): 使用应用的当前发布版本配置,用于生产环境
|
||||
|
||||
工作空间隔离:
|
||||
- 每个会话属于一个工作空间(workspace_id)
|
||||
- 同一个应用在不同工作空间有独立的会话记录
|
||||
- 支持应用分享后的会话隔离
|
||||
"""
|
||||
__tablename__ = "conversations"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
|
||||
# 关联信息
|
||||
app_id = Column(UUID(as_uuid=True), ForeignKey("apps.id"), nullable=False, comment="应用ID")
|
||||
workspace_id = Column(UUID(as_uuid=True), ForeignKey("workspaces.id"), nullable=False, comment="工作空间ID")
|
||||
user_id = Column(String, nullable=True, comment="用户ID(外部系统)")
|
||||
|
||||
# 会话信息
|
||||
title = Column(String(255), comment="会话标题")
|
||||
summary = Column(Text, comment="会话摘要")
|
||||
|
||||
# 会话类型:True=草稿会话(使用草稿配置),False=发布会话(使用发布配置)
|
||||
is_draft = Column(Boolean, default=True, nullable=False, comment="是否为草稿会话")
|
||||
|
||||
# 配置快照:保存创建会话时的完整配置,用于审计和问题追溯
|
||||
config_snapshot = Column(JSON, comment="配置快照(Agent配置、模型配置等)")
|
||||
|
||||
# 统计信息
|
||||
message_count = Column(Integer, default=0, comment="消息数量")
|
||||
|
||||
# 状态
|
||||
is_active = Column(Boolean, default=True, nullable=False, comment="是否活跃")
|
||||
|
||||
# 时间戳
|
||||
created_at = Column(DateTime, default=datetime.datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment="更新时间")
|
||||
|
||||
# 关联关系
|
||||
app = relationship("App", back_populates="conversations")
|
||||
workspace = relationship("Workspace")
|
||||
messages = relationship("Message", back_populates="conversation", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class Message(Base):
|
||||
"""消息表"""
|
||||
__tablename__ = "messages"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
|
||||
# 关联信息
|
||||
conversation_id = Column(UUID(as_uuid=True), ForeignKey("conversations.id"), nullable=False, comment="会话ID")
|
||||
|
||||
# 消息内容
|
||||
role = Column(String(20), nullable=False, comment="角色: user/assistant/system")
|
||||
content = Column(Text, nullable=False, comment="消息内容")
|
||||
|
||||
# 元数据(避免使用 metadata 保留字)
|
||||
meta_data = Column(JSON, comment="消息元数据(如模型、token使用等)")
|
||||
|
||||
# 时间戳
|
||||
created_at = Column(DateTime, default=datetime.datetime.now, comment="创建时间")
|
||||
|
||||
# 关联关系
|
||||
conversation = relationship("Conversation", back_populates="messages")
|
||||
71
api/app/models/data_config_model.py
Normal file
71
api/app/models/data_config_model.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, Integer, Float
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class DataConfig(Base):
|
||||
"""数据配置表 - 用于存储记忆系统的配置参数"""
|
||||
__tablename__ = "data_config"
|
||||
|
||||
# 主键
|
||||
config_id = Column(Integer, primary_key=True, autoincrement=True, comment="配置ID")
|
||||
|
||||
# 基本信息
|
||||
config_name = Column(String, nullable=False, comment="配置名称")
|
||||
config_desc = Column(String, nullable=True, comment="配置描述")
|
||||
|
||||
# 组织信息
|
||||
workspace_id = Column(UUID(as_uuid=True), nullable=True, comment="工作空间ID")
|
||||
group_id = Column(String, nullable=True, comment="组ID")
|
||||
user_id = Column(String, nullable=True, comment="用户ID")
|
||||
apply_id = Column(String, nullable=True, comment="应用ID")
|
||||
|
||||
# 模型选择(从workspace继承)
|
||||
llm_id = Column(String, nullable=True, comment="LLM模型配置ID")
|
||||
embedding_id = Column(String, nullable=True, comment="嵌入模型配置ID")
|
||||
rerank_id = Column(String, nullable=True, comment="重排序模型配置ID")
|
||||
llm = Column(String, nullable=True, comment="LLM模型配置ID")
|
||||
|
||||
# 记忆萃取引擎配置
|
||||
enable_llm_dedup_blockwise = Column(Boolean, default=True, comment="启用LLM决策去重")
|
||||
enable_llm_disambiguation = Column(Boolean, default=True, comment="启用LLM决策消歧")
|
||||
deep_retrieval = Column(Boolean, default=True, comment="深度检索开关")
|
||||
|
||||
# 阈值配置 (0-1 之间的浮点数)
|
||||
t_type_strict = Column(Float, default=0.8, comment="类型严格阈值")
|
||||
t_name_strict = Column(Float, default=0.8, comment="名称严格阈值")
|
||||
t_overall = Column(Float, default=0.8, comment="综合阈值")
|
||||
|
||||
# 状态配置
|
||||
state = Column(Boolean, default=False, comment="配置使用状态")
|
||||
|
||||
# 分块策略
|
||||
chunker_strategy = Column(String, default="RecursiveChunker", comment="分块策略")
|
||||
|
||||
# 剪枝配置
|
||||
pruning_enabled = Column(Boolean, default=False, comment="是否启动智能语义剪枝")
|
||||
pruning_scene = Column(String, nullable=True, comment="智能剪枝场景:education/online_service/outbound")
|
||||
pruning_threshold = Column(Float, nullable=True, comment="智能语义剪枝阈值(0-0.9)")
|
||||
|
||||
# 自我反思配置
|
||||
enable_self_reflexion = Column(Boolean, default=False, comment="是否启用自我反思")
|
||||
iteration_period = Column(String, default="3", comment="反思迭代周期")
|
||||
reflexion_range = Column(String, default="retrieval", comment="反思范围:部分/全部")
|
||||
baseline = Column(String, default="time", comment="基线:时间/事实/时间和事实")
|
||||
|
||||
# 遗忘引擎配置
|
||||
statement_granularity = Column(Integer, default=2, comment="陈述提取颗粒度,挡位 1/2/3")
|
||||
include_dialogue_context = Column(Boolean, default=False, comment="是否包含对话上下文")
|
||||
max_context = Column(Integer, default=1000, comment="对话语境中包含字符的最大数量")
|
||||
lambda_time = Column("lambda_time", Float, default=0.5, comment="最低保持度,0-1 小数")
|
||||
lambda_mem = Column("lambda_mem", Float, default=0.5, comment="遗忘率,0-1 小数")
|
||||
offset = Column("offset", Float, default=0.0, comment="偏移度,0-1 小数")
|
||||
|
||||
# 时间戳
|
||||
created_at = Column(DateTime, default=datetime.datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment="更新时间")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<DataConfig(config_id={self.config_id}, config_name={self.config_name})>"
|
||||
28
api/app/models/document_model.py
Normal file
28
api/app/models/document_model.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, JSON, DateTime, ForeignKey, Float
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from app.db import Base
|
||||
|
||||
class Document(Base):
|
||||
__tablename__ = "documents"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
kb_id = Column(UUID(as_uuid=True), nullable=False, comment="knowledges.id")
|
||||
created_by = Column(UUID(as_uuid=True), nullable=False, comment="users.id")
|
||||
file_id = Column(UUID(as_uuid=True), nullable=False, comment="files.id")
|
||||
file_name = Column(String, index=True, nullable=False, comment="file name")
|
||||
file_ext = Column(String, index=True, nullable=False, comment="file extension")
|
||||
file_size = Column(Integer, default=0, comment="file size(byte)")
|
||||
file_meta = Column(JSON, nullable=False, default={})
|
||||
parser_id = Column(String, index=True, nullable=False, comment="default parser ID")
|
||||
parser_config = Column(JSON, nullable=False, default={"layout_recognize": "DeepDOC", "chunk_token_num": 128, "delimiter": "\n"}, comment="default parser config")
|
||||
chunk_num = Column(Integer, default=0, comment="chunk num")
|
||||
progress = Column(Float, default=0)
|
||||
progress_msg = Column(String, default="", comment="process message")
|
||||
process_begin_at = Column(DateTime, default=datetime.datetime.now)
|
||||
process_duration = Column(Float, default=0)
|
||||
run = Column(Integer, default=0, comment="start to run processing or cancel.(1: run it; 2: cancel)")
|
||||
status = Column(Integer, default=1, comment="is it validate(0: wasted, 1: validate)")
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now)
|
||||
24
api/app/models/end_user_model.py
Normal file
24
api/app/models/end_user_model.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, DateTime, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
|
||||
class EndUser(Base):
|
||||
__tablename__ = "end_users"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, nullable=False, index=True)
|
||||
app_id = Column(UUID(as_uuid=True), ForeignKey("apps.id"), nullable=False)
|
||||
# end_user_id = Column(String, nullable=False, index=True)
|
||||
other_id = Column(String, nullable=True) # Store original user_id
|
||||
other_name = Column(String, default="", nullable=False)
|
||||
other_address = Column(String, default="", nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
|
||||
# 与 App 的反向关系
|
||||
app = relationship(
|
||||
"App",
|
||||
back_populates="end_users"
|
||||
)
|
||||
17
api/app/models/file_model.py
Normal file
17
api/app/models/file_model.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from app.db import Base
|
||||
|
||||
class File(Base):
|
||||
__tablename__ = "files"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
kb_id = Column(UUID(as_uuid=True), nullable=False, comment="knowledges.id")
|
||||
created_by = Column(UUID(as_uuid=True), nullable=False, comment="users.id")
|
||||
parent_id = Column(UUID(as_uuid=True), nullable=True, default=None, comment="parent folder id")
|
||||
file_name = Column(String, index=True, nullable=False, comment="file name or folder name,default folder name is /")
|
||||
file_ext = Column(String, index=True, nullable=False, comment="file extension:folder|pdf")
|
||||
file_size = Column(Integer, default=0, comment="file size(byte)")
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
52
api/app/models/generic_file_model.py
Normal file
52
api/app/models/generic_file_model.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Index, JSON
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class GenericFile(Base):
|
||||
"""
|
||||
通用文件模型,支持多种上传上下文(头像、应用图标、知识库文件、临时文件等)
|
||||
"""
|
||||
__tablename__ = "generic_files"
|
||||
|
||||
# 主键和租户信息
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True, comment="文件唯一标识")
|
||||
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True, comment="租户ID")
|
||||
created_by = Column(UUID(as_uuid=True), nullable=False, index=True, comment="创建者用户ID")
|
||||
|
||||
# 文件基本信息
|
||||
file_name = Column(String, nullable=False, comment="原始文件名")
|
||||
file_ext = Column(String, nullable=False, index=True, comment="文件扩展名")
|
||||
file_size = Column(Integer, nullable=False, comment="文件大小(字节)")
|
||||
mime_type = Column(String, nullable=True, comment="MIME类型")
|
||||
|
||||
# 上传上下文
|
||||
context = Column(String, nullable=False, index=True, comment="上传上下文(avatar/app_icon/knowledge_base/temp/attachment)")
|
||||
|
||||
# 存储信息
|
||||
storage_path = Column(String, nullable=False, comment="文件存储路径")
|
||||
|
||||
# 元数据(JSON格式,存储业务相关信息)
|
||||
file_metadata = Column(JSON, nullable=True, default={}, comment="业务元数据")
|
||||
|
||||
# 状态和访问控制
|
||||
status = Column(String, default="active", index=True, comment="文件状态(active/processing/deleted)")
|
||||
is_public = Column(Boolean, default=False, comment="是否公开访问")
|
||||
access_url = Column(String, nullable=True, comment="访问URL")
|
||||
|
||||
# 引用计数(用于判断文件是否可以删除)
|
||||
reference_count = Column(Integer, default=0, comment="引用计数")
|
||||
|
||||
# 时间戳
|
||||
created_at = Column(DateTime, default=datetime.datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment="更新时间")
|
||||
deleted_at = Column(DateTime, nullable=True, comment="删除时间(软删除)")
|
||||
|
||||
# 复合索引
|
||||
__table_args__ = (
|
||||
Index('idx_tenant_context', 'tenant_id', 'context'),
|
||||
Index('idx_tenant_status', 'tenant_id', 'status'),
|
||||
Index('idx_created_at', 'created_at'),
|
||||
)
|
||||
69
api/app/models/knowledge_model.py
Normal file
69
api/app/models/knowledge_model.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import datetime
|
||||
import uuid
|
||||
import enum
|
||||
from sqlalchemy import Column, Integer, String, JSON, DateTime, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from app.db import Base
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
|
||||
class KnowledgeType(enum.StrEnum):
|
||||
General = "General"
|
||||
Web = "Web"
|
||||
ThirdParty = "Third-party"
|
||||
FOLDER = "Folder"
|
||||
|
||||
|
||||
class ParserType(enum.StrEnum):
|
||||
NAIVE = "naive"
|
||||
QA = "qa"
|
||||
MANUAL = "manual"
|
||||
TABLE = "table"
|
||||
PRESENTATION = "presentation"
|
||||
LAWS = "laws"
|
||||
PAPER = "paper"
|
||||
RESUME = "resume"
|
||||
BOOK = "book"
|
||||
ONE = "one"
|
||||
AUDIO = "audio"
|
||||
EMAIL = "email"
|
||||
TAG = "tag"
|
||||
KG = "knowledge_graph"
|
||||
|
||||
|
||||
class PermissionType(enum.StrEnum):
|
||||
Private = "Private"
|
||||
Share = "Share"
|
||||
|
||||
class Knowledge(Base):
|
||||
__tablename__ = "knowledges"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
workspace_id = Column(UUID(as_uuid=True), nullable=False, comment="workspaces.id")
|
||||
created_by = Column(UUID(as_uuid=True), ForeignKey('users.id'), nullable=False, comment="users.id")
|
||||
parent_id = Column(UUID(as_uuid=True), nullable=True, default=None, comment="parent folder id when type is Folder")
|
||||
name = Column(String, index=True, nullable=False, comment="KB name")
|
||||
description = Column(String, comment="KB description")
|
||||
avatar = Column(String, comment="avatar url")
|
||||
type = Column(String, default="General", comment="Type:General|Web|Third-party|Folder")
|
||||
permission_id = Column(String, default="Private", comment="permission ID:Private|Share")
|
||||
embedding_id = Column(UUID(as_uuid=True), ForeignKey('model_configs.id', ondelete="SET NULL"), nullable=True, comment="default embedding model ID")
|
||||
reranker_id = Column(UUID(as_uuid=True), ForeignKey('model_configs.id', ondelete="SET NULL"), nullable=True, comment="default reranker model ID")
|
||||
llm_id = Column(UUID(as_uuid=True), ForeignKey('model_configs.id', ondelete="SET NULL"), nullable=True, comment="default llm model ID")
|
||||
image2text_id = Column(UUID(as_uuid=True), ForeignKey('model_configs.id', ondelete="SET NULL"), nullable=True, comment="default image2text model ID")
|
||||
doc_num = Column(Integer, default=0, comment="doc num")
|
||||
chunk_num = Column(Integer, default=0, comment="chunk num")
|
||||
parser_id = Column(String, index=True, default="naive", comment="default parser ID")
|
||||
parser_config = Column(JSON, nullable=False,
|
||||
default={"layout_recognize": "DeepDOC", "chunk_token_num": 128, "delimiter": "\n"},
|
||||
comment="default parser config")
|
||||
status = Column(Integer, index=True, default=1, comment="is it validate(0: disable, 1: enable, 2:Soft-delete)")
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now)
|
||||
|
||||
# Relationships
|
||||
created_user = relationship("User", backref="created_user")
|
||||
embedding = relationship("ModelConfig", foreign_keys=[embedding_id], uselist=False, backref="embedding")
|
||||
reranker = relationship("ModelConfig", foreign_keys=[reranker_id], uselist=False, backref="reranker")
|
||||
llm = relationship("ModelConfig", foreign_keys=[llm_id], uselist=False, backref="llm")
|
||||
image2text = relationship("ModelConfig", foreign_keys=[image2text_id], uselist=False, backref="image2text")
|
||||
24
api/app/models/knowledgeshare_model.py
Normal file
24
api/app/models/knowledgeshare_model.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, JSON, DateTime, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from app.db import Base
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
|
||||
class KnowledgeShare(Base):
|
||||
__tablename__ = "knowledge_shares"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
source_kb_id = Column(UUID(as_uuid=True), nullable=False, comment="source knowledges.id")
|
||||
source_workspace_id = Column(UUID(as_uuid=True), nullable=False, comment="source workspaces.id")
|
||||
target_kb_id = Column(UUID(as_uuid=True), ForeignKey('knowledges.id'), nullable=False, comment="target knowledges.id")
|
||||
target_workspace_id = Column(UUID(as_uuid=True), ForeignKey('workspaces.id'), nullable=False, comment="target workspaces.id")
|
||||
shared_by = Column(UUID(as_uuid=True), ForeignKey('users.id'), nullable=False, comment="shared users.id")
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now)
|
||||
|
||||
# Relationships
|
||||
target_kb = relationship("Knowledge", backref="target_kb")
|
||||
target_workspace = relationship("Workspace", backref="target_workspace")
|
||||
shared_user = relationship("User", backref="shared_user")
|
||||
18
api/app/models/memory_increment_model.py
Normal file
18
api/app/models/memory_increment_model.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import uuid
|
||||
import datetime
|
||||
from sqlalchemy import Column, ForeignKey, Integer, Date, DateTime
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
|
||||
class MemoryIncrement(Base):
|
||||
__tablename__ = "memory_increments"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
workspace_id = Column(UUID(as_uuid=True), ForeignKey("workspaces.id"), index=True, nullable=False)
|
||||
total_num = Column(Integer, default=0, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
|
||||
# 与 App 的关系(指向映射类名,而非表名)
|
||||
workspace = relationship("Workspace", back_populates="memory_increments")
|
||||
104
api/app/models/models_model.py
Normal file
104
api/app/models/models_model.py
Normal file
@@ -0,0 +1,104 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from enum import StrEnum
|
||||
from typing import Optional, List
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class ModelType(StrEnum):
|
||||
"""模型类型枚举"""
|
||||
LLM = "llm"
|
||||
CHAT = "chat"
|
||||
EMBEDDING = "embedding"
|
||||
RERANK = "rerank"
|
||||
|
||||
|
||||
class ModelProvider(StrEnum):
|
||||
"""模型提供商枚举"""
|
||||
OPENAI = "openai"
|
||||
# ANTHROPIC = "anthropic"
|
||||
# GOOGLE = "google"
|
||||
# BAIDU = "baidu"
|
||||
DASHSCOPE = "dashscope"
|
||||
# ZHIPU = "zhipu"
|
||||
# MOONSHOT = "moonshot"
|
||||
# DEEPSEEK = "deepseek"
|
||||
OLLAMA = "ollama"
|
||||
XINFERENCE = "xinference"
|
||||
GPUSTACK = "gpustack"
|
||||
BEDROCK = "bedrock"
|
||||
|
||||
|
||||
class ModelConfig(Base):
|
||||
"""模型配置表"""
|
||||
__tablename__ = "model_configs"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
name = Column(String, nullable=False, comment="模型显示名称")
|
||||
type = Column(String, nullable=False, index=True, comment="模型类型")
|
||||
description = Column(String, comment="模型描述")
|
||||
|
||||
# 模型配置参数
|
||||
config = Column(JSON, comment="模型配置参数")
|
||||
# - temperature : 控制生成文本的随机性。值越高,输出越随机、越有创造性;值越低,输出越确定、越保守。
|
||||
# - top_p : 一种替代 temperature 的采样方法,控制模型从概率最高的词中选择的范围。
|
||||
# - presence_penalty : 对新出现的主题进行惩罚,鼓励模型谈论已经提到过的话题。
|
||||
# - frequency_penalty : 对高频词进行惩罚,降低重复相同词语的可能性。
|
||||
# - stop 或 stop_sequences : 一个或多个字符串序列,当模型生成这些序列时会停止输出。
|
||||
# - 特定于提供商的参数 : 比如某些模型可能支持的 stream (流式输出) 开关、 seed (随机种子) 等。
|
||||
|
||||
# # 模型能力参数
|
||||
# max_tokens = Column(String, comment="最大token数")
|
||||
# context_length = Column(String, comment="上下文长度")
|
||||
|
||||
# 状态管理
|
||||
is_active = Column(Boolean, default=True, nullable=False, comment="是否激活")
|
||||
is_public = Column(Boolean, default=False, nullable=False, comment="是否公开")
|
||||
|
||||
# 时间戳
|
||||
created_at = Column(DateTime, default=datetime.datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment="更新时间")
|
||||
|
||||
# 关联关系
|
||||
api_keys = relationship("ModelApiKey", back_populates="model_config", cascade="all, delete-orphan")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ModelConfig(id={self.id}, name={self.name}, type={self.type})>"
|
||||
|
||||
|
||||
class ModelApiKey(Base):
|
||||
"""模型API密钥表"""
|
||||
__tablename__ = "model_api_keys"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
model_config_id = Column(UUID(as_uuid=True), ForeignKey("model_configs.id"), nullable=False, comment="模型配置ID")
|
||||
|
||||
# API Key 信息
|
||||
model_name = Column(String, nullable=False, comment="模型实际名称")
|
||||
provider = Column(String, nullable=False, comment="API Key提供商")
|
||||
api_key = Column(String, nullable=False, comment="API密钥")
|
||||
api_base = Column(String, comment="API基础URL")
|
||||
|
||||
# 配置参数
|
||||
config = Column(JSON, comment="API Key特定配置")
|
||||
|
||||
# 使用统计
|
||||
usage_count = Column(String, default="0", comment="使用次数")
|
||||
last_used_at = Column(DateTime, comment="最后使用时间")
|
||||
|
||||
# 状态管理
|
||||
is_active = Column(Boolean, default=True, nullable=False, comment="是否激活")
|
||||
priority = Column(String, default="1", comment="优先级")
|
||||
|
||||
# 时间戳
|
||||
created_at = Column(DateTime, default=datetime.datetime.now, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now, comment="更新时间")
|
||||
|
||||
# 关联关系
|
||||
model_config = relationship("ModelConfig", back_populates="api_keys")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ModelApiKey(id={self.id}, model_name={self.model_name}, provider={self.provider}, model_config_id={self.model_config_id})>"
|
||||
143
api/app/models/multi_agent_model.py
Normal file
143
api/app/models/multi_agent_model.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""多 Agent 相关数据模型"""
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, Integer, Float, Text, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class MultiAgentConfig(Base):
|
||||
"""多 Agent 配置表"""
|
||||
__tablename__ = "multi_agent_configs"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
|
||||
# 关联应用
|
||||
app_id = Column(UUID(as_uuid=True), ForeignKey("apps.id"), nullable=False, unique=True, index=True, comment="关联应用")
|
||||
|
||||
# 主 Agent (存储发布版本 ID)
|
||||
master_agent_id = Column(UUID(as_uuid=True), ForeignKey("app_releases.id"), nullable=False, comment="主 Agent 发布版本 ID")
|
||||
master_agent_name = Column(String(100), comment="主 Agent 名称")
|
||||
|
||||
# 协作模式
|
||||
orchestration_mode = Column(
|
||||
String(20),
|
||||
nullable=False,
|
||||
default="conditional",
|
||||
comment="协作模式: sequential|parallel|conditional|loop"
|
||||
)
|
||||
|
||||
# 子 Agent 列表
|
||||
sub_agents = Column(
|
||||
JSON,
|
||||
nullable=False,
|
||||
default=list,
|
||||
comment="子 Agent 列表: [{'agent_id': 'uuid', 'name': '...', 'role': '...', 'priority': 1}]"
|
||||
)
|
||||
|
||||
# 路由规则
|
||||
routing_rules = Column(
|
||||
JSON,
|
||||
comment="路由规则: [{'condition': '...', 'target_agent_id': 'uuid', 'priority': 1}]"
|
||||
)
|
||||
|
||||
# 执行配置
|
||||
execution_config = Column(
|
||||
JSON,
|
||||
nullable=False,
|
||||
default=dict,
|
||||
comment="执行配置: {'max_iterations': 5, 'timeout': 60, 'parallel_limit': 3}"
|
||||
)
|
||||
|
||||
# 结果整合策略
|
||||
aggregation_strategy = Column(
|
||||
String(20),
|
||||
nullable=False,
|
||||
default="merge",
|
||||
comment="结果整合策略: merge|vote|priority|custom"
|
||||
)
|
||||
|
||||
# 状态
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
|
||||
# 关系
|
||||
app = relationship("App")
|
||||
master_agent_release = relationship("AppRelease", foreign_keys=[master_agent_id])
|
||||
|
||||
def __repr__(self):
|
||||
return f"<MultiAgentConfig(id={self.id}, app_id={self.app_id}, mode={self.orchestration_mode})>"
|
||||
|
||||
|
||||
class AgentInvocation(Base):
|
||||
"""Agent 调用记录表"""
|
||||
__tablename__ = "agent_invocations"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
|
||||
# 调用关系
|
||||
caller_agent_id = Column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("agent_configs.id"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="调用者 Agent ID"
|
||||
)
|
||||
callee_agent_id = Column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("agent_configs.id"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="被调用者 Agent ID"
|
||||
)
|
||||
|
||||
# 关联信息
|
||||
conversation_id = Column(
|
||||
UUID(as_uuid=True),
|
||||
index=True,
|
||||
comment="关联会话 ID(不使用外键约束,避免循环依赖)"
|
||||
)
|
||||
parent_invocation_id = Column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("agent_invocations.id"),
|
||||
index=True,
|
||||
comment="父调用 ID(用于追踪调用链)"
|
||||
)
|
||||
|
||||
# 输入输出
|
||||
input_message = Column(Text, nullable=False, comment="输入消息")
|
||||
output_message = Column(Text, comment="输出消息")
|
||||
context = Column(JSON, comment="上下文信息")
|
||||
|
||||
# 状态
|
||||
status = Column(
|
||||
String(20),
|
||||
nullable=False,
|
||||
default="pending",
|
||||
index=True,
|
||||
comment="状态: pending|running|completed|failed"
|
||||
)
|
||||
error_message = Column(Text, comment="错误信息")
|
||||
|
||||
# 性能指标
|
||||
started_at = Column(DateTime, nullable=False, default=datetime.datetime.now, index=True)
|
||||
completed_at = Column(DateTime)
|
||||
elapsed_time = Column(Float, comment="耗时(秒)")
|
||||
token_usage = Column(JSON, comment="Token 使用情况")
|
||||
|
||||
# 元数据
|
||||
meta_data = Column(JSON, comment="额外元数据")
|
||||
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
|
||||
# 关系
|
||||
caller = relationship("AgentConfig", foreign_keys=[caller_agent_id])
|
||||
callee = relationship("AgentConfig", foreign_keys=[callee_agent_id])
|
||||
# conversation 不使用 relationship,避免外键约束问题
|
||||
parent_invocation = relationship("AgentInvocation", remote_side=[id], backref="child_invocations")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<AgentInvocation(id={self.id}, caller={self.caller_agent_id}, callee={self.callee_agent_id}, status={self.status})>"
|
||||
47
api/app/models/release_share_model.py
Normal file
47
api/app/models/release_share_model.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, Integer, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import UUID, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class ReleaseShare(Base):
|
||||
"""应用发布版本分享配置"""
|
||||
__tablename__ = "release_shares"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("release_id", name="uq_release_share_release_id"),
|
||||
)
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
release_id = Column(UUID(as_uuid=True), ForeignKey("app_releases.id", ondelete="CASCADE"), nullable=False, unique=True, index=True)
|
||||
app_id = Column(UUID(as_uuid=True), ForeignKey("apps.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||
|
||||
# 分享配置
|
||||
is_enabled = Column(Boolean, default=True, nullable=False, comment="是否启用公开分享")
|
||||
share_token = Column(String, nullable=False, unique=True, index=True, comment="公开访问的唯一标识")
|
||||
|
||||
# 访问控制
|
||||
require_password = Column(Boolean, default=False, nullable=False, comment="是否需要密码访问")
|
||||
password_hash = Column(String, nullable=True, comment="访问密码哈希")
|
||||
|
||||
# 嵌入配置
|
||||
allow_embed = Column(Boolean, default=False, nullable=False, comment="是否允许嵌入")
|
||||
embed_domains = Column(JSON, default=list, comment="允许嵌入的域名白名单")
|
||||
|
||||
# 统计数据
|
||||
view_count = Column(Integer, default=0, nullable=False, comment="访问次数")
|
||||
last_accessed_at = Column(DateTime, nullable=True, comment="最后访问时间")
|
||||
|
||||
# 元数据
|
||||
created_by = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False, comment="创建者")
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
|
||||
# 关系
|
||||
release = relationship("AppRelease", backref="share")
|
||||
app = relationship("App")
|
||||
creator = relationship("User")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ReleaseShare(id={self.id}, release_id={self.release_id}, share_token={self.share_token})>"
|
||||
13
api/app/models/retrieval_info.py
Normal file
13
api/app/models/retrieval_info.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, DateTime, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from app.db import Base
|
||||
|
||||
class RetrievalInfo(Base):
|
||||
__tablename__ = "retrieval_info"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, nullable=False, index=True)
|
||||
host_id = Column(UUID(as_uuid=True), nullable=False)
|
||||
retrieve_info = Column(Text, default="", nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
23
api/app/models/tenant_model.py
Normal file
23
api/app/models/tenant_model.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, DateTime, Boolean
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
|
||||
|
||||
class Tenants(Base):
|
||||
__tablename__ = "tenants"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
name = Column(String, index=True, nullable=False)
|
||||
description = Column(String, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# Relationship to users - one tenant has many users
|
||||
users = relationship("User", back_populates="tenant")
|
||||
|
||||
# Relationship to workspaces owned by the tenant
|
||||
owned_workspaces = relationship("Workspace", back_populates="tenant")
|
||||
30
api/app/models/user_model.py
Normal file
30
api/app/models/user_model.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Boolean, DateTime, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
username = Column(String, unique=True, index=True, nullable=False)
|
||||
email = Column(String, unique=True, index=True, nullable=False)
|
||||
hashed_password = Column(String, nullable=False)
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
is_superuser = Column(Boolean, default=False, nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
last_login_at = Column(DateTime, nullable=True) # 最后登录时间,可为空
|
||||
|
||||
current_workspace_id = Column(UUID(as_uuid=True), ForeignKey("workspaces.id"), nullable=True) # 当前工作空间ID,可为空
|
||||
|
||||
# Foreign key to tenant - each user belongs to exactly one tenant
|
||||
tenant_id = Column(UUID(as_uuid=True), ForeignKey("tenants.id"), nullable=False)
|
||||
|
||||
# Relationship to workspace memberships - users collaborate in workspaces through membership
|
||||
workspaces = relationship("WorkspaceMember", back_populates="user")
|
||||
|
||||
# Relationship to tenant - one-to-one relationship
|
||||
tenant = relationship("Tenants", back_populates="users")
|
||||
70
api/app/models/workspace_model.py
Normal file
70
api/app/models/workspace_model.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import datetime
|
||||
from enum import StrEnum
|
||||
import uuid
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Boolean
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.db import Base
|
||||
|
||||
class WorkspaceRole(StrEnum):
|
||||
manager = "manager"
|
||||
member = "member"
|
||||
|
||||
class InviteStatus(StrEnum):
|
||||
pending = "pending"
|
||||
accepted = "accepted"
|
||||
revoked = "revoked"
|
||||
expired = "expired"
|
||||
|
||||
class Workspace(Base):
|
||||
__tablename__ = "workspaces"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
name = Column(String, index=True, nullable=False)
|
||||
icon = Column(String, nullable=True)
|
||||
iconType = Column(String, nullable=True)
|
||||
description = Column(String, nullable=True)
|
||||
tenant_id = Column(UUID(as_uuid=True), ForeignKey("tenants.id"), nullable=False) # belongs to tenant
|
||||
storage_type = Column(String, nullable=True)
|
||||
llm = Column(String, nullable=True)
|
||||
embedding = Column(String, nullable=True)
|
||||
rerank = Column(String, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
# Relationships
|
||||
tenant = relationship("Tenants", back_populates="owned_workspaces") # belongs to tenant
|
||||
members = relationship("WorkspaceMember", back_populates="workspace") # users collaborate through membership
|
||||
api_keys = relationship("ApiKey", back_populates="workspace", cascade="all, delete-orphan") # API Keys
|
||||
memory_increments = relationship("MemoryIncrement", back_populates="workspace")
|
||||
|
||||
class WorkspaceMember(Base):
|
||||
__tablename__ = "workspace_members"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
|
||||
workspace_id = Column(UUID(as_uuid=True), ForeignKey("workspaces.id"), nullable=False)
|
||||
role = Column(String, nullable=False)
|
||||
is_active = Column(Boolean, default=True)
|
||||
user = relationship("User", back_populates="workspaces")
|
||||
workspace = relationship("Workspace", back_populates="members")
|
||||
|
||||
class WorkspaceInvite(Base):
|
||||
__tablename__ = "workspace_invites"
|
||||
|
||||
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, index=True)
|
||||
workspace_id = Column(UUID(as_uuid=True), ForeignKey("workspaces.id"), nullable=False)
|
||||
email = Column(String, nullable=False, index=True)
|
||||
role = Column(String, nullable=False) # WorkspaceRole: manager or member
|
||||
token_hash = Column(String, nullable=False, unique=True, index=True)
|
||||
status = Column(String, nullable=False, default=InviteStatus.pending) # InviteStatus
|
||||
expires_at = Column(DateTime, nullable=False)
|
||||
accepted_at = Column(DateTime, nullable=True)
|
||||
created_by_user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
|
||||
created_at = Column(DateTime, default=datetime.datetime.now)
|
||||
updated_at = Column(DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
|
||||
|
||||
# Relationships
|
||||
workspace = relationship("Workspace")
|
||||
created_by = relationship("User", foreign_keys=[created_by_user_id])
|
||||
Reference in New Issue
Block a user