grok_stream_idle.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647
  1. package service
  2. import (
  3. "fmt"
  4. "strings"
  5. "time"
  6. )
  7. // Default Grok stream idle when gateway.stream_data_interval_timeout is 0.
  8. // Long enough for slow thinking models, short enough to release hung sockets.
  9. const defaultGrokStreamIdleTimeout = 180 * time.Second
  10. // Shorter cool after a Grok stream-idle failure so the account can re-enter soon
  11. // but is not immediately re-picked in a tight failover loop.
  12. const grokStreamIdleCooldown = 2 * time.Minute
  13. // resolveGrokStreamIdleTimeout returns the effective upstream-read idle timeout
  14. // for Grok streams. Prefers the global gateway setting when positive; otherwise
  15. // applies a Grok-only default so hung SSE bodies still fail over.
  16. func resolveGrokStreamIdleTimeout(cfgStreamIntervalSec int) time.Duration {
  17. if cfgStreamIntervalSec > 0 {
  18. return time.Duration(cfgStreamIntervalSec) * time.Second
  19. }
  20. return defaultGrokStreamIdleTimeout
  21. }
  22. // grokStreamIdleFailoverError builds a pre-commit/handler-visible failover so
  23. // the gateway can switch OAuth accounts after a hung Grok upstream stream.
  24. func grokStreamIdleFailoverError(account *Account, idle time.Duration) *UpstreamFailoverError {
  25. msg := fmt.Sprintf("Grok stream idle timeout after %s with no upstream data", idle.Round(time.Second))
  26. return &UpstreamFailoverError{
  27. StatusCode: 502,
  28. ResponseBody: []byte(`{"error":{"code":"empty_upstream","message":"` + strings.ReplaceAll(msg, `"`, `'`) + `"}}`),
  29. SafeToFailoverAfterWrite: true,
  30. // An idle upstream stream is transient and should get the configured
  31. // same-account retry budget before switching credentials. This applies
  32. // to both pooled and dedicated Grok accounts; the handler still enforces
  33. // the request's retry limit.
  34. RetryableOnSameAccount: account != nil && account.Platform == PlatformGrok,
  35. RequestScopedTransient: true,
  36. SameAccountRetryMax: 1,
  37. // Permit at most one same-account replay after the idle failure. The
  38. // deadline is anchored at failure time, so a hung stream cannot consume
  39. // the normal three-attempt budget before failover.
  40. SameAccountRetryDeadline: time.Now().Add(idle),
  41. }
  42. }