Commit a461538d authored by shaw's avatar shaw
Browse files

fix: 修复gpt->claude转换无法命中codex缓存问题

parent ebe6f418
......@@ -560,6 +560,21 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
sessionHash := h.gatewayService.GenerateSessionHash(c, body)
promptCacheKey := h.gatewayService.ExtractSessionID(c, body)
// Anthropic 格式的请求在 metadata.user_id 中携带 session 标识,
// 而非 OpenAI 的 session_id/conversation_id headers。
// 从中派生 sessionHash(sticky session)和 promptCacheKey(upstream cache)。
if sessionHash == "" || promptCacheKey == "" {
if userID := strings.TrimSpace(gjson.GetBytes(body, "metadata.user_id").String()); userID != "" {
seed := reqModel + "-" + userID
if promptCacheKey == "" {
promptCacheKey = service.GenerateSessionUUID(seed)
}
if sessionHash == "" {
sessionHash = service.DeriveSessionHashFromSeed(seed)
}
}
}
maxAccountSwitches := h.maxAccountSwitches
switchCount := 0
failedAccountIDs := make(map[int64]struct{})
......
......@@ -997,6 +997,11 @@ func (s *GatewayService) buildOAuthMetadataUserID(parsed *ParsedRequest, account
return fmt.Sprintf("user_%s_account__session_%s", userID, sessionID)
}
// GenerateSessionUUID creates a deterministic UUID4 from a seed string.
func GenerateSessionUUID(seed string) string {
return generateSessionUUID(seed)
}
func generateSessionUUID(seed string) string {
if seed == "" {
return uuid.NewString()
......
......@@ -78,7 +78,12 @@ func (s *OpenAIGatewayService) ForwardAsAnthropic(
if err := json.Unmarshal(responsesBody, &reqBody); err != nil {
return nil, fmt.Errorf("unmarshal for codex transform: %w", err)
}
applyCodexOAuthTransform(reqBody, false, false)
codexResult := applyCodexOAuthTransform(reqBody, false, false)
if codexResult.PromptCacheKey != "" {
promptCacheKey = codexResult.PromptCacheKey
} else if promptCacheKey != "" {
reqBody["prompt_cache_key"] = promptCacheKey
}
// OAuth codex transform forces stream=true upstream, so always use
// the streaming response handler regardless of what the client asked.
isStream = true
......
......@@ -3646,6 +3646,13 @@ type OpenAIRecordUsageInput struct {
// RecordUsage records usage and deducts balance
func (s *OpenAIGatewayService) RecordUsage(ctx context.Context, input *OpenAIRecordUsageInput) error {
result := input.Result
// 跳过所有 token 均为零的用量记录——上游未返回 usage 时不应写入数据库
if result.Usage.InputTokens == 0 && result.Usage.OutputTokens == 0 &&
result.Usage.CacheCreationInputTokens == 0 && result.Usage.CacheReadInputTokens == 0 {
return nil
}
apiKey := input.APIKey
user := input.User
account := input.Account
......
......@@ -29,6 +29,13 @@ func openAIStickyCompatStats() (legacyReadFallbackTotal, legacyReadFallbackHit,
openAIStickyLegacyDualWriteTotal.Load()
}
// DeriveSessionHashFromSeed computes the current-format sticky-session hash
// from an arbitrary seed string.
func DeriveSessionHashFromSeed(seed string) string {
currentHash, _ := deriveOpenAISessionHashes(seed)
return currentHash
}
func deriveOpenAISessionHashes(sessionID string) (currentHash string, legacyHash string) {
normalized := strings.TrimSpace(sessionID)
if normalized == "" {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment