2026-01-21 19:15:52 +08:00
|
|
|
|
"""PostgreSQL 业务数据模型 - 用户、部门、对话等相关表"""
|
|
|
|
|
|
|
|
|
|
|
|
from typing import Any
|
|
|
|
|
|
|
2026-01-22 00:28:43 +08:00
|
|
|
|
from sqlalchemy import JSON, Column, DateTime, ForeignKey, Integer, String, Text
|
2026-01-21 19:15:52 +08:00
|
|
|
|
from sqlalchemy.ext.declarative import declarative_base
|
|
|
|
|
|
from sqlalchemy.orm import relationship
|
|
|
|
|
|
|
2026-01-22 00:28:43 +08:00
|
|
|
|
from src.utils.datetime_utils import format_utc_datetime, utc_now_naive
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
2026-01-22 00:28:43 +08:00
|
|
|
|
Base = declarative_base()
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Department(Base):
|
|
|
|
|
|
"""部门模型"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "departments"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
|
|
|
|
name = Column(String(50), nullable=False, unique=True, index=True)
|
|
|
|
|
|
description = Column(String(255), nullable=True)
|
2026-01-22 00:28:43 +08:00
|
|
|
|
created_at = Column(DateTime, default=utc_now_naive)
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
|
|
|
|
|
# 关联关系
|
|
|
|
|
|
users = relationship("User", back_populates="department", cascade="all, delete-orphan")
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"name": self.name,
|
|
|
|
|
|
"description": self.description,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"created_at": format_utc_datetime(self.created_at),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class User(Base):
|
|
|
|
|
|
"""用户模型"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "users"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
|
|
|
|
username = Column(String, nullable=False, unique=True, index=True) # 显示名称
|
|
|
|
|
|
user_id = Column(String, nullable=False, unique=True, index=True) # 登录ID
|
|
|
|
|
|
phone_number = Column(String, nullable=True, unique=True, index=True) # 手机号
|
|
|
|
|
|
avatar = Column(String, nullable=True) # 头像URL
|
|
|
|
|
|
password_hash = Column(String, nullable=False)
|
|
|
|
|
|
role = Column(String, nullable=False, default="user") # 角色: superadmin, admin, user
|
|
|
|
|
|
department_id = Column(Integer, ForeignKey("departments.id"), nullable=True) # 部门ID
|
2026-01-22 00:28:43 +08:00
|
|
|
|
created_at = Column(DateTime, default=utc_now_naive)
|
2026-01-21 19:15:52 +08:00
|
|
|
|
last_login = Column(DateTime, nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 登录失败限制相关字段
|
|
|
|
|
|
login_failed_count = Column(Integer, nullable=False, default=0) # 登录失败次数
|
|
|
|
|
|
last_failed_login = Column(DateTime, nullable=True) # 最后一次登录失败时间
|
|
|
|
|
|
login_locked_until = Column(DateTime, nullable=True) # 锁定到什么时候
|
|
|
|
|
|
|
|
|
|
|
|
# 软删除相关字段
|
|
|
|
|
|
is_deleted = Column(Integer, nullable=False, default=0, index=True) # 是否已删除:0=否,1=是
|
|
|
|
|
|
deleted_at = Column(DateTime, nullable=True) # 删除时间
|
|
|
|
|
|
|
|
|
|
|
|
# 关联操作日志
|
|
|
|
|
|
operation_logs = relationship("OperationLog", back_populates="user", cascade="all, delete-orphan")
|
|
|
|
|
|
|
|
|
|
|
|
# 关联部门
|
|
|
|
|
|
department = relationship("Department", back_populates="users")
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self, include_password: bool = False) -> dict[str, Any]:
|
|
|
|
|
|
result = {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"username": self.username,
|
|
|
|
|
|
"user_id": self.user_id,
|
|
|
|
|
|
"phone_number": self.phone_number,
|
|
|
|
|
|
"avatar": self.avatar,
|
|
|
|
|
|
"role": self.role,
|
|
|
|
|
|
"department_id": self.department_id,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"created_at": format_utc_datetime(self.created_at),
|
|
|
|
|
|
"last_login": format_utc_datetime(self.last_login),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
"login_failed_count": self.login_failed_count,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"last_failed_login": format_utc_datetime(self.last_failed_login),
|
|
|
|
|
|
"login_locked_until": format_utc_datetime(self.login_locked_until),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
"is_deleted": self.is_deleted,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"deleted_at": format_utc_datetime(self.deleted_at),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
if include_password:
|
|
|
|
|
|
result["password_hash"] = self.password_hash
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def is_login_locked(self) -> bool:
|
|
|
|
|
|
"""检查用户是否处于登录锁定状态"""
|
|
|
|
|
|
if self.login_locked_until is None:
|
|
|
|
|
|
return False
|
2026-01-22 00:28:43 +08:00
|
|
|
|
return utc_now_naive() < self.login_locked_until
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
|
|
|
|
|
def get_remaining_lock_time(self) -> int:
|
|
|
|
|
|
"""获取剩余锁定时间(秒)"""
|
|
|
|
|
|
if self.login_locked_until is None:
|
|
|
|
|
|
return 0
|
2026-01-22 00:28:43 +08:00
|
|
|
|
remaining = int((self.login_locked_until - utc_now_naive()).total_seconds())
|
2026-01-21 19:15:52 +08:00
|
|
|
|
return max(0, remaining)
|
|
|
|
|
|
|
|
|
|
|
|
def reset_failed_login(self):
|
|
|
|
|
|
"""重置登录失败相关字段"""
|
|
|
|
|
|
self.login_failed_count = 0
|
|
|
|
|
|
self.last_failed_login = None
|
|
|
|
|
|
self.login_locked_until = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Conversation(Base):
|
|
|
|
|
|
"""Conversation table - 对话表"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "conversations"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True, comment="Primary key")
|
|
|
|
|
|
thread_id = Column(String(64), unique=True, index=True, nullable=False, comment="Thread ID (UUID)")
|
|
|
|
|
|
user_id = Column(String(64), index=True, nullable=False, comment="User ID")
|
|
|
|
|
|
agent_id = Column(String(64), index=True, nullable=False, comment="Agent ID")
|
|
|
|
|
|
title = Column(String(255), nullable=True, comment="Conversation title")
|
|
|
|
|
|
status = Column(String(20), default="active", comment="Status: active/archived/deleted")
|
2026-01-22 00:28:43 +08:00
|
|
|
|
created_at = Column(DateTime, default=utc_now_naive, comment="Creation time")
|
|
|
|
|
|
updated_at = Column(DateTime, default=utc_now_naive, onupdate=utc_now_naive, comment="Update time")
|
2026-01-21 19:15:52 +08:00
|
|
|
|
extra_metadata = Column(JSON, nullable=True, comment="Additional metadata")
|
|
|
|
|
|
|
|
|
|
|
|
# Relationships
|
|
|
|
|
|
messages = relationship("Message", back_populates="conversation", cascade="all, delete-orphan")
|
2026-01-22 00:28:43 +08:00
|
|
|
|
stats = relationship(
|
|
|
|
|
|
"ConversationStats", back_populates="conversation", uselist=False, cascade="all, delete-orphan"
|
|
|
|
|
|
)
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"thread_id": self.thread_id,
|
|
|
|
|
|
"user_id": self.user_id,
|
|
|
|
|
|
"agent_id": self.agent_id,
|
|
|
|
|
|
"title": self.title,
|
|
|
|
|
|
"status": self.status,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"created_at": format_utc_datetime(self.created_at),
|
|
|
|
|
|
"updated_at": format_utc_datetime(self.updated_at),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
"metadata": self.extra_metadata or {},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Message(Base):
|
|
|
|
|
|
"""Message table - 消息表"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "messages"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True, comment="Primary key")
|
|
|
|
|
|
conversation_id = Column(
|
|
|
|
|
|
Integer, ForeignKey("conversations.id"), nullable=False, index=True, comment="Conversation ID"
|
|
|
|
|
|
)
|
|
|
|
|
|
role = Column(String(20), nullable=False, comment="Message role: user/assistant/system/tool")
|
|
|
|
|
|
content = Column(Text, nullable=False, comment="Message content")
|
|
|
|
|
|
message_type = Column(String(30), default="text", comment="Message type: text/tool_call/tool_result")
|
2026-01-22 00:28:43 +08:00
|
|
|
|
created_at = Column(DateTime, default=utc_now_naive, comment="Creation time")
|
2026-01-21 19:15:52 +08:00
|
|
|
|
token_count = Column(Integer, nullable=True, comment="Token count (optional)")
|
|
|
|
|
|
extra_metadata = Column(JSON, nullable=True, comment="Additional metadata (complete message dump)")
|
|
|
|
|
|
image_content = Column(Text, nullable=True, comment="Base64 encoded image content for multimodal messages")
|
|
|
|
|
|
|
|
|
|
|
|
# Relationships
|
|
|
|
|
|
conversation = relationship("Conversation", back_populates="messages")
|
|
|
|
|
|
tool_calls = relationship("ToolCall", back_populates="message", cascade="all, delete-orphan")
|
|
|
|
|
|
feedbacks = relationship("MessageFeedback", back_populates="message", cascade="all, delete-orphan")
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"conversation_id": self.conversation_id,
|
|
|
|
|
|
"role": self.role,
|
|
|
|
|
|
"content": self.content,
|
|
|
|
|
|
"message_type": self.message_type,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"created_at": format_utc_datetime(self.created_at),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
"token_count": self.token_count,
|
|
|
|
|
|
"metadata": self.extra_metadata or {},
|
|
|
|
|
|
"image_content": self.image_content,
|
|
|
|
|
|
"tool_calls": [tc.to_dict() for tc in self.tool_calls] if self.tool_calls else [],
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def to_simple_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"role": self.role,
|
|
|
|
|
|
"content": self.content,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ToolCall(Base):
|
|
|
|
|
|
"""ToolCall table - 工具调用表"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "tool_calls"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True, comment="Primary key")
|
|
|
|
|
|
message_id = Column(Integer, ForeignKey("messages.id"), nullable=False, index=True, comment="Message ID")
|
|
|
|
|
|
langgraph_tool_call_id = Column(String(100), nullable=True, index=True, comment="LangGraph tool_call_id")
|
|
|
|
|
|
tool_name = Column(String(100), nullable=False, comment="Tool name")
|
|
|
|
|
|
tool_input = Column(JSON, nullable=True, comment="Tool input parameters")
|
|
|
|
|
|
tool_output = Column(Text, nullable=True, comment="Tool execution result")
|
|
|
|
|
|
status = Column(String(20), default="pending", comment="Status: pending/success/error")
|
|
|
|
|
|
error_message = Column(Text, nullable=True, comment="Error message if failed")
|
2026-01-22 00:28:43 +08:00
|
|
|
|
created_at = Column(DateTime, default=utc_now_naive, comment="Creation time")
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
|
|
|
|
|
# Relationships
|
|
|
|
|
|
message = relationship("Message", back_populates="tool_calls")
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"message_id": self.message_id,
|
|
|
|
|
|
"langgraph_tool_call_id": self.langgraph_tool_call_id,
|
|
|
|
|
|
"tool_name": self.tool_name,
|
|
|
|
|
|
"tool_input": self.tool_input or {},
|
|
|
|
|
|
"tool_output": self.tool_output,
|
|
|
|
|
|
"status": self.status,
|
|
|
|
|
|
"error_message": self.error_message,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"created_at": format_utc_datetime(self.created_at),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConversationStats(Base):
|
|
|
|
|
|
"""ConversationStats table - 对话统计表"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "conversation_stats"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True, comment="Primary key")
|
|
|
|
|
|
conversation_id = Column(
|
|
|
|
|
|
Integer, ForeignKey("conversations.id"), unique=True, nullable=False, comment="Conversation ID"
|
|
|
|
|
|
)
|
|
|
|
|
|
message_count = Column(Integer, default=0, comment="Total message count")
|
|
|
|
|
|
total_tokens = Column(Integer, default=0, comment="Total tokens used")
|
|
|
|
|
|
model_used = Column(String(100), nullable=True, comment="Model used")
|
|
|
|
|
|
user_feedback = Column(JSON, nullable=True, comment="User feedback")
|
2026-01-22 00:28:43 +08:00
|
|
|
|
created_at = Column(DateTime, default=utc_now_naive, comment="Creation time")
|
|
|
|
|
|
updated_at = Column(DateTime, default=utc_now_naive, onupdate=utc_now_naive, comment="Update time")
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
|
|
|
|
|
# Relationships
|
|
|
|
|
|
conversation = relationship("Conversation", back_populates="stats")
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"conversation_id": self.conversation_id,
|
|
|
|
|
|
"message_count": self.message_count,
|
|
|
|
|
|
"total_tokens": self.total_tokens,
|
|
|
|
|
|
"model_used": self.model_used,
|
|
|
|
|
|
"user_feedback": self.user_feedback or {},
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"created_at": format_utc_datetime(self.created_at),
|
|
|
|
|
|
"updated_at": format_utc_datetime(self.updated_at),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OperationLog(Base):
|
|
|
|
|
|
"""操作日志模型"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "operation_logs"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
|
|
|
|
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
|
|
|
|
|
operation = Column(String, nullable=False)
|
|
|
|
|
|
details = Column(Text, nullable=True)
|
|
|
|
|
|
ip_address = Column(String, nullable=True)
|
2026-01-22 00:28:43 +08:00
|
|
|
|
timestamp = Column(DateTime, default=utc_now_naive)
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
|
|
|
|
|
# 关联用户
|
|
|
|
|
|
user = relationship("User", back_populates="operation_logs")
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"user_id": self.user_id,
|
|
|
|
|
|
"operation": self.operation,
|
|
|
|
|
|
"details": self.details,
|
|
|
|
|
|
"ip_address": self.ip_address,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"timestamp": format_utc_datetime(self.timestamp),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MessageFeedback(Base):
|
|
|
|
|
|
"""Message feedback table - 消息反馈表"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "message_feedbacks"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True, comment="Primary key")
|
2026-01-22 00:28:43 +08:00
|
|
|
|
message_id = Column(
|
|
|
|
|
|
Integer, ForeignKey("messages.id"), nullable=False, index=True, comment="Message ID being rated"
|
|
|
|
|
|
)
|
2026-01-21 19:15:52 +08:00
|
|
|
|
user_id = Column(String(64), nullable=False, index=True, comment="User ID who provided feedback")
|
|
|
|
|
|
rating = Column(String(10), nullable=False, comment="Feedback rating: like or dislike")
|
|
|
|
|
|
reason = Column(Text, nullable=True, comment="Optional reason for dislike feedback")
|
2026-01-22 00:28:43 +08:00
|
|
|
|
created_at = Column(DateTime, default=utc_now_naive, comment="Feedback creation time")
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
|
|
|
|
|
# Relationships
|
|
|
|
|
|
message = relationship("Message", back_populates="feedbacks")
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"id": self.id,
|
|
|
|
|
|
"message_id": self.message_id,
|
|
|
|
|
|
"user_id": self.user_id,
|
|
|
|
|
|
"rating": self.rating,
|
|
|
|
|
|
"reason": self.reason,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"created_at": format_utc_datetime(self.created_at),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MCPServer(Base):
|
|
|
|
|
|
"""MCP 服务器配置模型"""
|
|
|
|
|
|
|
|
|
|
|
|
__tablename__ = "mcp_servers"
|
|
|
|
|
|
|
|
|
|
|
|
# 核心字段 - name 作为主键
|
|
|
|
|
|
name = Column(String(100), primary_key=True, comment="服务器名称(唯一标识)")
|
|
|
|
|
|
description = Column(String(500), nullable=True, comment="描述")
|
|
|
|
|
|
|
|
|
|
|
|
# 连接配置
|
|
|
|
|
|
transport = Column(String(20), nullable=False, comment="传输类型:sse/streamable_http/stdio")
|
|
|
|
|
|
url = Column(String(500), nullable=True, comment="服务器 URL(sse/streamable_http)")
|
|
|
|
|
|
command = Column(String(500), nullable=True, comment="命令(stdio)")
|
|
|
|
|
|
args = Column(JSON, nullable=True, comment="命令参数数组(stdio)")
|
|
|
|
|
|
headers = Column(JSON, nullable=True, comment="HTTP 请求头")
|
|
|
|
|
|
timeout = Column(Integer, nullable=True, comment="HTTP 超时时间(秒)")
|
|
|
|
|
|
sse_read_timeout = Column(Integer, nullable=True, comment="SSE 读取超时(秒)")
|
|
|
|
|
|
|
|
|
|
|
|
# UI 增强字段
|
|
|
|
|
|
tags = Column(JSON, nullable=True, comment="标签数组")
|
|
|
|
|
|
icon = Column(String(50), nullable=True, comment="图标(emoji)")
|
|
|
|
|
|
|
|
|
|
|
|
# 状态字段
|
|
|
|
|
|
enabled = Column(Integer, nullable=False, default=1, comment="是否启用:1=是,0=否")
|
|
|
|
|
|
disabled_tools = Column(JSON, nullable=True, comment="禁用的工具名称列表")
|
|
|
|
|
|
|
|
|
|
|
|
# 用户追踪
|
|
|
|
|
|
created_by = Column(String(100), nullable=False, comment="创建人用户名")
|
|
|
|
|
|
updated_by = Column(String(100), nullable=False, comment="修改人用户名")
|
|
|
|
|
|
|
|
|
|
|
|
# 时间戳
|
2026-01-22 00:28:43 +08:00
|
|
|
|
created_at = Column(DateTime, default=utc_now_naive, comment="创建时间")
|
|
|
|
|
|
updated_at = Column(DateTime, default=utc_now_naive, onupdate=utc_now_naive, comment="更新时间")
|
2026-01-21 19:15:52 +08:00
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
return {
|
|
|
|
|
|
"name": self.name,
|
|
|
|
|
|
"description": self.description,
|
|
|
|
|
|
"transport": self.transport,
|
|
|
|
|
|
"url": self.url,
|
|
|
|
|
|
"command": self.command,
|
|
|
|
|
|
"args": self.args or [],
|
|
|
|
|
|
"headers": self.headers or {},
|
|
|
|
|
|
"timeout": self.timeout,
|
|
|
|
|
|
"sse_read_timeout": self.sse_read_timeout,
|
|
|
|
|
|
"tags": self.tags or [],
|
|
|
|
|
|
"icon": self.icon,
|
|
|
|
|
|
"enabled": bool(self.enabled),
|
|
|
|
|
|
"disabled_tools": self.disabled_tools or [],
|
|
|
|
|
|
"created_by": self.created_by,
|
|
|
|
|
|
"updated_by": self.updated_by,
|
2026-01-22 00:28:43 +08:00
|
|
|
|
"created_at": format_utc_datetime(self.created_at),
|
|
|
|
|
|
"updated_at": format_utc_datetime(self.updated_at),
|
2026-01-21 19:15:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def to_mcp_config(self) -> dict[str, Any]:
|
|
|
|
|
|
"""转换为 MCP 配置格式(用于加载到 MCP_SERVERS 缓存)"""
|
|
|
|
|
|
config = {"transport": self.transport}
|
|
|
|
|
|
if self.url:
|
|
|
|
|
|
config["url"] = self.url
|
|
|
|
|
|
if self.command:
|
|
|
|
|
|
config["command"] = self.command
|
|
|
|
|
|
if self.args:
|
|
|
|
|
|
config["args"] = self.args
|
|
|
|
|
|
if self.headers:
|
|
|
|
|
|
config["headers"] = self.headers
|
|
|
|
|
|
if self.timeout is not None:
|
|
|
|
|
|
config["timeout"] = self.timeout
|
|
|
|
|
|
if self.sse_read_timeout is not None:
|
|
|
|
|
|
config["sse_read_timeout"] = self.sse_read_timeout
|
|
|
|
|
|
if self.disabled_tools:
|
|
|
|
|
|
config["disabled_tools"] = self.disabled_tools
|
|
|
|
|
|
return config
|