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 (商汤)", "opencode_go": "OpencodeGo", "nvidia": "NVIDIA", "spark": "讯飞 Spark", "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)}