openai_gateway_anthropic_native_pump.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package service
  2. // 国产供应商 Anthropic 协议转换路径的上游 SSE 行泵。
  3. //
  4. // 这两条转换链(CC×anthropic / Responses×anthropic)的上游 ctx 是
  5. // WithoutCancel(detachStreamUpstreamContext)、http.Client 无整体 Timeout,
  6. // 客户端断开后的排水阶段若上游挂住 SSE(不发数据也不断连),
  7. // scanner.Scan() 将永久阻塞:goroutine 钉死、resp.Body 不归还、连接池位
  8. // 被占用、usage 永不落库。
  9. //
  10. // 与 handleAnthropicStreamingResponse / readOpenAICompatBufferedTerminal 的
  11. // 同类排水一致,本泵用 gateway.stream_data_interval_timeout(默认 180s)作为
  12. // 逐行读间隔上限,超时即向调用方返回 errAnthropicNativeStreamIdle,由调用方
  13. // 关闭 resp.Body 解除阻塞的读并结束排水。
  14. import (
  15. "bufio"
  16. "errors"
  17. "io"
  18. "time"
  19. )
  20. // errAnthropicNativeStreamIdle 表示上游流读间隔超时(见上方文件注释)。
  21. var errAnthropicNativeStreamIdle = errors.New("stream data interval timeout")
  22. // anthropicNativeLineEvent 是行泵交付的单次读取结果:line 为一行 SSE 文本,
  23. // err 为 scanner 读错误(流自然结束时 next 返回 io.EOF,不经过本字段)。
  24. type anthropicNativeLineEvent struct {
  25. line string
  26. err error
  27. }
  28. // anthropicNativeLinePump 以独立 goroutine 泵送 scanner 的行,并对逐行到达
  29. // 间隔施加 interval 上限(<=0 表示禁用,保持无界读的旧行为)。
  30. type anthropicNativeLinePump struct {
  31. events chan anthropicNativeLineEvent
  32. done chan struct{}
  33. timer *time.Timer
  34. interval time.Duration
  35. }
  36. // newAnthropicNativeLinePump 启动泵 goroutine;调用方 defer pump.stop()。
  37. func newAnthropicNativeLinePump(scanner *bufio.Scanner, interval time.Duration) *anthropicNativeLinePump {
  38. p := &anthropicNativeLinePump{
  39. events: make(chan anthropicNativeLineEvent, 16),
  40. done: make(chan struct{}),
  41. interval: interval,
  42. }
  43. if interval > 0 {
  44. p.timer = time.NewTimer(interval)
  45. }
  46. go func() {
  47. defer close(p.events)
  48. for scanner.Scan() {
  49. select {
  50. case p.events <- anthropicNativeLineEvent{line: scanner.Text()}:
  51. case <-p.done:
  52. return
  53. }
  54. }
  55. if err := scanner.Err(); err != nil {
  56. select {
  57. case p.events <- anthropicNativeLineEvent{err: err}:
  58. case <-p.done:
  59. }
  60. }
  61. }()
  62. return p
  63. }
  64. // next 阻塞返回下一行。返回 io.EOF 表示上游正常收流;errAnthropicNativeStreamIdle
  65. // 表示 interval 内无任何数据到达(计时从收到上一行时起算,事件处理耗时不算入,
  66. // 与 readOpenAICompatBufferedTerminal 的 resetTimeout 语义一致)。
  67. func (p *anthropicNativeLinePump) next() (string, error) {
  68. var timeoutCh <-chan time.Time
  69. if p.timer != nil {
  70. timeoutCh = p.timer.C
  71. }
  72. select {
  73. case ev, ok := <-p.events:
  74. if !ok {
  75. return "", io.EOF
  76. }
  77. p.resetTimer()
  78. return ev.line, ev.err
  79. case <-timeoutCh:
  80. return "", errAnthropicNativeStreamIdle
  81. }
  82. }
  83. // resetTimer 在收到一行后重启间隔计时器。
  84. func (p *anthropicNativeLinePump) resetTimer() {
  85. if p.timer == nil {
  86. return
  87. }
  88. if !p.timer.Stop() {
  89. select {
  90. case <-p.timer.C:
  91. default:
  92. }
  93. }
  94. p.timer.Reset(p.interval)
  95. }
  96. // stop 终止泵 goroutine。注意:goroutine 若正阻塞在 scanner.Read 上,需由
  97. // 调用方关闭 resp.Body(间隔超时分支已做)才能真正退出。
  98. func (p *anthropicNativeLinePump) stop() {
  99. close(p.done)
  100. if p.timer != nil {
  101. if !p.timer.Stop() {
  102. select {
  103. case <-p.timer.C:
  104. default:
  105. }
  106. }
  107. }
  108. }
  109. // anthropicNativeStreamInterval 返回本组转换路径适用的读间隔上限;
  110. // gateway.stream_data_interval_timeout <= 0 时视为禁用。
  111. func (s *OpenAIGatewayService) anthropicNativeStreamInterval() time.Duration {
  112. if s.cfg != nil && s.cfg.Gateway.StreamDataIntervalTimeout > 0 {
  113. return time.Duration(s.cfg.Gateway.StreamDataIntervalTimeout) * time.Second
  114. }
  115. return 0
  116. }