style(web): format code
This commit is contained in:
@@ -5,23 +5,27 @@ import {
|
||||
API,
|
||||
showError,
|
||||
showSuccess,
|
||||
showWarning, verifyJSON
|
||||
showWarning,
|
||||
verifyJSON,
|
||||
} from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Text from '@douyinfe/semi-ui/lib/es/typography/text';
|
||||
|
||||
const CLAUDE_HEADER = {
|
||||
'claude-3-7-sonnet-20250219-thinking': {
|
||||
'anthropic-beta': ['output-128k-2025-02-19', 'token-efficient-tools-2025-02-19'],
|
||||
}
|
||||
'anthropic-beta': [
|
||||
'output-128k-2025-02-19',
|
||||
'token-efficient-tools-2025-02-19',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const CLAUDE_DEFAULT_MAX_TOKENS = {
|
||||
'default': 8192,
|
||||
"claude-3-haiku-20240307": 4096,
|
||||
"claude-3-opus-20240229": 4096,
|
||||
default: 8192,
|
||||
'claude-3-haiku-20240307': 4096,
|
||||
'claude-3-opus-20240229': 4096,
|
||||
'claude-3-7-sonnet-20250219-thinking': 8192,
|
||||
}
|
||||
};
|
||||
|
||||
export default function SettingClaudeModel(props) {
|
||||
const { t } = useTranslation();
|
||||
@@ -41,7 +45,7 @@ export default function SettingClaudeModel(props) {
|
||||
if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
|
||||
const requestQueue = updateArray.map((item) => {
|
||||
let value = String(inputs[item.key]);
|
||||
|
||||
|
||||
return API.put('/api/option/', {
|
||||
key: item.key,
|
||||
value,
|
||||
@@ -53,7 +57,8 @@ export default function SettingClaudeModel(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
@@ -92,18 +97,29 @@ export default function SettingClaudeModel(props) {
|
||||
<Form.TextArea
|
||||
label={t('Claude请求头覆盖')}
|
||||
field={'claude.model_headers_settings'}
|
||||
placeholder={t('为一个 JSON 文本,例如:') + '\n' + JSON.stringify(CLAUDE_HEADER, null, 2)}
|
||||
extraText={t('示例') + '\n' + JSON.stringify(CLAUDE_HEADER, null, 2)}
|
||||
placeholder={
|
||||
t('为一个 JSON 文本,例如:') +
|
||||
'\n' +
|
||||
JSON.stringify(CLAUDE_HEADER, null, 2)
|
||||
}
|
||||
extraText={
|
||||
t('示例') + '\n' + JSON.stringify(CLAUDE_HEADER, null, 2)
|
||||
}
|
||||
autosize={{ minRows: 6, maxRows: 12 }}
|
||||
trigger='blur'
|
||||
stopValidateWithError
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: t('不是合法的 JSON 字符串')
|
||||
}
|
||||
message: t('不是合法的 JSON 字符串'),
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, 'claude.model_headers_settings': value })}
|
||||
onChange={(value) =>
|
||||
setInputs({
|
||||
...inputs,
|
||||
'claude.model_headers_settings': value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -112,18 +128,28 @@ export default function SettingClaudeModel(props) {
|
||||
<Form.TextArea
|
||||
label={t('缺省 MaxTokens')}
|
||||
field={'claude.default_max_tokens'}
|
||||
placeholder={t('为一个 JSON 文本,例如:') + '\n' + JSON.stringify(CLAUDE_DEFAULT_MAX_TOKENS, null, 2)}
|
||||
extraText={t('示例') + '\n' + JSON.stringify(CLAUDE_DEFAULT_MAX_TOKENS, null, 2)}
|
||||
placeholder={
|
||||
t('为一个 JSON 文本,例如:') +
|
||||
'\n' +
|
||||
JSON.stringify(CLAUDE_DEFAULT_MAX_TOKENS, null, 2)
|
||||
}
|
||||
extraText={
|
||||
t('示例') +
|
||||
'\n' +
|
||||
JSON.stringify(CLAUDE_DEFAULT_MAX_TOKENS, null, 2)
|
||||
}
|
||||
autosize={{ minRows: 6, maxRows: 12 }}
|
||||
trigger='blur'
|
||||
stopValidateWithError
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: t('不是合法的 JSON 字符串')
|
||||
}
|
||||
message: t('不是合法的 JSON 字符串'),
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, 'claude.default_max_tokens': value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, 'claude.default_max_tokens': value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -132,7 +158,12 @@ export default function SettingClaudeModel(props) {
|
||||
<Form.Switch
|
||||
label={t('启用Claude思考适配(-thinking后缀)')}
|
||||
field={'claude.thinking_adapter_enabled'}
|
||||
onChange={(value) => setInputs({ ...inputs, 'claude.thinking_adapter_enabled': value })}
|
||||
onChange={(value) =>
|
||||
setInputs({
|
||||
...inputs,
|
||||
'claude.thinking_adapter_enabled': value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -140,7 +171,9 @@ export default function SettingClaudeModel(props) {
|
||||
<Col span={16}>
|
||||
{/*//展示MaxTokens和BudgetTokens的计算公式, 并展示实际数字*/}
|
||||
<Text>
|
||||
{t('Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比')}
|
||||
{t(
|
||||
'Claude思考适配 BudgetTokens = MaxTokens * BudgetTokens 百分比',
|
||||
)}
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -153,7 +186,12 @@ export default function SettingClaudeModel(props) {
|
||||
extraText={t('0.1-1之间的小数')}
|
||||
min={0.1}
|
||||
max={1}
|
||||
onChange={(value) => setInputs({ ...inputs, 'claude.thinking_adapter_budget_tokens_percentage': value })}
|
||||
onChange={(value) =>
|
||||
setInputs({
|
||||
...inputs,
|
||||
'claude.thinking_adapter_budget_tokens_percentage': value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -5,20 +5,20 @@ import {
|
||||
API,
|
||||
showError,
|
||||
showSuccess,
|
||||
showWarning, verifyJSON
|
||||
showWarning,
|
||||
verifyJSON,
|
||||
} from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const GEMINI_SETTING_EXAMPLE = {
|
||||
'default': 'OFF',
|
||||
'HARM_CATEGORY_CIVIC_INTEGRITY': 'BLOCK_NONE',
|
||||
default: 'OFF',
|
||||
HARM_CATEGORY_CIVIC_INTEGRITY: 'BLOCK_NONE',
|
||||
};
|
||||
|
||||
const GEMINI_VERSION_EXAMPLE = {
|
||||
'default': 'v1beta',
|
||||
default: 'v1beta',
|
||||
};
|
||||
|
||||
|
||||
export default function SettingGeminiModel(props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -51,7 +51,8 @@ export default function SettingGeminiModel(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
@@ -89,19 +90,27 @@ export default function SettingGeminiModel(props) {
|
||||
<Col xs={24} sm={12} md={8} lg={8} xl={8}>
|
||||
<Form.TextArea
|
||||
label={t('Gemini安全设置')}
|
||||
placeholder={t('为一个 JSON 文本,例如:') + '\n' + JSON.stringify(GEMINI_SETTING_EXAMPLE, null, 2)}
|
||||
placeholder={
|
||||
t('为一个 JSON 文本,例如:') +
|
||||
'\n' +
|
||||
JSON.stringify(GEMINI_SETTING_EXAMPLE, null, 2)
|
||||
}
|
||||
field={'gemini.safety_settings'}
|
||||
extraText={t('default为默认设置,可单独设置每个分类的安全等级')}
|
||||
extraText={t(
|
||||
'default为默认设置,可单独设置每个分类的安全等级',
|
||||
)}
|
||||
autosize={{ minRows: 6, maxRows: 12 }}
|
||||
trigger='blur'
|
||||
stopValidateWithError
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: t('不是合法的 JSON 字符串')
|
||||
}
|
||||
message: t('不是合法的 JSON 字符串'),
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, 'gemini.safety_settings': value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, 'gemini.safety_settings': value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -109,7 +118,11 @@ export default function SettingGeminiModel(props) {
|
||||
<Col xs={24} sm={12} md={8} lg={8} xl={8}>
|
||||
<Form.TextArea
|
||||
label={t('Gemini版本设置')}
|
||||
placeholder={t('为一个 JSON 文本,例如:') + '\n' + JSON.stringify(GEMINI_VERSION_EXAMPLE, null, 2)}
|
||||
placeholder={
|
||||
t('为一个 JSON 文本,例如:') +
|
||||
'\n' +
|
||||
JSON.stringify(GEMINI_VERSION_EXAMPLE, null, 2)
|
||||
}
|
||||
field={'gemini.version_settings'}
|
||||
extraText={t('default为默认设置,可单独设置每个模型的版本')}
|
||||
autosize={{ minRows: 6, maxRows: 12 }}
|
||||
@@ -118,10 +131,12 @@ export default function SettingGeminiModel(props) {
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: t('不是合法的 JSON 字符串')
|
||||
}
|
||||
message: t('不是合法的 JSON 字符串'),
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, 'gemini.version_settings': value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, 'gemini.version_settings': value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
API,
|
||||
showError,
|
||||
showSuccess,
|
||||
showWarning, verifyJSON
|
||||
showWarning,
|
||||
verifyJSON,
|
||||
} from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -40,7 +41,8 @@ export default function SettingGlobalModel(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
@@ -79,8 +81,15 @@ export default function SettingGlobalModel(props) {
|
||||
<Form.Switch
|
||||
label={t('启用请求透传')}
|
||||
field={'global.pass_through_request_enabled'}
|
||||
onChange={(value) => setInputs({ ...inputs, 'global.pass_through_request_enabled': value })}
|
||||
extraText={'开启后,所有请求将直接透传给上游,不会进行任何处理(重定向和渠道适配也将失效),请谨慎开启'}
|
||||
onChange={(value) =>
|
||||
setInputs({
|
||||
...inputs,
|
||||
'global.pass_through_request_enabled': value,
|
||||
})
|
||||
}
|
||||
extraText={
|
||||
'开启后,所有请求将直接透传给上游,不会进行任何处理(重定向和渠道适配也将失效),请谨慎开启'
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -15,50 +15,59 @@ export default function GroupRatioSettings(props) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [inputs, setInputs] = useState({
|
||||
GroupRatio: '',
|
||||
UserUsableGroups: ''
|
||||
UserUsableGroups: '',
|
||||
});
|
||||
const refForm = useRef();
|
||||
const [inputsRow, setInputsRow] = useState(inputs);
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
await refForm.current.validate().then(() => {
|
||||
const updateArray = compareObjects(inputs, inputsRow);
|
||||
if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
|
||||
|
||||
const requestQueue = updateArray.map((item) => {
|
||||
const value = typeof inputs[item.key] === 'boolean'
|
||||
? String(inputs[item.key])
|
||||
: inputs[item.key];
|
||||
return API.put('/api/option/', { key: item.key, value });
|
||||
});
|
||||
await refForm.current
|
||||
.validate()
|
||||
.then(() => {
|
||||
const updateArray = compareObjects(inputs, inputsRow);
|
||||
if (!updateArray.length)
|
||||
return showWarning(t('你似乎并没有修改什么'));
|
||||
|
||||
setLoading(true);
|
||||
Promise.all(requestQueue)
|
||||
.then((res) => {
|
||||
if (res.includes(undefined)) {
|
||||
return showError(requestQueue.length > 1 ? t('部分保存失败,请重试') : t('保存失败'));
|
||||
}
|
||||
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
if (!res[i].data.success) {
|
||||
return showError(res[i].data.message);
|
||||
}
|
||||
}
|
||||
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Unexpected error:', error);
|
||||
showError(t('保存失败,请重试'));
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
const requestQueue = updateArray.map((item) => {
|
||||
const value =
|
||||
typeof inputs[item.key] === 'boolean'
|
||||
? String(inputs[item.key])
|
||||
: inputs[item.key];
|
||||
return API.put('/api/option/', { key: item.key, value });
|
||||
});
|
||||
}).catch(() => {
|
||||
showError(t('请检查输入'));
|
||||
});
|
||||
|
||||
setLoading(true);
|
||||
Promise.all(requestQueue)
|
||||
.then((res) => {
|
||||
if (res.includes(undefined)) {
|
||||
return showError(
|
||||
requestQueue.length > 1
|
||||
? t('部分保存失败,请重试')
|
||||
: t('保存失败'),
|
||||
);
|
||||
}
|
||||
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
if (!res[i].data.success) {
|
||||
return showError(res[i].data.message);
|
||||
}
|
||||
}
|
||||
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Unexpected error:', error);
|
||||
showError(t('保存失败,请重试'));
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
showError(t('请检查输入'));
|
||||
});
|
||||
} catch (error) {
|
||||
showError(t('请检查输入'));
|
||||
console.error(error);
|
||||
@@ -97,10 +106,12 @@ export default function GroupRatioSettings(props) {
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: t('不是合法的 JSON 字符串')
|
||||
}
|
||||
message: t('不是合法的 JSON 字符串'),
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, GroupRatio: value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, GroupRatio: value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -116,10 +127,12 @@ export default function GroupRatioSettings(props) {
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: t('不是合法的 JSON 字符串')
|
||||
}
|
||||
message: t('不是合法的 JSON 字符串'),
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, UserUsableGroups: value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, UserUsableGroups: value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -128,4 +141,4 @@ export default function GroupRatioSettings(props) {
|
||||
<Button onClick={onSubmit}>{t('保存分组倍率设置')}</Button>
|
||||
</Spin>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Button, Col, Form, Popconfirm, Row, Space, Spin } from '@douyinfe/semi-ui';
|
||||
import {
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Space,
|
||||
Spin,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
compareObjects,
|
||||
API,
|
||||
@@ -24,43 +32,52 @@ export default function ModelRatioSettings(props) {
|
||||
|
||||
async function onSubmit() {
|
||||
try {
|
||||
await refForm.current.validate().then(() => {
|
||||
const updateArray = compareObjects(inputs, inputsRow);
|
||||
if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
|
||||
|
||||
const requestQueue = updateArray.map((item) => {
|
||||
const value = typeof inputs[item.key] === 'boolean'
|
||||
? String(inputs[item.key])
|
||||
: inputs[item.key];
|
||||
return API.put('/api/option/', { key: item.key, value });
|
||||
});
|
||||
await refForm.current
|
||||
.validate()
|
||||
.then(() => {
|
||||
const updateArray = compareObjects(inputs, inputsRow);
|
||||
if (!updateArray.length)
|
||||
return showWarning(t('你似乎并没有修改什么'));
|
||||
|
||||
setLoading(true);
|
||||
Promise.all(requestQueue)
|
||||
.then((res) => {
|
||||
if (res.includes(undefined)) {
|
||||
return showError(requestQueue.length > 1 ? t('部分保存失败,请重试') : t('保存失败'));
|
||||
}
|
||||
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
if (!res[i].data.success) {
|
||||
return showError(res[i].data.message);
|
||||
}
|
||||
}
|
||||
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Unexpected error:', error);
|
||||
showError(t('保存失败,请重试'));
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
const requestQueue = updateArray.map((item) => {
|
||||
const value =
|
||||
typeof inputs[item.key] === 'boolean'
|
||||
? String(inputs[item.key])
|
||||
: inputs[item.key];
|
||||
return API.put('/api/option/', { key: item.key, value });
|
||||
});
|
||||
}).catch(() => {
|
||||
showError(t('请检查输入'));
|
||||
});
|
||||
|
||||
setLoading(true);
|
||||
Promise.all(requestQueue)
|
||||
.then((res) => {
|
||||
if (res.includes(undefined)) {
|
||||
return showError(
|
||||
requestQueue.length > 1
|
||||
? t('部分保存失败,请重试')
|
||||
: t('保存失败'),
|
||||
);
|
||||
}
|
||||
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
if (!res[i].data.success) {
|
||||
return showError(res[i].data.message);
|
||||
}
|
||||
}
|
||||
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Unexpected error:', error);
|
||||
showError(t('保存失败,请重试'));
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
showError(t('请检查输入'));
|
||||
});
|
||||
} catch (error) {
|
||||
showError(t('请检查输入'));
|
||||
console.error(error);
|
||||
@@ -106,7 +123,9 @@ export default function ModelRatioSettings(props) {
|
||||
<Form.TextArea
|
||||
label={t('模型固定价格')}
|
||||
extraText={t('一次调用消耗多少刀,优先级大于模型倍率')}
|
||||
placeholder={t('为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 "gpt-4-gizmo-*": 0.1,一次消耗0.1刀')}
|
||||
placeholder={t(
|
||||
'为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 "gpt-4-gizmo-*": 0.1,一次消耗0.1刀',
|
||||
)}
|
||||
field={'ModelPrice'}
|
||||
autosize={{ minRows: 6, maxRows: 12 }}
|
||||
trigger='blur'
|
||||
@@ -114,10 +133,12 @@ export default function ModelRatioSettings(props) {
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: '不是合法的 JSON 字符串'
|
||||
}
|
||||
message: '不是合法的 JSON 字符串',
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, ModelPrice: value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, ModelPrice: value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -133,10 +154,12 @@ export default function ModelRatioSettings(props) {
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: '不是合法的 JSON 字符串'
|
||||
}
|
||||
message: '不是合法的 JSON 字符串',
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, ModelRatio: value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, ModelRatio: value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -152,10 +175,12 @@ export default function ModelRatioSettings(props) {
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: '不是合法的 JSON 字符串'
|
||||
}
|
||||
message: '不是合法的 JSON 字符串',
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, CacheRatio: value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, CacheRatio: value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -172,10 +197,12 @@ export default function ModelRatioSettings(props) {
|
||||
rules={[
|
||||
{
|
||||
validator: (rule, value) => verifyJSON(value),
|
||||
message: '不是合法的 JSON 字符串'
|
||||
}
|
||||
message: '不是合法的 JSON 字符串',
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setInputs({ ...inputs, CompletionRatio: value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, CompletionRatio: value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -195,4 +222,4 @@ export default function ModelRatioSettings(props) {
|
||||
</Space>
|
||||
</Spin>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Table, Button, Input, Modal, Form, Space, Typography, Radio, Notification } from '@douyinfe/semi-ui';
|
||||
import { IconDelete, IconPlus, IconSearch, IconSave, IconBolt } from '@douyinfe/semi-icons';
|
||||
import {
|
||||
Table,
|
||||
Button,
|
||||
Input,
|
||||
Modal,
|
||||
Form,
|
||||
Space,
|
||||
Typography,
|
||||
Radio,
|
||||
Notification,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
IconDelete,
|
||||
IconPlus,
|
||||
IconSearch,
|
||||
IconSave,
|
||||
IconBolt,
|
||||
} from '@douyinfe/semi-icons';
|
||||
import { showError, showSuccess } from '../../../helpers';
|
||||
import { API } from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -20,7 +36,8 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
const [batchFillType, setBatchFillType] = useState('ratio');
|
||||
const [batchFillValue, setBatchFillValue] = useState('');
|
||||
const [batchRatioValue, setBatchRatioValue] = useState('');
|
||||
const [batchCompletionRatioValue, setBatchCompletionRatioValue] = useState('');
|
||||
const [batchCompletionRatioValue, setBatchCompletionRatioValue] =
|
||||
useState('');
|
||||
const { Text } = Typography;
|
||||
// 定义可选的每页显示条数
|
||||
const pageSizeOptions = [10, 20, 50, 100];
|
||||
@@ -38,7 +55,7 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
console.error(t('获取启用模型失败:'), error);
|
||||
showError(t('获取启用模型失败'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// 获取所有启用的模型
|
||||
@@ -52,20 +69,20 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
const completionRatio = JSON.parse(props.options.CompletionRatio || '{}');
|
||||
|
||||
// 找出所有未设置价格和倍率的模型
|
||||
const unsetModels = enabledModels.filter(modelName => {
|
||||
const unsetModels = enabledModels.filter((modelName) => {
|
||||
const hasPrice = modelPrice[modelName] !== undefined;
|
||||
const hasRatio = modelRatio[modelName] !== undefined;
|
||||
|
||||
|
||||
// 如果模型没有价格或者没有倍率设置,则显示
|
||||
return !hasPrice && !hasRatio;
|
||||
});
|
||||
|
||||
// 创建模型数据
|
||||
const modelData = unsetModels.map(name => ({
|
||||
const modelData = unsetModels.map((name) => ({
|
||||
name,
|
||||
price: modelPrice[name] || '',
|
||||
ratio: modelRatio[name] || '',
|
||||
completionRatio: completionRatio[name] || ''
|
||||
completionRatio: completionRatio[name] || '',
|
||||
}));
|
||||
|
||||
setModels(modelData);
|
||||
@@ -94,8 +111,10 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
};
|
||||
|
||||
// 在 return 语句之前,先处理过滤和分页逻辑
|
||||
const filteredModels = models.filter(model =>
|
||||
searchText ? model.name.toLowerCase().includes(searchText.toLowerCase()) : true
|
||||
const filteredModels = models.filter((model) =>
|
||||
searchText
|
||||
? model.name.toLowerCase().includes(searchText.toLowerCase())
|
||||
: true,
|
||||
);
|
||||
|
||||
// 然后基于过滤后的数据计算分页数据
|
||||
@@ -106,19 +125,23 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
const output = {
|
||||
ModelPrice: JSON.parse(props.options.ModelPrice || '{}'),
|
||||
ModelRatio: JSON.parse(props.options.ModelRatio || '{}'),
|
||||
CompletionRatio: JSON.parse(props.options.CompletionRatio || '{}')
|
||||
CompletionRatio: JSON.parse(props.options.CompletionRatio || '{}'),
|
||||
};
|
||||
|
||||
try {
|
||||
// 数据转换 - 只处理已修改的模型
|
||||
models.forEach(model => {
|
||||
models.forEach((model) => {
|
||||
// 只有当用户设置了值时才更新
|
||||
if (model.price !== '') {
|
||||
// 如果价格不为空,则转换为浮点数,忽略倍率参数
|
||||
output.ModelPrice[model.name] = parseFloat(model.price);
|
||||
} else {
|
||||
if (model.ratio !== '') output.ModelRatio[model.name] = parseFloat(model.ratio);
|
||||
if (model.completionRatio !== '') output.CompletionRatio[model.name] = parseFloat(model.completionRatio);
|
||||
if (model.ratio !== '')
|
||||
output.ModelRatio[model.name] = parseFloat(model.ratio);
|
||||
if (model.completionRatio !== '')
|
||||
output.CompletionRatio[model.name] = parseFloat(
|
||||
model.completionRatio,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -126,13 +149,13 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
const finalOutput = {
|
||||
ModelPrice: JSON.stringify(output.ModelPrice, null, 2),
|
||||
ModelRatio: JSON.stringify(output.ModelRatio, null, 2),
|
||||
CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2)
|
||||
CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2),
|
||||
};
|
||||
|
||||
const requestQueue = Object.entries(finalOutput).map(([key, value]) => {
|
||||
return API.put('/api/option/', {
|
||||
key,
|
||||
value
|
||||
value,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -159,7 +182,6 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
props.refresh();
|
||||
// 重新获取未设置的模型
|
||||
getAllEnabledModels();
|
||||
|
||||
} catch (error) {
|
||||
console.error(t('保存失败:'), error);
|
||||
showError(t('保存失败,请重试'));
|
||||
@@ -182,9 +204,9 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
<Input
|
||||
value={text}
|
||||
placeholder={t('按量计费')}
|
||||
onChange={value => updateModel(record.name, 'price', value)}
|
||||
onChange={(value) => updateModel(record.name, 'price', value)}
|
||||
/>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('模型倍率'),
|
||||
@@ -195,9 +217,9 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
value={text}
|
||||
placeholder={record.price !== '' ? t('模型倍率') : t('输入模型倍率')}
|
||||
disabled={record.price !== ''}
|
||||
onChange={value => updateModel(record.name, 'ratio', value)}
|
||||
onChange={(value) => updateModel(record.name, 'ratio', value)}
|
||||
/>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('补全倍率'),
|
||||
@@ -208,10 +230,12 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
value={text}
|
||||
placeholder={record.price !== '' ? t('补全倍率') : t('输入补全倍率')}
|
||||
disabled={record.price !== ''}
|
||||
onChange={value => updateModel(record.name, 'completionRatio', value)}
|
||||
onChange={(value) =>
|
||||
updateModel(record.name, 'completionRatio', value)
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const updateModel = (name, field, value) => {
|
||||
@@ -219,27 +243,28 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
showError(t('请输入数字'));
|
||||
return;
|
||||
}
|
||||
setModels(prev =>
|
||||
prev.map(model =>
|
||||
model.name === name
|
||||
? { ...model, [field]: value }
|
||||
: model
|
||||
)
|
||||
setModels((prev) =>
|
||||
prev.map((model) =>
|
||||
model.name === name ? { ...model, [field]: value } : model,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const addModel = (values) => {
|
||||
// 检查模型名称是否存在, 如果存在则拒绝添加
|
||||
if (models.some(model => model.name === values.name)) {
|
||||
if (models.some((model) => model.name === values.name)) {
|
||||
showError(t('模型名称已存在'));
|
||||
return;
|
||||
}
|
||||
setModels(prev => [{
|
||||
name: values.name,
|
||||
price: values.price || '',
|
||||
ratio: values.ratio || '',
|
||||
completionRatio: values.completionRatio || ''
|
||||
}, ...prev]);
|
||||
setModels((prev) => [
|
||||
{
|
||||
name: values.name,
|
||||
price: values.price || '',
|
||||
ratio: values.ratio || '',
|
||||
completionRatio: values.completionRatio || '',
|
||||
},
|
||||
...prev,
|
||||
]);
|
||||
setVisible(false);
|
||||
showSuccess(t('添加成功'));
|
||||
};
|
||||
@@ -272,39 +297,39 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
}
|
||||
|
||||
// 根据选择的类型批量更新模型
|
||||
setModels(prev =>
|
||||
prev.map(model => {
|
||||
setModels((prev) =>
|
||||
prev.map((model) => {
|
||||
if (selectedRowKeys.includes(model.name)) {
|
||||
if (batchFillType === 'price') {
|
||||
return {
|
||||
...model,
|
||||
price: batchFillValue,
|
||||
ratio: '',
|
||||
completionRatio: ''
|
||||
completionRatio: '',
|
||||
};
|
||||
} else if (batchFillType === 'ratio') {
|
||||
return {
|
||||
...model,
|
||||
price: '',
|
||||
ratio: batchFillValue
|
||||
ratio: batchFillValue,
|
||||
};
|
||||
} else if (batchFillType === 'completionRatio') {
|
||||
return {
|
||||
...model,
|
||||
price: '',
|
||||
completionRatio: batchFillValue
|
||||
completionRatio: batchFillValue,
|
||||
};
|
||||
} else if (batchFillType === 'bothRatio') {
|
||||
return {
|
||||
...model,
|
||||
price: '',
|
||||
ratio: batchRatioValue,
|
||||
completionRatio: batchCompletionRatioValue
|
||||
completionRatio: batchCompletionRatioValue,
|
||||
};
|
||||
}
|
||||
}
|
||||
return model;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
setBatchVisible(false);
|
||||
@@ -312,9 +337,14 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
title: t('批量设置成功'),
|
||||
content: t('已为 {{count}} 个模型设置{{type}}', {
|
||||
count: selectedRowKeys.length,
|
||||
type: batchFillType === 'price' ? t('固定价格') :
|
||||
batchFillType === 'ratio' ? t('模型倍率') :
|
||||
batchFillType === 'completionRatio' ? t('补全倍率') : t('模型倍率和补全倍率')
|
||||
type:
|
||||
batchFillType === 'price'
|
||||
? t('固定价格')
|
||||
: batchFillType === 'ratio'
|
||||
? t('模型倍率')
|
||||
: batchFillType === 'completionRatio'
|
||||
? t('补全倍率')
|
||||
: t('模型倍率和补全倍率'),
|
||||
}),
|
||||
duration: 3,
|
||||
});
|
||||
@@ -323,7 +353,7 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
const handleBatchTypeChange = (value) => {
|
||||
console.log(t('Changing batch type to:'), value);
|
||||
setBatchFillType(value);
|
||||
|
||||
|
||||
// 切换类型时清空对应的值
|
||||
if (value !== 'bothRatio') {
|
||||
setBatchFillValue('');
|
||||
@@ -342,56 +372,63 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Space vertical align="start" style={{ width: '100%' }}>
|
||||
<Space vertical align='start' style={{ width: '100%' }}>
|
||||
<Space>
|
||||
<Button icon={<IconPlus />} onClick={() => setVisible(true)}>
|
||||
{t('添加模型')}
|
||||
</Button>
|
||||
<Button
|
||||
icon={<IconBolt />}
|
||||
type="secondary"
|
||||
<Button
|
||||
icon={<IconBolt />}
|
||||
type='secondary'
|
||||
onClick={() => setBatchVisible(true)}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
>
|
||||
{t('批量设置')} ({selectedRowKeys.length})
|
||||
</Button>
|
||||
<Button type="primary" icon={<IconSave />} onClick={SubmitData} loading={loading}>
|
||||
<Button
|
||||
type='primary'
|
||||
icon={<IconSave />}
|
||||
onClick={SubmitData}
|
||||
loading={loading}
|
||||
>
|
||||
{t('应用更改')}
|
||||
</Button>
|
||||
<Input
|
||||
prefix={<IconSearch />}
|
||||
placeholder={t('搜索模型名称')}
|
||||
value={searchText}
|
||||
onChange={value => {
|
||||
setSearchText(value)
|
||||
onChange={(value) => {
|
||||
setSearchText(value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
style={{ width: 200 }}
|
||||
/>
|
||||
</Space>
|
||||
|
||||
<Text>{t('此页面仅显示未设置价格或倍率的模型,设置后将自动从列表中移除')}</Text>
|
||||
|
||||
<Text>
|
||||
{t('此页面仅显示未设置价格或倍率的模型,设置后将自动从列表中移除')}
|
||||
</Text>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={pagedData}
|
||||
rowSelection={rowSelection}
|
||||
rowKey="name"
|
||||
rowKey='name'
|
||||
pagination={{
|
||||
currentPage: currentPage,
|
||||
pageSize: pageSize,
|
||||
total: filteredModels.length,
|
||||
onPageChange: page => setCurrentPage(page),
|
||||
onPageChange: (page) => setCurrentPage(page),
|
||||
onPageSizeChange: handlePageSizeChange,
|
||||
pageSizeOptions: pageSizeOptions,
|
||||
formatPageText: (page) =>
|
||||
t('第 {{start}} - {{end}} 条,共 {{total}} 条', {
|
||||
start: page.currentStart,
|
||||
end: page.currentEnd,
|
||||
total: filteredModels.length
|
||||
total: filteredModels.length,
|
||||
}),
|
||||
showTotal: true,
|
||||
showSizeChanger: true
|
||||
showSizeChanger: true,
|
||||
}}
|
||||
empty={
|
||||
<div style={{ textAlign: 'center', padding: '20px' }}>
|
||||
@@ -412,45 +449,61 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
>
|
||||
<Form>
|
||||
<Form.Input
|
||||
field="name"
|
||||
field='name'
|
||||
label={t('模型名称')}
|
||||
placeholder="strawberry"
|
||||
placeholder='strawberry'
|
||||
required
|
||||
onChange={value => setCurrentModel(prev => ({ ...prev, name: value }))}
|
||||
onChange={(value) =>
|
||||
setCurrentModel((prev) => ({ ...prev, name: value }))
|
||||
}
|
||||
/>
|
||||
<Form.Switch
|
||||
field="priceMode"
|
||||
label={<>{t('定价模式')}:{currentModel?.priceMode ? t("固定价格") : t("倍率模式")}</>}
|
||||
onChange={checked => {
|
||||
setCurrentModel(prev => ({
|
||||
field='priceMode'
|
||||
label={
|
||||
<>
|
||||
{t('定价模式')}:
|
||||
{currentModel?.priceMode ? t('固定价格') : t('倍率模式')}
|
||||
</>
|
||||
}
|
||||
onChange={(checked) => {
|
||||
setCurrentModel((prev) => ({
|
||||
...prev,
|
||||
price: '',
|
||||
ratio: '',
|
||||
completionRatio: '',
|
||||
priceMode: checked
|
||||
priceMode: checked,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
{currentModel?.priceMode ? (
|
||||
<Form.Input
|
||||
field="price"
|
||||
field='price'
|
||||
label={t('固定价格(每次)')}
|
||||
placeholder={t('输入每次价格')}
|
||||
onChange={value => setCurrentModel(prev => ({ ...prev, price: value }))}
|
||||
onChange={(value) =>
|
||||
setCurrentModel((prev) => ({ ...prev, price: value }))
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Form.Input
|
||||
field="ratio"
|
||||
field='ratio'
|
||||
label={t('模型倍率')}
|
||||
placeholder={t('输入模型倍率')}
|
||||
onChange={value => setCurrentModel(prev => ({ ...prev, ratio: value }))}
|
||||
onChange={(value) =>
|
||||
setCurrentModel((prev) => ({ ...prev, ratio: value }))
|
||||
}
|
||||
/>
|
||||
<Form.Input
|
||||
field="completionRatio"
|
||||
field='completionRatio'
|
||||
label={t('补全倍率')}
|
||||
placeholder={t('输入补全价格')}
|
||||
onChange={value => setCurrentModel(prev => ({ ...prev, completionRatio: value }))}
|
||||
onChange={(value) =>
|
||||
setCurrentModel((prev) => ({
|
||||
...prev,
|
||||
completionRatio: value,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@@ -496,50 +549,56 @@ export default function ModelRatioNotSetEditor(props) {
|
||||
</Space>
|
||||
</div>
|
||||
</Form.Section>
|
||||
|
||||
|
||||
{batchFillType === 'bothRatio' ? (
|
||||
<>
|
||||
<Form.Input
|
||||
field="batchRatioValue"
|
||||
field='batchRatioValue'
|
||||
label={t('模型倍率值')}
|
||||
placeholder={t('请输入模型倍率')}
|
||||
value={batchRatioValue}
|
||||
onChange={value => setBatchRatioValue(value)}
|
||||
onChange={(value) => setBatchRatioValue(value)}
|
||||
/>
|
||||
<Form.Input
|
||||
field="batchCompletionRatioValue"
|
||||
field='batchCompletionRatioValue'
|
||||
label={t('补全倍率值')}
|
||||
placeholder={t('请输入补全倍率')}
|
||||
value={batchCompletionRatioValue}
|
||||
onChange={value => setBatchCompletionRatioValue(value)}
|
||||
onChange={(value) => setBatchCompletionRatioValue(value)}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Form.Input
|
||||
field="batchFillValue"
|
||||
field='batchFillValue'
|
||||
label={
|
||||
batchFillType === 'price'
|
||||
? t('固定价格值')
|
||||
batchFillType === 'price'
|
||||
? t('固定价格值')
|
||||
: batchFillType === 'ratio'
|
||||
? t('模型倍率值')
|
||||
: t('补全倍率值')
|
||||
}
|
||||
placeholder={t('请输入数值')}
|
||||
value={batchFillValue}
|
||||
onChange={value => setBatchFillValue(value)}
|
||||
onChange={(value) => setBatchFillValue(value)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Text type="tertiary">
|
||||
{t('将为选中的 ')} <Text strong>{selectedRowKeys.length}</Text> {t(' 个模型设置相同的值')}
|
||||
|
||||
<Text type='tertiary'>
|
||||
{t('将为选中的 ')} <Text strong>{selectedRowKeys.length}</Text>{' '}
|
||||
{t(' 个模型设置相同的值')}
|
||||
</Text>
|
||||
<div style={{ marginTop: '8px' }}>
|
||||
<Text type="tertiary">
|
||||
{t('当前设置类型: ')} <Text strong>{
|
||||
batchFillType === 'price' ? t('固定价格') :
|
||||
batchFillType === 'ratio' ? t('模型倍率') :
|
||||
batchFillType === 'completionRatio' ? t('补全倍率') : t('模型倍率和补全倍率')
|
||||
}</Text>
|
||||
<Text type='tertiary'>
|
||||
{t('当前设置类型: ')}{' '}
|
||||
<Text strong>
|
||||
{batchFillType === 'price'
|
||||
? t('固定价格')
|
||||
: batchFillType === 'ratio'
|
||||
? t('模型倍率')
|
||||
: batchFillType === 'completionRatio'
|
||||
? t('补全倍率')
|
||||
: t('模型倍率和补全倍率')}
|
||||
</Text>
|
||||
</Text>
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
@@ -1,7 +1,24 @@
|
||||
// ModelSettingsVisualEditor.js
|
||||
import React, { useContext, useEffect, useState, useRef } from 'react';
|
||||
import { Table, Button, Input, Modal, Form, Space, RadioGroup, Radio, Tabs, TabPane } from '@douyinfe/semi-ui';
|
||||
import { IconDelete, IconPlus, IconSearch, IconSave, IconEdit } from '@douyinfe/semi-icons';
|
||||
import {
|
||||
Table,
|
||||
Button,
|
||||
Input,
|
||||
Modal,
|
||||
Form,
|
||||
Space,
|
||||
RadioGroup,
|
||||
Radio,
|
||||
Tabs,
|
||||
TabPane,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
IconDelete,
|
||||
IconPlus,
|
||||
IconSearch,
|
||||
IconSave,
|
||||
IconEdit,
|
||||
} from '@douyinfe/semi-icons';
|
||||
import { showError, showSuccess } from '../../../helpers';
|
||||
import { API } from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -20,7 +37,7 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
const [pricingSubMode, setPricingSubMode] = useState('ratio'); // 'ratio' or 'token-price'
|
||||
const formRef = useRef(null);
|
||||
const pageSize = 10;
|
||||
const quotaPerUnit = getQuotaPerUnit()
|
||||
const quotaPerUnit = getQuotaPerUnit();
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
@@ -32,14 +49,15 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
const modelNames = new Set([
|
||||
...Object.keys(modelPrice),
|
||||
...Object.keys(modelRatio),
|
||||
...Object.keys(completionRatio)
|
||||
...Object.keys(completionRatio),
|
||||
]);
|
||||
|
||||
const modelData = Array.from(modelNames).map(name => ({
|
||||
const modelData = Array.from(modelNames).map((name) => ({
|
||||
name,
|
||||
price: modelPrice[name] === undefined ? '' : modelPrice[name],
|
||||
ratio: modelRatio[name] === undefined ? '' : modelRatio[name],
|
||||
completionRatio: completionRatio[name] === undefined ? '' : completionRatio[name]
|
||||
completionRatio:
|
||||
completionRatio[name] === undefined ? '' : completionRatio[name],
|
||||
}));
|
||||
|
||||
setModels(modelData);
|
||||
@@ -56,8 +74,10 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
};
|
||||
|
||||
// 在 return 语句之前,先处理过滤和分页逻辑
|
||||
const filteredModels = models.filter(model =>
|
||||
searchText ? model.name.toLowerCase().includes(searchText.toLowerCase()) : true
|
||||
const filteredModels = models.filter((model) =>
|
||||
searchText
|
||||
? model.name.toLowerCase().includes(searchText.toLowerCase())
|
||||
: true,
|
||||
);
|
||||
|
||||
// 然后基于过滤后的数据计算分页数据
|
||||
@@ -68,20 +88,24 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
const output = {
|
||||
ModelPrice: {},
|
||||
ModelRatio: {},
|
||||
CompletionRatio: {}
|
||||
CompletionRatio: {},
|
||||
};
|
||||
let currentConvertModelName = '';
|
||||
|
||||
try {
|
||||
// 数据转换
|
||||
models.forEach(model => {
|
||||
models.forEach((model) => {
|
||||
currentConvertModelName = model.name;
|
||||
if (model.price !== '') {
|
||||
// 如果价格不为空,则转换为浮点数,忽略倍率参数
|
||||
output.ModelPrice[model.name] = parseFloat(model.price)
|
||||
output.ModelPrice[model.name] = parseFloat(model.price);
|
||||
} else {
|
||||
if (model.ratio !== '') output.ModelRatio[model.name] = parseFloat(model.ratio);
|
||||
if (model.completionRatio !== '') output.CompletionRatio[model.name] = parseFloat(model.completionRatio);
|
||||
if (model.ratio !== '')
|
||||
output.ModelRatio[model.name] = parseFloat(model.ratio);
|
||||
if (model.completionRatio !== '')
|
||||
output.CompletionRatio[model.name] = parseFloat(
|
||||
model.completionRatio,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -89,13 +113,13 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
const finalOutput = {
|
||||
ModelPrice: JSON.stringify(output.ModelPrice, null, 2),
|
||||
ModelRatio: JSON.stringify(output.ModelRatio, null, 2),
|
||||
CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2)
|
||||
CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2),
|
||||
};
|
||||
|
||||
const requestQueue = Object.entries(finalOutput).map(([key, value]) => {
|
||||
return API.put('/api/option/', {
|
||||
key,
|
||||
value
|
||||
value,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -120,7 +144,6 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
|
||||
showSuccess('保存成功');
|
||||
props.refresh();
|
||||
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error);
|
||||
showError('保存失败,请重试');
|
||||
@@ -143,9 +166,9 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
<Input
|
||||
value={text}
|
||||
placeholder={t('按量计费')}
|
||||
onChange={value => updateModel(record.name, 'price', value)}
|
||||
onChange={(value) => updateModel(record.name, 'price', value)}
|
||||
/>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('模型倍率'),
|
||||
@@ -156,9 +179,9 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
value={text}
|
||||
placeholder={record.price !== '' ? t('模型倍率') : t('默认补全倍率')}
|
||||
disabled={record.price !== ''}
|
||||
onChange={value => updateModel(record.name, 'ratio', value)}
|
||||
onChange={(value) => updateModel(record.name, 'ratio', value)}
|
||||
/>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('补全倍率'),
|
||||
@@ -169,9 +192,11 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
value={text}
|
||||
placeholder={record.price !== '' ? t('补全倍率') : t('默认补全倍率')}
|
||||
disabled={record.price !== ''}
|
||||
onChange={value => updateModel(record.name, 'completionRatio', value)}
|
||||
onChange={(value) =>
|
||||
updateModel(record.name, 'completionRatio', value)
|
||||
}
|
||||
/>
|
||||
)
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('操作'),
|
||||
@@ -179,19 +204,18 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
type='primary'
|
||||
icon={<IconEdit />}
|
||||
onClick={() => editModel(record)}
|
||||
>
|
||||
</Button>
|
||||
></Button>
|
||||
<Button
|
||||
icon={<IconDelete />}
|
||||
type="danger"
|
||||
type='danger'
|
||||
onClick={() => deleteModel(record.name)}
|
||||
/>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const updateModel = (name, field, value) => {
|
||||
@@ -199,103 +223,114 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
showError('请输入数字');
|
||||
return;
|
||||
}
|
||||
setModels(prev =>
|
||||
prev.map(model =>
|
||||
model.name === name
|
||||
? { ...model, [field]: value }
|
||||
: model
|
||||
)
|
||||
setModels((prev) =>
|
||||
prev.map((model) =>
|
||||
model.name === name ? { ...model, [field]: value } : model,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const deleteModel = (name) => {
|
||||
setModels(prev => prev.filter(model => model.name !== name));
|
||||
setModels((prev) => prev.filter((model) => model.name !== name));
|
||||
};
|
||||
|
||||
|
||||
const calculateRatioFromTokenPrice = (tokenPrice) => {
|
||||
return tokenPrice / 2;
|
||||
};
|
||||
|
||||
const calculateCompletionRatioFromPrices = (modelTokenPrice, completionTokenPrice) => {
|
||||
|
||||
const calculateCompletionRatioFromPrices = (
|
||||
modelTokenPrice,
|
||||
completionTokenPrice,
|
||||
) => {
|
||||
if (!modelTokenPrice || modelTokenPrice === '0') {
|
||||
showError('模型价格不能为0');
|
||||
return '';
|
||||
}
|
||||
return completionTokenPrice / modelTokenPrice;
|
||||
};
|
||||
|
||||
const handleTokenPriceChange = (value) => {
|
||||
|
||||
const handleTokenPriceChange = (value) => {
|
||||
// Use a temporary variable to hold the new state
|
||||
let newState = {
|
||||
...(currentModel || {}),
|
||||
tokenPrice: value,
|
||||
ratio: 0
|
||||
ratio: 0,
|
||||
};
|
||||
|
||||
|
||||
if (!isNaN(value) && value !== '') {
|
||||
const tokenPrice = parseFloat(value);
|
||||
const ratio = calculateRatioFromTokenPrice(tokenPrice);
|
||||
newState.ratio = ratio;
|
||||
}
|
||||
|
||||
|
||||
// Set the state with the complete updated object
|
||||
setCurrentModel(newState);
|
||||
};
|
||||
|
||||
const handleCompletionTokenPriceChange = (value) => {
|
||||
|
||||
const handleCompletionTokenPriceChange = (value) => {
|
||||
// Use a temporary variable to hold the new state
|
||||
let newState = {
|
||||
...(currentModel || {}),
|
||||
completionTokenPrice: value,
|
||||
completionRatio: 0
|
||||
completionRatio: 0,
|
||||
};
|
||||
|
||||
|
||||
if (!isNaN(value) && value !== '' && currentModel?.tokenPrice) {
|
||||
const completionTokenPrice = parseFloat(value);
|
||||
const modelTokenPrice = parseFloat(currentModel.tokenPrice);
|
||||
|
||||
|
||||
if (modelTokenPrice > 0) {
|
||||
const completionRatio = calculateCompletionRatioFromPrices(modelTokenPrice, completionTokenPrice);
|
||||
const completionRatio = calculateCompletionRatioFromPrices(
|
||||
modelTokenPrice,
|
||||
completionTokenPrice,
|
||||
);
|
||||
newState.completionRatio = completionRatio;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set the state with the complete updated object
|
||||
setCurrentModel(newState);
|
||||
};
|
||||
|
||||
const addOrUpdateModel = (values) => {
|
||||
// Check if we're editing an existing model or adding a new one
|
||||
const existingModelIndex = models.findIndex(model => model.name === values.name);
|
||||
|
||||
const existingModelIndex = models.findIndex(
|
||||
(model) => model.name === values.name,
|
||||
);
|
||||
|
||||
if (existingModelIndex >= 0) {
|
||||
// Update existing model
|
||||
setModels(prev => prev.map((model, index) =>
|
||||
index === existingModelIndex ? {
|
||||
name: values.name,
|
||||
price: values.price || '',
|
||||
ratio: values.ratio || '',
|
||||
completionRatio: values.completionRatio || ''
|
||||
} : model
|
||||
));
|
||||
setModels((prev) =>
|
||||
prev.map((model, index) =>
|
||||
index === existingModelIndex
|
||||
? {
|
||||
name: values.name,
|
||||
price: values.price || '',
|
||||
ratio: values.ratio || '',
|
||||
completionRatio: values.completionRatio || '',
|
||||
}
|
||||
: model,
|
||||
),
|
||||
);
|
||||
setVisible(false);
|
||||
showSuccess(t('更新成功'));
|
||||
} else {
|
||||
// Add new model
|
||||
// Check if model name already exists
|
||||
if (models.some(model => model.name === values.name)) {
|
||||
if (models.some((model) => model.name === values.name)) {
|
||||
showError(t('模型名称已存在'));
|
||||
return;
|
||||
}
|
||||
|
||||
setModels(prev => [{
|
||||
name: values.name,
|
||||
price: values.price || '',
|
||||
ratio: values.ratio || '',
|
||||
completionRatio: values.completionRatio || ''
|
||||
}, ...prev]);
|
||||
|
||||
setModels((prev) => [
|
||||
{
|
||||
name: values.name,
|
||||
price: values.price || '',
|
||||
ratio: values.ratio || '',
|
||||
completionRatio: values.completionRatio || '',
|
||||
},
|
||||
...prev,
|
||||
]);
|
||||
setVisible(false);
|
||||
showSuccess(t('添加成功'));
|
||||
}
|
||||
@@ -304,7 +339,7 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
const calculateTokenPriceFromRatio = (ratio) => {
|
||||
return ratio * 2;
|
||||
};
|
||||
|
||||
|
||||
const resetModalState = () => {
|
||||
setCurrentModel(null);
|
||||
setPricingMode('per-token');
|
||||
@@ -312,40 +347,43 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
};
|
||||
|
||||
const editModel = (record) => {
|
||||
|
||||
// Determine which pricing mode to use based on the model's current configuration
|
||||
let initialPricingMode = 'per-token';
|
||||
let initialPricingSubMode = 'ratio';
|
||||
|
||||
|
||||
if (record.price !== '') {
|
||||
initialPricingMode = 'per-request';
|
||||
} else {
|
||||
initialPricingMode = 'per-token';
|
||||
// We default to ratio mode, but could set to token-price if needed
|
||||
}
|
||||
|
||||
|
||||
// Set the pricing modes for the form
|
||||
setPricingMode(initialPricingMode);
|
||||
setPricingSubMode(initialPricingSubMode);
|
||||
|
||||
|
||||
// Create a copy of the model data to avoid modifying the original
|
||||
const modelCopy = { ...record };
|
||||
|
||||
|
||||
// If the model has ratio data and we want to populate token price fields
|
||||
if (record.ratio) {
|
||||
modelCopy.tokenPrice = calculateTokenPriceFromRatio(parseFloat(record.ratio)).toString();
|
||||
|
||||
modelCopy.tokenPrice = calculateTokenPriceFromRatio(
|
||||
parseFloat(record.ratio),
|
||||
).toString();
|
||||
|
||||
if (record.completionRatio) {
|
||||
modelCopy.completionTokenPrice = (parseFloat(modelCopy.tokenPrice) * parseFloat(record.completionRatio)).toString();
|
||||
modelCopy.completionTokenPrice = (
|
||||
parseFloat(modelCopy.tokenPrice) * parseFloat(record.completionRatio)
|
||||
).toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set the current model
|
||||
setCurrentModel(modelCopy);
|
||||
|
||||
|
||||
// Open the modal
|
||||
setVisible(true);
|
||||
|
||||
|
||||
// Use setTimeout to ensure the form is rendered before setting values
|
||||
setTimeout(() => {
|
||||
if (formRef.current) {
|
||||
@@ -353,7 +391,7 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
const formValues = {
|
||||
name: modelCopy.name,
|
||||
};
|
||||
|
||||
|
||||
if (initialPricingMode === 'per-request') {
|
||||
formValues.priceInput = modelCopy.price;
|
||||
} else if (initialPricingMode === 'per-token') {
|
||||
@@ -362,7 +400,7 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
formValues.modelTokenPrice = modelCopy.tokenPrice;
|
||||
formValues.completionTokenPrice = modelCopy.completionTokenPrice;
|
||||
}
|
||||
|
||||
|
||||
formRef.current.setValues(formValues);
|
||||
}
|
||||
}, 0);
|
||||
@@ -370,23 +408,26 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Space vertical align="start" style={{ width: '100%' }}>
|
||||
<Space vertical align='start' style={{ width: '100%' }}>
|
||||
<Space>
|
||||
<Button icon={<IconPlus />} onClick={() => {
|
||||
resetModalState();
|
||||
setVisible(true);
|
||||
}}>
|
||||
<Button
|
||||
icon={<IconPlus />}
|
||||
onClick={() => {
|
||||
resetModalState();
|
||||
setVisible(true);
|
||||
}}
|
||||
>
|
||||
{t('添加模型')}
|
||||
</Button>
|
||||
<Button type="primary" icon={<IconSave />} onClick={SubmitData}>
|
||||
<Button type='primary' icon={<IconSave />} onClick={SubmitData}>
|
||||
{t('应用更改')}
|
||||
</Button>
|
||||
<Input
|
||||
prefix={<IconSearch />}
|
||||
placeholder={t('搜索模型名称')}
|
||||
value={searchText}
|
||||
onChange={value => {
|
||||
setSearchText(value)
|
||||
onChange={(value) => {
|
||||
setSearchText(value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
style={{ width: 200 }}
|
||||
@@ -399,21 +440,27 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
currentPage: currentPage,
|
||||
pageSize: pageSize,
|
||||
total: filteredModels.length,
|
||||
onPageChange: page => setCurrentPage(page),
|
||||
onPageChange: (page) => setCurrentPage(page),
|
||||
formatPageText: (page) =>
|
||||
t('第 {{start}} - {{end}} 条,共 {{total}} 条', {
|
||||
start: page.currentStart,
|
||||
end: page.currentEnd,
|
||||
total: filteredModels.length
|
||||
total: filteredModels.length,
|
||||
}),
|
||||
showTotal: true,
|
||||
showSizeChanger: false
|
||||
showSizeChanger: false,
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
|
||||
<Modal
|
||||
title={currentModel && currentModel.name && models.some(model => model.name === currentModel.name) ? t('编辑模型') : t('添加模型')}
|
||||
title={
|
||||
currentModel &&
|
||||
currentModel.name &&
|
||||
models.some((model) => model.name === currentModel.name)
|
||||
? t('编辑模型')
|
||||
: t('添加模型')
|
||||
}
|
||||
visible={visible}
|
||||
onCancel={() => {
|
||||
resetModalState();
|
||||
@@ -423,22 +470,33 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
if (currentModel) {
|
||||
// If we're in token price mode, make sure ratio values are properly set
|
||||
const valuesToSave = { ...currentModel };
|
||||
|
||||
if (pricingMode === 'per-token' && pricingSubMode === 'token-price' && currentModel.tokenPrice) {
|
||||
|
||||
if (
|
||||
pricingMode === 'per-token' &&
|
||||
pricingSubMode === 'token-price' &&
|
||||
currentModel.tokenPrice
|
||||
) {
|
||||
// Calculate and set ratio from token price
|
||||
const tokenPrice = parseFloat(currentModel.tokenPrice);
|
||||
valuesToSave.ratio = (tokenPrice / 2).toString();
|
||||
|
||||
|
||||
// Calculate and set completion ratio if both token prices are available
|
||||
if (currentModel.completionTokenPrice && currentModel.tokenPrice) {
|
||||
const completionPrice = parseFloat(currentModel.completionTokenPrice);
|
||||
if (
|
||||
currentModel.completionTokenPrice &&
|
||||
currentModel.tokenPrice
|
||||
) {
|
||||
const completionPrice = parseFloat(
|
||||
currentModel.completionTokenPrice,
|
||||
);
|
||||
const modelPrice = parseFloat(currentModel.tokenPrice);
|
||||
if (modelPrice > 0) {
|
||||
valuesToSave.completionRatio = (completionPrice / modelPrice).toString();
|
||||
valuesToSave.completionRatio = (
|
||||
completionPrice / modelPrice
|
||||
).toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clear price if we're in per-token mode
|
||||
if (pricingMode === 'per-token') {
|
||||
valuesToSave.price = '';
|
||||
@@ -447,139 +505,175 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
valuesToSave.ratio = '';
|
||||
valuesToSave.completionRatio = '';
|
||||
}
|
||||
|
||||
|
||||
addOrUpdateModel(valuesToSave);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Form getFormApi={api => formRef.current = api}>
|
||||
<Form getFormApi={(api) => (formRef.current = api)}>
|
||||
<Form.Input
|
||||
field="name"
|
||||
field='name'
|
||||
label={t('模型名称')}
|
||||
placeholder="strawberry"
|
||||
placeholder='strawberry'
|
||||
required
|
||||
disabled={currentModel && currentModel.name && models.some(model => model.name === currentModel.name)}
|
||||
onChange={value => setCurrentModel(prev => ({ ...prev, name: value }))}
|
||||
disabled={
|
||||
currentModel &&
|
||||
currentModel.name &&
|
||||
models.some((model) => model.name === currentModel.name)
|
||||
}
|
||||
onChange={(value) =>
|
||||
setCurrentModel((prev) => ({ ...prev, name: value }))
|
||||
}
|
||||
/>
|
||||
|
||||
|
||||
<Form.Section text={t('定价模式')}>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<RadioGroup type="button" value={pricingMode} onChange={(e) => {
|
||||
const newMode = e.target.value;
|
||||
const oldMode = pricingMode;
|
||||
setPricingMode(newMode);
|
||||
|
||||
// Instead of resetting all values, convert between modes
|
||||
if (currentModel) {
|
||||
const updatedModel = { ...currentModel };
|
||||
|
||||
// Update formRef with converted values
|
||||
if (formRef.current) {
|
||||
const formValues = {
|
||||
name: updatedModel.name
|
||||
};
|
||||
|
||||
if (newMode === 'per-request') {
|
||||
formValues.priceInput = updatedModel.price || '';
|
||||
} else if (newMode === 'per-token') {
|
||||
formValues.ratioInput = updatedModel.ratio || '';
|
||||
formValues.completionRatioInput = updatedModel.completionRatio || '';
|
||||
formValues.modelTokenPrice = updatedModel.tokenPrice || '';
|
||||
formValues.completionTokenPrice = updatedModel.completionTokenPrice || '';
|
||||
<RadioGroup
|
||||
type='button'
|
||||
value={pricingMode}
|
||||
onChange={(e) => {
|
||||
const newMode = e.target.value;
|
||||
const oldMode = pricingMode;
|
||||
setPricingMode(newMode);
|
||||
|
||||
// Instead of resetting all values, convert between modes
|
||||
if (currentModel) {
|
||||
const updatedModel = { ...currentModel };
|
||||
|
||||
// Update formRef with converted values
|
||||
if (formRef.current) {
|
||||
const formValues = {
|
||||
name: updatedModel.name,
|
||||
};
|
||||
|
||||
if (newMode === 'per-request') {
|
||||
formValues.priceInput = updatedModel.price || '';
|
||||
} else if (newMode === 'per-token') {
|
||||
formValues.ratioInput = updatedModel.ratio || '';
|
||||
formValues.completionRatioInput =
|
||||
updatedModel.completionRatio || '';
|
||||
formValues.modelTokenPrice =
|
||||
updatedModel.tokenPrice || '';
|
||||
formValues.completionTokenPrice =
|
||||
updatedModel.completionTokenPrice || '';
|
||||
}
|
||||
|
||||
formRef.current.setValues(formValues);
|
||||
}
|
||||
|
||||
formRef.current.setValues(formValues);
|
||||
|
||||
// Update the model state
|
||||
setCurrentModel(updatedModel);
|
||||
}
|
||||
|
||||
// Update the model state
|
||||
setCurrentModel(updatedModel);
|
||||
}
|
||||
}}>
|
||||
<Radio value="per-token">{t('按量计费')}</Radio>
|
||||
<Radio value="per-request">{t('按次计费')}</Radio>
|
||||
}}
|
||||
>
|
||||
<Radio value='per-token'>{t('按量计费')}</Radio>
|
||||
<Radio value='per-request'>{t('按次计费')}</Radio>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</Form.Section>
|
||||
|
||||
|
||||
{pricingMode === 'per-token' && (
|
||||
<>
|
||||
<Form.Section text={t('价格设置方式')}>
|
||||
<div style={{ marginBottom: '16px' }}>
|
||||
<RadioGroup type="button" value={pricingSubMode} onChange={(e) => {
|
||||
const newSubMode = e.target.value;
|
||||
const oldSubMode = pricingSubMode;
|
||||
setPricingSubMode(newSubMode);
|
||||
|
||||
// Handle conversion between submodes
|
||||
if (currentModel) {
|
||||
const updatedModel = { ...currentModel };
|
||||
|
||||
// Convert between ratio and token price
|
||||
if (oldSubMode === 'ratio' && newSubMode === 'token-price') {
|
||||
if (updatedModel.ratio) {
|
||||
updatedModel.tokenPrice = calculateTokenPriceFromRatio(parseFloat(updatedModel.ratio)).toString();
|
||||
|
||||
if (updatedModel.completionRatio) {
|
||||
updatedModel.completionTokenPrice = (parseFloat(updatedModel.tokenPrice) * parseFloat(updatedModel.completionRatio)).toString();
|
||||
<RadioGroup
|
||||
type='button'
|
||||
value={pricingSubMode}
|
||||
onChange={(e) => {
|
||||
const newSubMode = e.target.value;
|
||||
const oldSubMode = pricingSubMode;
|
||||
setPricingSubMode(newSubMode);
|
||||
|
||||
// Handle conversion between submodes
|
||||
if (currentModel) {
|
||||
const updatedModel = { ...currentModel };
|
||||
|
||||
// Convert between ratio and token price
|
||||
if (
|
||||
oldSubMode === 'ratio' &&
|
||||
newSubMode === 'token-price'
|
||||
) {
|
||||
if (updatedModel.ratio) {
|
||||
updatedModel.tokenPrice =
|
||||
calculateTokenPriceFromRatio(
|
||||
parseFloat(updatedModel.ratio),
|
||||
).toString();
|
||||
|
||||
if (updatedModel.completionRatio) {
|
||||
updatedModel.completionTokenPrice = (
|
||||
parseFloat(updatedModel.tokenPrice) *
|
||||
parseFloat(updatedModel.completionRatio)
|
||||
).toString();
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
oldSubMode === 'token-price' &&
|
||||
newSubMode === 'ratio'
|
||||
) {
|
||||
// Ratio values should already be calculated by the handlers
|
||||
}
|
||||
} else if (oldSubMode === 'token-price' && newSubMode === 'ratio') {
|
||||
// Ratio values should already be calculated by the handlers
|
||||
}
|
||||
|
||||
// Update the form values
|
||||
if (formRef.current) {
|
||||
const formValues = {};
|
||||
|
||||
if (newSubMode === 'ratio') {
|
||||
formValues.ratioInput = updatedModel.ratio || '';
|
||||
formValues.completionRatioInput = updatedModel.completionRatio || '';
|
||||
} else if (newSubMode === 'token-price') {
|
||||
formValues.modelTokenPrice = updatedModel.tokenPrice || '';
|
||||
formValues.completionTokenPrice = updatedModel.completionTokenPrice || '';
|
||||
|
||||
// Update the form values
|
||||
if (formRef.current) {
|
||||
const formValues = {};
|
||||
|
||||
if (newSubMode === 'ratio') {
|
||||
formValues.ratioInput = updatedModel.ratio || '';
|
||||
formValues.completionRatioInput =
|
||||
updatedModel.completionRatio || '';
|
||||
} else if (newSubMode === 'token-price') {
|
||||
formValues.modelTokenPrice =
|
||||
updatedModel.tokenPrice || '';
|
||||
formValues.completionTokenPrice =
|
||||
updatedModel.completionTokenPrice || '';
|
||||
}
|
||||
|
||||
formRef.current.setValues(formValues);
|
||||
}
|
||||
|
||||
formRef.current.setValues(formValues);
|
||||
|
||||
setCurrentModel(updatedModel);
|
||||
}
|
||||
|
||||
setCurrentModel(updatedModel);
|
||||
}
|
||||
}}>
|
||||
<Radio value="ratio">{t('按倍率设置')}</Radio>
|
||||
<Radio value="token-price">{t('按价格设置')}</Radio>
|
||||
}}
|
||||
>
|
||||
<Radio value='ratio'>{t('按倍率设置')}</Radio>
|
||||
<Radio value='token-price'>{t('按价格设置')}</Radio>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</Form.Section>
|
||||
|
||||
|
||||
{pricingSubMode === 'ratio' && (
|
||||
<>
|
||||
<Form.Input
|
||||
field="ratioInput"
|
||||
field='ratioInput'
|
||||
label={t('模型倍率')}
|
||||
placeholder={t('输入模型倍率')}
|
||||
onChange={value => setCurrentModel(prev => ({
|
||||
...prev || {},
|
||||
ratio: value
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setCurrentModel((prev) => ({
|
||||
...(prev || {}),
|
||||
ratio: value,
|
||||
}))
|
||||
}
|
||||
initValue={currentModel?.ratio || ''}
|
||||
/>
|
||||
<Form.Input
|
||||
field="completionRatioInput"
|
||||
field='completionRatioInput'
|
||||
label={t('补全倍率')}
|
||||
placeholder={t('输入补全倍率')}
|
||||
onChange={value => setCurrentModel(prev => ({
|
||||
...prev || {},
|
||||
completionRatio: value
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setCurrentModel((prev) => ({
|
||||
...(prev || {}),
|
||||
completionRatio: value,
|
||||
}))
|
||||
}
|
||||
initValue={currentModel?.completionRatio || ''}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
{pricingSubMode === 'token-price' && (
|
||||
<>
|
||||
<Form.Input
|
||||
field="modelTokenPrice"
|
||||
field='modelTokenPrice'
|
||||
label={t('输入价格')}
|
||||
onChange={(value) => {
|
||||
handleTokenPriceChange(value);
|
||||
@@ -588,7 +682,7 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
suffix={t('$/1M tokens')}
|
||||
/>
|
||||
<Form.Input
|
||||
field="completionTokenPrice"
|
||||
field='completionTokenPrice'
|
||||
label={t('输出价格')}
|
||||
onChange={(value) => {
|
||||
handleCompletionTokenPriceChange(value);
|
||||
@@ -600,16 +694,18 @@ export default function ModelSettingsVisualEditor(props) {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
{pricingMode === 'per-request' && (
|
||||
<Form.Input
|
||||
field="priceInput"
|
||||
field='priceInput'
|
||||
label={t('固定价格(每次)')}
|
||||
placeholder={t('输入每次价格')}
|
||||
onChange={value => setCurrentModel(prev => ({
|
||||
...prev || {},
|
||||
price: value
|
||||
}))}
|
||||
onChange={(value) =>
|
||||
setCurrentModel((prev) => ({
|
||||
...(prev || {}),
|
||||
price: value,
|
||||
}))
|
||||
}
|
||||
initValue={currentModel?.price || ''}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Banner, Button, Col, Form, Popconfirm, Row, Space, Spin } from '@douyinfe/semi-ui';
|
||||
import {
|
||||
Banner,
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
Popconfirm,
|
||||
Row,
|
||||
Space,
|
||||
Spin,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
compareObjects,
|
||||
API,
|
||||
@@ -7,7 +16,7 @@ import {
|
||||
showSuccess,
|
||||
showWarning,
|
||||
verifyJSON,
|
||||
verifyJSONPromise
|
||||
verifyJSONPromise,
|
||||
} from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -15,7 +24,7 @@ export default function SettingsChats(props) {
|
||||
const { t } = useTranslation();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [inputs, setInputs] = useState({
|
||||
Chats: "[]",
|
||||
Chats: '[]',
|
||||
});
|
||||
const refForm = useRef();
|
||||
const [inputsRow, setInputsRow] = useState(inputs);
|
||||
@@ -23,44 +32,48 @@ export default function SettingsChats(props) {
|
||||
async function onSubmit() {
|
||||
try {
|
||||
console.log('Starting validation...');
|
||||
await refForm.current.validate().then(() => {
|
||||
console.log('Validation passed');
|
||||
const updateArray = compareObjects(inputs, inputsRow);
|
||||
if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
|
||||
const requestQueue = updateArray.map((item) => {
|
||||
let value = '';
|
||||
if (typeof inputs[item.key] === 'boolean') {
|
||||
value = String(inputs[item.key]);
|
||||
} else {
|
||||
value = inputs[item.key];
|
||||
}
|
||||
return API.put('/api/option/', {
|
||||
key: item.key,
|
||||
value
|
||||
});
|
||||
});
|
||||
setLoading(true);
|
||||
Promise.all(requestQueue)
|
||||
.then((res) => {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
await refForm.current
|
||||
.validate()
|
||||
.then(() => {
|
||||
console.log('Validation passed');
|
||||
const updateArray = compareObjects(inputs, inputsRow);
|
||||
if (!updateArray.length)
|
||||
return showWarning(t('你似乎并没有修改什么'));
|
||||
const requestQueue = updateArray.map((item) => {
|
||||
let value = '';
|
||||
if (typeof inputs[item.key] === 'boolean') {
|
||||
value = String(inputs[item.key]);
|
||||
} else {
|
||||
value = inputs[item.key];
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
})
|
||||
.catch(() => {
|
||||
showError(t('保存失败,请重试'));
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
return API.put('/api/option/', {
|
||||
key: item.key,
|
||||
value,
|
||||
});
|
||||
});
|
||||
}).catch((error) => {
|
||||
console.error('Validation failed:', error);
|
||||
showError(t('请检查输入'));
|
||||
});
|
||||
setLoading(true);
|
||||
Promise.all(requestQueue)
|
||||
.then((res) => {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
})
|
||||
.catch(() => {
|
||||
showError(t('保存失败,请重试'));
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Validation failed:', error);
|
||||
showError(t('请检查输入'));
|
||||
});
|
||||
} catch (error) {
|
||||
showError(t('请检查输入'));
|
||||
console.error(error);
|
||||
@@ -109,11 +122,15 @@ export default function SettingsChats(props) {
|
||||
<Form.Section text={t('令牌聊天设置')}>
|
||||
<Banner
|
||||
type='warning'
|
||||
description={t('必须将上方聊天链接全部设置为空,才能使用下方聊天设置功能')}
|
||||
description={t(
|
||||
'必须将上方聊天链接全部设置为空,才能使用下方聊天设置功能',
|
||||
)}
|
||||
/>
|
||||
<Banner
|
||||
type='info'
|
||||
description={t('链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1')}
|
||||
description={t(
|
||||
'链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1',
|
||||
)}
|
||||
/>
|
||||
<Form.TextArea
|
||||
label={t('聊天配置')}
|
||||
@@ -128,22 +145,20 @@ export default function SettingsChats(props) {
|
||||
validator: (rule, value) => {
|
||||
return verifyJSON(value);
|
||||
},
|
||||
message: t('不是合法的 JSON 字符串')
|
||||
}
|
||||
message: t('不是合法的 JSON 字符串'),
|
||||
},
|
||||
]}
|
||||
onChange={(value) =>
|
||||
setInputs({
|
||||
...inputs,
|
||||
Chats: value
|
||||
Chats: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Form.Section>
|
||||
</Form>
|
||||
<Space>
|
||||
<Button onClick={onSubmit}>
|
||||
{t('保存聊天设置')}
|
||||
</Button>
|
||||
<Button onClick={onSubmit}>{t('保存聊天设置')}</Button>
|
||||
</Space>
|
||||
</Spin>
|
||||
);
|
||||
|
||||
@@ -42,7 +42,8 @@ export default function SettingsCreditLimit(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
export default function DataDashboard(props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
const optionsDataExportDefaultTime = [
|
||||
{ key: 'hour', label: t('小时'), value: 'hour' },
|
||||
{ key: 'day', label: t('天'), value: 'day' },
|
||||
@@ -47,7 +47,8 @@ export default function DataDashboard(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
|
||||
@@ -44,7 +44,8 @@ export default function SettingsDrawing(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
@@ -146,7 +147,8 @@ export default function SettingsDrawing(props) {
|
||||
label={
|
||||
<>
|
||||
{t('开启之后会清除用户提示词中的')} <Tag>--fast</Tag> 、
|
||||
<Tag>--relax</Tag> {t('以及')} <Tag>--turbo</Tag> {t('参数')}
|
||||
<Tag>--relax</Tag> {t('以及')} <Tag>--turbo</Tag>{' '}
|
||||
{t('参数')}
|
||||
</>
|
||||
}
|
||||
size='default'
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { Banner, Button, Col, Form, Row, Spin, Collapse, Modal } from '@douyinfe/semi-ui';
|
||||
import {
|
||||
Banner,
|
||||
Button,
|
||||
Col,
|
||||
Form,
|
||||
Row,
|
||||
Spin,
|
||||
Collapse,
|
||||
Modal,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import {
|
||||
compareObjects,
|
||||
API,
|
||||
@@ -54,7 +63,8 @@ export default function GeneralSettings(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
@@ -198,7 +208,7 @@ export default function GeneralSettings(props) {
|
||||
</Form.Section>
|
||||
</Form>
|
||||
</Spin>
|
||||
|
||||
|
||||
<Modal
|
||||
title={t('警告')}
|
||||
visible={showQuotaWarning}
|
||||
@@ -209,7 +219,9 @@ export default function GeneralSettings(props) {
|
||||
>
|
||||
<Banner
|
||||
type='warning'
|
||||
description={t('此设置用于系统内部计算,默认值500000是为了精确到6位小数点设计,不推荐修改。')}
|
||||
description={t(
|
||||
'此设置用于系统内部计算,默认值500000是为了精确到6位小数点设计,不推荐修改。',
|
||||
)}
|
||||
bordered
|
||||
fullMode={false}
|
||||
closeIcon={null}
|
||||
|
||||
@@ -45,7 +45,8 @@ export default function SettingsLog(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
API,
|
||||
showError,
|
||||
showSuccess,
|
||||
showWarning, verifyJSON
|
||||
showWarning,
|
||||
verifyJSON,
|
||||
} from '../../../helpers';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -43,7 +44,8 @@ export default function SettingsMonitoring(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
@@ -67,7 +69,7 @@ export default function SettingsMonitoring(props) {
|
||||
setInputsRow(structuredClone(currentInputs));
|
||||
refForm.current.setValues(currentInputs);
|
||||
}, [props.options]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Spin spinning={loading}>
|
||||
@@ -84,7 +86,9 @@ export default function SettingsMonitoring(props) {
|
||||
step={1}
|
||||
min={0}
|
||||
suffix={t('秒')}
|
||||
extraText={t('当运行通道全部测试时,超过此时间将自动禁用通道')}
|
||||
extraText={t(
|
||||
'当运行通道全部测试时,超过此时间将自动禁用通道',
|
||||
)}
|
||||
placeholder={''}
|
||||
field={'ChannelDisableThreshold'}
|
||||
onChange={(value) =>
|
||||
@@ -150,10 +154,14 @@ export default function SettingsMonitoring(props) {
|
||||
<Form.TextArea
|
||||
label={t('自动禁用关键词')}
|
||||
placeholder={t('一行一个,不区分大小写')}
|
||||
extraText={t('当上游通道返回错误中包含这些关键词时(不区分大小写),自动禁用通道')}
|
||||
extraText={t(
|
||||
'当上游通道返回错误中包含这些关键词时(不区分大小写),自动禁用通道',
|
||||
)}
|
||||
field={'AutomaticDisableKeywords'}
|
||||
autosize={{ minRows: 6, maxRows: 12 }}
|
||||
onChange={(value) => setInputs({ ...inputs, AutomaticDisableKeywords: value })}
|
||||
onChange={(value) =>
|
||||
setInputs({ ...inputs, AutomaticDisableKeywords: value })
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -41,7 +41,8 @@ export default function SettingsSensitiveWords(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
|
||||
@@ -17,7 +17,7 @@ export default function RequestRateLimit(props) {
|
||||
ModelRequestRateLimitEnabled: false,
|
||||
ModelRequestRateLimitCount: -1,
|
||||
ModelRequestRateLimitSuccessCount: 1000,
|
||||
ModelRequestRateLimitDurationMinutes: 1
|
||||
ModelRequestRateLimitDurationMinutes: 1,
|
||||
});
|
||||
const refForm = useRef();
|
||||
const [inputsRow, setInputsRow] = useState(inputs);
|
||||
@@ -43,7 +43,8 @@ export default function RequestRateLimit(props) {
|
||||
if (requestQueue.length === 1) {
|
||||
if (res.includes(undefined)) return;
|
||||
} else if (requestQueue.length > 1) {
|
||||
if (res.includes(undefined)) return showError(t('部分保存失败,请重试'));
|
||||
if (res.includes(undefined))
|
||||
return showError(t('部分保存失败,请重试'));
|
||||
}
|
||||
showSuccess(t('保存成功'));
|
||||
props.refresh();
|
||||
|
||||
Reference in New Issue
Block a user