first commit
This commit is contained in:
143
frontend/src/pages/Settings.tsx
Normal file
143
frontend/src/pages/Settings.tsx
Normal file
@@ -0,0 +1,143 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Card, Form, Input, Select, Button, Alert, Space, message } from "antd";
|
||||
import {
|
||||
api,
|
||||
type RuntimeSettings,
|
||||
getApiBaseUrl,
|
||||
setApiBaseUrl,
|
||||
} from "../services/api";
|
||||
|
||||
export default function Settings() {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [runtime, setRuntime] = useState<RuntimeSettings | null>(null);
|
||||
|
||||
const loadSettings = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const data = await api.settings.get();
|
||||
setRuntime(data);
|
||||
form.setFieldsValue({
|
||||
system_api_base_url: getApiBaseUrl(),
|
||||
llm_provider: data.llm_provider,
|
||||
custom_openai_base_url: data.base_urls?.custom_openai || "",
|
||||
custom_openai_model: data.models?.custom_openai || "gpt-4o-mini",
|
||||
});
|
||||
} catch {
|
||||
message.error("加载设置失败");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
loadSettings();
|
||||
}, []);
|
||||
|
||||
const onFinish = async (values: {
|
||||
system_api_base_url?: string;
|
||||
llm_provider: "openai" | "anthropic" | "deepseek" | "custom_openai";
|
||||
openai_api_key?: string;
|
||||
anthropic_api_key?: string;
|
||||
deepseek_api_key?: string;
|
||||
custom_openai_api_key?: string;
|
||||
custom_openai_base_url?: string;
|
||||
custom_openai_model?: string;
|
||||
}) => {
|
||||
setSaving(true);
|
||||
try {
|
||||
setApiBaseUrl(values.system_api_base_url || "");
|
||||
const payload = {
|
||||
llm_provider: values.llm_provider,
|
||||
openai_api_key: values.openai_api_key?.trim() || undefined,
|
||||
anthropic_api_key: values.anthropic_api_key?.trim() || undefined,
|
||||
deepseek_api_key: values.deepseek_api_key?.trim() || undefined,
|
||||
custom_openai_api_key: values.custom_openai_api_key?.trim() || undefined,
|
||||
custom_openai_base_url: values.custom_openai_base_url?.trim() || undefined,
|
||||
custom_openai_model: values.custom_openai_model?.trim() || undefined,
|
||||
};
|
||||
const data = await api.settings.update(payload);
|
||||
setRuntime(data);
|
||||
message.success("设置已保存并生效(含系统 API BaseURL)");
|
||||
} catch {
|
||||
message.error("保存失败");
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card title="LLM 设置" loading={loading}>
|
||||
<Alert
|
||||
type="info"
|
||||
showIcon
|
||||
style={{ marginBottom: 16 }}
|
||||
message="LLM API Key 仅在当前服务进程运行期内生效,不会自动写入磁盘。"
|
||||
/>
|
||||
<Form form={form} layout="vertical" onFinish={onFinish}>
|
||||
<Form.Item
|
||||
label="系统 API BaseURL(前端请求后端)"
|
||||
name="system_api_base_url"
|
||||
extra="默认 /api;若前后端分离部署,可填如 http://127.0.0.1:8000/api"
|
||||
>
|
||||
<Input placeholder="/api 或 http://127.0.0.1:8000/api" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="默认模型提供商"
|
||||
name="llm_provider"
|
||||
rules={[{ required: true, message: "请选择提供商" }]}
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ label: "OpenAI", value: "openai" },
|
||||
{ label: "Anthropic", value: "anthropic" },
|
||||
{ label: "DeepSeek", value: "deepseek" },
|
||||
{ label: "自定义(OpenAI兼容)", value: "custom_openai" },
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="OpenAI API Key" name="openai_api_key">
|
||||
<Input.Password placeholder="sk-..." />
|
||||
</Form.Item>
|
||||
<Form.Item label="Anthropic API Key" name="anthropic_api_key">
|
||||
<Input.Password placeholder="sk-ant-..." />
|
||||
</Form.Item>
|
||||
<Form.Item label="DeepSeek API Key" name="deepseek_api_key">
|
||||
<Input.Password placeholder="sk-..." />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label="自定义厂商 BaseURL(OpenAI兼容)"
|
||||
name="custom_openai_base_url"
|
||||
extra="例如 https://api.xxx.com/v1"
|
||||
>
|
||||
<Input placeholder="https://api.xxx.com/v1" />
|
||||
</Form.Item>
|
||||
<Form.Item label="自定义厂商 Model" name="custom_openai_model">
|
||||
<Input placeholder="gpt-4o-mini / qwen-vl-plus / ..." />
|
||||
</Form.Item>
|
||||
<Form.Item label="自定义厂商 API Key" name="custom_openai_api_key">
|
||||
<Input.Password placeholder="sk-..." />
|
||||
</Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit" loading={saving}>
|
||||
保存设置
|
||||
</Button>
|
||||
<Button onClick={loadSettings}>刷新</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
|
||||
{runtime && (
|
||||
<Card title="当前状态" size="small" style={{ marginTop: 16 }}>
|
||||
<div>系统 API BaseURL: {getApiBaseUrl()}</div>
|
||||
<div>当前提供商: {runtime.llm_provider}</div>
|
||||
<div>OpenAI Key: {runtime.has_keys.openai ? "已配置" : "未配置"}</div>
|
||||
<div>Anthropic Key: {runtime.has_keys.anthropic ? "已配置" : "未配置"}</div>
|
||||
<div>DeepSeek Key: {runtime.has_keys.deepseek ? "已配置" : "未配置"}</div>
|
||||
<div>自定义厂商 Key: {runtime.has_keys.custom_openai ? "已配置" : "未配置"}</div>
|
||||
<div>自定义厂商 BaseURL: {runtime.base_urls.custom_openai || "-"}</div>
|
||||
</Card>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user