feat: redesign param override editing with guided modal and Monaco JSON hints
This commit is contained in:
parent
7633863c96
commit
b7bfa12837
@ -7,6 +7,7 @@
|
|||||||
"@douyinfe/semi-icons": "^2.63.1",
|
"@douyinfe/semi-icons": "^2.63.1",
|
||||||
"@douyinfe/semi-ui": "^2.69.1",
|
"@douyinfe/semi-ui": "^2.69.1",
|
||||||
"@lobehub/icons": "^2.0.0",
|
"@lobehub/icons": "^2.0.0",
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@visactor/react-vchart": "~1.8.8",
|
"@visactor/react-vchart": "~1.8.8",
|
||||||
"@visactor/vchart": "~1.8.8",
|
"@visactor/vchart": "~1.8.8",
|
||||||
"@visactor/vchart-semi-theme": "~1.8.8",
|
"@visactor/vchart-semi-theme": "~1.8.8",
|
||||||
@ -20,6 +21,7 @@
|
|||||||
"lucide-react": "^0.511.0",
|
"lucide-react": "^0.511.0",
|
||||||
"marked": "^4.1.1",
|
"marked": "^4.1.1",
|
||||||
"mermaid": "^11.6.0",
|
"mermaid": "^11.6.0",
|
||||||
|
"monaco-editor": "^0.55.1",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
|
|||||||
@ -59,6 +59,7 @@ import ModelSelectModal from './ModelSelectModal';
|
|||||||
import SingleModelSelectModal from './SingleModelSelectModal';
|
import SingleModelSelectModal from './SingleModelSelectModal';
|
||||||
import OllamaModelModal from './OllamaModelModal';
|
import OllamaModelModal from './OllamaModelModal';
|
||||||
import CodexOAuthModal from './CodexOAuthModal';
|
import CodexOAuthModal from './CodexOAuthModal';
|
||||||
|
import ParamOverrideEditorModal from './ParamOverrideEditorModal';
|
||||||
import JSONEditor from '../../../common/ui/JSONEditor';
|
import JSONEditor from '../../../common/ui/JSONEditor';
|
||||||
import SecureVerificationModal from '../../../common/modals/SecureVerificationModal';
|
import SecureVerificationModal from '../../../common/modals/SecureVerificationModal';
|
||||||
import ChannelKeyDisplay from '../../../common/ui/ChannelKeyDisplay';
|
import ChannelKeyDisplay from '../../../common/ui/ChannelKeyDisplay';
|
||||||
@ -143,6 +144,7 @@ const EditChannelModal = (props) => {
|
|||||||
base_url: '',
|
base_url: '',
|
||||||
other: '',
|
other: '',
|
||||||
model_mapping: '',
|
model_mapping: '',
|
||||||
|
param_override: '',
|
||||||
status_code_mapping: '',
|
status_code_mapping: '',
|
||||||
models: [],
|
models: [],
|
||||||
auto_ban: 1,
|
auto_ban: 1,
|
||||||
@ -224,11 +226,69 @@ const EditChannelModal = (props) => {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}, [inputs.model_mapping]);
|
}, [inputs.model_mapping]);
|
||||||
|
const paramOverrideMeta = useMemo(() => {
|
||||||
|
const raw =
|
||||||
|
typeof inputs.param_override === 'string'
|
||||||
|
? inputs.param_override.trim()
|
||||||
|
: '';
|
||||||
|
if (!raw) {
|
||||||
|
return {
|
||||||
|
tagLabel: t('不更改'),
|
||||||
|
tagColor: 'grey',
|
||||||
|
preview: t(
|
||||||
|
'此项可选,用于覆盖请求参数。不支持覆盖 stream 参数',
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!verifyJSON(raw)) {
|
||||||
|
return {
|
||||||
|
tagLabel: t('JSON格式错误'),
|
||||||
|
tagColor: 'red',
|
||||||
|
preview: raw,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
const pretty = JSON.stringify(parsed, null, 2);
|
||||||
|
if (
|
||||||
|
parsed &&
|
||||||
|
typeof parsed === 'object' &&
|
||||||
|
!Array.isArray(parsed) &&
|
||||||
|
Array.isArray(parsed.operations)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
tagLabel: `${t('新格式模板')} (${parsed.operations.length})`,
|
||||||
|
tagColor: 'cyan',
|
||||||
|
preview: pretty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
||||||
|
return {
|
||||||
|
tagLabel: `${t('旧格式模板')} (${Object.keys(parsed).length})`,
|
||||||
|
tagColor: 'blue',
|
||||||
|
preview: pretty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
tagLabel: 'Custom JSON',
|
||||||
|
tagColor: 'orange',
|
||||||
|
preview: pretty,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
tagLabel: t('JSON格式错误'),
|
||||||
|
tagColor: 'red',
|
||||||
|
preview: raw,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [inputs.param_override, t]);
|
||||||
const [isIonetChannel, setIsIonetChannel] = useState(false);
|
const [isIonetChannel, setIsIonetChannel] = useState(false);
|
||||||
const [ionetMetadata, setIonetMetadata] = useState(null);
|
const [ionetMetadata, setIonetMetadata] = useState(null);
|
||||||
const [codexOAuthModalVisible, setCodexOAuthModalVisible] = useState(false);
|
const [codexOAuthModalVisible, setCodexOAuthModalVisible] = useState(false);
|
||||||
const [codexCredentialRefreshing, setCodexCredentialRefreshing] =
|
const [codexCredentialRefreshing, setCodexCredentialRefreshing] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
const [paramOverrideEditorVisible, setParamOverrideEditorVisible] =
|
||||||
|
useState(false);
|
||||||
|
|
||||||
// 密钥显示状态
|
// 密钥显示状态
|
||||||
const [keyDisplayState, setKeyDisplayState] = useState({
|
const [keyDisplayState, setKeyDisplayState] = useState({
|
||||||
@ -1170,6 +1230,7 @@ const EditChannelModal = (props) => {
|
|||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
const formValues = formApiRef.current ? formApiRef.current.getValues() : {};
|
const formValues = formApiRef.current ? formApiRef.current.getValues() : {};
|
||||||
let localInputs = { ...formValues };
|
let localInputs = { ...formValues };
|
||||||
|
localInputs.param_override = inputs.param_override;
|
||||||
|
|
||||||
if (localInputs.type === 57) {
|
if (localInputs.type === 57) {
|
||||||
if (batch) {
|
if (batch) {
|
||||||
@ -3043,28 +3104,20 @@ const EditChannelModal = (props) => {
|
|||||||
initValue={autoBan}
|
initValue={autoBan}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Form.TextArea
|
<div className='mb-4'>
|
||||||
field='param_override'
|
<div className='flex items-center justify-between gap-2 mb-1'>
|
||||||
label={t('参数覆盖')}
|
<Text className='text-sm font-medium'>{t('参数覆盖')}</Text>
|
||||||
placeholder={
|
<Space wrap>
|
||||||
t(
|
<Button
|
||||||
'此项可选,用于覆盖请求参数。不支持覆盖 stream 参数',
|
size='small'
|
||||||
) +
|
type='primary'
|
||||||
'\n' +
|
icon={<IconCode size={14} />}
|
||||||
t('旧格式(直接覆盖):') +
|
onClick={() => setParamOverrideEditorVisible(true)}
|
||||||
'\n{\n "temperature": 0,\n "max_tokens": 1000\n}' +
|
>
|
||||||
'\n\n' +
|
{t('可视化编辑')}
|
||||||
t('新格式(支持条件判断与json自定义):') +
|
</Button>
|
||||||
'\n{\n "operations": [\n {\n "path": "temperature",\n "mode": "set",\n "value": 0.7,\n "conditions": [\n {\n "path": "model",\n "mode": "prefix",\n "value": "gpt"\n }\n ]\n }\n ]\n}'
|
<Button
|
||||||
}
|
size='small'
|
||||||
autosize
|
|
||||||
onChange={(value) =>
|
|
||||||
handleInputChange('param_override', value)
|
|
||||||
}
|
|
||||||
extraText={
|
|
||||||
<div className='flex gap-2 flex-wrap'>
|
|
||||||
<Text
|
|
||||||
className='!text-semi-color-primary cursor-pointer'
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleInputChange(
|
handleInputChange(
|
||||||
'param_override',
|
'param_override',
|
||||||
@ -3073,9 +3126,9 @@ const EditChannelModal = (props) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{t('旧格式模板')}
|
{t('旧格式模板')}
|
||||||
</Text>
|
</Button>
|
||||||
<Text
|
<Button
|
||||||
className='!text-semi-color-primary cursor-pointer'
|
size='small'
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleInputChange(
|
handleInputChange(
|
||||||
'param_override',
|
'param_override',
|
||||||
@ -3104,17 +3157,40 @@ const EditChannelModal = (props) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{t('新格式模板')}
|
{t('新格式模板')}
|
||||||
</Text>
|
</Button>
|
||||||
<Text
|
<Button
|
||||||
className='!text-semi-color-primary cursor-pointer'
|
size='small'
|
||||||
onClick={() => formatJsonField('param_override')}
|
type='tertiary'
|
||||||
|
onClick={() => handleInputChange('param_override', '')}
|
||||||
>
|
>
|
||||||
{t('格式化')}
|
{t('不更改')}
|
||||||
</Text>
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
<Text type='tertiary' size='small'>
|
||||||
|
{t('此项可选,用于覆盖请求参数。不支持覆盖 stream 参数')}
|
||||||
|
</Text>
|
||||||
|
<div
|
||||||
|
className='mt-2 rounded-lg border p-3'
|
||||||
|
style={{ backgroundColor: 'var(--semi-color-fill-0)' }}
|
||||||
|
>
|
||||||
|
<div className='flex items-center justify-between mb-2'>
|
||||||
|
<Tag color={paramOverrideMeta.tagColor}>
|
||||||
|
{paramOverrideMeta.tagLabel}
|
||||||
|
</Tag>
|
||||||
|
<Button
|
||||||
|
size='small'
|
||||||
|
type='tertiary'
|
||||||
|
onClick={() => setParamOverrideEditorVisible(true)}
|
||||||
|
>
|
||||||
|
{t('编辑')}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
}
|
<pre className='mb-0 text-xs leading-5 whitespace-pre-wrap break-all max-h-56 overflow-auto'>
|
||||||
showClear
|
{paramOverrideMeta.preview}
|
||||||
/>
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
field='header_override'
|
field='header_override'
|
||||||
@ -3494,6 +3570,16 @@ const EditChannelModal = (props) => {
|
|||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<ParamOverrideEditorModal
|
||||||
|
visible={paramOverrideEditorVisible}
|
||||||
|
value={inputs.param_override || ''}
|
||||||
|
onCancel={() => setParamOverrideEditorVisible(false)}
|
||||||
|
onSave={(nextValue) => {
|
||||||
|
handleInputChange('param_override', nextValue);
|
||||||
|
setParamOverrideEditorVisible(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<ModelSelectModal
|
<ModelSelectModal
|
||||||
visible={modelModalVisible}
|
visible={modelModalVisible}
|
||||||
models={fetchedModels}
|
models={fetchedModels}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user