2026-03-09 08:10:48 +00:00
|
|
|
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
|
|
|
import {
|
2026-03-12 12:39:49 +00:00
|
|
|
AutoComplete,
|
2026-03-09 08:10:48 +00:00
|
|
|
Button,
|
|
|
|
|
Card,
|
|
|
|
|
Col,
|
|
|
|
|
Divider,
|
|
|
|
|
Drawer,
|
|
|
|
|
Form,
|
|
|
|
|
Input,
|
|
|
|
|
InputNumber,
|
|
|
|
|
Popconfirm,
|
|
|
|
|
Row,
|
|
|
|
|
Select,
|
|
|
|
|
Space,
|
|
|
|
|
Switch,
|
|
|
|
|
Table,
|
|
|
|
|
Tabs,
|
|
|
|
|
Tag,
|
|
|
|
|
Tooltip,
|
|
|
|
|
Typography,
|
|
|
|
|
message,
|
|
|
|
|
} from "antd";
|
|
|
|
|
import {
|
|
|
|
|
DeleteOutlined,
|
|
|
|
|
EditOutlined,
|
|
|
|
|
PlusOutlined,
|
|
|
|
|
SafetyCertificateOutlined,
|
|
|
|
|
SaveOutlined,
|
|
|
|
|
SearchOutlined,
|
|
|
|
|
SyncOutlined,
|
|
|
|
|
} from "@ant-design/icons";
|
|
|
|
|
import { useDict } from "../../hooks/useDict";
|
|
|
|
|
import {
|
|
|
|
|
AiModelDTO,
|
2026-03-02 11:59:47 +00:00
|
|
|
AiModelVO,
|
2026-03-09 08:10:48 +00:00
|
|
|
deleteAiModelByType,
|
|
|
|
|
getAiModelPage,
|
|
|
|
|
getRemoteModelList,
|
|
|
|
|
saveAiModel,
|
|
|
|
|
updateAiModel,
|
|
|
|
|
} from "../../api/business/aimodel";
|
2026-03-02 11:59:47 +00:00
|
|
|
|
|
|
|
|
const { Option } = Select;
|
2026-03-09 08:10:48 +00:00
|
|
|
const { Title } = Typography;
|
|
|
|
|
|
|
|
|
|
type ModelType = "ASR" | "LLM";
|
|
|
|
|
|
|
|
|
|
const PROVIDER_BASE_URL_MAP: Record<string, string> = {
|
|
|
|
|
openai: "https://api.openai.com/v1",
|
|
|
|
|
deepseek: "https://api.deepseek.com",
|
|
|
|
|
aliyun: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
|
|
|
qwen: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
|
|
|
dashscope: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
|
|
|
moonshot: "https://api.moonshot.cn/v1",
|
|
|
|
|
kimi: "https://api.moonshot.cn/v1",
|
|
|
|
|
groq: "https://api.groq.com/openai/v1",
|
|
|
|
|
};
|
2026-03-02 11:59:47 +00:00
|
|
|
|
|
|
|
|
const AiModels: React.FC = () => {
|
|
|
|
|
const [form] = Form.useForm();
|
2026-03-09 08:10:48 +00:00
|
|
|
const { items: providers } = useDict("biz_ai_provider");
|
|
|
|
|
|
|
|
|
|
const [activeType, setActiveType] = useState<ModelType>("ASR");
|
2026-03-02 11:59:47 +00:00
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
const [data, setData] = useState<AiModelVO[]>([]);
|
|
|
|
|
const [total, setTotal] = useState(0);
|
|
|
|
|
const [current, setCurrent] = useState(1);
|
|
|
|
|
const [size, setSize] = useState(10);
|
2026-03-09 08:10:48 +00:00
|
|
|
const [searchName, setSearchName] = useState("");
|
|
|
|
|
|
2026-03-02 11:59:47 +00:00
|
|
|
const [drawerVisible, setDrawerVisible] = useState(false);
|
|
|
|
|
const [editingId, setEditingId] = useState<number | null>(null);
|
|
|
|
|
const [submitLoading, setSubmitLoading] = useState(false);
|
|
|
|
|
const [fetchLoading, setFetchLoading] = useState(false);
|
|
|
|
|
const [remoteModels, setRemoteModels] = useState<string[]>([]);
|
2026-03-09 08:10:48 +00:00
|
|
|
const modelNameAutoFilledRef = useRef(false);
|
2026-03-02 11:59:47 +00:00
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
const provider = Form.useWatch("provider", form);
|
|
|
|
|
|
|
|
|
|
const isPlatformAdmin = useMemo(() => {
|
2026-03-02 11:59:47 +00:00
|
|
|
const profileStr = sessionStorage.getItem("userProfile");
|
2026-03-09 08:10:48 +00:00
|
|
|
if (!profileStr) {
|
|
|
|
|
return false;
|
2026-03-02 11:59:47 +00:00
|
|
|
}
|
2026-03-12 12:39:49 +00:00
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
const profile = JSON.parse(profileStr);
|
|
|
|
|
return profile.isPlatformAdmin === true;
|
2026-03-02 11:59:47 +00:00
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-03-12 12:39:49 +00:00
|
|
|
void fetchData();
|
2026-03-09 08:10:48 +00:00
|
|
|
}, [current, size, searchName, activeType]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!drawerVisible || !provider) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-03-12 12:39:49 +00:00
|
|
|
|
|
|
|
|
const providerItem = providers.find((item) => item.itemValue === provider);
|
2026-03-09 08:10:48 +00:00
|
|
|
const providerLabel = providerItem?.itemLabel || provider;
|
|
|
|
|
const currentDisplayName = form.getFieldValue("modelName");
|
|
|
|
|
if (!editingId && (!currentDisplayName || modelNameAutoFilledRef.current)) {
|
|
|
|
|
form.setFieldValue("modelName", providerLabel);
|
|
|
|
|
modelNameAutoFilledRef.current = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const baseUrl = form.getFieldValue("baseUrl");
|
|
|
|
|
const providerKey = String(provider).toLowerCase();
|
|
|
|
|
const defaultBaseUrl = PROVIDER_BASE_URL_MAP[providerKey];
|
|
|
|
|
if (!baseUrl && defaultBaseUrl) {
|
|
|
|
|
form.setFieldValue("baseUrl", defaultBaseUrl);
|
|
|
|
|
}
|
|
|
|
|
}, [provider, drawerVisible, editingId, providers, form]);
|
2026-03-02 11:59:47 +00:00
|
|
|
|
|
|
|
|
const fetchData = async () => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
try {
|
2026-03-09 08:10:48 +00:00
|
|
|
const res = await getAiModelPage({
|
|
|
|
|
current,
|
|
|
|
|
size,
|
|
|
|
|
name: searchName || undefined,
|
|
|
|
|
type: activeType,
|
|
|
|
|
});
|
|
|
|
|
const pageData = (res as any)?.data?.data ?? (res as any);
|
|
|
|
|
setData(pageData?.records || []);
|
|
|
|
|
setTotal(pageData?.total || 0);
|
2026-03-02 11:59:47 +00:00
|
|
|
} finally {
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
const openDrawer = (record?: AiModelVO) => {
|
2026-03-02 11:59:47 +00:00
|
|
|
setRemoteModels([]);
|
2026-03-09 08:10:48 +00:00
|
|
|
modelNameAutoFilledRef.current = false;
|
2026-03-12 12:39:49 +00:00
|
|
|
|
2026-03-02 11:59:47 +00:00
|
|
|
if (record) {
|
|
|
|
|
setEditingId(record.id);
|
2026-03-09 08:10:48 +00:00
|
|
|
form.setFieldsValue({
|
|
|
|
|
...record,
|
|
|
|
|
modelType: record.modelType,
|
|
|
|
|
isDefaultChecked: record.isDefault === 1,
|
|
|
|
|
statusChecked: record.status === 1,
|
|
|
|
|
});
|
|
|
|
|
if (record.modelCode) {
|
|
|
|
|
setRemoteModels([record.modelCode]);
|
|
|
|
|
}
|
2026-03-02 11:59:47 +00:00
|
|
|
} else {
|
|
|
|
|
setEditingId(null);
|
|
|
|
|
form.resetFields();
|
2026-03-09 08:10:48 +00:00
|
|
|
form.setFieldsValue({
|
|
|
|
|
modelType: activeType,
|
|
|
|
|
isDefaultChecked: false,
|
|
|
|
|
statusChecked: true,
|
|
|
|
|
temperature: 0.7,
|
|
|
|
|
topP: 0.9,
|
|
|
|
|
apiPath: "/v1/chat/completions",
|
|
|
|
|
});
|
2026-03-02 11:59:47 +00:00
|
|
|
}
|
2026-03-12 12:39:49 +00:00
|
|
|
|
2026-03-02 11:59:47 +00:00
|
|
|
setDrawerVisible(true);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleFetchRemote = async () => {
|
2026-03-12 12:39:49 +00:00
|
|
|
const values = form.getFieldsValue(["provider", "baseUrl", "apiKey"]);
|
|
|
|
|
if (!values.provider || !values.baseUrl) {
|
2026-03-09 08:10:48 +00:00
|
|
|
message.warning("请先填写提供商和 Base URL");
|
2026-03-02 11:59:47 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2026-03-12 12:39:49 +00:00
|
|
|
|
2026-03-02 11:59:47 +00:00
|
|
|
setFetchLoading(true);
|
|
|
|
|
try {
|
2026-03-12 12:39:49 +00:00
|
|
|
const res = await getRemoteModelList(values);
|
2026-03-09 08:10:48 +00:00
|
|
|
const rawModels = (res as any)?.data?.data ?? (Array.isArray(res) ? res : []);
|
|
|
|
|
const models = Array.isArray(rawModels) ? rawModels : [];
|
|
|
|
|
setRemoteModels(models);
|
|
|
|
|
message.success(`获取到 ${models.length} 个模型`);
|
2026-03-02 11:59:47 +00:00
|
|
|
} finally {
|
|
|
|
|
setFetchLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleSubmit = async () => {
|
2026-03-09 08:10:48 +00:00
|
|
|
const values = await form.validateFields();
|
|
|
|
|
const payload: AiModelDTO = {
|
|
|
|
|
id: editingId ?? undefined,
|
|
|
|
|
modelType: values.modelType,
|
|
|
|
|
modelName: values.modelName,
|
|
|
|
|
provider: values.provider,
|
|
|
|
|
baseUrl: values.baseUrl,
|
|
|
|
|
apiPath: values.apiPath,
|
|
|
|
|
apiKey: values.apiKey,
|
|
|
|
|
modelCode: values.modelCode,
|
|
|
|
|
wsUrl: values.wsUrl,
|
|
|
|
|
temperature: values.temperature,
|
|
|
|
|
topP: values.topP,
|
|
|
|
|
isDefault: values.isDefaultChecked ? 1 : 0,
|
|
|
|
|
status: values.statusChecked ? 1 : 0,
|
|
|
|
|
remark: values.remark,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setSubmitLoading(true);
|
2026-03-02 11:59:47 +00:00
|
|
|
try {
|
|
|
|
|
if (editingId) {
|
2026-03-09 08:10:48 +00:00
|
|
|
await updateAiModel(payload);
|
|
|
|
|
message.success("更新成功");
|
2026-03-02 11:59:47 +00:00
|
|
|
} else {
|
2026-03-09 08:10:48 +00:00
|
|
|
await saveAiModel(payload);
|
|
|
|
|
message.success("新增成功");
|
2026-03-02 11:59:47 +00:00
|
|
|
}
|
|
|
|
|
setDrawerVisible(false);
|
2026-03-12 12:39:49 +00:00
|
|
|
void fetchData();
|
2026-03-02 11:59:47 +00:00
|
|
|
} finally {
|
|
|
|
|
setSubmitLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
const handleDelete = async (record: AiModelVO) => {
|
|
|
|
|
await deleteAiModelByType(record.id, record.modelType);
|
|
|
|
|
message.success("删除成功");
|
2026-03-12 12:39:49 +00:00
|
|
|
void fetchData();
|
2026-03-09 08:10:48 +00:00
|
|
|
};
|
|
|
|
|
|
2026-03-02 11:59:47 +00:00
|
|
|
const columns = [
|
|
|
|
|
{
|
2026-03-09 08:10:48 +00:00
|
|
|
title: "模型名称",
|
|
|
|
|
dataIndex: "modelName",
|
|
|
|
|
key: "modelName",
|
2026-03-02 11:59:47 +00:00
|
|
|
render: (text: string, record: AiModelVO) => (
|
|
|
|
|
<Space>
|
|
|
|
|
{text}
|
|
|
|
|
{record.isDefault === 1 && <Tag color="gold">默认</Tag>}
|
2026-03-09 08:10:48 +00:00
|
|
|
{record.tenantId === 0 && (
|
|
|
|
|
<Tooltip title="系统预置">
|
|
|
|
|
<SafetyCertificateOutlined style={{ color: "#52c41a" }} />
|
|
|
|
|
</Tooltip>
|
|
|
|
|
)}
|
2026-03-02 11:59:47 +00:00
|
|
|
</Space>
|
2026-03-09 08:10:48 +00:00
|
|
|
),
|
2026-03-02 11:59:47 +00:00
|
|
|
},
|
|
|
|
|
{
|
2026-03-09 08:10:48 +00:00
|
|
|
title: "提供商",
|
|
|
|
|
dataIndex: "provider",
|
|
|
|
|
key: "provider",
|
2026-03-12 12:39:49 +00:00
|
|
|
render: (value: string) => {
|
|
|
|
|
const item = providers.find((providerItem) => providerItem.itemValue === value);
|
|
|
|
|
return item ? <Tag>{item.itemLabel}</Tag> : value;
|
2026-03-09 08:10:48 +00:00
|
|
|
},
|
2026-03-02 11:59:47 +00:00
|
|
|
},
|
2026-03-09 08:10:48 +00:00
|
|
|
{ title: "模型名称(code)", dataIndex: "modelCode", key: "modelCode" },
|
2026-03-02 11:59:47 +00:00
|
|
|
{
|
2026-03-09 08:10:48 +00:00
|
|
|
title: "状态",
|
|
|
|
|
dataIndex: "status",
|
|
|
|
|
key: "status",
|
|
|
|
|
render: (status: number) =>
|
|
|
|
|
status === 1 ? <Tag color="green">启用</Tag> : <Tag>禁用</Tag>,
|
2026-03-02 11:59:47 +00:00
|
|
|
},
|
|
|
|
|
{
|
2026-03-09 08:10:48 +00:00
|
|
|
title: "操作",
|
|
|
|
|
key: "action",
|
|
|
|
|
render: (_: unknown, record: AiModelVO) => {
|
2026-03-02 11:59:47 +00:00
|
|
|
const canEdit = record.tenantId !== 0 || isPlatformAdmin;
|
|
|
|
|
return (
|
2026-03-09 08:10:48 +00:00
|
|
|
<Space>
|
2026-03-02 11:59:47 +00:00
|
|
|
{canEdit && (
|
2026-03-09 08:10:48 +00:00
|
|
|
<Button type="link" icon={<EditOutlined />} onClick={() => openDrawer(record)}>
|
|
|
|
|
编辑
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
{canEdit && (
|
|
|
|
|
<Popconfirm title="确定删除吗?" onConfirm={() => handleDelete(record)}>
|
|
|
|
|
<Button type="link" danger icon={<DeleteOutlined />}>
|
|
|
|
|
删除
|
|
|
|
|
</Button>
|
2026-03-02 11:59:47 +00:00
|
|
|
</Popconfirm>
|
|
|
|
|
)}
|
|
|
|
|
</Space>
|
|
|
|
|
);
|
2026-03-09 08:10:48 +00:00
|
|
|
},
|
|
|
|
|
},
|
2026-03-02 11:59:47 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return (
|
2026-03-09 08:10:48 +00:00
|
|
|
<div style={{ padding: 24 }}>
|
|
|
|
|
<Card
|
|
|
|
|
title="AI 模型配置"
|
|
|
|
|
extra={
|
|
|
|
|
<Space>
|
|
|
|
|
<Input
|
|
|
|
|
placeholder="搜索模型名称"
|
|
|
|
|
prefix={<SearchOutlined />}
|
|
|
|
|
allowClear
|
2026-03-12 12:39:49 +00:00
|
|
|
onPressEnter={(event) => setSearchName((event.target as HTMLInputElement).value)}
|
2026-03-09 08:10:48 +00:00
|
|
|
style={{ width: 220 }}
|
|
|
|
|
/>
|
|
|
|
|
<Button type="primary" icon={<PlusOutlined />} onClick={() => openDrawer()}>
|
|
|
|
|
新增模型
|
|
|
|
|
</Button>
|
|
|
|
|
</Space>
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<Tabs
|
|
|
|
|
activeKey={activeType}
|
|
|
|
|
onChange={(key) => {
|
|
|
|
|
setActiveType(key as ModelType);
|
|
|
|
|
setCurrent(1);
|
|
|
|
|
}}
|
|
|
|
|
items={[
|
|
|
|
|
{ key: "ASR", label: "ASR 模型" },
|
|
|
|
|
{ key: "LLM", label: "LLM 模型" },
|
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<Table
|
|
|
|
|
rowKey="id"
|
|
|
|
|
columns={columns}
|
|
|
|
|
dataSource={data}
|
|
|
|
|
loading={loading}
|
|
|
|
|
pagination={{
|
|
|
|
|
current,
|
|
|
|
|
pageSize: size,
|
|
|
|
|
total,
|
2026-03-12 12:39:49 +00:00
|
|
|
onChange: (page, pageSize) => {
|
|
|
|
|
setCurrent(page);
|
|
|
|
|
setSize(pageSize);
|
2026-03-09 08:10:48 +00:00
|
|
|
},
|
|
|
|
|
}}
|
2026-03-02 11:59:47 +00:00
|
|
|
/>
|
|
|
|
|
</Card>
|
|
|
|
|
|
|
|
|
|
<Drawer
|
|
|
|
|
width={600}
|
|
|
|
|
open={drawerVisible}
|
2026-03-09 08:10:48 +00:00
|
|
|
onClose={() => setDrawerVisible(false)}
|
|
|
|
|
title={<Title level={4} style={{ margin: 0 }}>{editingId ? "编辑模型" : "新增模型"}</Title>}
|
2026-03-02 11:59:47 +00:00
|
|
|
extra={
|
|
|
|
|
<Space>
|
|
|
|
|
<Button onClick={() => setDrawerVisible(false)}>取消</Button>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Button type="primary" icon={<SaveOutlined />} loading={submitLoading} onClick={handleSubmit}>
|
|
|
|
|
保存
|
|
|
|
|
</Button>
|
2026-03-02 11:59:47 +00:00
|
|
|
</Space>
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<Form form={form} layout="vertical">
|
2026-03-09 08:10:48 +00:00
|
|
|
<Form.Item name="modelType" hidden>
|
|
|
|
|
<Input />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
|
|
<Form.Item label="模型类型">
|
|
|
|
|
<Tag color={activeType === "ASR" ? "blue" : "purple"}>
|
|
|
|
|
{activeType === "ASR" ? "语音识别 (ASR)" : "总结模型 (LLM)"}
|
|
|
|
|
</Tag>
|
2026-03-02 11:59:47 +00:00
|
|
|
</Form.Item>
|
|
|
|
|
|
|
|
|
|
<Row gutter={16}>
|
|
|
|
|
<Col span={12}>
|
2026-03-12 12:39:49 +00:00
|
|
|
<Form.Item
|
|
|
|
|
name="modelName"
|
|
|
|
|
label="显示名称"
|
|
|
|
|
rules={[{ required: true, message: "请输入显示名称" }]}
|
|
|
|
|
>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Input
|
|
|
|
|
onChange={() => {
|
|
|
|
|
modelNameAutoFilledRef.current = false;
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2026-03-02 11:59:47 +00:00
|
|
|
</Form.Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
2026-03-12 12:39:49 +00:00
|
|
|
<Form.Item
|
|
|
|
|
name="provider"
|
|
|
|
|
label="提供商"
|
|
|
|
|
rules={[{ required: true, message: "请选择提供商" }]}
|
|
|
|
|
>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Select allowClear placeholder="请选择">
|
|
|
|
|
{providers.map((item) => (
|
|
|
|
|
<Option key={item.itemValue} value={item.itemValue}>
|
|
|
|
|
{item.itemLabel}
|
|
|
|
|
</Option>
|
2026-03-02 11:59:47 +00:00
|
|
|
))}
|
|
|
|
|
</Select>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
<Form.Item name="baseUrl" label="Base URL" rules={[{ required: true, message: "请输入 Base URL" }]}>
|
2026-03-02 11:59:47 +00:00
|
|
|
<Input placeholder="https://api.example.com/v1" />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
{!(activeType === "ASR" && provider === "Custom") && (
|
|
|
|
|
<Form.Item name="apiKey" label="API Key">
|
|
|
|
|
<Input.Password />
|
2026-03-04 07:19:40 +00:00
|
|
|
</Form.Item>
|
|
|
|
|
)}
|
2026-03-02 11:59:47 +00:00
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
<Divider orientation="left" style={{ fontSize: 14, color: "#999" }}>
|
|
|
|
|
模型参数
|
|
|
|
|
</Divider>
|
|
|
|
|
|
2026-03-12 12:39:49 +00:00
|
|
|
<Form.Item
|
|
|
|
|
label="模型名称"
|
|
|
|
|
required
|
|
|
|
|
tooltip="可从远程列表选择,也可手动输入;值将作为模型 code 传给后端"
|
|
|
|
|
>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Space.Compact style={{ width: "100%" }}>
|
2026-03-12 12:39:49 +00:00
|
|
|
<Form.Item
|
|
|
|
|
name="modelCode"
|
|
|
|
|
noStyle
|
|
|
|
|
rules={[{ required: true, message: "请输入或选择模型名称" }]}
|
|
|
|
|
>
|
|
|
|
|
<AutoComplete
|
2026-03-09 08:10:48 +00:00
|
|
|
allowClear
|
|
|
|
|
style={{ width: "calc(100% - 100px)" }}
|
2026-03-12 12:39:49 +00:00
|
|
|
placeholder="可选择或自定义输入模型名称"
|
|
|
|
|
options={remoteModels.map((model) => ({ value: model }))}
|
|
|
|
|
filterOption={(inputValue, option) =>
|
|
|
|
|
String(option?.value || "").toLowerCase().includes(inputValue.toLowerCase())
|
|
|
|
|
}
|
2026-03-02 11:59:47 +00:00
|
|
|
>
|
2026-03-12 12:39:49 +00:00
|
|
|
<Input />
|
|
|
|
|
</AutoComplete>
|
2026-03-02 11:59:47 +00:00
|
|
|
</Form.Item>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Button icon={<SyncOutlined spin={fetchLoading} />} onClick={handleFetchRemote} style={{ width: 100 }}>
|
|
|
|
|
刷新
|
|
|
|
|
</Button>
|
2026-03-02 11:59:47 +00:00
|
|
|
</Space.Compact>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
{activeType === "ASR" && (
|
|
|
|
|
<Form.Item name="wsUrl" label="WebSocket 地址">
|
2026-03-02 11:59:47 +00:00
|
|
|
<Input placeholder="wss://api.example.com/v1/ws" />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
{activeType === "LLM" && (
|
2026-03-02 11:59:47 +00:00
|
|
|
<>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Form.Item name="apiPath" label="API 路径" initialValue="/v1/chat/completions">
|
|
|
|
|
<Input />
|
2026-03-02 11:59:47 +00:00
|
|
|
</Form.Item>
|
|
|
|
|
<Row gutter={16}>
|
|
|
|
|
<Col span={12}>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Form.Item name="temperature" label="Temperature">
|
|
|
|
|
<InputNumber min={0} max={2} step={0.1} style={{ width: "100%" }} />
|
2026-03-02 11:59:47 +00:00
|
|
|
</Form.Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Form.Item name="topP" label="Top P">
|
|
|
|
|
<InputNumber min={0} max={1} step={0.1} style={{ width: "100%" }} />
|
2026-03-02 11:59:47 +00:00
|
|
|
</Form.Item>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<Row gutter={16}>
|
|
|
|
|
<Col span={8}>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Form.Item name="isDefaultChecked" label="设为默认" valuePropName="checked">
|
|
|
|
|
<Switch checkedChildren="是" unCheckedChildren="否" />
|
2026-03-02 11:59:47 +00:00
|
|
|
</Form.Item>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={8}>
|
2026-03-09 08:10:48 +00:00
|
|
|
<Form.Item name="statusChecked" label="状态" valuePropName="checked">
|
|
|
|
|
<Switch checkedChildren="启用" unCheckedChildren="禁用" />
|
2026-03-02 11:59:47 +00:00
|
|
|
</Form.Item>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
|
2026-03-09 08:10:48 +00:00
|
|
|
<Form.Item name="remark" label="备注">
|
2026-03-02 11:59:47 +00:00
|
|
|
<Input.TextArea rows={2} />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
</Form>
|
|
|
|
|
</Drawer>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default AiModels;
|