|
@@ -71,6 +71,7 @@ import {
|
|
|
IconServer,
|
|
IconServer,
|
|
|
IconSetting,
|
|
IconSetting,
|
|
|
IconCode,
|
|
IconCode,
|
|
|
|
|
+ IconCopy,
|
|
|
IconGlobe,
|
|
IconGlobe,
|
|
|
IconBolt,
|
|
IconBolt,
|
|
|
IconSearch,
|
|
IconSearch,
|
|
@@ -95,6 +96,28 @@ const REGION_EXAMPLE = {
|
|
|
'claude-3-5-sonnet-20240620': 'europe-west1',
|
|
'claude-3-5-sonnet-20240620': 'europe-west1',
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const PARAM_OVERRIDE_LEGACY_TEMPLATE = {
|
|
|
|
|
+ temperature: 0,
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const PARAM_OVERRIDE_OPERATIONS_TEMPLATE = {
|
|
|
|
|
+ operations: [
|
|
|
|
|
+ {
|
|
|
|
|
+ path: 'temperature',
|
|
|
|
|
+ mode: 'set',
|
|
|
|
|
+ value: 0.7,
|
|
|
|
|
+ conditions: [
|
|
|
|
|
+ {
|
|
|
|
|
+ path: 'model',
|
|
|
|
|
+ mode: 'prefix',
|
|
|
|
|
+ value: 'openai/',
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ logic: 'AND',
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
// 支持并且已适配通过接口获取模型列表的渠道类型
|
|
// 支持并且已适配通过接口获取模型列表的渠道类型
|
|
|
const MODEL_FETCHABLE_TYPES = new Set([
|
|
const MODEL_FETCHABLE_TYPES = new Set([
|
|
|
1, 4, 14, 34, 17, 26, 27, 24, 47, 25, 20, 23, 31, 40, 42, 48, 43,
|
|
1, 4, 14, 34, 17, 26, 27, 24, 47, 25, 20, 23, 31, 40, 42, 48, 43,
|
|
@@ -270,7 +293,7 @@ const EditChannelModal = (props) => {
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
return {
|
|
return {
|
|
|
- tagLabel: 'Custom JSON',
|
|
|
|
|
|
|
+ tagLabel: t('自定义 JSON'),
|
|
|
tagColor: 'orange',
|
|
tagColor: 'orange',
|
|
|
preview: pretty,
|
|
preview: pretty,
|
|
|
};
|
|
};
|
|
@@ -608,6 +631,100 @@ const EditChannelModal = (props) => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const copyParamOverrideJson = async () => {
|
|
|
|
|
+ const raw =
|
|
|
|
|
+ typeof inputs.param_override === 'string'
|
|
|
|
|
+ ? inputs.param_override.trim()
|
|
|
|
|
+ : '';
|
|
|
|
|
+ if (!raw) {
|
|
|
|
|
+ showInfo(t('暂无可复制 JSON'));
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let content = raw;
|
|
|
|
|
+ if (verifyJSON(raw)) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ content = JSON.stringify(JSON.parse(raw), null, 2);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ content = raw;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const ok = await copy(content);
|
|
|
|
|
+ if (ok) {
|
|
|
|
|
+ showSuccess(t('参数覆盖 JSON 已复制'));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showError(t('复制失败'));
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const parseParamOverrideInput = () => {
|
|
|
|
|
+ const raw =
|
|
|
|
|
+ typeof inputs.param_override === 'string'
|
|
|
|
|
+ ? inputs.param_override.trim()
|
|
|
|
|
+ : '';
|
|
|
|
|
+ if (!raw) return null;
|
|
|
|
|
+ if (!verifyJSON(raw)) {
|
|
|
|
|
+ throw new Error(t('当前参数覆盖不是合法的 JSON'));
|
|
|
|
|
+ }
|
|
|
|
|
+ return JSON.parse(raw);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const applyParamOverrideTemplate = (
|
|
|
|
|
+ templateType = 'operations',
|
|
|
|
|
+ applyMode = 'fill',
|
|
|
|
|
+ ) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const parsedCurrent = parseParamOverrideInput();
|
|
|
|
|
+ if (templateType === 'legacy') {
|
|
|
|
|
+ if (applyMode === 'fill') {
|
|
|
|
|
+ handleInputChange(
|
|
|
|
|
+ 'param_override',
|
|
|
|
|
+ JSON.stringify(PARAM_OVERRIDE_LEGACY_TEMPLATE, null, 2),
|
|
|
|
|
+ );
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const currentLegacy =
|
|
|
|
|
+ parsedCurrent &&
|
|
|
|
|
+ typeof parsedCurrent === 'object' &&
|
|
|
|
|
+ !Array.isArray(parsedCurrent) &&
|
|
|
|
|
+ !Array.isArray(parsedCurrent.operations)
|
|
|
|
|
+ ? parsedCurrent
|
|
|
|
|
+ : {};
|
|
|
|
|
+ const merged = {
|
|
|
|
|
+ ...PARAM_OVERRIDE_LEGACY_TEMPLATE,
|
|
|
|
|
+ ...currentLegacy,
|
|
|
|
|
+ };
|
|
|
|
|
+ handleInputChange('param_override', JSON.stringify(merged, null, 2));
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (applyMode === 'fill') {
|
|
|
|
|
+ handleInputChange(
|
|
|
|
|
+ 'param_override',
|
|
|
|
|
+ JSON.stringify(PARAM_OVERRIDE_OPERATIONS_TEMPLATE, null, 2),
|
|
|
|
|
+ );
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const currentOperations =
|
|
|
|
|
+ parsedCurrent &&
|
|
|
|
|
+ typeof parsedCurrent === 'object' &&
|
|
|
|
|
+ !Array.isArray(parsedCurrent) &&
|
|
|
|
|
+ Array.isArray(parsedCurrent.operations)
|
|
|
|
|
+ ? parsedCurrent.operations
|
|
|
|
|
+ : [];
|
|
|
|
|
+ const merged = {
|
|
|
|
|
+ operations: [
|
|
|
|
|
+ ...currentOperations,
|
|
|
|
|
+ ...PARAM_OVERRIDE_OPERATIONS_TEMPLATE.operations,
|
|
|
|
|
+ ],
|
|
|
|
|
+ };
|
|
|
|
|
+ handleInputChange('param_override', JSON.stringify(merged, null, 2));
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ showError(error.message || t('模板应用失败'));
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
const loadChannel = async () => {
|
|
const loadChannel = async () => {
|
|
|
setLoading(true);
|
|
setLoading(true);
|
|
|
let res = await API.get(`/api/channel/${channelId}`);
|
|
let res = await API.get(`/api/channel/${channelId}`);
|
|
@@ -3119,51 +3236,18 @@ const EditChannelModal = (props) => {
|
|
|
<Button
|
|
<Button
|
|
|
size='small'
|
|
size='small'
|
|
|
onClick={() =>
|
|
onClick={() =>
|
|
|
- handleInputChange(
|
|
|
|
|
- 'param_override',
|
|
|
|
|
- JSON.stringify({ temperature: 0 }, null, 2),
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ applyParamOverrideTemplate('operations', 'fill')
|
|
|
}
|
|
}
|
|
|
>
|
|
>
|
|
|
- {t('旧格式模板')}
|
|
|
|
|
|
|
+ {t('填充新模板')}
|
|
|
</Button>
|
|
</Button>
|
|
|
<Button
|
|
<Button
|
|
|
size='small'
|
|
size='small'
|
|
|
onClick={() =>
|
|
onClick={() =>
|
|
|
- handleInputChange(
|
|
|
|
|
- 'param_override',
|
|
|
|
|
- JSON.stringify(
|
|
|
|
|
- {
|
|
|
|
|
- operations: [
|
|
|
|
|
- {
|
|
|
|
|
- path: 'temperature',
|
|
|
|
|
- mode: 'set',
|
|
|
|
|
- value: 0.7,
|
|
|
|
|
- conditions: [
|
|
|
|
|
- {
|
|
|
|
|
- path: 'model',
|
|
|
|
|
- mode: 'prefix',
|
|
|
|
|
- value: 'gpt',
|
|
|
|
|
- },
|
|
|
|
|
- ],
|
|
|
|
|
- logic: 'AND',
|
|
|
|
|
- },
|
|
|
|
|
- ],
|
|
|
|
|
- },
|
|
|
|
|
- null,
|
|
|
|
|
- 2,
|
|
|
|
|
- ),
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ applyParamOverrideTemplate('legacy', 'fill')
|
|
|
}
|
|
}
|
|
|
>
|
|
>
|
|
|
- {t('新格式模板')}
|
|
|
|
|
- </Button>
|
|
|
|
|
- <Button
|
|
|
|
|
- size='small'
|
|
|
|
|
- type='tertiary'
|
|
|
|
|
- onClick={() => handleInputChange('param_override', '')}
|
|
|
|
|
- >
|
|
|
|
|
- {t('不更改')}
|
|
|
|
|
|
|
+ {t('填充旧模板')}
|
|
|
</Button>
|
|
</Button>
|
|
|
</Space>
|
|
</Space>
|
|
|
</div>
|
|
</div>
|
|
@@ -3171,20 +3255,33 @@ const EditChannelModal = (props) => {
|
|
|
{t('此项可选,用于覆盖请求参数。不支持覆盖 stream 参数')}
|
|
{t('此项可选,用于覆盖请求参数。不支持覆盖 stream 参数')}
|
|
|
</Text>
|
|
</Text>
|
|
|
<div
|
|
<div
|
|
|
- className='mt-2 rounded-lg border p-3'
|
|
|
|
|
- style={{ backgroundColor: 'var(--semi-color-fill-0)' }}
|
|
|
|
|
|
|
+ className='mt-2 rounded-xl p-3'
|
|
|
|
|
+ style={{
|
|
|
|
|
+ backgroundColor: 'var(--semi-color-fill-0)',
|
|
|
|
|
+ border: '1px solid var(--semi-color-fill-2)',
|
|
|
|
|
+ }}
|
|
|
>
|
|
>
|
|
|
<div className='flex items-center justify-between mb-2'>
|
|
<div className='flex items-center justify-between mb-2'>
|
|
|
<Tag color={paramOverrideMeta.tagColor}>
|
|
<Tag color={paramOverrideMeta.tagColor}>
|
|
|
{paramOverrideMeta.tagLabel}
|
|
{paramOverrideMeta.tagLabel}
|
|
|
</Tag>
|
|
</Tag>
|
|
|
- <Button
|
|
|
|
|
- size='small'
|
|
|
|
|
- type='tertiary'
|
|
|
|
|
- onClick={() => setParamOverrideEditorVisible(true)}
|
|
|
|
|
- >
|
|
|
|
|
- {t('编辑')}
|
|
|
|
|
- </Button>
|
|
|
|
|
|
|
+ <Space spacing={8}>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ size='small'
|
|
|
|
|
+ icon={<IconCopy />}
|
|
|
|
|
+ type='tertiary'
|
|
|
|
|
+ onClick={copyParamOverrideJson}
|
|
|
|
|
+ >
|
|
|
|
|
+ {t('复制')}
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ size='small'
|
|
|
|
|
+ type='tertiary'
|
|
|
|
|
+ onClick={() => setParamOverrideEditorVisible(true)}
|
|
|
|
|
+ >
|
|
|
|
|
+ {t('编辑')}
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </Space>
|
|
|
</div>
|
|
</div>
|
|
|
<pre className='mb-0 text-xs leading-5 whitespace-pre-wrap break-all max-h-56 overflow-auto'>
|
|
<pre className='mb-0 text-xs leading-5 whitespace-pre-wrap break-all max-h-56 overflow-auto'>
|
|
|
{paramOverrideMeta.preview}
|
|
{paramOverrideMeta.preview}
|