Files
2026-07-03 11:14:24 +08:00

163 lines
5.4 KiB
Python

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
import httpx
from database import get_db
from models.ai_model import AiModel
from schemas.schemas import AiModelCreate, AiModelUpdate, Response
from services.ai_service import call_ai
from services.security import decrypt_text, encrypt_text, mask_secret
router = APIRouter()
def _is_deepseek_model(model: AiModel) -> bool:
provider = (model.provider or "").strip().lower()
return provider == "deepseek"
def _serialize_model(model: AiModel) -> dict:
api_key = decrypt_text(model.api_key_encrypted)
return {
"id": model.id,
"name": model.name,
"provider": model.provider,
"api_format": model.api_format,
"api_endpoint": model.api_endpoint,
"api_key_preview": mask_secret(api_key),
"supports_streaming": bool(model.supports_streaming),
"enable_reasoning": bool(model.enable_reasoning),
"status": model.status,
"created_at": model.created_at,
}
@router.get("")
async def list_models(db: AsyncSession = Depends(get_db)):
result = await db.execute(select(AiModel).order_by(AiModel.id.desc()))
items = [_serialize_model(item) for item in result.scalars().all()]
return Response(data=items)
@router.post("")
async def create_model(body: AiModelCreate, db: AsyncSession = Depends(get_db)):
model = AiModel(
name=body.name,
provider=body.provider,
api_format=body.api_format,
api_endpoint=body.api_endpoint,
api_key_encrypted=encrypt_text(body.api_key),
supports_streaming=body.supports_streaming,
enable_reasoning=body.enable_reasoning,
status=body.status,
)
db.add(model)
await db.commit()
await db.refresh(model)
return Response(data=_serialize_model(model))
@router.put("/{model_id}")
async def update_model(model_id: int, body: AiModelUpdate, db: AsyncSession = Depends(get_db)):
model = await db.get(AiModel, model_id)
if model is None:
raise HTTPException(status_code=404, detail="模型不存在")
if body.name is not None:
model.name = body.name
if body.provider is not None:
model.provider = body.provider
if body.api_format is not None:
model.api_format = body.api_format
if body.api_endpoint is not None:
model.api_endpoint = body.api_endpoint
if body.supports_streaming is not None:
model.supports_streaming = body.supports_streaming
if body.enable_reasoning is not None:
model.enable_reasoning = body.enable_reasoning
if body.status is not None:
model.status = body.status
if body.api_key:
model.api_key_encrypted = encrypt_text(body.api_key)
await db.commit()
await db.refresh(model)
return Response(data=_serialize_model(model))
@router.delete("/{model_id}")
async def delete_model(model_id: int, db: AsyncSession = Depends(get_db)):
model = await db.get(AiModel, model_id)
if model is None:
raise HTTPException(status_code=404, detail="模型不存在")
await db.delete(model)
await db.commit()
return Response(data={"id": model_id})
@router.post("/{model_id}/test")
async def test_model(model_id: int, db: AsyncSession = Depends(get_db)):
model = await db.get(AiModel, model_id)
if model is None:
raise HTTPException(status_code=404, detail="模型不存在")
class FakeParagraph:
title = "连接测试"
content = "请返回一段非常简短的测试文本。"
need_prompt = False
prompt_text = ""
output_format = "text"
enable_reasoning = bool(model.enable_reasoning)
try:
result = await call_ai(FakeParagraph(), model)
return Response(
data={
"id": model.id,
"success": True,
"message": f"模型 {model.name} 连接测试成功",
"preview": result.content,
}
)
except Exception as error:
return Response(
code=-1,
message=str(error),
data={"id": model.id, "success": False},
)
@router.get("/{model_id}/balance")
async def get_model_balance(model_id: int, db: AsyncSession = Depends(get_db)):
model = await db.get(AiModel, model_id)
if model is None:
raise HTTPException(status_code=404, detail="模型不存在")
if not _is_deepseek_model(model):
raise HTTPException(status_code=400, detail="仅 DeepSeek 模型支持余额查询")
api_key = decrypt_text(model.api_key_encrypted)
if not api_key:
raise HTTPException(status_code=400, detail="模型 API Key 不可用")
try:
async with httpx.AsyncClient(timeout=20, trust_env=False) as client:
response = await client.get(
"https://api.deepseek.com/user/balance",
headers={"Authorization": f"Bearer {api_key}", "Accept": "application/json"},
)
response.raise_for_status()
payload = response.json()
except Exception as error:
raise HTTPException(status_code=400, detail=f"查询余额失败:{error}")
return Response(
data={
"id": model.id,
"provider": model.provider,
"is_available": payload.get("is_available", False),
"balance_infos": payload.get("balance_infos", []),
}
)