diff --git a/constant/context_key.go b/constant/context_key.go index 26ff1738..b82b19e7 100644 --- a/constant/context_key.go +++ b/constant/context_key.go @@ -22,6 +22,7 @@ const ( ContextKeyChannelBaseUrl ContextKey = "base_url" ContextKeyChannelType ContextKey = "channel_type" ContextKeyChannelSetting ContextKey = "channel_setting" + ContextKeyChannelOtherSetting ContextKey = "channel_other_setting" ContextKeyChannelParamOverride ContextKey = "param_override" ContextKeyChannelOrganization ContextKey = "channel_organization" ContextKeyChannelAutoBan ContextKey = "auto_ban" diff --git a/dto/channel_settings.go b/dto/channel_settings.go index 1c697048..2c58795c 100644 --- a/dto/channel_settings.go +++ b/dto/channel_settings.go @@ -8,3 +8,7 @@ type ChannelSettings struct { SystemPrompt string `json:"system_prompt,omitempty"` SystemPromptOverride bool `json:"system_prompt_override,omitempty"` } + +type ChannelOtherSettings struct { + AzureResponsesVersion string `json:"azure_responses_version,omitempty"` +} diff --git a/middleware/distributor.go b/middleware/distributor.go index dea30abf..751f58ef 100644 --- a/middleware/distributor.go +++ b/middleware/distributor.go @@ -244,6 +244,7 @@ func SetupContextForSelectedChannel(c *gin.Context, channel *model.Channel, mode common.SetContextKey(c, constant.ContextKeyChannelType, channel.Type) common.SetContextKey(c, constant.ContextKeyChannelCreateTime, channel.CreatedTime) common.SetContextKey(c, constant.ContextKeyChannelSetting, channel.GetSetting()) + common.SetContextKey(c, constant.ContextKeyChannelOtherSetting, channel.GetOtherSettings()) common.SetContextKey(c, constant.ContextKeyChannelParamOverride, channel.GetParamOverride()) if nil != channel.OpenAIOrganization && *channel.OpenAIOrganization != "" { common.SetContextKey(c, constant.ContextKeyChannelOrganization, *channel.OpenAIOrganization) diff --git a/model/channel.go b/model/channel.go index b670b9dc..6239f05c 100644 --- a/model/channel.go +++ b/model/channel.go @@ -42,7 +42,7 @@ type Channel struct { Priority *int64 `json:"priority" gorm:"bigint;default:0"` AutoBan *int `json:"auto_ban" gorm:"default:1"` OtherInfo string `json:"other_info"` - Settings string `json:"settings"` + OtherSettings string `json:"settings" gorm:"column:settings"` // 其他设置 Tag *string `json:"tag" gorm:"index"` Setting *string `json:"setting" gorm:"type:text"` // 渠道额外设置 ParamOverride *string `json:"param_override" gorm:"type:text"` @@ -838,6 +838,28 @@ func (channel *Channel) SetSetting(setting dto.ChannelSettings) { channel.Setting = common.GetPointer[string](string(settingBytes)) } +func (channel *Channel) GetOtherSettings() dto.ChannelOtherSettings { + setting := dto.ChannelOtherSettings{} + if channel.OtherSettings != "" { + err := common.UnmarshalJsonStr(channel.OtherSettings, &setting) + if err != nil { + common.SysError("failed to unmarshal setting: " + err.Error()) + channel.OtherSettings = "{}" // 清空设置以避免后续错误 + _ = channel.Save() // 保存修改 + } + } + return setting +} + +func (channel *Channel) SetOtherSettings(setting dto.ChannelOtherSettings) { + settingBytes, err := common.Marshal(setting) + if err != nil { + common.SysError("failed to marshal setting: " + err.Error()) + return + } + channel.OtherSettings = string(settingBytes) +} + func (channel *Channel) GetParamOverride() map[string]interface{} { paramOverride := make(map[string]interface{}) if channel.ParamOverride != nil && *channel.ParamOverride != "" { diff --git a/relay/channel/openai/adaptor.go b/relay/channel/openai/adaptor.go index c2751c28..561b7d3e 100644 --- a/relay/channel/openai/adaptor.go +++ b/relay/channel/openai/adaptor.go @@ -128,7 +128,17 @@ func (a *Adaptor) GetRequestURL(info *relaycommon.RelayInfo) (string, error) { // 特殊处理 responses API if info.RelayMode == relayconstant.RelayModeResponses { - requestURL = fmt.Sprintf("/openai/v1/responses?api-version=preview") + responsesApiVersion := "preview" + if info.ChannelOtherSettings.AzureResponsesVersion != "" { + responsesApiVersion = info.ChannelOtherSettings.AzureResponsesVersion + } + + subUrl := "/openai/v1/responses" + if strings.Contains(info.BaseUrl, "cognitiveservices.azure.com") { + subUrl = "/openai/responses" + } + + requestURL = fmt.Sprintf("%s?api-version=%s", subUrl, responsesApiVersion) return relaycommon.GetFullRequestURL(info.BaseUrl, requestURL, info.ChannelType), nil } diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 9b7e3db5..5cd9223b 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -102,6 +102,7 @@ type RelayInfo struct { AudioUsage bool ReasoningEffort string ChannelSetting dto.ChannelSettings + ChannelOtherSettings dto.ChannelOtherSettings ParamOverride map[string]interface{} UserSetting dto.UserSetting UserEmail string @@ -292,6 +293,12 @@ func GenRelayInfo(c *gin.Context) *RelayInfo { if ok { info.ChannelSetting = channelSetting } + + channelOtherSettings, ok := common.GetContextKeyType[dto.ChannelOtherSettings](c, constant.ContextKeyChannelOtherSetting) + if ok { + info.ChannelOtherSettings = channelOtherSettings + } + userSetting, ok := common.GetContextKeyType[dto.UserSetting](c, constant.ContextKeyUserSetting) if ok { info.UserSetting = userSetting diff --git a/web/src/components/table/channels/modals/EditChannelModal.jsx b/web/src/components/table/channels/modals/EditChannelModal.jsx index 360aedf2..a9b0bd31 100644 --- a/web/src/components/table/channels/modals/EditChannelModal.jsx +++ b/web/src/components/table/channels/modals/EditChannelModal.jsx @@ -132,6 +132,7 @@ const EditChannelModal = (props) => { pass_through_body_enabled: false, system_prompt: '', system_prompt_override: false, + settings: '', }; const [batch, setBatch] = useState(false); const [multiToSingle, setMultiToSingle] = useState(false); @@ -187,38 +188,31 @@ const EditChannelModal = (props) => { 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 handleChannelOtherSettingsChange = (key, value) => { + // 更新内部状态 + setChannelSettings(prev => ({ ...prev, [key]: value })); + + // 同步更新到表单字段 + if (formApiRef.current) { + formApiRef.current.setValue(key, value); } - }; + + // 同步更新inputs状态 + setInputs(prev => ({ ...prev, [key]: value })); + + // 需要更新settings,是一个json,例如{"azure_responses_version": "preview"} + let settings = {}; + if (inputs.settings) { + try { + settings = JSON.parse(inputs.settings); + } catch (error) { + console.error('解析设置失败:', error); + } + } + settings[key] = value; + const settingsJson = JSON.stringify(settings); + handleInputChange('settings', settingsJson); + } const handleInputChange = (name, value) => { if (formApiRef.current) { @@ -360,6 +354,17 @@ const EditChannelModal = (props) => { data.system_prompt_override = false; } + if (data.settings) { + try { + const parsedSettings = JSON.parse(data.settings); + data.azure_responses_version = parsedSettings.azure_responses_version || ''; + } catch (error) { + console.error('解析其他设置失败:', error); + data.azure_responses_version = ''; + data.region = ''; + } + } + setInputs(data); if (formApiRef.current) { formApiRef.current.setValues(data); @@ -1377,6 +1382,15 @@ const EditChannelModal = (props) => { showClear /> +
+ handleChannelOtherSettingsChange('azure_responses_version', value)} + showClear + /> +
)}