openai_gateway_reasoning_failover.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. package handler
  2. import (
  3. "github.com/Wei-Shaw/sub2api/internal/service"
  4. "go.uber.org/zap"
  5. )
  6. // openAIPassthroughFailoverState tracks whether this forwarding loop has attempted
  7. // an OpenAI passthrough account. Once it has, every subsequent non-passthrough
  8. // attempt must use a sanitized body because the immutable canonical body can carry
  9. // provider-specific encrypted reasoning produced by that passthrough upstream.
  10. type openAIPassthroughFailoverState struct {
  11. passthroughSeen bool
  12. }
  13. // deriveOpenAIForwardAttemptBody returns the request body for the upcoming forward
  14. // attempt against account. It always derives from the immutable canonical body and
  15. // only removes encrypted reasoning input item(s) when the loop has already attempted
  16. // a passthrough account and the upcoming account is non-passthrough. This remains
  17. // sticky across retries and additional non-passthrough accounts. Attempts before
  18. // any passthrough account, and all passthrough attempts, forward the canonical body
  19. // unchanged. The canonical slice is never mutated.
  20. //
  21. // This method is invoked exactly once per forward attempt, immediately before the
  22. // Forward call, and advances the failover state as a side effect.
  23. func (h *OpenAIGatewayHandler) deriveOpenAIForwardAttemptBody(
  24. reqLog *zap.Logger,
  25. canonicalBody []byte,
  26. account *service.Account,
  27. state *openAIPassthroughFailoverState,
  28. ) []byte {
  29. currentPassthrough := account.IsOpenAIPassthroughEnabled()
  30. if currentPassthrough {
  31. state.passthroughSeen = true
  32. return canonicalBody
  33. }
  34. if !state.passthroughSeen {
  35. return canonicalBody
  36. }
  37. sanitized, changed, err := service.SanitizeOpenAICrossModeFailoverReasoning(canonicalBody)
  38. if err != nil {
  39. if reqLog != nil {
  40. reqLog.Warn("openai.failover_cross_mode_reasoning_sanitize_failed",
  41. zap.Int64("account_id", account.ID),
  42. zap.Error(err),
  43. )
  44. }
  45. return canonicalBody
  46. }
  47. if !changed {
  48. return canonicalBody
  49. }
  50. if reqLog != nil {
  51. reqLog.Info("openai.failover_cross_mode_reasoning_stripped",
  52. zap.Int64("account_id", account.ID),
  53. zap.Bool("account_passthrough", currentPassthrough),
  54. zap.Bool("passthrough_seen", true),
  55. )
  56. }
  57. return sanitized
  58. }