Files
trade-assistant/backend/app/api/v1/admin_ai.py
T
TradeMate Dev f17a6ccbac chore: post-deployment cleanup and docs update
- Make AI routing rules DB-driven (read from system_configs, removed from config.py)
- Add translation quota tracking to LLM translation (OpenAIProvider)
- Add Alibaba MT ECS RAM role support (STS token, no AccessKey needed)
- Fix admin sidebar link for AI模型配置 page
- Fix Quota.vue API path (quotas → translation-quotas)
- Fix login auto-redirect to dashboard
- Add provider dropdown selects to AI routing config UI
- Clean up stale ai_provider_* system_configs records
- Remove OpencodeGo, Spark providers (code + DB)
- Update deploy config: nginx port 8000, systemd cwd
2026-06-02 15:40:02 +08:00

168 lines
5.2 KiB
Python

from typing import Optional
from pydantic import BaseModel
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.database import get_db
from app.api.v1.deps import get_current_user
from app.models.ai_provider import AIProvider
from app.ai.router import get_ai_router
router = APIRouter()
async def require_admin(current_user: dict = Depends(get_current_user)) -> dict:
if current_user.get("role") != "admin":
raise HTTPException(status_code=403, detail="Admin access required")
return current_user
class AIProviderCreate(BaseModel):
name: str
provider_type: str
api_key: Optional[str] = None
api_secret: Optional[str] = None
base_url: Optional[str] = None
model_name: str = "deepseek-v4-flash"
extra_config: Optional[dict] = None
priority: int = 0
enabled: bool = True
class AIProviderUpdate(BaseModel):
name: Optional[str] = None
api_key: Optional[str] = None
api_secret: Optional[str] = None
base_url: Optional[str] = None
model_name: Optional[str] = None
extra_config: Optional[dict] = None
priority: Optional[int] = None
enabled: Optional[bool] = None
PROVIDER_TYPE_LABELS = {
"sensenova": "Sensenova (商汤)",
"nvidia": "NVIDIA",
"alibaba-mt": "阿里翻译",
}
@router.get("/ai-providers")
async def list_providers(
page: int = Query(1, ge=1),
size: int = Query(50, ge=1, le=100),
_: dict = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(AIProvider).order_by(AIProvider.priority).offset((page - 1) * size).limit(size)
)
providers = result.scalars().all()
total_result = await db.execute(select(AIProvider))
total = len(total_result.scalars().all())
return {
"items": [
{
"id": str(p.id),
"name": p.name,
"provider_type": p.provider_type,
"type_label": PROVIDER_TYPE_LABELS.get(p.provider_type, p.provider_type),
"api_key": p.api_key[:8] + "..." if p.api_key and len(p.api_key) > 8 else (p.api_key or ""),
"api_secret": bool(p.api_secret),
"base_url": p.base_url,
"model_name": p.model_name,
"extra_config": p.extra_config,
"priority": p.priority,
"enabled": p.enabled,
"created_at": p.created_at.isoformat() if p.created_at else None,
"updated_at": p.updated_at.isoformat() if p.updated_at else None,
}
for p in providers
],
"total": total,
"page": page,
"size": size,
}
@router.post("/ai-providers")
async def create_provider(
data: AIProviderCreate,
_: dict = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
provider = AIProvider(
name=data.name,
provider_type=data.provider_type,
api_key=data.api_key,
api_secret=data.api_secret,
base_url=data.base_url,
model_name=data.model_name,
extra_config=data.extra_config or {},
priority=data.priority,
enabled=data.enabled,
)
db.add(provider)
await db.commit()
await db.refresh(provider)
await get_ai_router().reload_from_db(db)
return {"id": str(provider.id), "message": "AI provider created"}
@router.put("/ai-providers/{provider_id}")
async def update_provider(
provider_id: str,
data: AIProviderUpdate,
_: dict = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(AIProvider).where(AIProvider.id == provider_id))
provider = result.scalar_one_or_none()
if not provider:
raise HTTPException(status_code=404, detail="AI provider not found")
update_data = data.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(provider, key, value)
await db.commit()
await db.refresh(provider)
await get_ai_router().reload_from_db(db)
return {"id": str(provider.id), "message": "AI provider updated"}
@router.delete("/ai-providers/{provider_id}")
async def delete_provider(
provider_id: str,
_: dict = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(AIProvider).where(AIProvider.id == provider_id))
provider = result.scalar_one_or_none()
if not provider:
raise HTTPException(status_code=404, detail="AI provider not found")
await db.delete(provider)
await db.commit()
await get_ai_router().reload_from_db(db)
return {"message": "AI provider deleted"}
@router.post("/ai-providers/reload")
async def reload_providers(
_: dict = Depends(require_admin),
db: AsyncSession = Depends(get_db),
):
count = await get_ai_router().reload_from_db(db)
return {"message": f"AI providers reloaded, {count} providers active"}
@router.get("/ai-providers/status")
async def get_provider_status(
_: dict = Depends(require_admin),
):
router = get_ai_router()
active = list(router.providers.keys())
routing = router.routing_rules
return {"active_providers": active, "routing_rules": routing, "provider_count": len(active)}