fix: delete agent 500 error + dynamic personality + deployment guide
- Fix delete agent 500: clean up FK records (agent_llm_logs, permissions, schedules, executions, team_members) and unbind goals/tasks before delete - Remove hardcoded personality templates in Android, replace with dynamic system prompt generation from name + description - Set promptSectionsEnabled=false to bypass PromptComposer for personality - Add Tencent Cloud Linux deployment guide (Docker Compose) - Accumulated backend service updates, frontend UI fixes, Android app changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,5 +29,9 @@ from app.models.knowledge_entry import KnowledgeEntry
|
||||
from app.models.user_fingerprint import UserFingerprint
|
||||
from app.models.shadow_comparison import ShadowComparison
|
||||
from app.models.feedback_record import FeedbackRecord
|
||||
from app.models.audit_log import AuditLog
|
||||
from app.models.workspace import Workspace, WorkspaceMembership
|
||||
from app.models.scene_contract import SceneContract
|
||||
from app.models.team import Team, TeamMember
|
||||
|
||||
__all__ = ["User", "Workflow", "WorkflowVersion", "Agent", "GlobalKnowledge", "AgentRating", "AgentFavorite", "Execution", "ExecutionLog", "ModelConfig", "DataSource", "WorkflowTemplate", "TemplateRating", "TemplateFavorite", "NodeTemplate", "Role", "Permission", "WorkflowPermission", "AgentPermission", "AlertRule", "AlertLog", "PersistentUserMemory", "AgentLLMLog", "AgentVectorMemory", "AgentLearningPattern", "AgentSchedule", "KnowledgeBase", "Document", "DocumentChunk", "Notification", "UserFeishuOpenId", "NodePlugin", "OrchestrationTemplate", "Goal", "Task", "AgentExecutionLog", "UserBehaviorLog", "KnowledgeEntry", "UserFingerprint", "ShadowComparison", "FeedbackRecord"]
|
||||
__all__ = ["User", "Workflow", "WorkflowVersion", "Agent", "GlobalKnowledge", "AgentRating", "AgentFavorite", "Execution", "ExecutionLog", "ModelConfig", "DataSource", "WorkflowTemplate", "TemplateRating", "TemplateFavorite", "NodeTemplate", "Role", "Permission", "WorkflowPermission", "AgentPermission", "AlertRule", "AlertLog", "PersistentUserMemory", "AgentLLMLog", "AgentVectorMemory", "AgentLearningPattern", "AgentSchedule", "KnowledgeBase", "Document", "DocumentChunk", "Notification", "UserFeishuOpenId", "NodePlugin", "OrchestrationTemplate", "Goal", "Task", "AgentExecutionLog", "UserBehaviorLog", "KnowledgeEntry", "UserFingerprint", "ShadowComparison", "FeedbackRecord", "AuditLog", "Workspace", "WorkspaceMembership", "SceneContract", "Team", "TeamMember"]
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
智能体模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -27,6 +27,7 @@ class Agent(Base):
|
||||
version = Column(Integer, default=1, comment="版本号")
|
||||
status = Column(String(20), default="draft", comment="状态: draft/published/running/stopped")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), comment="创建者ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
|
||||
# 技能市场字段
|
||||
category = Column(String(50), nullable=True, comment="分类: llm/data_processing/automation/integration/other")
|
||||
@@ -39,6 +40,7 @@ class Agent(Base):
|
||||
use_count = Column(Integer, default=0, comment="被安装次数")
|
||||
view_count = Column(Integer, default=0, comment="查看次数")
|
||||
forked_from_id = Column(CHAR(36), nullable=True, comment="从哪个Agent Fork而来(市场安装)")
|
||||
parent_agent_id = Column(CHAR(36), nullable=True, comment="父 Agent ID(自主创建时继承经验)")
|
||||
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Agent LLM 调用日志模型 — 记录每次 Agent Runtime 发起的 LLM 调用
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from app.core.database import Base
|
||||
import uuid
|
||||
@@ -27,3 +27,11 @@ class AgentLLMLog(Base):
|
||||
status = Column(String(20), default="success", comment="状态: success/error")
|
||||
error_message = Column(Text, nullable=True, comment="错误信息")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_llm_agent_id", "agent_id"),
|
||||
Index("idx_llm_user_id", "user_id"),
|
||||
Index("idx_llm_session_id", "session_id"),
|
||||
Index("idx_llm_created_at", "created_at"),
|
||||
Index("idx_llm_agent_created", "agent_id", "created_at"),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Agent 定时任务表:按 cron 表达式周期执行 Agent"""
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, Boolean, JSON
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, Boolean, JSON, Index
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from app.core.database import Base
|
||||
|
||||
@@ -25,6 +25,7 @@ class AgentSchedule(Base):
|
||||
last_run_status = Column(String(32), nullable=True, comment="上次执行状态: success/failed")
|
||||
next_run_at = Column(DateTime, nullable=False, comment="下次执行时间")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, index=True, comment="创建者 ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
created_at = Column(DateTime, default=datetime.utcnow, comment="创建时间")
|
||||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间")
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
告警规则模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, Boolean, DateTime, ForeignKey, JSON, func
|
||||
from sqlalchemy import Column, String, Text, Integer, Boolean, DateTime, ForeignKey, Index, JSON, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -45,6 +45,12 @@ class AlertRule(Base):
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_ar_alert_type", "alert_type"),
|
||||
Index("idx_ar_enabled", "enabled"),
|
||||
Index("idx_ar_target_type", "target_type"),
|
||||
)
|
||||
|
||||
# 关系
|
||||
user = relationship("User", backref="alert_rules")
|
||||
|
||||
@@ -77,6 +83,12 @@ class AlertLog(Base):
|
||||
acknowledged_at = Column(DateTime, comment="确认时间")
|
||||
acknowledged_by = Column(CHAR(36), ForeignKey("users.id"), nullable=True, comment="确认人ID")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_al_rule_id", "rule_id"),
|
||||
Index("idx_al_status", "status"),
|
||||
Index("idx_al_triggered_at", "triggered_at"),
|
||||
)
|
||||
|
||||
# 关系
|
||||
rule = relationship("AlertRule", backref="logs")
|
||||
acknowledged_user = relationship("User", foreign_keys=[acknowledged_by])
|
||||
|
||||
26
backend/app/models/audit_log.py
Normal file
26
backend/app/models/audit_log.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
操作审计日志模型
|
||||
"""
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Text, DateTime, JSON, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from app.core.database import Base
|
||||
|
||||
|
||||
class AuditLog(Base):
|
||||
__tablename__ = "audit_logs"
|
||||
|
||||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="日志ID")
|
||||
user_id = Column(CHAR(36), nullable=False, index=True, comment="操作用户ID")
|
||||
username = Column(String(100), nullable=False, comment="操作用户名")
|
||||
action = Column(String(50), nullable=False, index=True, comment="操作类型: CREATE/UPDATE/DELETE/EXECUTE/LOGIN")
|
||||
resource_type = Column(String(100), nullable=False, comment="资源类型: agent/workflow/user/permission/...")
|
||||
resource_id = Column(String(100), nullable=True, comment="资源ID")
|
||||
resource_name = Column(String(255), nullable=True, comment="资源名称")
|
||||
detail = Column(JSON, nullable=True, comment="操作详情")
|
||||
ip_address = Column(String(45), nullable=True, comment="客户端IP")
|
||||
status = Column(String(20), nullable=False, default="success", comment="操作状态: success/failure")
|
||||
created_at = Column(DateTime, default=func.now(), index=True, comment="操作时间")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<AuditLog(id={self.id}, user={self.username}, action={self.action}, resource={self.resource_type})>"
|
||||
43
backend/app/models/conversation_branch.py
Normal file
43
backend/app/models/conversation_branch.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
对话分支模型 — 支持从任意历史点分叉对话
|
||||
|
||||
参考 Claude Code src/commands/branch/branch.ts
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, Boolean
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from app.core.database import Base
|
||||
|
||||
|
||||
class ConversationBranch(Base):
|
||||
"""对话分支 — 保存某个时间点的完整对话快照"""
|
||||
|
||||
__tablename__ = "conversation_branches"
|
||||
|
||||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="分支ID")
|
||||
user_id = Column(String(36), nullable=False, index=True, comment="用户ID")
|
||||
agent_id = Column(String(36), nullable=True, comment="关联Agent ID")
|
||||
agent_name = Column(String(200), nullable=True, comment="Agent 名称(冗余便于展示)")
|
||||
|
||||
# 溯源链
|
||||
parent_session_id = Column(String(100), nullable=False, comment="分叉来源会话ID")
|
||||
branch_session_id = Column(String(100), nullable=False, unique=True, comment="新分支会话ID")
|
||||
|
||||
# 元信息
|
||||
title = Column(String(500), nullable=False, comment="分支标题(取自首条用户消息)")
|
||||
message_count = Column(Integer, default=0, comment="快照包含的消息数")
|
||||
first_user_message = Column(Text, nullable=True, comment="首条用户消息(用于列表展示)")
|
||||
|
||||
# 完整消息快照 (JSON)
|
||||
messages = Column(JSON, nullable=True, comment="分支创建时的完整消息列表")
|
||||
|
||||
# 状态
|
||||
is_active = Column(Boolean, default=True, comment="是否活跃")
|
||||
|
||||
created_at = Column(DateTime, default=datetime.now, comment="创建时间")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ConversationBranch(id={self.id}, title={self.title}, messages={self.message_count})>"
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
数据源模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, DateTime, JSON, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, DateTime, JSON, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -19,6 +19,7 @@ class DataSource(Base):
|
||||
config = Column(JSON, nullable=False, comment="连接配置(加密存储敏感信息)")
|
||||
status = Column(String(20), default="active", comment="状态: active/inactive/error")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="创建者ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
last_connected_at = Column(DateTime, comment="最后连接时间")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
@@ -26,5 +27,9 @@ class DataSource(Base):
|
||||
# 关系
|
||||
user = relationship("User", backref="data_sources")
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_data_sources_workspace", "workspace_id"),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<DataSource(id={self.id}, name={self.name}, type={self.type})>"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
执行记录模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -34,6 +34,7 @@ class Execution(Base):
|
||||
depth = Column(Integer, default=0, nullable=False, comment="执行深度(根为0)")
|
||||
pause_state = Column(JSON, nullable=True, comment="挂起快照(审批节点 HITL,恢复时消费)")
|
||||
goal_id = Column(CHAR(36), ForeignKey("goals.id"), nullable=True, comment="关联目标ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
|
||||
# 关系
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
执行日志模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -22,6 +22,13 @@ class ExecutionLog(Base):
|
||||
timestamp = Column(DateTime, default=func.now(), comment="时间戳")
|
||||
duration = Column(Integer, comment="执行耗时(ms)")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_exec_execution_id", "execution_id"),
|
||||
Index("idx_exec_timestamp", "timestamp"),
|
||||
Index("idx_exec_level", "level"),
|
||||
Index("idx_exec_node_id", "node_id"),
|
||||
)
|
||||
|
||||
# 关系
|
||||
execution = relationship("Execution", backref="logs")
|
||||
|
||||
|
||||
18
backend/app/models/fcm_token.py
Normal file
18
backend/app/models/fcm_token.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""FCM / 设备推送 Token 模型"""
|
||||
from sqlalchemy import Column, String, DateTime, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from app.core.database import Base
|
||||
import uuid
|
||||
|
||||
|
||||
class FcmToken(Base):
|
||||
"""设备推送 Token(Firebase Cloud Messaging / APNs)"""
|
||||
|
||||
__tablename__ = "fcm_tokens"
|
||||
|
||||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
user_id = Column(CHAR(36), nullable=False, index=True, comment="关联用户ID")
|
||||
token = Column(String(512), nullable=False, unique=True, comment="FCM/APNs 设备令牌")
|
||||
platform = Column(String(16), nullable=False, default="android", comment="android / ios / web")
|
||||
created_at = Column(DateTime, server_default=func.now(), comment="注册时间")
|
||||
last_used_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), comment="最后推送时间")
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
目标模型 — Main Agent 管理的顶层目标
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, Float, DateTime, JSON, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, Integer, Float, DateTime, JSON, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -29,6 +29,7 @@ class Goal(Base):
|
||||
creator_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="创建者ID")
|
||||
main_agent_id = Column(CHAR(36), ForeignKey("agents.id"), nullable=True, comment="管理此目标的 Main Agent ID")
|
||||
parent_goal_id = Column(CHAR(36), ForeignKey("goals.id"), nullable=True, comment="父目标ID,支持目标嵌套")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
|
||||
# 时间
|
||||
started_at = Column(DateTime, comment="开始时间")
|
||||
|
||||
@@ -12,6 +12,7 @@ from datetime import datetime
|
||||
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, ForeignKey, Index
|
||||
from sqlalchemy.dialects.mysql import JSON as MySQLJSON
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.core.database import Base
|
||||
@@ -25,6 +26,7 @@ class KnowledgeBase(Base):
|
||||
name = Column(String(200), nullable=False, comment="知识库名称")
|
||||
description = Column(Text, nullable=True, comment="描述")
|
||||
user_id = Column(String(36), nullable=True, index=True, comment="创建者 ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
chunk_size = Column(Integer, default=500, comment="分块大小(字符数)")
|
||||
chunk_overlap = Column(Integer, default=50, comment="分块重叠(字符数)")
|
||||
doc_count = Column(Integer, default=0, comment="文档数量")
|
||||
|
||||
@@ -12,6 +12,7 @@ class KnowledgeEntry(Base):
|
||||
__tablename__ = "knowledge_entries"
|
||||
|
||||
id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
agent_id = Column(String(36), nullable=True, index=True, comment="所属 Agent ID(NULL=全Agent共享)")
|
||||
title = Column(String(500), nullable=False, comment="知识标题(一句话概括)")
|
||||
category = Column(String(30), nullable=False, index=True,
|
||||
comment="类别: bug_fix/best_practice/workaround/optimization/insight")
|
||||
@@ -44,7 +45,6 @@ class KnowledgeEntry(Base):
|
||||
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间")
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_knowledge_entries_category", "category"),
|
||||
Index("ix_knowledge_entries_active", "is_active"),
|
||||
)
|
||||
|
||||
@@ -54,6 +54,7 @@ class KnowledgeEntry(Base):
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"id": self.id,
|
||||
"agent_id": self.agent_id,
|
||||
"title": self.title,
|
||||
"category": self.category,
|
||||
"tags": self.tags or [],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
模型配置模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, DateTime, ForeignKey, func
|
||||
from sqlalchemy import Column, String, DateTime, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -19,11 +19,16 @@ class ModelConfig(Base):
|
||||
api_key = Column(String(500), nullable=False, comment="API密钥(加密存储)")
|
||||
base_url = Column(String(255), comment="API地址")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), comment="所属用户ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
# 关系
|
||||
user = relationship("User", backref="model_configs")
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_model_configs_workspace", "workspace_id"),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ModelConfig(id={self.id}, name={self.name}, provider={self.provider})>"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
节点模板模型
|
||||
用于管理和复用LLM节点的提示词模板
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, func
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -37,6 +37,12 @@ class NodeTemplate(Base):
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_nt_category", "category"),
|
||||
Index("idx_nt_user_id", "user_id"),
|
||||
Index("idx_nt_is_public", "is_public"),
|
||||
)
|
||||
|
||||
# 关系
|
||||
user = relationship("User", backref="node_templates")
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
编排模板模型 — 保存可视化 Agent 编排画布配置
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, JSON, DateTime, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, JSON, DateTime, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -18,10 +18,15 @@ class OrchestrationTemplate(Base):
|
||||
nodes = Column(JSON, nullable=False, comment="编排节点(含Agent配置)")
|
||||
edges = Column(JSON, nullable=False, comment="编排连线")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), comment="创建者ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
user = relationship("User", backref="orchestration_templates")
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_orchestration_templates_workspace", "workspace_id"),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<OrchestrationTemplate(id={self.id}, name={self.name})>"
|
||||
|
||||
18
backend/app/models/push_subscription.py
Normal file
18
backend/app/models/push_subscription.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""浏览器推送订阅模型"""
|
||||
from sqlalchemy import Column, String, Text, DateTime, func, JSON
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from app.core.database import Base
|
||||
import uuid
|
||||
|
||||
|
||||
class PushSubscription(Base):
|
||||
__tablename__ = "push_subscriptions"
|
||||
|
||||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
||||
user_id = Column(CHAR(36), nullable=True, index=True, comment="关联用户ID")
|
||||
endpoint = Column(Text, nullable=False, comment="推送端点URL")
|
||||
p256dh = Column(Text, nullable=False, comment="公钥")
|
||||
auth = Column(Text, nullable=False, comment="认证密钥")
|
||||
user_agent = Column(String(500), nullable=True, comment="设备UA")
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
98
backend/app/models/scene_contract.py
Normal file
98
backend/app/models/scene_contract.py
Normal file
@@ -0,0 +1,98 @@
|
||||
"""
|
||||
场景契约模型 — 统一 DSL 输入契约(目标/约束/产物/验收)
|
||||
让不同模板复用统一输入格式,实现"场景可编程输入"
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
import uuid
|
||||
|
||||
|
||||
class SceneContract(Base):
|
||||
"""场景契约表 — 定义标准输入契约"""
|
||||
__tablename__ = "scene_contracts"
|
||||
|
||||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="契约ID")
|
||||
name = Column(String(100), nullable=False, comment="契约名称")
|
||||
description = Column(Text, comment="契约描述")
|
||||
|
||||
# ── 核心 DSL 字段 ──
|
||||
goal = Column(Text, nullable=False, comment="场景目标(一句话描述要达成什么)")
|
||||
role = Column(Text, nullable=True, comment="Agent 扮演的角色/人设")
|
||||
|
||||
# 输入规范
|
||||
input_description = Column(Text, nullable=True, comment="期望的输入描述")
|
||||
input_schema = Column(JSON, nullable=True, comment="输入 JSON Schema 约束")
|
||||
|
||||
# 约束条件
|
||||
constraints = Column(JSON, nullable=True, comment="行为约束列表")
|
||||
forbidden_actions = Column(JSON, nullable=True, comment="禁止的操作列表")
|
||||
required_tools = Column(JSON, nullable=True, comment="必需的工具列表")
|
||||
|
||||
# 产物定义
|
||||
deliverables = Column(JSON, nullable=True, comment="期望产出物列表 [{name, format, description}]")
|
||||
|
||||
# 验收标准
|
||||
acceptance_criteria = Column(JSON, nullable=True, comment="验收条件列表")
|
||||
|
||||
# 输出规范
|
||||
output_schema = Column(JSON, nullable=True, comment="输出 JSON Schema 约束")
|
||||
|
||||
# Few-shot 示例
|
||||
examples = Column(JSON, nullable=True, comment="示例列表 [{input, output}]")
|
||||
|
||||
# 元数据
|
||||
category = Column(String(50), nullable=True, comment="分类")
|
||||
tags = Column(JSON, nullable=True, comment="标签列表")
|
||||
version = Column(Integer, default=1, comment="版本号")
|
||||
is_public = Column(Integer, default=0, comment="是否公开: 0=私有 1=公开")
|
||||
use_count = Column(Integer, default=0, comment="使用次数")
|
||||
|
||||
# 关联
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=True, comment="创建者ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
template_binding = Column(String(100), nullable=True, comment="绑定的场景模板ID(可选)")
|
||||
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_sc_category", "category"),
|
||||
Index("idx_sc_user_id", "user_id"),
|
||||
Index("idx_sc_is_public", "is_public"),
|
||||
Index("idx_sc_template_binding", "template_binding"),
|
||||
)
|
||||
|
||||
user = relationship("User", backref="scene_contracts")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<SceneContract(id={self.id}, name={self.name})>"
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"goal": self.goal,
|
||||
"role": self.role,
|
||||
"input_description": self.input_description,
|
||||
"input_schema": self.input_schema,
|
||||
"constraints": self.constraints or [],
|
||||
"forbidden_actions": self.forbidden_actions or [],
|
||||
"required_tools": self.required_tools or [],
|
||||
"deliverables": self.deliverables or [],
|
||||
"acceptance_criteria": self.acceptance_criteria or [],
|
||||
"output_schema": self.output_schema,
|
||||
"examples": self.examples or [],
|
||||
"category": self.category,
|
||||
"tags": self.tags or [],
|
||||
"version": self.version,
|
||||
"is_public": self.is_public,
|
||||
"use_count": self.use_count,
|
||||
"user_id": self.user_id,
|
||||
"workspace_id": self.workspace_id,
|
||||
"template_binding": self.template_binding,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
任务模型 — Goal 拆解的子任务
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, Boolean, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, Boolean, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -25,9 +25,13 @@ class Task(Base):
|
||||
# 任务编排配置
|
||||
task_config = Column(JSON, comment="编排配置: {orchestration_mode, agents:[], workflow_id, input_data}")
|
||||
|
||||
# 依赖关系
|
||||
# 依赖关系(参考 Claude Code task_system 设计)
|
||||
parent_task_id = Column(CHAR(36), ForeignKey("tasks.id"), nullable=True, comment="父任务ID")
|
||||
depends_on = Column(JSON, default=list, comment="前置依赖任务ID列表")
|
||||
depends_on = Column(JSON, default=list, comment="前置依赖任务ID列表 (blockedBy)")
|
||||
blocks = Column(JSON, default=list, comment="被此任务阻塞的任务ID列表")
|
||||
|
||||
# 认领机制(参考 Claude Code claimTask)
|
||||
owner = Column(String(200), nullable=True, comment="认领此任务的 Agent 标识 (区别于 assigned_agent_id)")
|
||||
|
||||
# 执行结果
|
||||
result = Column(JSON, comment="执行输出结果")
|
||||
@@ -42,6 +46,7 @@ class Task(Base):
|
||||
requires_approval = Column(Boolean, default=False, comment="是否需要人工审批")
|
||||
approver_id = Column(CHAR(36), ForeignKey("users.id"), nullable=True, comment="审批人ID")
|
||||
approval_status = Column(String(20), comment="审批状态: pending/approved/rejected")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
|
||||
# 时间
|
||||
started_at = Column(DateTime, comment="开始时间")
|
||||
|
||||
88
backend/app/models/team.py
Normal file
88
backend/app/models/team.py
Normal file
@@ -0,0 +1,88 @@
|
||||
"""
|
||||
虚拟团队模型 — Team + TeamMember
|
||||
用于将多个 Agent 组织为角色化的虚拟软件团队
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
import uuid
|
||||
|
||||
|
||||
class Team(Base):
|
||||
"""虚拟团队表"""
|
||||
__tablename__ = "teams"
|
||||
|
||||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="团队ID")
|
||||
name = Column(String(100), nullable=False, comment="团队名称")
|
||||
description = Column(Text, comment="团队描述")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="创建者ID")
|
||||
is_template = Column(Boolean, default=False, comment="是否为预置模板")
|
||||
config = Column(JSON, nullable=True, comment="团队配置(协作模式等)")
|
||||
status = Column(String(20), default="active", comment="状态: active/inactive")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_team_workspace", "workspace_id"),
|
||||
Index("idx_team_user_id", "user_id"),
|
||||
)
|
||||
|
||||
members = relationship("TeamMember", back_populates="team", cascade="all, delete-orphan")
|
||||
user = relationship("User", backref="teams")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Team(id={self.id}, name={self.name})>"
|
||||
|
||||
def to_dict(self, include_members=False):
|
||||
result = {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"workspace_id": self.workspace_id,
|
||||
"user_id": self.user_id,
|
||||
"is_template": self.is_template,
|
||||
"config": self.config,
|
||||
"status": self.status,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
}
|
||||
if include_members:
|
||||
result["members"] = [m.to_dict() for m in self.members] if self.members else []
|
||||
return result
|
||||
|
||||
|
||||
class TeamMember(Base):
|
||||
"""团队成员表 — Agent 在团队中的角色映射"""
|
||||
__tablename__ = "team_members"
|
||||
|
||||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="成员ID")
|
||||
team_id = Column(CHAR(36), ForeignKey("teams.id"), nullable=False, comment="团队ID")
|
||||
agent_id = Column(CHAR(36), ForeignKey("agents.id"), nullable=False, comment="Agent ID")
|
||||
role = Column(String(30), nullable=False, comment="角色: pm/developer/qa/designer/devops/custom")
|
||||
position = Column(Integer, default=0, comment="排序位置")
|
||||
is_lead = Column(Boolean, default=False, comment="是否为团队Leader")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_tm_team_id", "team_id"),
|
||||
Index("idx_tm_agent_id", "agent_id"),
|
||||
)
|
||||
|
||||
team = relationship("Team", back_populates="members")
|
||||
agent = relationship("Agent", backref="team_memberships")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<TeamMember(team={self.team_id}, agent={self.agent_id}, role={self.role})>"
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"team_id": self.team_id,
|
||||
"agent_id": self.agent_id,
|
||||
"role": self.role,
|
||||
"position": self.position,
|
||||
"is_lead": self.is_lead,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
工具定义模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, JSON, DateTime, Boolean, ForeignKey, Integer, func
|
||||
from sqlalchemy import Column, String, Text, JSON, DateTime, Boolean, ForeignKey, Integer, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -26,13 +26,20 @@ class Tool(Base):
|
||||
|
||||
# 元数据
|
||||
is_public = Column(Boolean, default=False, comment="是否公开")
|
||||
status = Column(String(20), default="active", comment="状态: active/draft/deprecated")
|
||||
tags = Column(JSON, comment="标签列表")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=True, comment="创建者ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
use_count = Column(Integer, default=0, comment="使用次数")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
|
||||
# 关系
|
||||
user = relationship("User", backref="tools")
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_tools_workspace", "workspace_id"),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Tool(id={self.id}, name={self.name})>"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
工作流模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -20,11 +20,16 @@ class Workflow(Base):
|
||||
version = Column(Integer, default=1, comment="版本号")
|
||||
status = Column(String(20), default="draft", comment="状态: draft/published/running/stopped")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), comment="创建者ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
# 关系
|
||||
user = relationship("User", backref="workflows")
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_workflows_workspace", "workspace_id"),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Workflow(id={self.id}, name={self.name})>"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
工作流模板市场模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, Float, func
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Boolean, Float, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -27,11 +27,16 @@ class WorkflowTemplate(Base):
|
||||
rating_count = Column(Integer, default=0, comment="评分次数")
|
||||
rating_avg = Column(Float, default=0.0, comment="平均评分")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="创建者ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id"), nullable=True, comment="所属工作区ID")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
# 关系
|
||||
user = relationship("User", backref="shared_templates")
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_workflow_templates_workspace", "workspace_id"),
|
||||
)
|
||||
ratings = relationship("TemplateRating", back_populates="template", cascade="all, delete-orphan")
|
||||
favorites = relationship("TemplateFavorite", back_populates="template", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
工作流版本模型
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, func
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
@@ -24,6 +24,11 @@ class WorkflowVersion(Base):
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
comment = Column(Text, comment="版本备注")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_wv_workflow_id", "workflow_id"),
|
||||
Index("idx_wv_status", "status"),
|
||||
)
|
||||
|
||||
# 关系
|
||||
workflow = relationship("Workflow", backref="versions")
|
||||
creator = relationship("User", foreign_keys=[created_by])
|
||||
|
||||
61
backend/app/models/workspace.py
Normal file
61
backend/app/models/workspace.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
工作区 (Workspace) 模型 — 多租户数据隔离
|
||||
"""
|
||||
from sqlalchemy import Column, String, Text, Integer, DateTime, JSON, ForeignKey, Index, func
|
||||
from sqlalchemy.dialects.mysql import CHAR
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.core.database import Base
|
||||
import uuid
|
||||
|
||||
|
||||
class Workspace(Base):
|
||||
"""工作区/租户表"""
|
||||
__tablename__ = "workspaces"
|
||||
|
||||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="工作区ID")
|
||||
name = Column(String(100), nullable=False, comment="工作区名称")
|
||||
description = Column(Text, comment="描述")
|
||||
avatar = Column(String(500), nullable=True, comment="Logo URL")
|
||||
is_default = Column(Integer, default=0, comment="是否为默认工作区: 0=否 1=是")
|
||||
owner_id = Column(CHAR(36), ForeignKey("users.id"), nullable=False, comment="所有者ID")
|
||||
max_members = Column(Integer, default=50, comment="最大成员数")
|
||||
settings = Column(JSON, nullable=True, comment="工作区设置")
|
||||
status = Column(String(20), default="active", comment="状态: active/disabled/deleted")
|
||||
created_at = Column(DateTime, default=func.now(), comment="创建时间")
|
||||
updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), comment="更新时间")
|
||||
|
||||
owner = relationship("User", backref="owned_workspaces")
|
||||
memberships = relationship("WorkspaceMembership", back_populates="workspace", cascade="all, delete-orphan")
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"avatar": self.avatar,
|
||||
"is_default": bool(self.is_default),
|
||||
"owner_id": self.owner_id,
|
||||
"max_members": self.max_members,
|
||||
"settings": self.settings,
|
||||
"status": self.status,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
}
|
||||
|
||||
|
||||
class WorkspaceMembership(Base):
|
||||
"""工作区成员关联表"""
|
||||
__tablename__ = "workspace_memberships"
|
||||
|
||||
id = Column(CHAR(36), primary_key=True, default=lambda: str(uuid.uuid4()), comment="关联ID")
|
||||
workspace_id = Column(CHAR(36), ForeignKey("workspaces.id", ondelete="CASCADE"), nullable=False, comment="工作区ID")
|
||||
user_id = Column(CHAR(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, comment="用户ID")
|
||||
role = Column(String(20), nullable=False, default="member", comment="角色: admin/member")
|
||||
joined_at = Column(DateTime, default=func.now(), comment="加入时间")
|
||||
|
||||
workspace = relationship("Workspace", back_populates="memberships")
|
||||
user = relationship("User", backref="workspace_memberships")
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_ws_membership_workspace_user", "workspace_id", "user_id", unique=True),
|
||||
)
|
||||
Reference in New Issue
Block a user