Files
taiji-AI-PAD/services/mcp-server/models.py
T
2025-12-26 15:16:32 +00:00

905 lines
30 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
数据库模型定义
"""
import uuid
from datetime import datetime
from typing import Dict, List, Optional, Any
from sqlalchemy import (
Column, String, Text, DateTime, Boolean, Integer,
JSON, ForeignKey, Index, UniqueConstraint
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.types import TypeDecorator, CHAR
import sqlalchemy as sa
Base = declarative_base()
class GUID(TypeDecorator):
"""跨数据库兼容的UUID类型"""
impl = CHAR(36)
cache_ok = True
def load_dialect_impl(self, dialect):
if dialect.name == "postgresql":
return dialect.type_descriptor(UUID(as_uuid=True))
return dialect.type_descriptor(CHAR(36))
def process_bind_param(self, value, dialect):
if value is None:
return value
if isinstance(value, uuid.UUID):
return str(value)
return str(uuid.UUID(value))
def process_result_value(self, value, dialect):
if value is None:
return value
# 如果已经是UUID对象(包括asyncpg的UUID),先转换为字符串再转换
if hasattr(value, '__str__') and not isinstance(value, str):
# 处理asyncpg的UUID对象
try:
from asyncpg.pgproto.pgproto import UUID as AsyncUUID
if isinstance(value, AsyncUUID):
value = str(value)
except (ImportError, AttributeError):
pass
# 如果是标准UUID对象,直接返回
if isinstance(value, uuid.UUID):
return value
# 其他对象转换为字符串
value = str(value)
# 字符串转换为UUID
return uuid.UUID(value)
class BaseModel:
"""基础模型类"""
id = Column(GUID(), primary_key=True, default=uuid.uuid4)
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
updated_at = Column(
DateTime,
default=datetime.utcnow,
onupdate=datetime.utcnow,
nullable=False
)
class User(BaseModel, Base):
"""用户模型(租户使用者)"""
__tablename__ = "users"
name = Column(String(100), nullable=False)
email = Column(String(255), unique=True, nullable=False)
password_hash = Column(String(255), nullable=False)
role = Column(String(50), nullable=False, default="user") # user, channel_admin, billing_admin, operations_admin, admin, super_admin, provider_admin
channel_id = Column(GUID(), ForeignKey("channels.id"))
# 订阅和计费
subscription_tier = Column(String(20), default="free")
discount = Column(sa.Numeric(5, 2), default=0)
balance = Column(sa.Numeric(12, 2), default=0) # 账户余额
credit_limit = Column(sa.Numeric(12, 2), default=0) # 授信额度
status = Column(String(20), default="active")
# 兼容旧字段
username = Column(String(50))
hashed_password = Column(String(255))
full_name = Column(String(100))
is_active = Column(Boolean, default=True)
is_admin = Column(Boolean, default=False)
# 关联关系
channel = relationship("Channel", back_populates="tenants")
agents = relationship("Agent", back_populates="owner", cascade="all, delete-orphan")
sessions = relationship("Session", back_populates="user", cascade="all, delete-orphan")
api_keys = relationship("APIKey", back_populates="user", cascade="all, delete-orphan")
workflows = relationship("Workflow", back_populates="user", cascade="all, delete-orphan")
# 索引
__table_args__ = (
Index("idx_user_email", email),
Index("idx_user_channel", channel_id),
)
class Agent(BaseModel, Base):
"""Agent模型(平台Agent和自定义Agent)"""
__tablename__ = "agents"
name = Column(String(100), nullable=False)
type = Column(String(20), nullable=False, default="platform") # platform, custom
description = Column(Text)
category = Column(String(50))
# Agent配置
role = Column(String(200)) # Agent的角色定义
goal = Column(Text) # Agent的目标描述
config = Column(JSON, default=dict) # Agent的配置信息
tools = Column(JSON, default=list) # Agent授权使用的工具列表
capabilities = Column(JSON, default=list) # Agent的能力列表
# 资源配置
cpu = Column(sa.Numeric(5, 2), nullable=False, default=2)
memory = Column(sa.Numeric(5, 2), nullable=False, default=4) # GB
max_instances = Column(Integer, default=100)
# 状态信息
status = Column(String(20), default="active") # active, inactive, error, available, unavailable
version = Column(String(20), default="1.0.0")
# 性能统计
total_executions = Column(Integer, default=0)
success_rate = Column(sa.Float, default=0.0)
avg_execution_time = Column(sa.Float, default=0.0) # 毫秒
# 关联关系
owner_id = Column(GUID(), ForeignKey("users.id"))
owner = relationship("User", back_populates="agents")
executions = relationship("Execution", back_populates="agent", cascade="all, delete-orphan")
resource_allocations = relationship("ResourceAllocation",
foreign_keys="[ResourceAllocation.resource_id]",
primaryjoin="and_(Agent.id==ResourceAllocation.resource_id, ResourceAllocation.resource_type=='agent')")
# 索引和约束
__table_args__ = (
Index("idx_agent_name", name),
Index("idx_agent_type", type),
Index("idx_agent_owner", owner_id),
Index("idx_agent_status", status),
)
class Tool(BaseModel, Base):
"""工具模型"""
__tablename__ = "tools"
name = Column(String(100), nullable=False)
description = Column(Text)
category = Column(String(50)) # api, function, integration等
# 工具定义
schema = Column(JSON, nullable=False) # OpenAPI或Pydantic schema
endpoint = Column(String(500)) # API端点URL
method = Column(String(10), default="POST") # HTTP方法
# 认证信息
auth_type = Column(String(20)) # api_key, oauth, basic等
auth_config = Column(JSON, default=dict)
# 限制和配额
rate_limit = Column(Integer, default=100) # 每分钟调用次数
cost_per_call = Column(sa.Float, default=0.0) # 每次调用成本(EU)
timeout = Column(Integer, default=30) # 超时时间(秒)
# 状态信息
is_active = Column(Boolean, default=True)
is_public = Column(Boolean, default=False) # 是否公开可用
# 统计信息
total_calls = Column(Integer, default=0)
success_rate = Column(sa.Float, default=0.0)
avg_response_time = Column(sa.Float, default=0.0)
# 关联关系
owner_id = Column(GUID(), ForeignKey("users.id"))
owner = relationship("User")
# 索引
__table_args__ = (
Index("idx_tool_name", name),
Index("idx_tool_category", category),
Index("idx_tool_active", is_active),
)
class GatewayAPI(BaseModel, Base):
"""Service gateway API definitions (JSON or URL based)."""
__tablename__ = "gateway_apis"
name = Column(String(150), nullable=False)
method = Column(String(10), default="json") # json | url
content = Column(Text, nullable=False)
owner_id = Column(GUID(), ForeignKey("users.id"))
__table_args__ = (
Index("idx_gateway_api_name", name),
)
class DataTemplate(BaseModel, Base):
"""Data templates for ingestion or cloud storage configs."""
__tablename__ = "data_templates"
name = Column(String(150), nullable=False)
type = Column(String(50), nullable=False) # json_api | cloud_storage
config = Column(JSON, default=dict)
owner_id = Column(GUID(), ForeignKey("users.id"))
__table_args__ = (
Index("idx_data_template_name", name),
Index("idx_data_template_type", type),
)
class Session(BaseModel, Base):
"""会话模型"""
__tablename__ = "sessions"
session_id = Column(String(100), unique=True, nullable=False)
# 会话信息
context = Column(JSON, default=dict) # 会话上下文
session_metadata = Column(JSON, default=dict) # 元数据
# 状态
status = Column(String(20), default="active") # active, completed, failed
# 关联关系
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
user = relationship("User", back_populates="sessions")
executions = relationship("Execution", back_populates="session", cascade="all, delete-orphan")
# 索引
__table_args__ = (
Index("idx_session_id", session_id),
Index("idx_session_user", user_id),
Index("idx_session_status", status),
)
class Channel(BaseModel, Base):
"""渠道合作伙伴模型"""
__tablename__ = "channels"
name = Column(String(100), nullable=False)
email = Column(String(255), unique=True, nullable=False)
password_hash = Column(String(255), nullable=False)
commission_rate = Column(sa.Numeric(5, 2), default=0)
channel_credit = Column(sa.Numeric(12, 2), default=0) # 渠道授信额度
custom_agent_cpu = Column(sa.Numeric(12, 2), default=2) # 自定义Agent CPU
custom_agent_memory = Column(sa.Numeric(12, 2), default=4) # 自定义Agent 内存(GB)
status = Column(String(20), default="active")
# 关联关系
tenants = relationship("User", back_populates="channel")
resource_allocations = relationship("ResourceAllocation",
foreign_keys="[ResourceAllocation.target_id]",
primaryjoin="and_(Channel.id==ResourceAllocation.target_id, ResourceAllocation.target_type=='channel')")
applications = relationship("Application", back_populates="channel", cascade="all, delete-orphan")
__table_args__ = (
Index("idx_channel_name", name),
Index("idx_channel_email", email),
)
class ModelProvider(BaseModel, Base):
"""模型供应商表"""
__tablename__ = "model_providers"
name = Column(String(100), nullable=False)
provider = Column(String(50), nullable=False) # openai, anthropic, azure, google, aws
api_url = Column(String(500), nullable=False)
api_key_encrypted = Column(Text, nullable=False)
supported_models = Column(JSON, default=list, nullable=False)
rpm = Column(Integer, nullable=False, default=0)
tpm = Column(Integer, nullable=False, default=0)
status = Column(String(20), default="active")
owner_id = Column(GUID(), ForeignKey("users.id"))
is_active = Column(Boolean, default=True)
__table_args__ = (
Index("idx_model_provider_name", name),
Index("idx_model_provider_active", is_active),
)
class ResourceAllocation(BaseModel, Base):
"""资源分配表(渠道和租户的资源分配)"""
__tablename__ = "resource_allocations"
target_id = Column(GUID(), nullable=False)
target_type = Column(String(20), nullable=False) # channel, tenant
resource_type = Column(String(20), nullable=False) # agent, model
resource_id = Column(GUID(), nullable=False)
# Agent资源配置
quantity = Column(Integer) # Agent数量
# 模型资源配置
rpm = Column(Integer) # 模型RPM
tpm = Column(Integer) # 模型TPM
__table_args__ = (
Index("idx_resource_allocation_target", target_id, target_type),
Index("idx_resource_allocation_resource", resource_id, resource_type),
)
class Execution(BaseModel, Base):
"""执行记录模型"""
__tablename__ = "executions"
execution_id = Column(String(100), unique=True, nullable=False)
# 执行信息
method = Column(String(50), nullable=False) # MCP方法名
params = Column(JSON, default=dict) # 执行参数
result = Column(JSON, default=dict) # 执行结果
error = Column(Text) # 错误信息
# 时间信息
started_at = Column(DateTime, nullable=False)
completed_at = Column(DateTime)
execution_time = Column(sa.Float) # 执行时间(毫秒)
# 状态
status = Column(String(20), nullable=False) # running, completed, failed
# 资源消耗
cpu_usage = Column(sa.Float, default=0.0) # CPU使用率
memory_usage = Column(sa.Float, default=0.0) # 内存使用(MB)
network_io = Column(sa.Float, default=0.0) # 网络IO(KB)
eu_consumed = Column(sa.Float, default=0.0) # 消耗的执行单元
# 关联关系
agent_id = Column(GUID(), ForeignKey("agents.id"), nullable=False)
agent = relationship("Agent", back_populates="executions")
session_id = Column(GUID(), ForeignKey("sessions.id"))
session = relationship("Session", back_populates="executions")
# 索引
__table_args__ = (
Index("idx_execution_id", execution_id),
Index("idx_execution_agent", agent_id),
Index("idx_execution_status", status),
Index("idx_execution_started", started_at),
)
class APIKey(BaseModel, Base):
"""API密钥模型"""
__tablename__ = "api_keys"
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
api_key_hash = Column(String(255), nullable=False)
api_key_prefix = Column(String(10), nullable=False) # sk-xxxx
last_used = Column(DateTime)
# 兼容旧字段
name = Column(String(100))
key_hash = Column(String(255))
prefix = Column(String(20))
scopes = Column(JSON, default=list)
rate_limit = Column(Integer, default=1000)
is_active = Column(Boolean, default=True)
expires_at = Column(DateTime)
total_requests = Column(Integer, default=0)
# 关联关系
user = relationship("User", back_populates="api_keys")
# 索引
__table_args__ = (
Index("idx_api_key_hash", api_key_hash),
Index("idx_api_key_prefix", api_key_prefix),
Index("idx_api_key_user", user_id),
)
class Billing(BaseModel, Base):
"""计费详情模型(按执行记录计费)"""
__tablename__ = "billing"
execution_id = Column(GUID(), ForeignKey("executions.id"), nullable=False)
eu_consumed = Column(sa.Float, nullable=False, default=0.0)
cost = Column(sa.Float, nullable=False, default=0.0)
currency = Column(String(10), default="EU")
# 资源使用详情
cpu_time = Column(sa.Float, default=0.0)
memory_max = Column(sa.Float, default=0.0)
network_io = Column(sa.Float, default=0.0)
storage_io = Column(sa.Float, default=0.0)
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
# 关联关系
user = relationship("User")
execution = relationship("Execution")
# 索引
__table_args__ = (
Index("idx_billing_execution", execution_id),
Index("idx_billing_user", user_id),
Index("idx_billing_created", "created_at"),
)
class Balance(BaseModel, Base):
"""用户余额模型"""
__tablename__ = "balances"
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False, unique=True)
eu_balance = Column(sa.Float, nullable=False, default=0.0)
# 关联关系
user = relationship("User")
# 索引
__table_args__ = (
Index("idx_balance_user", user_id),
)
class BillingRecord(BaseModel, Base):
"""计费记录模型"""
__tablename__ = "billing_records"
timestamp = Column(DateTime, nullable=False, default=datetime.utcnow)
channel_id = Column(GUID(), ForeignKey("channels.id"))
tenant_id = Column(GUID(), ForeignKey("users.id")) # 租户即为user
agent_id = Column(GUID(), ForeignKey("agents.id"))
agent_name = Column(String(100), nullable=False)
duration = Column(Integer, nullable=False) # 秒
eu = Column(Integer, nullable=False) # 执行单元:1 EU = 10秒
cost = Column(sa.Numeric(12, 4), nullable=False)
# 关联关系
channel = relationship("Channel")
tenant = relationship("User", foreign_keys=[tenant_id])
agent = relationship("Agent")
# 索引
__table_args__ = (
Index("idx_billing_timestamp", timestamp),
Index("idx_billing_channel", channel_id),
Index("idx_billing_tenant", tenant_id),
)
class RechargeRecord(BaseModel, Base):
"""充值记录表"""
__tablename__ = "recharge_records"
user_id = Column(GUID(), ForeignKey("users.id"))
channel_id = Column(GUID(), ForeignKey("channels.id"))
amount = Column(sa.Numeric(12, 2), nullable=False)
payment_method = Column(String(50))
status = Column(String(20), default="pending") # pending, success, failed
order_id = Column(String(100))
completed_at = Column(DateTime)
# 关联关系
user = relationship("User")
channel = relationship("Channel")
__table_args__ = (
Index("idx_recharge_user", user_id),
Index("idx_recharge_channel", channel_id),
Index("idx_recharge_status", status),
)
class Application(BaseModel, Base):
"""申请审批表(渠道申请资源)"""
__tablename__ = "applications"
channel_id = Column(GUID(), ForeignKey("channels.id"), nullable=False)
type = Column(String(20), nullable=False) # model, agent
# 模型申请
model_name = Column(String(100))
rpm = Column(Integer)
tpm = Column(Integer)
# Agent申请
agent_type = Column(String(100))
quantity = Column(Integer)
reason = Column(Text)
status = Column(String(20), default="pending") # pending, approved, rejected
reviewed_by = Column(GUID())
review_reason = Column(Text)
reviewed_at = Column(DateTime)
# 关联关系
channel = relationship("Channel", back_populates="applications")
__table_args__ = (
Index("idx_application_channel", channel_id),
Index("idx_application_status", status),
)
class Workflow(BaseModel, Base):
"""工作流表"""
__tablename__ = "workflows"
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
name = Column(String(100), nullable=False)
description = Column(Text)
gateway = Column(String(20), nullable=False) # MCP, A2A, API
nodes = Column(JSON, nullable=False, default=list) # 最多3个节点
status = Column(String(20), default="active")
# 关联关系
user = relationship("User", back_populates="workflows")
__table_args__ = (
Index("idx_workflow_user", user_id),
Index("idx_workflow_status", status),
)
class AuditLog(BaseModel, Base):
"""审计日志模型"""
__tablename__ = "audit_logs"
# 操作信息
action = Column(String(50), nullable=False) # 操作类型
resource_type = Column(String(50), nullable=False) # 资源类型
resource_id = Column(String(100)) # 资源ID
# 详细信息
details = Column(JSON, default=dict) # 操作详情
ip_address = Column(String(45)) # IP地址
user_agent = Column(Text) # 用户代理
# 结果
success = Column(Boolean, nullable=False)
error_message = Column(Text)
# 关联关系
user_id = Column(GUID(), ForeignKey("users.id"))
user = relationship("User")
# 索引
__table_args__ = (
Index("idx_audit_action", action),
Index("idx_audit_resource", resource_type, resource_id),
Index("idx_audit_user", user_id),
Index("idx_audit_created", "created_at"),
)
class ChannelAgentQuota(BaseModel, Base):
"""渠道Agent配额模型"""
__tablename__ = "channel_agent_quotas"
channel_id = Column(GUID(), ForeignKey("channels.id"), nullable=False)
agent_id = Column(GUID(), ForeignKey("agents.id"), nullable=False)
quantity = Column(Integer, nullable=False, default=0)
# 关联关系
channel = relationship("Channel")
agent = relationship("Agent")
# 索引
__table_args__ = (
Index("idx_channel_agent_quota_channel", channel_id),
Index("idx_channel_agent_quota_agent", agent_id),
UniqueConstraint("channel_id", "agent_id", name="uq_channel_agent"),
)
class ProviderModel(BaseModel, Base):
"""模型提供商模型(前端集成使用)"""
__tablename__ = "provider_models"
name = Column(String(100), nullable=False)
api_url = Column(String(500), nullable=False)
api_key = Column(Text, nullable=False)
supported_models = Column(JSON, default=list)
rpm = Column(Integer, default=0)
tpm = Column(Integer, default=0)
status = Column(String(20), default="active")
# 索引
__table_args__ = (
Index("idx_provider_model_name", name),
)
# 别名:Tenant 指向 User(租户即为user)
Tenant = User
class TokenBlacklist(BaseModel, Base):
"""Token黑名单(用于JWT登出和Token失效)"""
__tablename__ = "token_blacklist"
token_jti = Column(String(255), unique=True, nullable=False) # JWT ID
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
expires_at = Column(DateTime, nullable=False) # Token过期时间
reason = Column(String(100)) # 黑名单原因:logout, revoked, security
# 关联关系
user = relationship("User")
__table_args__ = (
Index("idx_token_blacklist_jti", token_jti),
Index("idx_token_blacklist_expires", expires_at),
)
class ResourceUsage(BaseModel, Base):
"""资源使用记录(用于监控和统计)"""
__tablename__ = "resource_usage"
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
agent_id = Column(GUID(), ForeignKey("agents.id"))
# 资源使用量
cpu_seconds = Column(sa.Float, default=0.0) # CPU使用秒数
memory_mb_seconds = Column(sa.Float, default=0.0) # 内存MB*秒
network_bytes = Column(sa.BigInteger, default=0) # 网络流量字节
storage_bytes = Column(sa.BigInteger, default=0) # 存储使用字节
api_calls = Column(Integer, default=0) # API调用次数
# 时间范围
period_start = Column(DateTime, nullable=False)
period_end = Column(DateTime, nullable=False)
granularity = Column(String(20), default="hourly") # hourly, daily, monthly
# 关联关系
user = relationship("User")
agent = relationship("Agent")
__table_args__ = (
Index("idx_resource_usage_user", user_id),
Index("idx_resource_usage_period", period_start, period_end),
Index("idx_resource_usage_granularity", granularity),
)
class QuotaAlert(BaseModel, Base):
"""配额预警记录"""
__tablename__ = "quota_alerts"
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
channel_id = Column(GUID(), ForeignKey("channels.id"))
# 预警信息
alert_type = Column(String(50), nullable=False) # balance_low, quota_exceeded, rate_limit
threshold_percent = Column(Integer) # 触发阈值百分比
current_value = Column(sa.Numeric(12, 4)) # 当前值
threshold_value = Column(sa.Numeric(12, 4)) # 阈值
# 状态
status = Column(String(20), default="active") # active, acknowledged, resolved
acknowledged_at = Column(DateTime)
resolved_at = Column(DateTime)
# 通知
notification_sent = Column(Boolean, default=False)
notification_channel = Column(String(50)) # email, webhook, sms
# 关联关系
user = relationship("User")
channel = relationship("Channel")
__table_args__ = (
Index("idx_quota_alert_user", user_id),
Index("idx_quota_alert_status", status),
Index("idx_quota_alert_type", alert_type),
)
class ModelPricing(BaseModel, Base):
"""模型定价配置"""
__tablename__ = "model_pricing"
provider_id = Column(GUID(), ForeignKey("model_providers.id"), nullable=False)
model_name = Column(String(100), nullable=False)
# 定价(每1K tokens)
input_price_per_1k = Column(sa.Numeric(10, 6), nullable=False) # 输入价格
output_price_per_1k = Column(sa.Numeric(10, 6), nullable=False) # 输出价格
# EU转换
eu_per_1k_tokens = Column(sa.Numeric(8, 4), default=0.1) # 每1K tokens消耗的EU
# 限制
max_context_length = Column(Integer, default=4096)
max_output_tokens = Column(Integer, default=2048)
# 状态
is_active = Column(Boolean, default=True)
effective_from = Column(DateTime, default=datetime.utcnow)
effective_until = Column(DateTime)
# 关联关系
provider = relationship("ModelProvider")
__table_args__ = (
Index("idx_model_pricing_provider", provider_id),
Index("idx_model_pricing_model", model_name),
UniqueConstraint("provider_id", "model_name", name="uq_provider_model_pricing"),
)
class ProviderHealthCheck(BaseModel, Base):
"""供应商健康检查记录"""
__tablename__ = "provider_health_checks"
provider_id = Column(GUID(), ForeignKey("model_providers.id"), nullable=False)
# 检查结果
is_healthy = Column(Boolean, nullable=False)
response_time_ms = Column(Integer) # 响应时间毫秒
status_code = Column(Integer) # HTTP状态码
error_message = Column(Text)
# 检查类型
check_type = Column(String(50), default="ping") # ping, model_list, completion
# 关联关系
provider = relationship("ModelProvider")
__table_args__ = (
Index("idx_provider_health_provider", provider_id),
Index("idx_provider_health_time", "created_at"),
Index("idx_provider_health_status", is_healthy),
)
class AgentTrace(BaseModel, Base):
"""Agent执行轨迹(详细追踪记录)"""
__tablename__ = "agent_traces"
execution_id = Column(GUID(), ForeignKey("executions.id"), nullable=False)
agent_id = Column(GUID(), ForeignKey("agents.id"), nullable=False)
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
# 轨迹信息
trace_id = Column(String(100), nullable=False) # 分布式追踪ID
span_id = Column(String(100), nullable=False) # 跨度ID
parent_span_id = Column(String(100)) # 父跨度ID
# 操作信息
operation_name = Column(String(200), nullable=False)
operation_type = Column(String(50)) # tool_call, llm_call, function_exec
# 时间信息
started_at = Column(DateTime, nullable=False)
ended_at = Column(DateTime)
duration_ms = Column(Integer)
# 输入输出
input_data = Column(JSON) # 输入数据(脱敏)
output_data = Column(JSON) # 输出数据(脱敏)
# 状态
status = Column(String(20), nullable=False) # running, success, error
error_type = Column(String(100))
error_message = Column(Text)
# 资源消耗
tokens_used = Column(Integer, default=0)
eu_consumed = Column(sa.Numeric(10, 4), default=0)
# 关联关系
execution = relationship("Execution")
agent = relationship("Agent")
user = relationship("User")
__table_args__ = (
Index("idx_agent_trace_execution", execution_id),
Index("idx_agent_trace_trace_id", trace_id),
Index("idx_agent_trace_user", user_id),
Index("idx_agent_trace_time", started_at),
)
class BillingEvent(BaseModel, Base):
"""计费事件(用于NATS消息队列)"""
__tablename__ = "billing_events"
# 事件信息
event_type = Column(String(50), nullable=False) # execution_start, execution_end, quota_check
event_id = Column(String(100), unique=True, nullable=False)
# 关联实体
user_id = Column(GUID(), ForeignKey("users.id"), nullable=False)
agent_id = Column(GUID(), ForeignKey("agents.id"))
execution_id = Column(GUID(), ForeignKey("executions.id"))
# 事件数据
payload = Column(JSON, nullable=False)
# 处理状态
status = Column(String(20), default="pending") # pending, processing, completed, failed
processed_at = Column(DateTime)
retry_count = Column(Integer, default=0)
error_message = Column(Text)
# 关联关系
user = relationship("User")
agent = relationship("Agent")
execution = relationship("Execution")
__table_args__ = (
Index("idx_billing_event_type", event_type),
Index("idx_billing_event_status", status),
Index("idx_billing_event_user", user_id),
Index("idx_billing_event_time", "created_at"),
)
class ChannelProviderAccess(BaseModel, Base):
"""渠道供应商授权表(记录渠道可使用的供应商)"""
__tablename__ = "channel_provider_access"
channel_id = Column(GUID(), ForeignKey("channels.id"), nullable=False)
provider_id = Column(GUID(), ForeignKey("model_providers.id"), nullable=False)
# 授权信息
status = Column(String(20), default="active") # active, suspended, expired
# 限制配置(可覆盖供应商默认值)
rpm_limit = Column(Integer) # 每分钟请求数限制
tpm_limit = Column(Integer) # 每分钟Token数限制
# 审批信息
approved_by = Column(GUID(), ForeignKey("users.id"))
approved_at = Column(DateTime)
expires_at = Column(DateTime) # 可选的过期时间
# 关联关系
channel = relationship("Channel")
provider = relationship("ModelProvider")
approver = relationship("User", foreign_keys=[approved_by])
__table_args__ = (
Index("idx_channel_provider_channel", channel_id),
Index("idx_channel_provider_provider", provider_id),
Index("idx_channel_provider_status", status),
UniqueConstraint("channel_id", "provider_id", name="uq_channel_provider"),
)
class ProviderApplication(BaseModel, Base):
"""供应商使用申请表(渠道申请使用供应商)"""
__tablename__ = "provider_applications"
channel_id = Column(GUID(), ForeignKey("channels.id"), nullable=False)
provider_id = Column(GUID(), ForeignKey("model_providers.id"), nullable=False)
# 申请信息
requested_rpm = Column(Integer) # 申请的RPM限制
requested_tpm = Column(Integer) # 申请的TPM限制
reason = Column(Text, nullable=False) # 申请理由
# 审批状态
status = Column(String(20), default="pending") # pending, approved, rejected
reviewed_by = Column(GUID(), ForeignKey("users.id"))
review_reason = Column(Text) # 审批意见
reviewed_at = Column(DateTime)
# 关联关系
channel = relationship("Channel")
provider = relationship("ModelProvider")
reviewer = relationship("User", foreign_keys=[reviewed_by])
__table_args__ = (
Index("idx_provider_app_channel", channel_id),
Index("idx_provider_app_provider", provider_id),
Index("idx_provider_app_status", status),
)