ByteNoteByteNote
空行的长度并不是零
字

字节笔记本

2026年10月7日 · 约 2 分钟读完

空行的长度并不是零

API中转
¥120

函数用缓冲读取器按换行读上游,再用 c.Stream 把内容往外写。原来的循环把读到的行拼上三个等号打印。所以日志里每一段末尾的 === 是打印加上的,不是上游多出来的字段。

四段正文和一次停止

剥开前缀之后,增量依次是管理和、使用、账户、句号。最后一条的 content 是空字符串,finish_reason 是 stop。用量是 prompt 9、completion 460、total 469,缓存命中 0、未命中 9。模型名是 deepseek-chat。中间几行的概率字段拼写并不都是 logprobs,解析结构只取 content 和 finish_reason,那些拼写不会改变抽出的正文。

改写用 TrimPrefix 去掉 data: ,再把 [DONE] 加换行当成结束。结束时返回 false,流停止。解析失败则返回 true,继续下一行。有内容就做成只含 content 的对象;finish_reason 非空时再加 status。然后仍用 SSEvent 以 message 的名字发出去。

长度为零的判断走不到

ReadString 会把分隔用的换行留在结果里。视觉上的空行,长度是一,不是零。len(line) == 0 因此跳不过这些行。它们进到去前缀和解析,解析失败后再继续。结束行若是 data: [DONE] 再加换行,去前缀后才等于代码里比较的那一串。若换行是回车加换行,比较会失败,这一行被当成坏的 JSON 跳过,流不会因为结束标记而停。

出错分支里,是不是文件结束都 return false。外层的判断没有第二条出路。

三个等号是打印接上去的

用户要的是直接得到内容和状态。改写仍是一条条服务端事件,不是一次写完的响应体。事件名字是 message。外面再包一层之后,客户端看到的还是事件流。

示例输出四条内容和一条带 stop 的空内容,和上面的增量一致。用量、编号、创建时间没有放进这个简化对象。

空行仍带着换行所以长度不是零

解析用的结构只声明了 choices 里的 delta.content 和 finish_reason。别的字段被忽略。这是简化能成立的原因,也是用量不会出现在新事件里的原因。

继续返回 true 表示还要读下一行。结束标记和读错误才返回 false。空行解析失败走的是继续,不是结束。

相关文章

分享: