Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
39 lines
1.6 KiB
Go
39 lines
1.6 KiB
Go
package service
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Default Grok stream idle when gateway.stream_data_interval_timeout is 0.
|
|
// Long enough for slow thinking models, short enough to release hung sockets.
|
|
const defaultGrokStreamIdleTimeout = 180 * time.Second
|
|
|
|
// Shorter cool after a Grok stream-idle failure so the account can re-enter soon
|
|
// but is not immediately re-picked in a tight failover loop.
|
|
const grokStreamIdleCooldown = 2 * time.Minute
|
|
|
|
// resolveGrokStreamIdleTimeout returns the effective upstream-read idle timeout
|
|
// for Grok streams. Prefers the global gateway setting when positive; otherwise
|
|
// applies a Grok-only default so hung SSE bodies still fail over.
|
|
func resolveGrokStreamIdleTimeout(cfgStreamIntervalSec int) time.Duration {
|
|
if cfgStreamIntervalSec > 0 {
|
|
return time.Duration(cfgStreamIntervalSec) * time.Second
|
|
}
|
|
return defaultGrokStreamIdleTimeout
|
|
}
|
|
|
|
// grokStreamIdleFailoverError builds a pre-commit/handler-visible failover so
|
|
// the gateway can switch OAuth accounts after a hung Grok upstream stream.
|
|
func grokStreamIdleFailoverError(account *Account, idle time.Duration) *UpstreamFailoverError {
|
|
msg := fmt.Sprintf("Grok stream idle timeout after %s with no upstream data", idle.Round(time.Second))
|
|
return &UpstreamFailoverError{
|
|
StatusCode: 502,
|
|
ResponseBody: []byte(`{"error":{"code":"empty_upstream","message":"` + strings.ReplaceAll(msg, `"`, `'`) + `"}}`),
|
|
SafeToFailoverAfterWrite: true,
|
|
// Allow pool-mode retries; normal OAuth switches account via handler.
|
|
RetryableOnSameAccount: account != nil && account.IsPoolMode(),
|
|
}
|
|
}
|