refactor(channel): 抽取渠道映射公共函数 + OpenAI映射到body + 空响应修复 + 清理日志
- 抽取 ResolveChannelMappingAndRestrict 统一入口(5处→1个方法) - 抽取 BuildModelMappingChain 到 ChannelMappingResult 方法(5处→1行调用) - OpenAI 三入口 Forward 前应用渠道映射到请求体 - OpenAI Responses/Messages 限制检查添加错误响应 - 清理前端 3 处 console.log 调试日志
This commit is contained in:
@@ -185,17 +185,11 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
|
||||
setOpsRequestContext(c, reqModel, reqStream, body)
|
||||
setOpsEndpointContext(c, "", int16(service.RequestTypeFromLegacy(reqStream, false)))
|
||||
|
||||
// 解析渠道级模型映射
|
||||
var channelMapping service.ChannelMappingResult
|
||||
if apiKey.GroupID != nil {
|
||||
channelMapping = h.gatewayService.ResolveChannelMapping(c.Request.Context(), *apiKey.GroupID, reqModel)
|
||||
}
|
||||
|
||||
// 渠道模型限制检查:先映射再判断
|
||||
if apiKey.GroupID != nil {
|
||||
if h.gatewayService.IsModelRestricted(c.Request.Context(), *apiKey.GroupID, channelMapping.MappedModel) {
|
||||
return
|
||||
}
|
||||
// 解析渠道级模型映射 + 限制检查
|
||||
channelMapping, restricted := h.gatewayService.ResolveChannelMappingAndRestrict(c.Request.Context(), apiKey.GroupID, reqModel)
|
||||
if restricted {
|
||||
h.errorResponse(c, http.StatusServiceUnavailable, "api_error", "No available accounts")
|
||||
return
|
||||
}
|
||||
|
||||
// 提前校验 function_call_output 是否具备可关联上下文,避免上游 400。
|
||||
@@ -297,7 +291,12 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
|
||||
// Forward request
|
||||
service.SetOpsLatencyMs(c, service.OpsRoutingLatencyMsKey, time.Since(routingStart).Milliseconds())
|
||||
forwardStart := time.Now()
|
||||
result, err := h.gatewayService.Forward(c.Request.Context(), c, account, body)
|
||||
// 应用渠道模型映射到请求体
|
||||
forwardBody := body
|
||||
if channelMapping.Mapped {
|
||||
forwardBody = h.gatewayService.ReplaceModelInBody(body, channelMapping.MappedModel)
|
||||
}
|
||||
result, err := h.gatewayService.Forward(c.Request.Context(), c, account, forwardBody)
|
||||
forwardDurationMs := time.Since(forwardStart).Milliseconds()
|
||||
if accountReleaseFunc != nil {
|
||||
accountReleaseFunc()
|
||||
@@ -395,18 +394,7 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
|
||||
ChannelID: channelMapping.ChannelID,
|
||||
OriginalModel: reqModel,
|
||||
BillingModelSource: channelMapping.BillingModelSource,
|
||||
ModelMappingChain: func() string {
|
||||
if !channelMapping.Mapped {
|
||||
if result.UpstreamModel != "" && result.UpstreamModel != result.Model {
|
||||
return reqModel + "→" + result.UpstreamModel
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if result.UpstreamModel != "" && result.UpstreamModel != channelMapping.MappedModel {
|
||||
return reqModel + "→" + channelMapping.MappedModel + "→" + result.UpstreamModel
|
||||
}
|
||||
return reqModel + "→" + channelMapping.MappedModel
|
||||
}(),
|
||||
ModelMappingChain: channelMapping.BuildModelMappingChain(reqModel, result.UpstreamModel),
|
||||
}); err != nil {
|
||||
logger.L().With(
|
||||
zap.String("component", "handler.openai_gateway.responses"),
|
||||
@@ -577,17 +565,11 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
|
||||
setOpsRequestContext(c, reqModel, reqStream, body)
|
||||
setOpsEndpointContext(c, "", int16(service.RequestTypeFromLegacy(reqStream, false)))
|
||||
|
||||
// 解析渠道级模型映射
|
||||
var channelMappingMsg service.ChannelMappingResult
|
||||
if apiKey.GroupID != nil {
|
||||
channelMappingMsg = h.gatewayService.ResolveChannelMapping(c.Request.Context(), *apiKey.GroupID, reqModel)
|
||||
}
|
||||
|
||||
// 渠道模型限制检查:先映射再判断
|
||||
if apiKey.GroupID != nil {
|
||||
if h.gatewayService.IsModelRestricted(c.Request.Context(), *apiKey.GroupID, channelMappingMsg.MappedModel) {
|
||||
return
|
||||
}
|
||||
// 解析渠道级模型映射 + 限制检查
|
||||
channelMappingMsg, restricted := h.gatewayService.ResolveChannelMappingAndRestrict(c.Request.Context(), apiKey.GroupID, reqModel)
|
||||
if restricted {
|
||||
h.anthropicErrorResponse(c, http.StatusServiceUnavailable, "api_error", "No available accounts")
|
||||
return
|
||||
}
|
||||
|
||||
// 绑定错误透传服务,允许 service 层在非 failover 错误场景复用规则。
|
||||
@@ -714,7 +696,12 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
|
||||
// Forward 层需要始终拿到 group 默认映射模型,这样未命中账号级映射的
|
||||
// Claude 兼容模型才不会在后续 Codex 规范化中意外退化到 gpt-5.1。
|
||||
defaultMappedModel := resolveOpenAIForwardDefaultMappedModel(apiKey, c.GetString("openai_messages_fallback_model"))
|
||||
result, err := h.gatewayService.ForwardAsAnthropic(c.Request.Context(), c, account, body, promptCacheKey, defaultMappedModel)
|
||||
// 应用渠道模型映射到请求体
|
||||
forwardBody := body
|
||||
if channelMappingMsg.Mapped {
|
||||
forwardBody = h.gatewayService.ReplaceModelInBody(body, channelMappingMsg.MappedModel)
|
||||
}
|
||||
result, err := h.gatewayService.ForwardAsAnthropic(c.Request.Context(), c, account, forwardBody, promptCacheKey, defaultMappedModel)
|
||||
|
||||
forwardDurationMs := time.Since(forwardStart).Milliseconds()
|
||||
if accountReleaseFunc != nil {
|
||||
@@ -803,18 +790,7 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
|
||||
ChannelID: channelMappingMsg.ChannelID,
|
||||
OriginalModel: reqModel,
|
||||
BillingModelSource: channelMappingMsg.BillingModelSource,
|
||||
ModelMappingChain: func() string {
|
||||
if !channelMappingMsg.Mapped {
|
||||
if result.UpstreamModel != "" && result.UpstreamModel != result.Model {
|
||||
return reqModel + "→" + result.UpstreamModel
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if result.UpstreamModel != "" && result.UpstreamModel != channelMappingMsg.MappedModel {
|
||||
return reqModel + "→" + channelMappingMsg.MappedModel + "→" + result.UpstreamModel
|
||||
}
|
||||
return reqModel + "→" + channelMappingMsg.MappedModel
|
||||
}(),
|
||||
ModelMappingChain: channelMappingMsg.BuildModelMappingChain(reqModel, result.UpstreamModel),
|
||||
}); err != nil {
|
||||
logger.L().With(
|
||||
zap.String("component", "handler.openai_gateway.messages"),
|
||||
@@ -1157,18 +1133,11 @@ func (h *OpenAIGatewayHandler) ResponsesWebSocket(c *gin.Context) {
|
||||
setOpsRequestContext(c, reqModel, true, firstMessage)
|
||||
setOpsEndpointContext(c, "", int16(service.RequestTypeWSV2))
|
||||
|
||||
// 解析渠道级模型映射
|
||||
var channelMappingWS service.ChannelMappingResult
|
||||
if apiKey.GroupID != nil {
|
||||
channelMappingWS = h.gatewayService.ResolveChannelMapping(ctx, *apiKey.GroupID, reqModel)
|
||||
}
|
||||
|
||||
// 渠道模型限制检查:先映射再判断
|
||||
if apiKey.GroupID != nil {
|
||||
if h.gatewayService.IsModelRestricted(ctx, *apiKey.GroupID, channelMappingWS.MappedModel) {
|
||||
closeOpenAIClientWS(wsConn, coderws.StatusPolicyViolation, "model not allowed")
|
||||
return
|
||||
}
|
||||
// 解析渠道级模型映射 + 限制检查
|
||||
channelMappingWS, restricted := h.gatewayService.ResolveChannelMappingAndRestrict(ctx, apiKey.GroupID, reqModel)
|
||||
if restricted {
|
||||
closeOpenAIClientWS(wsConn, coderws.StatusPolicyViolation, "model not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
var currentUserRelease func()
|
||||
@@ -1332,18 +1301,7 @@ func (h *OpenAIGatewayHandler) ResponsesWebSocket(c *gin.Context) {
|
||||
ChannelID: channelMappingWS.ChannelID,
|
||||
OriginalModel: reqModel,
|
||||
BillingModelSource: channelMappingWS.BillingModelSource,
|
||||
ModelMappingChain: func() string {
|
||||
if !channelMappingWS.Mapped {
|
||||
if result.UpstreamModel != "" && result.UpstreamModel != result.Model {
|
||||
return reqModel + "→" + result.UpstreamModel
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if result.UpstreamModel != "" && result.UpstreamModel != channelMappingWS.MappedModel {
|
||||
return reqModel + "→" + channelMappingWS.MappedModel + "→" + result.UpstreamModel
|
||||
}
|
||||
return reqModel + "→" + channelMappingWS.MappedModel
|
||||
}(),
|
||||
ModelMappingChain: channelMappingWS.BuildModelMappingChain(reqModel, result.UpstreamModel),
|
||||
}); err != nil {
|
||||
reqLog.Error("openai.websocket_record_usage_failed",
|
||||
zap.Int64("account_id", account.ID),
|
||||
@@ -1355,7 +1313,13 @@ func (h *OpenAIGatewayHandler) ResponsesWebSocket(c *gin.Context) {
|
||||
},
|
||||
}
|
||||
|
||||
if err := h.gatewayService.ProxyResponsesWebSocketFromClient(ctx, c, wsConn, account, token, firstMessage, hooks); err != nil {
|
||||
// 应用渠道模型映射到 WebSocket 首条消息
|
||||
wsFirstMessage := firstMessage
|
||||
if channelMappingWS.Mapped {
|
||||
wsFirstMessage = h.gatewayService.ReplaceModelInBody(firstMessage, channelMappingWS.MappedModel)
|
||||
}
|
||||
|
||||
if err := h.gatewayService.ProxyResponsesWebSocketFromClient(ctx, c, wsConn, account, token, wsFirstMessage, hooks); err != nil {
|
||||
h.gatewayService.ReportOpenAIAccountScheduleResult(account.ID, false, nil)
|
||||
closeStatus, closeReason := summarizeWSCloseErrorForLog(err)
|
||||
reqLog.Warn("openai.websocket_proxy_failed",
|
||||
|
||||
Reference in New Issue
Block a user