diff --git a/controller/channel.go b/controller/channel.go index eba990955..7bdaf437e 100644 --- a/controller/channel.go +++ b/controller/channel.go @@ -649,13 +649,15 @@ func DeleteDisabledChannel(c *gin.Context) { } type ChannelTag struct { - Tag string `json:"tag"` - NewTag *string `json:"new_tag"` - Priority *int64 `json:"priority"` - Weight *uint `json:"weight"` - ModelMapping *string `json:"model_mapping"` - Models *string `json:"models"` - Groups *string `json:"groups"` + Tag string `json:"tag"` + NewTag *string `json:"new_tag"` + Priority *int64 `json:"priority"` + Weight *uint `json:"weight"` + ModelMapping *string `json:"model_mapping"` + Models *string `json:"models"` + Groups *string `json:"groups"` + ParamOverride *string `json:"param_override"` + HeaderOverride *string `json:"header_override"` } func DisableTagChannels(c *gin.Context) { @@ -721,7 +723,29 @@ func EditTagChannels(c *gin.Context) { }) return } - err = model.EditChannelByTag(channelTag.Tag, channelTag.NewTag, channelTag.ModelMapping, channelTag.Models, channelTag.Groups, channelTag.Priority, channelTag.Weight) + if channelTag.ParamOverride != nil { + trimmed := strings.TrimSpace(*channelTag.ParamOverride) + if trimmed != "" && !json.Valid([]byte(trimmed)) { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "参数覆盖必须是合法的 JSON 格式", + }) + return + } + channelTag.ParamOverride = common.GetPointer[string](trimmed) + } + if channelTag.HeaderOverride != nil { + trimmed := strings.TrimSpace(*channelTag.HeaderOverride) + if trimmed != "" && !json.Valid([]byte(trimmed)) { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "请求头覆盖必须是合法的 JSON 格式", + }) + return + } + channelTag.HeaderOverride = common.GetPointer[string](trimmed) + } + err = model.EditChannelByTag(channelTag.Tag, channelTag.NewTag, channelTag.ModelMapping, channelTag.Models, channelTag.Groups, channelTag.Priority, channelTag.Weight, channelTag.ParamOverride, channelTag.HeaderOverride) if err != nil { common.ApiError(c, err) return diff --git a/model/channel.go b/model/channel.go index d01f00c97..1dccaf2f2 100644 --- a/model/channel.go +++ b/model/channel.go @@ -688,7 +688,7 @@ func DisableChannelByTag(tag string) error { return err } -func EditChannelByTag(tag string, newTag *string, modelMapping *string, models *string, group *string, priority *int64, weight *uint) error { +func EditChannelByTag(tag string, newTag *string, modelMapping *string, models *string, group *string, priority *int64, weight *uint, paramOverride *string, headerOverride *string) error { updateData := Channel{} shouldReCreateAbilities := false updatedTag := tag @@ -714,6 +714,12 @@ func EditChannelByTag(tag string, newTag *string, modelMapping *string, models * if weight != nil { updateData.Weight = weight } + if paramOverride != nil { + updateData.ParamOverride = paramOverride + } + if headerOverride != nil { + updateData.HeaderOverride = headerOverride + } err := DB.Model(&Channel{}).Where("tag = ?", tag).Updates(updateData).Error if err != nil { diff --git a/web/src/components/table/channels/modals/EditTagModal.jsx b/web/src/components/table/channels/modals/EditTagModal.jsx index 0b2f17d90..a8e303593 100644 --- a/web/src/components/table/channels/modals/EditTagModal.jsx +++ b/web/src/components/table/channels/modals/EditTagModal.jsx @@ -45,6 +45,7 @@ import { IconBookmark, IconUser, IconCode, + IconSetting, } from '@douyinfe/semi-icons'; import { getChannelModels } from '../../../../helpers'; import { useTranslation } from 'react-i18next'; @@ -69,6 +70,8 @@ const EditTagModal = (props) => { model_mapping: null, groups: [], models: [], + param_override: null, + header_override: null, }; const [inputs, setInputs] = useState(originInputs); const formApiRef = useRef(null); @@ -190,12 +193,48 @@ const EditTagModal = (props) => { if (formVals.models && formVals.models.length > 0) { data.models = formVals.models.join(','); } + if ( + formVals.param_override !== undefined && + formVals.param_override !== null + ) { + if (typeof formVals.param_override !== 'string') { + showInfo('参数覆盖必须是合法的 JSON 格式!'); + setLoading(false); + return; + } + const trimmedParamOverride = formVals.param_override.trim(); + if (trimmedParamOverride !== '' && !verifyJSON(trimmedParamOverride)) { + showInfo('参数覆盖必须是合法的 JSON 格式!'); + setLoading(false); + return; + } + data.param_override = trimmedParamOverride; + } + if ( + formVals.header_override !== undefined && + formVals.header_override !== null + ) { + if (typeof formVals.header_override !== 'string') { + showInfo('请求头覆盖必须是合法的 JSON 格式!'); + setLoading(false); + return; + } + const trimmedHeaderOverride = formVals.header_override.trim(); + if (trimmedHeaderOverride !== '' && !verifyJSON(trimmedHeaderOverride)) { + showInfo('请求头覆盖必须是合法的 JSON 格式!'); + setLoading(false); + return; + } + data.header_override = trimmedHeaderOverride; + } data.new_tag = formVals.new_tag; if ( data.model_mapping === undefined && data.groups === undefined && data.models === undefined && - data.new_tag === undefined + data.new_tag === undefined && + data.param_override === undefined && + data.header_override === undefined ) { showWarning('没有任何修改!'); setLoading(false); @@ -491,6 +530,157 @@ const EditTagModal = (props) => { + + {/* Header: Advanced Settings */} +
+ + + +
+ {t('高级设置')} +
+ {t('渠道的高级配置选项')} +
+
+
+ +
+ + handleInputChange('param_override', value) + } + extraText={ +
+ + handleInputChange( + 'param_override', + JSON.stringify({ temperature: 0 }, null, 2), + ) + } + > + {t('旧格式模板')} + + + handleInputChange( + 'param_override', + JSON.stringify( + { + operations: [ + { + path: 'temperature', + mode: 'set', + value: 0.7, + conditions: [ + { + path: 'model', + mode: 'prefix', + value: 'gpt', + }, + ], + logic: 'AND', + }, + ], + }, + null, + 2, + ), + ) + } + > + {t('新格式模板')} + + + handleInputChange('param_override', null) + } + > + {t('不更改')} + +
+ } + /> + + + handleInputChange('header_override', value) + } + extraText={ +
+
+ + handleInputChange( + 'header_override', + JSON.stringify( + { + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0', + Authorization: 'Bearer {api_key}', + }, + null, + 2, + ), + ) + } + > + {t('填入模板')} + + + handleInputChange('header_override', null) + } + > + {t('不更改')} + +
+
+ + {t('支持变量:')} + +
+
+ {t('渠道密钥')}: {'{api_key}'} +
+
+
+
+ } + /> +
+
+ {/* Header: Group Settings */}