feat: improve OpenAI messages compatibility for Claude Code
This commit is contained in:
@@ -211,9 +211,10 @@ type OpenAIUsage struct {
|
||||
|
||||
// OpenAIForwardResult represents the result of forwarding
|
||||
type OpenAIForwardResult struct {
|
||||
RequestID string
|
||||
Usage OpenAIUsage
|
||||
Model string // 原始模型(用于响应和日志显示)
|
||||
RequestID string
|
||||
ResponseID string
|
||||
Usage OpenAIUsage
|
||||
Model string // 原始模型(用于响应和日志显示)
|
||||
// BillingModel is the model used for cost calculation.
|
||||
// When non-empty, CalculateCost uses this instead of Model.
|
||||
// This is set by the Anthropic Messages conversion path where
|
||||
@@ -346,10 +347,12 @@ type OpenAIGatewayService struct {
|
||||
openaiWSPassthroughDialer openAIWSClientDialer
|
||||
openaiAccountStats *openAIAccountRuntimeStats
|
||||
|
||||
openaiWSFallbackUntil sync.Map // key: int64(accountID), value: time.Time
|
||||
openaiWSRetryMetrics openAIWSRetryMetrics
|
||||
responseHeaderFilter *responseheaders.CompiledHeaderFilter
|
||||
codexSnapshotThrottle *accountWriteThrottle
|
||||
openaiWSFallbackUntil sync.Map // key: int64(accountID), value: time.Time
|
||||
openaiWSRetryMetrics openAIWSRetryMetrics
|
||||
responseHeaderFilter *responseheaders.CompiledHeaderFilter
|
||||
codexSnapshotThrottle *accountWriteThrottle
|
||||
openaiCompatSessionResponses sync.Map
|
||||
openaiCompatAnthropicDigestSessions sync.Map
|
||||
}
|
||||
|
||||
// NewOpenAIGatewayService creates a new OpenAIGatewayService
|
||||
@@ -1992,6 +1995,8 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
|
||||
originalBody := body
|
||||
reqModel, reqStream, promptCacheKey := extractOpenAIRequestMetaFromBody(body)
|
||||
originalModel := reqModel
|
||||
compatMessagesBridge := isOpenAICompatMessagesBridgeBody(body)
|
||||
setOpenAICompatMessagesBridgeContext(c, compatMessagesBridge)
|
||||
|
||||
isCodexCLI := openai.IsCodexOfficialClientByHeaders(c.GetHeader("User-Agent"), c.GetHeader("originator")) || (s.cfg != nil && s.cfg.Gateway.ForceCodexCLI)
|
||||
wsDecision := s.getOpenAIWSProtocolResolver().Resolve(account)
|
||||
@@ -2117,7 +2122,7 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
|
||||
}
|
||||
|
||||
// 非透传模式下,instructions 为空时注入默认指令。
|
||||
if isInstructionsEmpty(reqBody) {
|
||||
if isInstructionsEmpty(reqBody) && !compatMessagesBridge {
|
||||
reqBody["instructions"] = "You are a helpful coding assistant."
|
||||
bodyModified = true
|
||||
markPatchSet("instructions", "You are a helpful coding assistant.")
|
||||
@@ -2246,7 +2251,20 @@ func (s *OpenAIGatewayService) Forward(ctx context.Context, c *gin.Context, acco
|
||||
}
|
||||
|
||||
if account.Type == AccountTypeOAuth {
|
||||
codexResult := applyCodexOAuthTransform(reqBody, isCodexCLI, isCompactRequest)
|
||||
codexResult := codexTransformResult{}
|
||||
if compatMessagesBridge {
|
||||
codexResult = applyCodexOAuthTransformWithOptions(reqBody, codexOAuthTransformOptions{
|
||||
IsCodexCLI: isCodexCLI,
|
||||
IsCompact: isCompactRequest,
|
||||
SkipDefaultInstructions: true,
|
||||
PreserveToolCallIDs: true,
|
||||
})
|
||||
ensureCodexOAuthInstructionsField(reqBody)
|
||||
bodyModified = true
|
||||
disablePatch()
|
||||
} else {
|
||||
codexResult = applyCodexOAuthTransform(reqBody, isCodexCLI, isCompactRequest)
|
||||
}
|
||||
if codexResult.Modified {
|
||||
bodyModified = true
|
||||
disablePatch()
|
||||
@@ -3831,12 +3849,19 @@ func (s *OpenAIGatewayService) buildUpstreamRequest(ctx context.Context, c *gin.
|
||||
}
|
||||
}
|
||||
if account.Type == AccountTypeOAuth {
|
||||
compatMessagesBridge := isOpenAICompatMessagesBridgeContext(c) || isOpenAICompatMessagesBridgeBody(body)
|
||||
// 清除客户端透传的 session 头,后续用隔离后的值重新设置,防止跨用户会话碰撞。
|
||||
clientConversationID := strings.TrimSpace(req.Header.Get("conversation_id"))
|
||||
req.Header.Del("conversation_id")
|
||||
req.Header.Del("session_id")
|
||||
|
||||
req.Header.Set("OpenAI-Beta", "responses=experimental")
|
||||
req.Header.Set("originator", resolveOpenAIUpstreamOriginator(c, isCodexCLI))
|
||||
if compatMessagesBridge {
|
||||
req.Header.Del("OpenAI-Beta")
|
||||
req.Header.Del("originator")
|
||||
} else {
|
||||
req.Header.Set("OpenAI-Beta", "responses=experimental")
|
||||
req.Header.Set("originator", resolveOpenAIUpstreamOriginator(c, isCodexCLI))
|
||||
}
|
||||
apiKeyID := getAPIKeyIDFromContext(c)
|
||||
if isOpenAIResponsesCompactPath(c) {
|
||||
req.Header.Set("accept", "application/json")
|
||||
@@ -3850,8 +3875,10 @@ func (s *OpenAIGatewayService) buildUpstreamRequest(ctx context.Context, c *gin.
|
||||
}
|
||||
if promptCacheKey != "" {
|
||||
isolated := isolateOpenAISessionID(apiKeyID, promptCacheKey)
|
||||
req.Header.Set("conversation_id", isolated)
|
||||
req.Header.Set("session_id", isolated)
|
||||
if !compatMessagesBridge || clientConversationID != "" {
|
||||
req.Header.Set("conversation_id", isolated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user