让 Grok stream idle 重试上限作用于主路径

This commit is contained in:
IanShaw
2026-08-20 07:07:55 -07:00
parent f7bc1970ef
commit c628b3eea7
7 changed files with 30 additions and 10 deletions
+14 -1
View File
@@ -82,6 +82,19 @@ func sameAccountRetryDeadlineAllows(failoverErr *service.UpstreamFailoverError)
return failoverErr == nil || failoverErr.SameAccountRetryDeadline.IsZero() || time.Now().Before(failoverErr.SameAccountRetryDeadline)
}
// effectiveSameAccountRetryLimit applies an error-specific cap without
// overriding an explicit account setting of zero (which disables retries).
func effectiveSameAccountRetryLimit(failoverErr *service.UpstreamFailoverError, account *service.Account) int {
if account == nil {
return 0
}
limit := account.GetPoolModeRetryCount()
if limit > 0 && failoverErr != nil && failoverErr.SameAccountRetryMax > 0 && failoverErr.SameAccountRetryMax < limit {
return failoverErr.SameAccountRetryMax
}
return limit
}
// FailoverState 跨循环迭代共享的 failover 状态
type FailoverState struct {
SwitchCount int
@@ -171,7 +184,7 @@ func (s *FailoverState) HandleFailoverError(
// 同账号重试不算切换账号,粘性会话仅在实际切换时强制缓存计费。
retryCount := s.SameAccountRetryCount[accountID]
if failoverErr.SameAccountRetryMax > 0 && (retryLimit <= 0 || failoverErr.SameAccountRetryMax < retryLimit) {
if failoverErr.SameAccountRetryMax > 0 && failoverErr.SameAccountRetryMax < retryLimit {
retryLimit = failoverErr.SameAccountRetryMax
}
sameAccountRetryAllowed := failoverErr.RetryableOnSameAccount && retryLimit > 0 && retryCount < retryLimit
@@ -70,6 +70,13 @@ func TestSameAccountRetryDeadlineAllows(t *testing.T) {
}))
}
func TestEffectiveSameAccountRetryLimitHonorsErrorCapAndDisabledAccount(t *testing.T) {
account := &service.Account{Type: service.AccountTypeAPIKey, Credentials: map[string]any{"pool_mode": true, "pool_mode_retry_count": float64(3)}}
require.Equal(t, 1, effectiveSameAccountRetryLimit(&service.UpstreamFailoverError{SameAccountRetryMax: 1}, account))
account.Credentials["pool_mode_retry_count"] = float64(0)
require.Equal(t, 0, effectiveSameAccountRetryLimit(&service.UpstreamFailoverError{SameAccountRetryMax: 1}, account))
}
// ---------------------------------------------------------------------------
// Helper
// ---------------------------------------------------------------------------
+1 -1
View File
@@ -361,7 +361,7 @@ func (h *OpenAIGatewayHandler) handleGrokMedia(c *gin.Context, endpoint service.
return
}
if failoverErr.RetryableOnSameAccount {
retryLimit := account.GetPoolModeRetryCount()
retryLimit := effectiveSameAccountRetryLimit(failoverErr, account)
if sameAccountRetryCount[account.ID] < retryLimit && sameAccountRetryDeadlineAllows(failoverErr) {
sameAccountRetryCount[account.ID]++
retryDelay := sameAccountRetryDelayFor(failoverErr, sameAccountRetryCount[account.ID])
@@ -327,7 +327,7 @@ func (h *OpenAIGatewayHandler) ChatCompletions(c *gin.Context) {
}
// Pool mode: retry on the same account
if failoverErr.RetryableOnSameAccount {
retryLimit := account.GetPoolModeRetryCount()
retryLimit := effectiveSameAccountRetryLimit(failoverErr, account)
if sameAccountRetryCount[account.ID] < retryLimit && sameAccountRetryDeadlineAllows(failoverErr) {
sameAccountRetryCount[account.ID]++
retryDelay := sameAccountRetryDelayFor(failoverErr, sameAccountRetryCount[account.ID])
@@ -717,7 +717,7 @@ func (h *OpenAIGatewayHandler) Responses(c *gin.Context) {
}
// 池模式:同账号重试
if failoverErr.RetryableOnSameAccount {
retryLimit := account.GetPoolModeRetryCount()
retryLimit := effectiveSameAccountRetryLimit(failoverErr, account)
if sameAccountRetryCount[account.ID] < retryLimit && sameAccountRetryDeadlineAllows(failoverErr) {
sameAccountRetryCount[account.ID]++
retryDelay := sameAccountRetryDelayFor(failoverErr, sameAccountRetryCount[account.ID])
@@ -1266,7 +1266,7 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
}
// 池模式:同账号重试
if failoverErr.RetryableOnSameAccount {
retryLimit := account.GetPoolModeRetryCount()
retryLimit := effectiveSameAccountRetryLimit(failoverErr, account)
if sameAccountRetryCount[account.ID] < retryLimit && sameAccountRetryDeadlineAllows(failoverErr) {
sameAccountRetryCount[account.ID]++
retryDelay := sameAccountRetryDelayFor(failoverErr, sameAccountRetryCount[account.ID])
+1 -1
View File
@@ -304,7 +304,7 @@ func (h *OpenAIGatewayHandler) Images(c *gin.Context) {
return
}
if failoverErr.RetryableOnSameAccount {
retryLimit := account.GetPoolModeRetryCount()
retryLimit := effectiveSameAccountRetryLimit(failoverErr, account)
if sameAccountRetryCount[account.ID] < retryLimit && sameAccountRetryDeadlineAllows(failoverErr) {
sameAccountRetryCount[account.ID]++
retryDelay := sameAccountRetryDelayFor(failoverErr, sameAccountRetryCount[account.ID])
@@ -635,10 +635,10 @@ func (s *OpenAIGatewayService) forwardGrokChatCompletionsViaResponses(
if s.shouldFailoverGrokUpstreamError(resp.StatusCode, respBody) {
retryable, retryDelay, retryDeadline := grokSameAccountRetryMetadata(account, resp.StatusCode, respBody)
return nil, &UpstreamFailoverError{
StatusCode: resp.StatusCode,
ResponseBody: respBody,
ResponseHeaders: resp.Header.Clone(),
RetryableOnSameAccount: retryable,
StatusCode: resp.StatusCode,
ResponseBody: respBody,
ResponseHeaders: resp.Header.Clone(),
RetryableOnSameAccount: retryable,
RequestScopedTransient: retryable && resp.StatusCode == http.StatusTooManyRequests,
SameAccountRetryDelay: retryDelay,
SameAccountRetryDeadline: retryDeadline,