Merge branch 'main' into test-dev
This commit is contained in:
@@ -384,6 +384,7 @@ func (s *GeminiMessagesCompatService) Forward(ctx context.Context, c *gin.Contex
|
||||
if err != nil {
|
||||
return nil, s.writeClaudeError(c, http.StatusBadRequest, "invalid_request_error", err.Error())
|
||||
}
|
||||
originalClaudeBody := body
|
||||
|
||||
proxyURL := ""
|
||||
if account.ProxyID != nil && account.Proxy != nil {
|
||||
@@ -516,6 +517,7 @@ func (s *GeminiMessagesCompatService) Forward(ctx context.Context, c *gin.Contex
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
signatureRetryStage := 0
|
||||
for attempt := 1; attempt <= geminiMaxRetries; attempt++ {
|
||||
upstreamReq, idHeader, err := buildReq(ctx)
|
||||
if err != nil {
|
||||
@@ -540,6 +542,46 @@ func (s *GeminiMessagesCompatService) Forward(ctx context.Context, c *gin.Contex
|
||||
return nil, s.writeClaudeError(c, http.StatusBadGateway, "upstream_error", "Upstream request failed after retries: "+sanitizeUpstreamErrorMessage(err.Error()))
|
||||
}
|
||||
|
||||
// Special-case: signature/thought_signature validation errors are not transient, but may be fixed by
|
||||
// downgrading Claude thinking/tool history to plain text (conservative two-stage retry).
|
||||
if resp.StatusCode == http.StatusBadRequest && signatureRetryStage < 2 {
|
||||
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 2<<20))
|
||||
_ = resp.Body.Close()
|
||||
|
||||
if isGeminiSignatureRelatedError(respBody) {
|
||||
var strippedClaudeBody []byte
|
||||
stageName := ""
|
||||
switch signatureRetryStage {
|
||||
case 0:
|
||||
// Stage 1: disable thinking + thinking->text
|
||||
strippedClaudeBody = FilterThinkingBlocksForRetry(originalClaudeBody)
|
||||
stageName = "thinking-only"
|
||||
signatureRetryStage = 1
|
||||
default:
|
||||
// Stage 2: additionally downgrade tool_use/tool_result blocks to text
|
||||
strippedClaudeBody = FilterSignatureSensitiveBlocksForRetry(originalClaudeBody)
|
||||
stageName = "thinking+tools"
|
||||
signatureRetryStage = 2
|
||||
}
|
||||
retryGeminiReq, txErr := convertClaudeMessagesToGeminiGenerateContent(strippedClaudeBody)
|
||||
if txErr == nil {
|
||||
log.Printf("Gemini account %d: detected signature-related 400, retrying with downgraded Claude blocks (%s)", account.ID, stageName)
|
||||
geminiReq = retryGeminiReq
|
||||
// Consume one retry budget attempt and continue with the updated request payload.
|
||||
sleepGeminiBackoff(1)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Restore body for downstream error handling.
|
||||
resp = &http.Response{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
Header: resp.Header.Clone(),
|
||||
Body: io.NopCloser(bytes.NewReader(respBody)),
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 && s.shouldRetryGeminiUpstreamError(account, resp.StatusCode) {
|
||||
respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 2<<20))
|
||||
_ = resp.Body.Close()
|
||||
@@ -637,6 +679,14 @@ func (s *GeminiMessagesCompatService) Forward(ctx context.Context, c *gin.Contex
|
||||
}, nil
|
||||
}
|
||||
|
||||
func isGeminiSignatureRelatedError(respBody []byte) bool {
|
||||
msg := strings.ToLower(strings.TrimSpace(extractAntigravityErrorMessage(respBody)))
|
||||
if msg == "" {
|
||||
msg = strings.ToLower(string(respBody))
|
||||
}
|
||||
return strings.Contains(msg, "thought_signature") || strings.Contains(msg, "signature")
|
||||
}
|
||||
|
||||
func (s *GeminiMessagesCompatService) ForwardNative(ctx context.Context, c *gin.Context, account *Account, originalModel string, action string, stream bool, body []byte) (*ForwardResult, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user