mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-03 06:52:13 +08:00
fix: 修复gpt->claude转换无法命中codex缓存问题
This commit is contained in:
@@ -560,6 +560,21 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
|
|||||||
sessionHash := h.gatewayService.GenerateSessionHash(c, body)
|
sessionHash := h.gatewayService.GenerateSessionHash(c, body)
|
||||||
promptCacheKey := h.gatewayService.ExtractSessionID(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
|
maxAccountSwitches := h.maxAccountSwitches
|
||||||
switchCount := 0
|
switchCount := 0
|
||||||
failedAccountIDs := make(map[int64]struct{})
|
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)
|
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 {
|
func generateSessionUUID(seed string) string {
|
||||||
if seed == "" {
|
if seed == "" {
|
||||||
return uuid.NewString()
|
return uuid.NewString()
|
||||||
|
|||||||
@@ -78,7 +78,12 @@ func (s *OpenAIGatewayService) ForwardAsAnthropic(
|
|||||||
if err := json.Unmarshal(responsesBody, &reqBody); err != nil {
|
if err := json.Unmarshal(responsesBody, &reqBody); err != nil {
|
||||||
return nil, fmt.Errorf("unmarshal for codex transform: %w", err)
|
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
|
// OAuth codex transform forces stream=true upstream, so always use
|
||||||
// the streaming response handler regardless of what the client asked.
|
// the streaming response handler regardless of what the client asked.
|
||||||
isStream = true
|
isStream = true
|
||||||
|
|||||||
@@ -3646,6 +3646,13 @@ type OpenAIRecordUsageInput struct {
|
|||||||
// RecordUsage records usage and deducts balance
|
// RecordUsage records usage and deducts balance
|
||||||
func (s *OpenAIGatewayService) RecordUsage(ctx context.Context, input *OpenAIRecordUsageInput) error {
|
func (s *OpenAIGatewayService) RecordUsage(ctx context.Context, input *OpenAIRecordUsageInput) error {
|
||||||
result := input.Result
|
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
|
apiKey := input.APIKey
|
||||||
user := input.User
|
user := input.User
|
||||||
account := input.Account
|
account := input.Account
|
||||||
|
|||||||
@@ -29,6 +29,13 @@ func openAIStickyCompatStats() (legacyReadFallbackTotal, legacyReadFallbackHit,
|
|||||||
openAIStickyLegacyDualWriteTotal.Load()
|
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) {
|
func deriveOpenAISessionHashes(sessionID string) (currentHash string, legacyHash string) {
|
||||||
normalized := strings.TrimSpace(sessionID)
|
normalized := strings.TrimSpace(sessionID)
|
||||||
if normalized == "" {
|
if normalized == "" {
|
||||||
|
|||||||
Reference in New Issue
Block a user