feat: AI聊天室多Agent协作讨论平台

- 实现Agent管理,支持AI辅助生成系统提示词
- 支持多个AI提供商(OpenRouter、智谱、MiniMax等)
- 实现聊天室和讨论引擎
- WebSocket实时消息推送
- 前端使用React + Ant Design
- 后端使用FastAPI + MongoDB

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude Code
2026-02-03 19:20:02 +08:00
commit edbddf855d
76 changed files with 14681 additions and 0 deletions

View File

@@ -0,0 +1,241 @@
"""
AI接口管理路由
"""
from typing import List, Optional, Dict, Any
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel, Field
from loguru import logger
from services.ai_provider_service import AIProviderService
from utils.encryption import mask_api_key
router = APIRouter()
# ============ 请求/响应模型 ============
class ProxyConfigModel(BaseModel):
"""代理配置模型"""
http_proxy: Optional[str] = None
https_proxy: Optional[str] = None
no_proxy: List[str] = []
class RateLimitModel(BaseModel):
"""速率限制模型"""
requests_per_minute: int = 60
tokens_per_minute: int = 100000
class ProviderCreateRequest(BaseModel):
"""创建AI接口请求"""
provider_type: str = Field(..., description="提供商类型: minimax, zhipu, openrouter, kimi, deepseek, gemini, ollama, llmstudio")
name: str = Field(..., description="自定义名称")
model: str = Field(..., description="模型名称")
api_key: str = Field(default="", description="API密钥")
base_url: str = Field(default="", description="API基础URL")
use_proxy: bool = Field(default=False, description="是否使用代理")
proxy_config: Optional[ProxyConfigModel] = None
rate_limit: Optional[RateLimitModel] = None
timeout: int = Field(default=60, description="超时时间(秒)")
extra_params: Dict[str, Any] = Field(default_factory=dict, description="额外参数")
class Config:
json_schema_extra = {
"example": {
"provider_type": "openrouter",
"name": "OpenRouter GPT-4",
"model": "openai/gpt-4-turbo",
"api_key": "sk-xxx",
"use_proxy": True,
"proxy_config": {
"http_proxy": "http://127.0.0.1:7890",
"https_proxy": "http://127.0.0.1:7890"
}
}
}
class ProviderUpdateRequest(BaseModel):
"""更新AI接口请求"""
name: Optional[str] = None
model: Optional[str] = None
api_key: Optional[str] = None
base_url: Optional[str] = None
use_proxy: Optional[bool] = None
proxy_config: Optional[ProxyConfigModel] = None
rate_limit: Optional[RateLimitModel] = None
timeout: Optional[int] = None
extra_params: Optional[Dict[str, Any]] = None
enabled: Optional[bool] = None
class ProviderResponse(BaseModel):
"""AI接口响应"""
provider_id: str
provider_type: str
name: str
api_key_masked: str
base_url: str
model: str
use_proxy: bool
proxy_config: Dict[str, Any]
rate_limit: Dict[str, int]
timeout: int
extra_params: Dict[str, Any]
enabled: bool
created_at: str
updated_at: str
class TestConfigRequest(BaseModel):
"""测试配置请求"""
provider_type: str
api_key: str
base_url: str = ""
model: str = ""
use_proxy: bool = False
proxy_config: Optional[ProxyConfigModel] = None
timeout: int = 30
class TestResponse(BaseModel):
"""测试响应"""
success: bool
message: str
model: Optional[str] = None
latency_ms: Optional[float] = None
# ============ 路由处理 ============
@router.post("", response_model=ProviderResponse, status_code=status.HTTP_201_CREATED)
async def create_provider(request: ProviderCreateRequest):
"""
创建新的AI接口配置
"""
try:
provider = await AIProviderService.create_provider(
provider_type=request.provider_type,
name=request.name,
model=request.model,
api_key=request.api_key,
base_url=request.base_url,
use_proxy=request.use_proxy,
proxy_config=request.proxy_config.dict() if request.proxy_config else None,
rate_limit=request.rate_limit.dict() if request.rate_limit else None,
timeout=request.timeout,
extra_params=request.extra_params
)
return _to_response(provider)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"创建AI接口失败: {e}")
raise HTTPException(status_code=500, detail="创建失败")
@router.get("", response_model=List[ProviderResponse])
async def list_providers(enabled_only: bool = False):
"""
获取所有AI接口配置
"""
providers = await AIProviderService.get_all_providers(enabled_only)
return [_to_response(p) for p in providers]
@router.get("/{provider_id}", response_model=ProviderResponse)
async def get_provider(provider_id: str):
"""
获取指定AI接口配置
"""
provider = await AIProviderService.get_provider(provider_id)
if not provider:
raise HTTPException(status_code=404, detail="AI接口不存在")
return _to_response(provider)
@router.put("/{provider_id}", response_model=ProviderResponse)
async def update_provider(provider_id: str, request: ProviderUpdateRequest):
"""
更新AI接口配置
"""
update_data = request.dict(exclude_unset=True)
# 转换嵌套模型
if "proxy_config" in update_data and update_data["proxy_config"]:
update_data["proxy_config"] = update_data["proxy_config"].dict() if hasattr(update_data["proxy_config"], "dict") else update_data["proxy_config"]
if "rate_limit" in update_data and update_data["rate_limit"]:
update_data["rate_limit"] = update_data["rate_limit"].dict() if hasattr(update_data["rate_limit"], "dict") else update_data["rate_limit"]
try:
provider = await AIProviderService.update_provider(provider_id, **update_data)
if not provider:
raise HTTPException(status_code=404, detail="AI接口不存在")
return _to_response(provider)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
@router.delete("/{provider_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_provider(provider_id: str):
"""
删除AI接口配置
"""
success = await AIProviderService.delete_provider(provider_id)
if not success:
raise HTTPException(status_code=404, detail="AI接口不存在")
@router.post("/{provider_id}/test", response_model=TestResponse)
async def test_provider(provider_id: str):
"""
测试AI接口连接
"""
result = await AIProviderService.test_provider(provider_id)
return TestResponse(**result)
@router.post("/test", response_model=TestResponse)
async def test_provider_config(request: TestConfigRequest):
"""
测试AI接口配置不保存
"""
result = await AIProviderService.test_provider_config(
provider_type=request.provider_type,
api_key=request.api_key,
base_url=request.base_url,
model=request.model,
use_proxy=request.use_proxy,
proxy_config=request.proxy_config.dict() if request.proxy_config else None,
timeout=request.timeout
)
return TestResponse(**result)
# ============ 辅助函数 ============
def _to_response(provider) -> ProviderResponse:
"""
转换为响应模型
"""
return ProviderResponse(
provider_id=provider.provider_id,
provider_type=provider.provider_type,
name=provider.name,
api_key_masked=mask_api_key(provider.api_key) if provider.api_key else "",
base_url=provider.base_url,
model=provider.model,
use_proxy=provider.use_proxy,
proxy_config=provider.proxy_config,
rate_limit=provider.rate_limit,
timeout=provider.timeout,
extra_params=provider.extra_params,
enabled=provider.enabled,
created_at=provider.created_at.isoformat(),
updated_at=provider.updated_at.isoformat()
)