Sfoglia il codice sorgente

Merge pull request #2483 from seefs001/fix/vertex-function-response-id

fix: 模型设置增加针对Vertex渠道过滤content[].part[].functionResponse.id的选项,默认启用
Calcium-Ion 1 settimana fa
parent
commit
d8aa327f05

+ 1 - 0
.gitignore

@@ -16,6 +16,7 @@ new-api
 tiktoken_cache
 .eslintcache
 .gocache
+.gomodcache/
 .cache
 web/bun.lock
 

+ 33 - 0
relay/channel/vertex/adaptor.go

@@ -51,10 +51,43 @@ type Adaptor struct {
 }
 
 func (a *Adaptor) ConvertGeminiRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.GeminiChatRequest) (any, error) {
+	// Vertex AI does not support functionResponse.id; keep it stripped here for consistency.
+	if model_setting.GetGeminiSettings().RemoveFunctionResponseIdEnabled {
+		removeFunctionResponseID(request)
+	}
 	geminiAdaptor := gemini.Adaptor{}
 	return geminiAdaptor.ConvertGeminiRequest(c, info, request)
 }
 
+func removeFunctionResponseID(request *dto.GeminiChatRequest) {
+	if request == nil {
+		return
+	}
+
+	if len(request.Contents) > 0 {
+		for i := range request.Contents {
+			if len(request.Contents[i].Parts) == 0 {
+				continue
+			}
+			for j := range request.Contents[i].Parts {
+				part := &request.Contents[i].Parts[j]
+				if part.FunctionResponse == nil {
+					continue
+				}
+				if len(part.FunctionResponse.ID) > 0 {
+					part.FunctionResponse.ID = nil
+				}
+			}
+		}
+	}
+
+	if len(request.Requests) > 0 {
+		for i := range request.Requests {
+			removeFunctionResponseID(&request.Requests[i])
+		}
+	}
+}
+
 func (a *Adaptor) ConvertClaudeRequest(c *gin.Context, info *relaycommon.RelayInfo, request *dto.ClaudeRequest) (any, error) {
 	if v, ok := claudeModelMap[info.UpstreamModelName]; ok {
 		c.Set("request_model", v)

+ 45 - 0
relay/common/relay_info.go

@@ -11,6 +11,7 @@ import (
 	"github.com/QuantumNous/new-api/constant"
 	"github.com/QuantumNous/new-api/dto"
 	relayconstant "github.com/QuantumNous/new-api/relay/constant"
+	"github.com/QuantumNous/new-api/setting/model_setting"
 	"github.com/QuantumNous/new-api/types"
 
 	"github.com/gin-gonic/gin"
@@ -634,3 +635,47 @@ func RemoveDisabledFields(jsonData []byte, channelOtherSettings dto.ChannelOther
 	}
 	return jsonDataAfter, nil
 }
+
+// RemoveGeminiDisabledFields removes disabled fields from Gemini request JSON data
+// Currently supports removing functionResponse.id field which Vertex AI does not support
+func RemoveGeminiDisabledFields(jsonData []byte) ([]byte, error) {
+	if !model_setting.GetGeminiSettings().RemoveFunctionResponseIdEnabled {
+		return jsonData, nil
+	}
+
+	var data map[string]interface{}
+	if err := common.Unmarshal(jsonData, &data); err != nil {
+		common.SysError("RemoveGeminiDisabledFields Unmarshal error: " + err.Error())
+		return jsonData, nil
+	}
+
+	// Process contents array
+	// Handle both camelCase (functionResponse) and snake_case (function_response)
+	if contents, ok := data["contents"].([]interface{}); ok {
+		for _, content := range contents {
+			if contentMap, ok := content.(map[string]interface{}); ok {
+				if parts, ok := contentMap["parts"].([]interface{}); ok {
+					for _, part := range parts {
+						if partMap, ok := part.(map[string]interface{}); ok {
+							// Check functionResponse (camelCase)
+							if funcResp, ok := partMap["functionResponse"].(map[string]interface{}); ok {
+								delete(funcResp, "id")
+							}
+							// Check function_response (snake_case)
+							if funcResp, ok := partMap["function_response"].(map[string]interface{}); ok {
+								delete(funcResp, "id")
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	jsonDataAfter, err := common.Marshal(data)
+	if err != nil {
+		common.SysError("RemoveGeminiDisabledFields Marshal error: " + err.Error())
+		return jsonData, nil
+	}
+	return jsonDataAfter, nil
+}

+ 3 - 1
setting/model_setting/gemini.go

@@ -4,7 +4,7 @@ import (
 	"github.com/QuantumNous/new-api/setting/config"
 )
 
-// GeminiSettings 定义Gemini模型的配置
+// GeminiSettings defines Gemini model configuration. 注意bool要以enabled结尾才可以生效编辑
 type GeminiSettings struct {
 	SafetySettings                        map[string]string `json:"safety_settings"`
 	VersionSettings                       map[string]string `json:"version_settings"`
@@ -12,6 +12,7 @@ type GeminiSettings struct {
 	ThinkingAdapterEnabled                bool              `json:"thinking_adapter_enabled"`
 	ThinkingAdapterBudgetTokensPercentage float64           `json:"thinking_adapter_budget_tokens_percentage"`
 	FunctionCallThoughtSignatureEnabled   bool              `json:"function_call_thought_signature_enabled"`
+	RemoveFunctionResponseIdEnabled       bool              `json:"remove_function_response_id_enabled"`
 }
 
 // 默认配置
@@ -30,6 +31,7 @@ var defaultGeminiSettings = GeminiSettings{
 	ThinkingAdapterEnabled:                false,
 	ThinkingAdapterBudgetTokensPercentage: 0.6,
 	FunctionCallThoughtSignatureEnabled:   true,
+	RemoveFunctionResponseIdEnabled:       true,
 }
 
 // 全局实例

+ 2 - 0
web/src/components/settings/ModelSetting.jsx

@@ -32,6 +32,7 @@ const ModelSetting = () => {
     'gemini.safety_settings': '',
     'gemini.version_settings': '',
     'gemini.supported_imagine_models': '',
+    'gemini.remove_function_response_id_enabled': true,
     'claude.model_headers_settings': '',
     'claude.thinking_adapter_enabled': true,
     'claude.default_max_tokens': '',
@@ -64,6 +65,7 @@ const ModelSetting = () => {
             item.value = JSON.stringify(JSON.parse(item.value), null, 2);
           }
         }
+        // Keep boolean config keys ending with enabled/Enabled so UI parses correctly.
         if (item.key.endsWith('Enabled') || item.key.endsWith('enabled')) {
           newInputs[item.key] = toBoolean(item.value);
         } else {

+ 2 - 0
web/src/i18n/locales/en.json

@@ -153,6 +153,7 @@
     "URL链接": "URL Link",
     "USD (美元)": "USD (US Dollar)",
     "User Info Endpoint": "User Info Endpoint",
+    "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI does not support the functionResponse.id field. When enabled, this field will be automatically removed",
     "Webhook 密钥": "Webhook Secret",
     "Webhook 签名密钥": "Webhook Signature Key",
     "Webhook地址": "Webhook URL",
@@ -1510,6 +1511,7 @@
     "私有IP访问详细说明": "⚠️ Security Warning: Enabling this allows access to internal network resources (localhost, private networks). Only enable if you need to access internal services and understand the security implications.",
     "私有部署地址": "Private Deployment Address",
     "秒": "Second",
+    "移除 functionResponse.id 字段": "Remove functionResponse.id Field",
     "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "Removal of One API copyright mark must first be authorized. Project maintenance requires a lot of effort. If this project is meaningful to you, please actively support it.",
     "窗口处理": "window handling",
     "窗口等待": "window wait",

+ 2 - 0
web/src/i18n/locales/fr.json

@@ -154,6 +154,7 @@
     "URL链接": "Lien URL",
     "USD (美元)": "USD (Dollar US)",
     "User Info Endpoint": "Point de terminaison des informations utilisateur",
+    "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI ne prend pas en charge le champ functionResponse.id. Lorsqu'il est activé, ce champ sera automatiquement supprimé",
     "Webhook 密钥": "Clé Webhook",
     "Webhook 签名密钥": "Clé de signature Webhook",
     "Webhook地址": "URL du Webhook",
@@ -1520,6 +1521,7 @@
     "私有IP访问详细说明": "⚠️ Avertissement de sécurité : l'activation de cette option autorise l'accès aux ressources du réseau interne (localhost, réseaux privés). N'activez cette option que si vous devez accéder à des services internes et que vous comprenez les implications en matière de sécurité.",
     "私有部署地址": "Adresse de déploiement privée",
     "秒": "Seconde",
+    "移除 functionResponse.id 字段": "Supprimer le champ functionResponse.id",
     "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "La suppression de la marque de copyright de One API doit d'abord être autorisée. La maintenance du projet demande beaucoup d'efforts. Si ce projet a du sens pour vous, veuillez le soutenir activement.",
     "窗口处理": "gestion des fenêtres",
     "窗口等待": "attente de la fenêtre",

+ 2 - 0
web/src/i18n/locales/ja.json

@@ -136,6 +136,7 @@
     "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Uptime Kumaの監視分類管理:サービスステータス表示用に、複数の監視分類を設定できます(最大20個)",
     "URL链接": "URL",
     "User Info Endpoint": "User Info Endpoint",
+    "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AIはfunctionResponse.idフィールドをサポートしていません。有効にすると、このフィールドは自動的に削除されます",
     "Webhook 签名密钥": "Webhook署名シークレット",
     "Webhook地址": "Webhook URL",
     "Webhook地址必须以https://开头": "Webhook URLは、https://で始まることが必須です",
@@ -1440,6 +1441,7 @@
     "私有IP访问详细说明": "プライベートIPアクセスの詳細説明",
     "私有部署地址": "プライベートデプロイ先URL",
     "秒": "秒",
+    "移除 functionResponse.id 字段": "functionResponse.idフィールドを削除",
     "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "One APIの著作権表示を削除するには、事前の許可が必要です。プロジェクトの維持には多大な労力がかかります。もしこのプロジェクトがあなたにとって有意義でしたら、積極的なご支援をお願いいたします",
     "窗口处理": "ウィンドウ処理",
     "窗口等待": "ウィンドウ待機中",

+ 2 - 0
web/src/i18n/locales/ru.json

@@ -156,6 +156,7 @@
     "URL链接": "URL ссылка",
     "USD (美元)": "USD (доллар США)",
     "User Info Endpoint": "Конечная точка информации о пользователе",
+    "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI не поддерживает поле functionResponse.id. При включении это поле будет автоматически удалено",
     "Webhook 密钥": "Секрет вебхука",
     "Webhook 签名密钥": "Ключ подписи Webhook",
     "Webhook地址": "Адрес Webhook",
@@ -1531,6 +1532,7 @@
     "私有IP访问详细说明": "⚠️ Предупреждение безопасности: включение этой опции позволит доступ к ресурсам внутренней сети (localhost, частные сети). Включайте только при необходимости доступа к внутренним службам и понимании рисков безопасности.",
     "私有部署地址": "Адрес частного развёртывания",
     "秒": "секунда",
+    "移除 functionResponse.id 字段": "Удалить поле functionResponse.id",
     "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "Удаление авторских знаков One API требует предварительного разрешения, поддержка проекта требует больших усилий, если этот проект важен для вас, пожалуйста, поддержите его",
     "窗口处理": "Обработка окна",
     "窗口等待": "Ожидание окна",

+ 2 - 0
web/src/i18n/locales/vi.json

@@ -136,6 +136,7 @@
     "Uptime Kuma监控分类管理,可以配置多个监控分类用于服务状态展示(最多20个)": "Quản lý danh mục giám sát Uptime Kuma, bạn có thể cấu hình nhiều danh mục giám sát để hiển thị trạng thái dịch vụ (tối đa 20)",
     "URL链接": "Liên kết URL",
     "User Info Endpoint": "User Info Endpoint",
+    "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI không hỗ trợ trường functionResponse.id. Khi bật, trường này sẽ tự động bị xóa",
     "Webhook 签名密钥": "Khóa chữ ký Webhook",
     "Webhook地址": "URL Webhook",
     "Webhook地址必须以https://开头": "URL Webhook phải bắt đầu bằng https://",
@@ -2648,6 +2649,7 @@
     "私有IP访问详细说明": "⚠️ Cảnh báo bảo mật: Bật tính năng này cho phép truy cập vào tài nguyên mạng nội bộ (localhost, mạng riêng). Chỉ bật nếu bạn cần truy cập các dịch vụ nội bộ và hiểu rõ các rủi ro bảo mật.",
     "私有部署地址": "Địa chỉ triển khai riêng",
     "秒": "Giây",
+    "移除 functionResponse.id 字段": "Xóa trường functionResponse.id",
     "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "Việc xóa dấu bản quyền One API trước tiên phải được ủy quyền. Việc bảo trì dự án đòi hỏi rất nhiều nỗ lực. Nếu dự án này có ý nghĩa với bạn, vui lòng chủ động ủng hộ dự án này.",
     "窗口处理": "xử lý cửa sổ",
     "窗口等待": "chờ cửa sổ",

+ 2 - 0
web/src/i18n/locales/zh.json

@@ -150,6 +150,7 @@
     "URL链接": "URL链接",
     "USD (美元)": "USD (美元)",
     "User Info Endpoint": "User Info Endpoint",
+    "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段": "Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段",
     "Webhook 密钥": "Webhook 密钥",
     "Webhook 签名密钥": "Webhook 签名密钥",
     "Webhook地址": "Webhook地址",
@@ -1498,6 +1499,7 @@
     "私有IP访问详细说明": "⚠️ 安全警告:启用此选项将允许访问内网资源(本地主机、私有网络)。仅在需要访问内部服务且了解安全风险的情况下启用。",
     "私有部署地址": "私有部署地址",
     "秒": "秒",
+    "移除 functionResponse.id 字段": "移除 functionResponse.id 字段",
     "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目": "移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目",
     "窗口处理": "窗口处理",
     "窗口等待": "窗口等待",

+ 18 - 0
web/src/pages/Setting/Model/SettingGeminiModel.jsx

@@ -46,6 +46,7 @@ const DEFAULT_GEMINI_INPUTS = {
   'gemini.thinking_adapter_enabled': false,
   'gemini.thinking_adapter_budget_tokens_percentage': 0.6,
   'gemini.function_call_thought_signature_enabled': true,
+  'gemini.remove_function_response_id_enabled': true,
 };
 
 export default function SettingGeminiModel(props) {
@@ -186,6 +187,23 @@ export default function SettingGeminiModel(props) {
                 />
               </Col>
             </Row>
+            <Row>
+              <Col span={16}>
+                <Form.Switch
+                  label={t('移除 functionResponse.id 字段')}
+                  field={'gemini.remove_function_response_id_enabled'}
+                  extraText={t(
+                    'Vertex AI 不支持 functionResponse.id 字段,开启后将自动移除该字段',
+                  )}
+                  onChange={(value) =>
+                    setInputs({
+                      ...inputs,
+                      'gemini.remove_function_response_id_enabled': value,
+                    })
+                  }
+                />
+              </Col>
+            </Row>
             <Row>
               <Col xs={24} sm={12} md={8} lg={8} xl={8}>
                 <Form.TextArea