第一次提交
This commit is contained in:
286
backend/app/api/model_configs.py
Normal file
286
backend/app/api/model_configs.py
Normal file
@@ -0,0 +1,286 @@
|
||||
"""
|
||||
模型配置管理API
|
||||
"""
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from app.core.database import get_db
|
||||
from app.models.model_config import ModelConfig
|
||||
from app.api.auth import get_current_user
|
||||
from app.models.user import User
|
||||
from app.core.exceptions import NotFoundError, ValidationError, ConflictError
|
||||
from app.services.encryption_service import EncryptionService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/api/v1/model-configs",
|
||||
tags=["model-configs"],
|
||||
responses={
|
||||
401: {"description": "未授权"},
|
||||
404: {"description": "资源不存在"},
|
||||
400: {"description": "请求参数错误"},
|
||||
500: {"description": "服务器内部错误"}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ModelConfigCreate(BaseModel):
|
||||
"""模型配置创建模型"""
|
||||
name: str
|
||||
provider: str # openai/deepseek/anthropic/local
|
||||
model_name: str
|
||||
api_key: str
|
||||
base_url: Optional[str] = None
|
||||
|
||||
|
||||
class ModelConfigUpdate(BaseModel):
|
||||
"""模型配置更新模型"""
|
||||
name: Optional[str] = None
|
||||
provider: Optional[str] = None
|
||||
model_name: Optional[str] = None
|
||||
api_key: Optional[str] = None
|
||||
base_url: Optional[str] = None
|
||||
|
||||
|
||||
class ModelConfigResponse(BaseModel):
|
||||
"""模型配置响应模型"""
|
||||
id: str
|
||||
name: str
|
||||
provider: str
|
||||
model_name: str
|
||||
base_url: Optional[str]
|
||||
user_id: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
@router.get("", response_model=List[ModelConfigResponse])
|
||||
async def get_model_configs(
|
||||
skip: int = Query(0, ge=0, description="跳过记录数"),
|
||||
limit: int = Query(100, ge=1, le=100, description="每页记录数"),
|
||||
provider: Optional[str] = Query(None, description="提供商筛选"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
获取模型配置列表
|
||||
|
||||
支持分页和提供商筛选
|
||||
"""
|
||||
query = db.query(ModelConfig).filter(ModelConfig.user_id == current_user.id)
|
||||
|
||||
# 筛选:按提供商筛选
|
||||
if provider:
|
||||
query = query.filter(ModelConfig.provider == provider)
|
||||
|
||||
# 排序和分页
|
||||
configs = query.order_by(ModelConfig.created_at.desc()).offset(skip).limit(limit).all()
|
||||
return configs
|
||||
|
||||
|
||||
@router.post("", response_model=ModelConfigResponse, status_code=status.HTTP_201_CREATED)
|
||||
async def create_model_config(
|
||||
config_data: ModelConfigCreate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
创建模型配置
|
||||
|
||||
注意:API密钥会加密存储
|
||||
"""
|
||||
# 验证提供商
|
||||
valid_providers = ['openai', 'deepseek', 'anthropic', 'local']
|
||||
if config_data.provider not in valid_providers:
|
||||
raise ValidationError(f"不支持的提供商: {config_data.provider}")
|
||||
|
||||
# 检查名称是否重复
|
||||
existing_config = db.query(ModelConfig).filter(
|
||||
ModelConfig.name == config_data.name,
|
||||
ModelConfig.user_id == current_user.id
|
||||
).first()
|
||||
if existing_config:
|
||||
raise ConflictError(f"模型配置名称 '{config_data.name}' 已存在")
|
||||
|
||||
# 创建模型配置
|
||||
# API密钥加密存储
|
||||
encrypted_api_key = EncryptionService.encrypt(config_data.api_key)
|
||||
model_config = ModelConfig(
|
||||
name=config_data.name,
|
||||
provider=config_data.provider,
|
||||
model_name=config_data.model_name,
|
||||
api_key=encrypted_api_key,
|
||||
base_url=config_data.base_url,
|
||||
user_id=current_user.id
|
||||
)
|
||||
db.add(model_config)
|
||||
db.commit()
|
||||
db.refresh(model_config)
|
||||
|
||||
logger.info(f"用户 {current_user.username} 创建了模型配置: {model_config.name} ({model_config.id})")
|
||||
return model_config
|
||||
|
||||
|
||||
@router.get("/{config_id}", response_model=ModelConfigResponse)
|
||||
async def get_model_config(
|
||||
config_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
获取模型配置详情
|
||||
|
||||
注意:API密钥不会返回
|
||||
"""
|
||||
config = db.query(ModelConfig).filter(
|
||||
ModelConfig.id == config_id,
|
||||
ModelConfig.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not config:
|
||||
raise NotFoundError(f"模型配置不存在: {config_id}")
|
||||
|
||||
return config
|
||||
|
||||
|
||||
@router.put("/{config_id}", response_model=ModelConfigResponse)
|
||||
async def update_model_config(
|
||||
config_id: str,
|
||||
config_data: ModelConfigUpdate,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
更新模型配置
|
||||
"""
|
||||
config = db.query(ModelConfig).filter(
|
||||
ModelConfig.id == config_id,
|
||||
ModelConfig.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not config:
|
||||
raise NotFoundError(f"模型配置不存在: {config_id}")
|
||||
|
||||
# 更新字段
|
||||
if config_data.name is not None:
|
||||
# 检查名称是否重复(排除当前配置)
|
||||
existing_config = db.query(ModelConfig).filter(
|
||||
ModelConfig.name == config_data.name,
|
||||
ModelConfig.user_id == current_user.id,
|
||||
ModelConfig.id != config_id
|
||||
).first()
|
||||
if existing_config:
|
||||
raise ConflictError(f"模型配置名称 '{config_data.name}' 已存在")
|
||||
config.name = config_data.name
|
||||
|
||||
if config_data.provider is not None:
|
||||
valid_providers = ['openai', 'deepseek', 'anthropic', 'local']
|
||||
if config_data.provider not in valid_providers:
|
||||
raise ValidationError(f"不支持的提供商: {config_data.provider}")
|
||||
config.provider = config_data.provider
|
||||
|
||||
if config_data.model_name is not None:
|
||||
config.model_name = config_data.model_name
|
||||
|
||||
if config_data.api_key is not None:
|
||||
# API密钥加密存储
|
||||
config.api_key = EncryptionService.encrypt(config_data.api_key)
|
||||
|
||||
if config_data.base_url is not None:
|
||||
config.base_url = config_data.base_url
|
||||
|
||||
db.commit()
|
||||
db.refresh(config)
|
||||
|
||||
logger.info(f"用户 {current_user.username} 更新了模型配置: {config.name} ({config.id})")
|
||||
return config
|
||||
|
||||
|
||||
@router.delete("/{config_id}", status_code=status.HTTP_200_OK)
|
||||
async def delete_model_config(
|
||||
config_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
删除模型配置
|
||||
"""
|
||||
config = db.query(ModelConfig).filter(
|
||||
ModelConfig.id == config_id,
|
||||
ModelConfig.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not config:
|
||||
raise NotFoundError(f"模型配置不存在: {config_id}")
|
||||
|
||||
config_name = config.name
|
||||
db.delete(config)
|
||||
db.commit()
|
||||
|
||||
logger.info(f"用户 {current_user.username} 删除了模型配置: {config_name} ({config_id})")
|
||||
return {"message": "模型配置已删除"}
|
||||
|
||||
|
||||
@router.post("/{config_id}/test", status_code=status.HTTP_200_OK)
|
||||
async def test_model_config(
|
||||
config_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
测试模型配置连接
|
||||
|
||||
尝试调用模型API验证配置是否正确
|
||||
"""
|
||||
config = db.query(ModelConfig).filter(
|
||||
ModelConfig.id == config_id,
|
||||
ModelConfig.user_id == current_user.id
|
||||
).first()
|
||||
|
||||
if not config:
|
||||
raise NotFoundError(f"模型配置不存在: {config_id}")
|
||||
|
||||
try:
|
||||
# 根据提供商测试连接
|
||||
from app.services.llm_service import llm_service
|
||||
|
||||
# 解密API密钥用于测试
|
||||
decrypted_api_key = EncryptionService.decrypt(config.api_key)
|
||||
|
||||
if config.provider == 'openai':
|
||||
result = await llm_service.call_openai(
|
||||
prompt="test",
|
||||
model=config.model_name,
|
||||
api_key=decrypted_api_key,
|
||||
base_url=config.base_url
|
||||
)
|
||||
elif config.provider == 'deepseek':
|
||||
result = await llm_service.call_deepseek(
|
||||
prompt="test",
|
||||
model=config.model_name,
|
||||
api_key=decrypted_api_key,
|
||||
base_url=config.base_url
|
||||
)
|
||||
else:
|
||||
return {
|
||||
"status": "warning",
|
||||
"message": f"提供商 {config.provider} 的测试功能暂未实现"
|
||||
}
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "模型配置测试成功"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"模型配置测试失败: {str(e)}")
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"模型配置测试失败: {str(e)}"
|
||||
}
|
||||
Reference in New Issue
Block a user