Parcourir la source

✨ feat: Add ChannelOtherSettings to manage additional channel configurations

CaIon il y a 6 mois
Parent
commit
7f462a084c

+ 1 - 0
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"

+ 4 - 0
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"`
+}

+ 1 - 0
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)

+ 23 - 1
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 != "" {

+ 5 - 1
relay/channel/openai/adaptor.go

@@ -128,7 +128,11 @@ 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
+			}
+			requestURL = fmt.Sprintf("/openai/v1/responses?api-version=%s", responsesApiVersion)
 			return relaycommon.GetFullRequestURL(info.BaseUrl, requestURL, info.ChannelType), nil
 		}
 

+ 7 - 0
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

+ 44 - 30
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: '',
-        });
+  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);
       }
-    } catch (error) {
-      console.error('解析渠道设置失败:', error);
-      setChannelSettings({
-        force_format: false,
-        thinking_to_content: false,
-        proxy: '',
-        pass_through_body_enabled: false,
-        system_prompt: '',
-      });
     }
-  };
+    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
                           />
                         </div>
+                        <div>
+                          <Form.Input
+                            field='azure_responses_version'
+                            label={t('默认 Responses API 版本,为空则使用上方版本')}
+                            placeholder={t('例如:preview')}
+                            onChange={(value) => handleChannelOtherSettingsChange('azure_responses_version', value)}
+                            showClear
+                          />
+                        </div>
                       </>
                     )}