从上游移植 override.go 的完整增强功能(603行→2057行),包括: 新增 9 种 Operation Mode: - return_error: 规则直接返回 HTTP 错误中断请求 - prune_objects: 按 JSON Schema 过滤数组元素 - set_header / delete_header / copy_header / move_header: 动态管理上游请求头 - pass_headers: 透传下游请求头到上游 - sync_fields: 请求体不同路径间同步字段值 - 通配符路径支持: messages.*.role 等 新增核心模块: - Header Override 系统:从上下文取值、正则匹配、列表追加 - Audit 审计系统:关键操作自动记录到 RelayInfo.ParamOverrideAudit - Return Error 机制:含状态码/错误类型/跳过重试控制 - Legacy 兼容层:同时执行旧格式和新格式 param override - RelayInfo 集成:丰富上下文(retry_index、last_error、request_headers 等) RelayInfo 新增字段:RetryIndex, LastError, RequestHeaders, RuntimeHeadersOverride, UseRuntimeHeadersOverride, ParamOverrideAudit 所有 handler 统一使用 ApplyParamOverrideWithRelayInfo, 全部测试通过(100+ test cases)。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>feat/alipay-payment
| @@ -70,7 +70,6 @@ func applySystemPromptIfNeeded(c *gin.Context, info *relaycommon.RelayInfo, requ | |||||
| } | } | ||||
| func chatCompletionsViaResponses(c *gin.Context, info *relaycommon.RelayInfo, adaptor channel.Adaptor, request *dto.GeneralOpenAIRequest) (*dto.Usage, *types.NewAPIError) { | func chatCompletionsViaResponses(c *gin.Context, info *relaycommon.RelayInfo, adaptor channel.Adaptor, request *dto.GeneralOpenAIRequest) (*dto.Usage, *types.NewAPIError) { | ||||
| overrideCtx := relaycommon.BuildParamOverrideContext(info) | |||||
| chatJSON, err := common.Marshal(request) | chatJSON, err := common.Marshal(request) | ||||
| if err != nil { | if err != nil { | ||||
| return nil, types.NewError(err, types.ErrorCodeConvertRequestFailed, types.ErrOptionWithSkipRetry()) | return nil, types.NewError(err, types.ErrorCodeConvertRequestFailed, types.ErrOptionWithSkipRetry()) | ||||
| @@ -82,7 +81,7 @@ func chatCompletionsViaResponses(c *gin.Context, info *relaycommon.RelayInfo, ad | |||||
| } | } | ||||
| if len(info.ParamOverride) > 0 { | if len(info.ParamOverride) > 0 { | ||||
| chatJSON, err = relaycommon.ApplyParamOverride(chatJSON, info.ParamOverride, overrideCtx) | |||||
| chatJSON, err = relaycommon.ApplyParamOverrideWithRelayInfo(chatJSON, info) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | return nil, types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | ||||
| } | } | ||||
| @@ -153,7 +153,7 @@ func ClaudeHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ | |||||
| // apply param override | // apply param override | ||||
| if len(info.ParamOverride) > 0 { | if len(info.ParamOverride) > 0 { | ||||
| jsonData, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, relaycommon.BuildParamOverrideContext(info)) | |||||
| jsonData, err = relaycommon.ApplyParamOverrideWithRelayInfo(jsonData, info) | |||||
| if err != nil { | if err != nil { | ||||
| return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | ||||
| } | } | ||||
| @@ -144,6 +144,12 @@ type RelayInfo struct { | |||||
| SubscriptionAmountUsedAfterPreConsume int64 | SubscriptionAmountUsedAfterPreConsume int64 | ||||
| IsClaudeBetaQuery bool // /v1/messages?beta=true | IsClaudeBetaQuery bool // /v1/messages?beta=true | ||||
| IsChannelTest bool // channel test request | IsChannelTest bool // channel test request | ||||
| RetryIndex int | |||||
| LastError *types.NewAPIError | |||||
| RequestHeaders map[string]string | |||||
| RuntimeHeadersOverride map[string]interface{} | |||||
| UseRuntimeHeadersOverride bool | |||||
| ParamOverrideAudit []string | |||||
| PriceData types.PriceData | PriceData types.PriceData | ||||
| @@ -473,6 +479,7 @@ func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo { | |||||
| //promptTokens: common.GetContextKeyInt(c, constant.ContextKeyPromptTokens), | //promptTokens: common.GetContextKeyInt(c, constant.ContextKeyPromptTokens), | ||||
| estimatePromptTokens: common.GetContextKeyInt(c, constant.ContextKeyEstimatedTokens), | estimatePromptTokens: common.GetContextKeyInt(c, constant.ContextKeyEstimatedTokens), | ||||
| }, | }, | ||||
| RequestHeaders: cloneRequestHeaders(c), | |||||
| } | } | ||||
| if info.RelayMode == relayconstant.RelayModeUnknown { | if info.RelayMode == relayconstant.RelayModeUnknown { | ||||
| @@ -493,6 +500,27 @@ func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo { | |||||
| return info | return info | ||||
| } | } | ||||
| func cloneRequestHeaders(c *gin.Context) map[string]string { | |||||
| if c == nil || c.Request == nil { | |||||
| return nil | |||||
| } | |||||
| if len(c.Request.Header) == 0 { | |||||
| return nil | |||||
| } | |||||
| headers := make(map[string]string, len(c.Request.Header)) | |||||
| for key := range c.Request.Header { | |||||
| value := strings.TrimSpace(c.Request.Header.Get(key)) | |||||
| if value == "" { | |||||
| continue | |||||
| } | |||||
| headers[key] = value | |||||
| } | |||||
| if len(headers) == 0 { | |||||
| return nil | |||||
| } | |||||
| return headers | |||||
| } | |||||
| func GenRelayInfo(c *gin.Context, relayFormat types.RelayFormat, request dto.Request, ws *websocket.Conn) (*RelayInfo, error) { | func GenRelayInfo(c *gin.Context, relayFormat types.RelayFormat, request dto.Request, ws *websocket.Conn) (*RelayInfo, error) { | ||||
| var info *RelayInfo | var info *RelayInfo | ||||
| var err error | var err error | ||||
| @@ -172,7 +172,7 @@ func TextHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *types | |||||
| // apply param override | // apply param override | ||||
| if len(info.ParamOverride) > 0 { | if len(info.ParamOverride) > 0 { | ||||
| jsonData, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, relaycommon.BuildParamOverrideContext(info)) | |||||
| jsonData, err = relaycommon.ApplyParamOverrideWithRelayInfo(jsonData, info) | |||||
| if err != nil { | if err != nil { | ||||
| return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | ||||
| } | } | ||||
| @@ -52,7 +52,7 @@ func EmbeddingHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError * | |||||
| } | } | ||||
| if len(info.ParamOverride) > 0 { | if len(info.ParamOverride) > 0 { | ||||
| jsonData, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, relaycommon.BuildParamOverrideContext(info)) | |||||
| jsonData, err = relaycommon.ApplyParamOverrideWithRelayInfo(jsonData, info) | |||||
| if err != nil { | if err != nil { | ||||
| return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | ||||
| } | } | ||||
| @@ -157,7 +157,7 @@ func GeminiHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ | |||||
| // apply param override | // apply param override | ||||
| if len(info.ParamOverride) > 0 { | if len(info.ParamOverride) > 0 { | ||||
| jsonData, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, relaycommon.BuildParamOverrideContext(info)) | |||||
| jsonData, err = relaycommon.ApplyParamOverrideWithRelayInfo(jsonData, info) | |||||
| if err != nil { | if err != nil { | ||||
| return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | ||||
| } | } | ||||
| @@ -70,7 +70,7 @@ func ImageHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *type | |||||
| // apply param override | // apply param override | ||||
| if len(info.ParamOverride) > 0 { | if len(info.ParamOverride) > 0 { | ||||
| jsonData, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, relaycommon.BuildParamOverrideContext(info)) | |||||
| jsonData, err = relaycommon.ApplyParamOverrideWithRelayInfo(jsonData, info) | |||||
| if err != nil { | if err != nil { | ||||
| return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | ||||
| } | } | ||||
| @@ -61,7 +61,7 @@ func RerankHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError *typ | |||||
| // apply param override | // apply param override | ||||
| if len(info.ParamOverride) > 0 { | if len(info.ParamOverride) > 0 { | ||||
| jsonData, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, relaycommon.BuildParamOverrideContext(info)) | |||||
| jsonData, err = relaycommon.ApplyParamOverrideWithRelayInfo(jsonData, info) | |||||
| if err != nil { | if err != nil { | ||||
| return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | ||||
| } | } | ||||
| @@ -96,7 +96,7 @@ func ResponsesHelper(c *gin.Context, info *relaycommon.RelayInfo) (newAPIError * | |||||
| // apply param override | // apply param override | ||||
| if len(info.ParamOverride) > 0 { | if len(info.ParamOverride) > 0 { | ||||
| jsonData, err = relaycommon.ApplyParamOverride(jsonData, info.ParamOverride, relaycommon.BuildParamOverrideContext(info)) | |||||
| jsonData, err = relaycommon.ApplyParamOverrideWithRelayInfo(jsonData, info) | |||||
| if err != nil { | if err != nil { | ||||
| return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | return types.NewError(err, types.ErrorCodeChannelParamOverrideInvalid, types.ErrOptionWithSkipRetry()) | ||||
| } | } | ||||