feat: 模型测试功能 - 测试模型配置是否可用
- API: POST /api/v1/models/{id}/test 发送简单提示词验证模型连通性
- 前端: 模型列表新增测试按钮(实验图标),弹窗显示结果或错误详情
- 帮助用户快速定位配置问题(如 endpoint、api_key、model 参数错误)
This commit is contained in:
parent
ba2171e13e
commit
a8604ebc34
|
|
@ -1,5 +1,6 @@
|
|||
# Storage
|
||||
storage/
|
||||
backend/.vite/
|
||||
|
||||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"hash": "9f45c68f",
|
||||
"configHash": "0e3c7f00",
|
||||
"lockfileHash": "e3b0c442",
|
||||
"browserHash": "3bfaa9a9",
|
||||
"optimized": {},
|
||||
"chunks": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"type": "module"
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ from app.core.database import get_db
|
|||
from app.core.security import encrypt_api_key
|
||||
from app.models.ai_model import AIModel
|
||||
from app.schemas.ai_model import AIModelCreate, AIModelUpdate, AIModelToggle, AIModelResponse
|
||||
from app.services.ai_adapter import call_ai_model
|
||||
|
||||
router = APIRouter(prefix="/models", tags=["AI模型管理"])
|
||||
|
||||
|
|
@ -91,3 +92,24 @@ async def toggle_model(model_id: str, data: AIModelToggle, db: AsyncSession = De
|
|||
await db.flush()
|
||||
await db.refresh(model)
|
||||
return model
|
||||
|
||||
|
||||
@router.post("/{model_id}/test")
|
||||
async def test_model(model_id: str, db: AsyncSession = Depends(get_db)):
|
||||
result = await db.execute(select(AIModel).where(AIModel.id == model_id))
|
||||
model = result.scalar_one_or_none()
|
||||
if not model:
|
||||
raise HTTPException(status_code=404, detail="模型不存在")
|
||||
|
||||
model_config = {
|
||||
"provider": model.provider,
|
||||
"endpoint": model.endpoint,
|
||||
"api_key": model.api_key,
|
||||
"extra_params": model.extra_params,
|
||||
}
|
||||
|
||||
try:
|
||||
response = await call_ai_model(model_config, "请用一句话介绍你自己。")
|
||||
return {"success": True, "result": response}
|
||||
except Exception as e:
|
||||
return {"success": False, "error": str(e)}
|
||||
|
|
|
|||
|
|
@ -29,3 +29,8 @@ export async function toggleModel(id: string, is_enabled: boolean) {
|
|||
const { data } = await request.patch<AIModel>(`/models/${id}/toggle`, { is_enabled });
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function testModel(id: string) {
|
||||
const { data } = await request.post<{ success: boolean; result?: string; error?: string }>(`/models/${id}/test`);
|
||||
return data;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
Table, Button, Modal, Form, Input, Select, Switch,
|
||||
Space, message, Popconfirm, Tag,
|
||||
} from "antd";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined } from "@ant-design/icons";
|
||||
import { PlusOutlined, EditOutlined, DeleteOutlined, ExperimentOutlined } from "@ant-design/icons";
|
||||
import type { AIModel } from "../types";
|
||||
import * as modelApi from "../api/models";
|
||||
|
||||
|
|
@ -110,6 +110,21 @@ export default function ModelList() {
|
|||
fetchModels();
|
||||
};
|
||||
|
||||
const handleTest = async (id: string, name: string) => {
|
||||
message.loading({ content: `正在测试 ${name}...`, key: "test" });
|
||||
try {
|
||||
const res = await modelApi.testModel(id);
|
||||
if (res.success) {
|
||||
message.success({ content: `${name} 测试通过`, key: "test" });
|
||||
Modal.info({ title: `测试结果 - ${name}`, content: res.result, width: 600 });
|
||||
} else {
|
||||
message.error({ content: `测试失败: ${res.error}`, key: "test", duration: 5 });
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
message.error({ content: err instanceof Error ? err.message : "测试失败", key: "test", duration: 5 });
|
||||
}
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ title: "名称", dataIndex: "name", key: "name", width: 150 },
|
||||
{
|
||||
|
|
@ -150,6 +165,12 @@ export default function ModelList() {
|
|||
width: 160,
|
||||
render: (_: unknown, record: AIModel) => (
|
||||
<Space>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
icon={<ExperimentOutlined />}
|
||||
onClick={() => handleTest(record.id, record.name)}
|
||||
/>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
|
|
|
|||
Loading…
Reference in New Issue