feat: enhance request handling with pass-through options and system prompt support

This commit is contained in:
CaIon
2025-07-26 11:39:09 +08:00
parent d6cbf43373
commit 1297addfb1
10 changed files with 417 additions and 70 deletions

View File

@@ -121,6 +121,12 @@ const EditChannelModal = (props) => {
weight: 0,
tag: '',
multi_key_mode: 'random',
// 渠道额外设置的默认值
force_format: false,
thinking_to_content: false,
proxy: '',
pass_through_body_enabled: false,
system_prompt: '',
};
const [batch, setBatch] = useState(false);
const [multiToSingle, setMultiToSingle] = useState(false);
@@ -142,8 +148,69 @@ const EditChannelModal = (props) => {
const [isMultiKeyChannel, setIsMultiKeyChannel] = useState(false);
const [channelSearchValue, setChannelSearchValue] = useState('');
const [useManualInput, setUseManualInput] = useState(false); // 是否使用手动输入模式
// 渠道额外设置状态
const [channelSettings, setChannelSettings] = useState({
force_format: false,
thinking_to_content: false,
proxy: '',
pass_through_body_enabled: false,
system_prompt: '',
});
const showApiConfigCard = inputs.type !== 45; // 控制是否显示 API 配置卡片(仅当渠道类型不是 豆包 时显示)
const getInitValues = () => ({ ...originInputs });
// 处理渠道额外设置的更新
const handleChannelSettingsChange = (key, value) => {
// 更新内部状态
setChannelSettings(prev => ({ ...prev, [key]: value }));
// 同步更新到表单字段
if (formApiRef.current) {
formApiRef.current.setValue(key, value);
}
// 同步更新inputs状态
setInputs(prev => ({ ...prev, [key]: value }));
// 生成setting JSON并更新
const newSettings = { ...channelSettings, [key]: value };
const settingsJson = JSON.stringify(newSettings);
handleInputChange('setting', settingsJson);
};
// 解析渠道设置JSON为单独的状态
const parseChannelSettings = (settingJson) => {
try {
if (settingJson && settingJson.trim()) {
const parsed = JSON.parse(settingJson);
setChannelSettings({
force_format: parsed.force_format || false,
thinking_to_content: parsed.thinking_to_content || false,
proxy: parsed.proxy || '',
pass_through_body_enabled: parsed.pass_through_body_enabled || false,
system_prompt: parsed.system_prompt || '',
});
} else {
setChannelSettings({
force_format: false,
thinking_to_content: false,
proxy: '',
pass_through_body_enabled: false,
system_prompt: '',
});
}
} catch (error) {
console.error('解析渠道设置失败:', error);
setChannelSettings({
force_format: false,
thinking_to_content: false,
proxy: '',
pass_through_body_enabled: false,
system_prompt: '',
});
}
};
const handleInputChange = (name, value) => {
if (formApiRef.current) {
formApiRef.current.setValue(name, value);
@@ -256,6 +323,30 @@ const EditChannelModal = (props) => {
setBatch(false);
setMultiToSingle(false);
}
// 解析渠道额外设置并合并到data中
if (data.setting) {
try {
const parsedSettings = JSON.parse(data.setting);
data.force_format = parsedSettings.force_format || false;
data.thinking_to_content = parsedSettings.thinking_to_content || false;
data.proxy = parsedSettings.proxy || '';
data.pass_through_body_enabled = parsedSettings.pass_through_body_enabled || false;
data.system_prompt = parsedSettings.system_prompt || '';
} catch (error) {
console.error('解析渠道设置失败:', error);
data.force_format = false;
data.thinking_to_content = false;
data.proxy = '';
data.pass_through_body_enabled = false;
}
} else {
data.force_format = false;
data.thinking_to_content = false;
data.proxy = '';
data.pass_through_body_enabled = false;
data.system_prompt = '';
}
setInputs(data);
if (formApiRef.current) {
formApiRef.current.setValues(data);
@@ -266,6 +357,14 @@ const EditChannelModal = (props) => {
setAutoBan(true);
}
setBasicModels(getChannelModels(data.type));
// 同步更新channelSettings状态显示
setChannelSettings({
force_format: data.force_format,
thinking_to_content: data.thinking_to_content,
proxy: data.proxy,
pass_through_body_enabled: data.pass_through_body_enabled,
system_prompt: data.system_prompt,
});
// console.log(data);
} else {
showError(message);
@@ -446,6 +545,14 @@ const EditChannelModal = (props) => {
setUseManualInput(false);
} else {
formApiRef.current?.reset();
// 重置渠道设置状态
setChannelSettings({
force_format: false,
thinking_to_content: false,
proxy: '',
pass_through_body_enabled: false,
system_prompt: '',
});
}
}, [props.visible, channelId]);
@@ -579,6 +686,24 @@ const EditChannelModal = (props) => {
if (localInputs.type === 18 && localInputs.other === '') {
localInputs.other = 'v2.1';
}
// 生成渠道额外设置JSON
const channelExtraSettings = {
force_format: localInputs.force_format || false,
thinking_to_content: localInputs.thinking_to_content || false,
proxy: localInputs.proxy || '',
pass_through_body_enabled: localInputs.pass_through_body_enabled || false,
system_prompt: localInputs.system_prompt || '',
};
localInputs.setting = JSON.stringify(channelExtraSettings);
// 清理不需要发送到后端的字段
delete localInputs.force_format;
delete localInputs.thinking_to_content;
delete localInputs.proxy;
delete localInputs.pass_through_body_enabled;
delete localInputs.system_prompt;
let res;
localInputs.auto_ban = localInputs.auto_ban ? 1 : 0;
localInputs.models = localInputs.models.join(',');
@@ -1446,33 +1571,103 @@ const EditChannelModal = (props) => {
showClear
/>
<Form.TextArea
field='setting'
label={t('渠道额外设置')}
placeholder={
t('此项可选,用于配置渠道特定设置,为一个 JSON 字符串,例如:') +
'\n{\n "force_format": true\n}'
}
autosize
onChange={(value) => handleInputChange('setting', value)}
extraText={(
<Space wrap>
<div className="mb-6">
<Text className="text-sm font-medium mb-3 block">
{t('渠道额外设置')}
</Text>
<div className="space-y-6 p-4 bg-gray-50 rounded-lg">
<Row gutter={16}>
<Col span={16}>
<div>
<Text className="font-medium block mb-1">{t('强制格式化只适用于OpenAI渠道类型')}</Text>
<Text type="tertiary" size="small">
{t('强制将响应格式化为 OpenAI 标准格式')}
</Text>
</div>
</Col>
<Col span={8} className="flex items-center justify-end">
<Form.Switch
field='force_format'
checkedText={t('开')}
uncheckedText={t('关')}
onChange={(val) => handleChannelSettingsChange('force_format', val)}
/>
</Col>
</Row>
<Row gutter={16}>
<Col span={16}>
<div>
<Text className="font-medium block mb-1">{t('思考内容转换')}</Text>
<Text type="tertiary" size="small">
{t('将 reasoning_content 转换为 <think> 标签拼接到内容中')}
</Text>
</div>
</Col>
<Col span={8} className="flex items-center justify-end">
<Form.Switch
field='thinking_to_content'
checkedText={t('开')}
uncheckedText={t('关')}
onChange={(val) => handleChannelSettingsChange('thinking_to_content', val)}
/>
</Col>
</Row>
<Row gutter={16}>
<Col span={16}>
<div>
<Text className="font-medium block mb-1">{t('透传请求体')}</Text>
<Text type="tertiary" size="small">
{t('启用请求体透传功能')}
</Text>
</div>
</Col>
<Col span={8} className="flex items-center justify-end">
<Form.Switch
field='pass_through_body_enabled'
checkedText={t('开')}
uncheckedText={t('关')}
onChange={(val) => handleChannelSettingsChange('pass_through_body_enabled', val)}
/>
</Col>
</Row>
<div>
<Form.Input
field='proxy'
label={t('代理地址')}
placeholder={t('例如: socks5://user:pass@host:port')}
onChange={(val) => handleChannelSettingsChange('proxy', val)}
showClear
helpText={t('用于配置网络代理')}
/>
</div>
<div>
<Form.TextArea
field='system_prompt'
label={t('系统提示词')}
placeholder={t('输入系统提示词,用户的系统提示词将优先于此设置')}
onChange={(val) => handleChannelSettingsChange('system_prompt', val)}
autosize
showClear
helpText={t('用户优先:如果用户在请求中指定了系统提示词,将优先使用用户的设置')}
/>
</div>
<div className="text-right pt-2 border-t border-gray-200">
<Text
className="!text-semi-color-primary cursor-pointer"
onClick={() => handleInputChange('setting', JSON.stringify({ force_format: true }, null, 2))}
>
{t('填入模板')}
</Text>
<Text
className="!text-semi-color-primary cursor-pointer"
className="!text-semi-color-primary cursor-pointer text-sm"
onClick={() => window.open('https://github.com/QuantumNous/new-api/blob/main/docs/channel/other_setting.md')}
>
{t('设置说明')}
</Text>
</Space>
)}
showClear
/>
</div>
</div>
</div>
</Card>
</div>
</Spin>