From 363ef7d7b682709353693421e0850e56d19ee287 Mon Sep 17 00:00:00 2001 From: qyyfw Date: Fri, 10 Jul 2026 20:48:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=8C=89=E8=83=BD?= =?UTF-8?q?=E5=8A=9B=E9=BB=98=E8=AE=A4=E6=A8=A1=E5=9E=8B/=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E6=B5=8B=E8=AF=95=E7=9A=84=E8=B7=AF=E7=94=B1=E8=A2=AB?= =?UTF-8?q?=20/{id}/test-connection=20=E8=B7=AF=E5=BE=84=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=8B=A6=E6=88=AA=E5=AF=BC=E8=87=B4=20404=20=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=B8=8D=E5=AD=98=E5=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/python/app/api/admin/models.py | 260 +++++++++++++------------ 1 file changed, 132 insertions(+), 128 deletions(-) diff --git a/backend/python/app/api/admin/models.py b/backend/python/app/api/admin/models.py index bf5d039..0d345d3 100644 --- a/backend/python/app/api/admin/models.py +++ b/backend/python/app/api/admin/models.py @@ -487,6 +487,69 @@ async def create_model( return {"message": "模型已创建", "id": model.id} +# ============================================================ +# 按能力默认模型(必须定义在 /{id} 路由之前,避免被路径参数拦截) +# ============================================================ + +@router.get("/capability-defaults") +async def get_capability_defaults_api( + admin_user=Depends(get_current_admin), + db: Session = Depends(get_db), +): + """获取每个能力当前配置的默认模型,以及可选模型列表""" + defaults = get_capability_defaults(db) + models = db.query(AIModel).filter(AIModel.is_enabled == True).all() + model_list = [{ + "id": m.id, + "name": m.name, + "display_name": getattr(m, "display_name", None) or m.name, + "provider_name": ( + db.query(ModelProvider).filter(ModelProvider.id == m.provider_id).first().name + if m.provider_id else "" + ), + "capabilities": m.capabilities or [], + } for m in models] + return { + "capabilities": [ + {**c, "color": CAPABILITY_COLORS.get(c["id"], "#6B7280")} + for c in CAPABILITY_TYPES + ], + "models": model_list, + "defaults": defaults, + } + + +class CapabilityDefaultRequest(BaseModel): + capability: str + model_id: Optional[str] = None + + +@router.put("/capability-defaults") +async def set_capability_default_api( + data: CapabilityDefaultRequest, + admin_user=Depends(get_current_admin), + db: Session = Depends(get_db), +): + """为某个能力设置 / 清除默认模型""" + valid = {c["id"] for c in CAPABILITY_TYPES} + if data.capability not in valid: + raise HTTPException(status_code=400, detail=f"无效的能力类型: {data.capability}") + if data.model_id: + model = db.query(AIModel).filter(AIModel.id == data.model_id).first() + if not model: + raise HTTPException(status_code=404, detail="模型不存在") + updated = set_capability_default(db, data.capability, data.model_id) + LogService.log_audit( + admin_id=admin_user.id, + admin_name=admin_user.username, + action="set_capability_default", + target_type="model_capability", + target_id=data.capability, + detail=f"设置能力 {data.capability} 的默认模型为 {data.model_id or '(清除)'}", + ) + return {"message": "已更新能力默认模型", "defaults": updated} + + @router.put("/{id}") async def update_model( id: str, @@ -640,64 +703,9 @@ async def get_model_stats( # ============================================================ # 按能力指定默认模型(供技能 / 应用 / IM Bot 自动匹配) # ============================================================ - -@router.get("/capability-defaults") -async def get_capability_defaults_api( - admin_user=Depends(get_current_admin), - db: Session = Depends(get_db), -): - """获取每个能力当前配置的默认模型,以及可选模型列表""" - defaults = get_capability_defaults(db) - models = db.query(AIModel).filter(AIModel.is_enabled == True).all() - model_list = [{ - "id": m.id, - "name": m.name, - "display_name": getattr(m, "display_name", None) or m.name, - "provider_name": ( - db.query(ModelProvider).filter(ModelProvider.id == m.provider_id).first().name - if m.provider_id else "" - ), - "capabilities": m.capabilities or [], - } for m in models] - return { - "capabilities": [ - {**c, "color": CAPABILITY_COLORS.get(c["id"], "#6B7280")} - for c in CAPABILITY_TYPES - ], - "models": model_list, - "defaults": defaults, - } +# (按能力默认模型路由已移至 /{id} 路由之前,避免路径参数冲突) -class CapabilityDefaultRequest(BaseModel): - capability: str - model_id: Optional[str] = None - - -@router.put("/capability-defaults") -async def set_capability_default_api( - data: CapabilityDefaultRequest, - admin_user=Depends(get_current_admin), - db: Session = Depends(get_db), -): - """为某个能力设置 / 清除默认模型""" - valid = {c["id"] for c in CAPABILITY_TYPES} - if data.capability not in valid: - raise HTTPException(status_code=400, detail=f"无效的能力类型: {data.capability}") - if data.model_id: - model = db.query(AIModel).filter(AIModel.id == data.model_id).first() - if not model: - raise HTTPException(status_code=404, detail="模型不存在") - updated = set_capability_default(db, data.capability, data.model_id) - LogService.log_audit( - admin_id=admin_user.id, - admin_name=admin_user.username, - action="set_capability_default", - target_type="model_capability", - target_id=data.capability, - detail=f"设置能力 {data.capability} 的默认模型为 {data.model_id or '(清除)'}", - ) - return {"message": "已更新能力默认模型", "defaults": updated} # ============================================================ @@ -752,6 +760,73 @@ def _build_api_base(provider, ai_model): return api_key, base_url +# ============================================================ +# 批量连接测试(必须定义在 /{model_id}/test-connection 之前,避免被路径参数拦截) +# ============================================================ + +@router.post("/test-all") +async def test_all_models( + admin_user=Depends(get_current_admin), + db: Session = Depends(get_db), +): + """批量测试所有已启用模型的连接状态""" + import asyncio + + models = db.query(AIModel).filter(AIModel.is_enabled == True).all() + results = [] + + for m in models: + provider = db.query(ModelProvider).filter(ModelProvider.id == m.provider_id).first() + if not provider: + results.append({ + "model_id": m.id, + "model_name": m.name, + "display_name": m.display_name or m.name, + "success": False, + "message": "供应商不存在", + "latency_ms": None, + "model_type": "unknown", + }) + continue + + # 复用 test_model_connection 逻辑 + try: + result = await test_model_connection(m.id, admin_user, db) + if isinstance(result, dict): + result["model_id"] = m.id + result["model_name"] = m.name + result["display_name"] = m.display_name or m.name + results.append(result) + else: + results.append({ + "model_id": m.id, + "model_name": m.name, + "display_name": m.display_name or m.name, + "success": False, + "message": "测试返回异常", + "latency_ms": None, + "model_type": "unknown", + }) + except Exception as e: + results.append({ + "model_id": m.id, + "model_name": m.name, + "display_name": m.display_name or m.name, + "success": False, + "message": f"测试异常: {str(e)}", + "latency_ms": None, + "model_type": "unknown", + }) + + success_count = sum(1 for r in results if r.get("success")) + return { + "total": len(results), + "success": success_count, + "failed": len(results) - success_count, + "results": results, + } + + @router.post("/{model_id}/test-connection") async def test_model_connection( model_id: str, @@ -1104,76 +1179,8 @@ def _build_error_response(resp, latency, model_type): } -@router.post("/test-all") -async def test_all_models( - admin_user=Depends(get_current_admin), - db: Session = Depends(get_db), -): - """批量测试所有已启用模型的连接状态""" - import asyncio - - models = db.query(AIModel).filter(AIModel.is_enabled == True).all() - results = [] - - for m in models: - provider = db.query(ModelProvider).filter(ModelProvider.id == m.provider_id).first() - if not provider: - results.append({ - "model_id": m.id, - "model_name": m.name, - "display_name": m.display_name or m.name, - "success": False, - "message": "供应商不存在", - "latency_ms": None, - "model_type": "unknown", - }) - continue - - # 复用 test_model_connection 逻辑 - try: - result = await test_model_connection(m.id, admin_user, db) - if isinstance(result, dict): - result["model_id"] = m.id - result["model_name"] = m.name - result["display_name"] = m.display_name or m.name - results.append(result) - else: - results.append({ - "model_id": m.id, - "model_name": m.name, - "display_name": m.display_name or m.name, - "success": False, - "message": "测试返回异常", - "latency_ms": None, - "model_type": "unknown", - }) - except Exception as e: - results.append({ - "model_id": m.id, - "model_name": m.name, - "display_name": m.display_name or m.name, - "success": False, - "message": f"测试异常: {str(e)}", - "latency_ms": None, - "model_type": "unknown", - }) - - success_count = sum(1 for r in results if r.get("success")) - return { - "total": len(results), - "success": success_count, - "failed": len(results) - success_count, - "results": results, - } - - - timeout: int = 60 - status: str = "paid" - price_per_million_tokens: float = 0.0 - is_enabled: bool = True - - class UpdateModelRequest(BaseModel): + """更新模型请求(保留兼容,实际更新使用 ModelUpdateRequest)""" name: Optional[str] = None api_key: Optional[str] = None endpoint: Optional[str] = None @@ -1183,6 +1190,3 @@ class UpdateModelRequest(BaseModel): price_per_million_tokens: Optional[float] = None is_enabled: Optional[bool] = None - -from typing import Optional -