Skip to content

feat(provider):增加透明重传以及解决缓冲区溢出问题#171

Merged
minorcell merged 4 commits into
1024XEngineer:mainfrom
phantom5099:main
Apr 6, 2026
Merged

feat(provider):增加透明重传以及解决缓冲区溢出问题#171
minorcell merged 4 commits into
1024XEngineer:mainfrom
phantom5099:main

Conversation

@phantom5099

Copy link
Copy Markdown
Collaborator

变更说明

本 PR 解决 OpenAI provider 层 SSE 流的两个核心缺陷:传输中途断连时无自动恢复能力、无缓冲区溢出防护。改动对上层(runtime/tui)完全透明,不影响 Provider 接口签名。

主要改动

1. 新增 internal/provider/openai/sse_reader.go(~70 行)

纯同步有界 SSE 行读取器 boundedSSEReader,包装 bufio.Reader 并施加两级硬限制:

L1: maxSSELineSize     = 256 KB   // 单行上限
L3: maxStreamTotalSize = 10 MB    // 流总量上限
  • ReadLine() 返回去 \r\n 的行内容
  • 超限时分别返回 ErrLineTooLong / ErrStreamTooLarge 哨兵错误
  • 无 goroutine/channel,零并发开销,适配 SSE 顺序消费模型

2. internal/provider/errors.go — 追加内容

// 3 个新哨兵错误
ErrStreamInterrupted  // 通用流中断标记(可恢复)
ErrLineTooLong        // 单行超长(不可恢复)
ErrStreamTooLarge     // 总量超限(不可恢复)

// 1 个新公开函数
func IsRecoverableStreamError(err error) bool

IsRecoverableStreamError 判定逻辑:

错误类型 可恢复 说明
context.Canceled / DeadlineExceeded 调用方主动终止
ErrLineTooLong / ErrStreamTooLarge 重连同样会触发
ProviderError.Retryable == false(4xx) 业务错误
ProviderError.Retryable == true(5xx/429) 服务端临时故障
*net.OpError 网络层临时故障
ErrStreamInterrupted 显式可恢复标记

3. internal/provider/openai/openai.go — 核心重构

Chat() — 从单次执行变为重连循环

for attempt := 0..maxReconnects(3):
  └─ chatOnce(ctx, req, events, &accumText, &accumCalls)
      失败且 IsRecoverableStreamError → 退避(1s/2s/4s) → 继续下一轮
      失败但不可恢复 / 已达最大次数 → 返回 error
      成功 → return nil

跨周期状态通过闭包局部变量持久化:

  • accumText strings.Builder — 已累积文本
  • accumCalls map[int]*provider.ToolCall — 已累积 tool call

提取 chatOnce() — 单次 HTTP 请求 + 流消费

从原 Chat() 提取,职责单一:构建请求 → 发送 HTTP → 校验状态码 → consumeStream()

改写 consumeStream() — 接入累积状态 + 有界读取器

变更项 修改前 修改后
行读取器 bufio.NewReader(body).ReadString('\n') newBoundedSSEReader(body).ReadLine()
文本处理 仅发送 text_delta 事件 同步写入 accumText + 发送事件
tool call 处理 mergeToolCallDelta() mergeToolCallDeltaWithAccum() 同步写入 accumCalls
非 EOF 错误 返回原始 error 包装为 ErrStreamInterrupted

新增辅助方法

  • buildAssistantMsg(accumText, accumCalls) provider.Message — 从累积状态构造 assistant 角色 OpenAI 消息,用于重连时注入上下文
  • mergeToolCallDeltaWithAccum(ctx, events, accumCalls, delta) — 在原 mergeToolCallDelta 基础上同步维护跨周期累积状态

预期收益

  1. 用户体验提升:网络波动不再导致整轮对话丢失,断点续传对用户完全无感
  2. 安全性保障:内存使用有确定上限(单行 256KB / 总量 10MB),杜绝 OOM 风险
  3. 可观测性增强:流错误有了明确的类型分类(哨兵错误),便于上层日志和监控
  4. 零侵入:Provider 接口签名不变,runtime/tui 无需任何适配代码
  5. 可控风险:最大 3 次重连 + 指数退避 + 明确的可恢复性边界,避免无限重试雪崩

@codecov

codecov Bot commented Apr 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.65421% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/provider/openai/openai.go 89.65% 4 Missing and 2 partials ⚠️
internal/provider/errors.go 92.59% 1 Missing and 1 partial ⚠️
internal/provider/openai/sse_reader.go 90.90% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@minorcell minorcell merged commit 34063b0 into 1024XEngineer:main Apr 6, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants