GPTMap

openai-python 3.14.x 与 openai-node 7.16 / 7.17 更新解读:流式错误规范化、WebSocket 背压与 SSE 兜底

OpenAI 官方 SDK 三天四版,主题罕见地统一:可靠性。python 侧流式消费的超时与断连改抛 SDK 异常、错误码统一字符串化、max_retries 请求前预校验;node 侧 WebSocket 迭代器有了 maxBufferedEvents 背压上限、SSE 末事件缺尾空行不再丢。逐项对应 commit 拆解,附可复制示例。

TL;DR
2026-09-14 至 09-16,OpenAI 官方 SDK 四版连发,主题是流式与连接可靠性。python v3.14.0/v3.14.1:流消费读超时抛 APITimeoutError、其他 httpx 失败抛 APIConnectionError(原异常在 __cause__;流消费不自动重试);APIStatusError.code 统一转字符串(404 变 '404');max_retries 请求前预校验(0 关闭重试);responses.parse 不再解析 commentary。node v7.16.0/v7.17.0:WebSocket 迭代器新增 maxBufferedEvents 背压(溢出弃积压并对 next() 抛 WebSocketError);SSE 末事件缺尾空行时 EOF 兜底 flush;函数式 API key 无已解析凭据时 WS 构造即抛错。
openai SDK 2026-09 可靠性批次是 openai-python v3.14.0 / v3.14.1 与 openai-node v7.16.0 / v7.17.0 四个版本(2026-09-14 至 09-16 发布)的合称:python 侧把流式消费的传输层异常规范化为 SDK 异常、统一错误码类型并收紧重试参数校验;node 侧给 WebSocket 流式迭代器加背压上限、修复 SSE 终止事件丢失并收紧函数式 API key 的 WebSocket 构造。

OpenAI 官方 SDK 在 2026-09-14 至 09-16 三天里连发四个版本,和一个多月前那批"新 API 面"式的更新(prompt cache 诊断、Live API、Agents API)不同,这一批的关键词只有一个:可靠性。流式消费中途断线抛什么异常、错误码是什么类型、WebSocket 迭代器积压怎么办、服务端忘了给最后一个 SSE 事件收尾怎么办——全是生产环境里真实会撞上的问题。本文逐项对应到 commit,基于当日从各版本 tag 提取的 diff 与文档原文拆解。

1. 版本时间线

包版本发布时间(UTC)主题
openai-pythonv3.14.02026-09-14T23:28流式异常规范化(#3827)+ 错误码字符串化等
openai-pythonv3.14.12026-09-15T23:12max_retries 预校验(#3867)+ parse commentary 例外
openai-nodev7.16.02026-09-15T16:48WebSocket 迭代器背压(#2748)
openai-nodev7.17.02026-09-16T19:23compaction 进度事件(#2749)+ SSE 兜底与 WS 凭据收紧

两个包的版本号依旧不一一对应,同一主题的修复落在两包不同版本是常态;下文按包分组展开。

2. python:流消费异常规范化(#3827,v3.14.0)

这是本批 python 侧唯一的 feature 级改动,也是对生产影响最大的一条。此前从 Stream / AsyncStream 里消费事件时,传输层异常会以 httpx 原始异常的形态漏出来;v3.14.0 起统一规范化为 SDK 异常:

  • 读超时 → APITimeoutError;
  • 其他 httpx 请求失败 → APIConnectionError;
  • 原始异常始终挂在 __cause__ 上;
  • 官方 README 明确:流消费不会自动重试——重放请求可能把已经交付给应用的输出重复投递一遍;
  • 兼容性边界:Assistants 事件处理器助手与裸的 with_streaming_response 迭代器保留原有异常行为(前者会把 SDK 异常解包回旧传输异常)。

按 README 的异常处理段落改写的流式消费模式:

import openai
from openai import OpenAI

client = OpenAI()

try:
    stream = client.responses.create(
        model="gpt-5.6-terra",
        input="给我讲讲 prompt caching",
        stream=True,
    )
    for event in stream:
        handle(event)  # 应用层建议持久化已处理位置,断连后自行决定从哪续
except openai.APITimeoutError as e:
    print("读超时,原始异常:", e.__cause__)
except openai.APIConnectionError as e:
    print("连接失败,原始异常:", e.__cause__)

注意官方的措辞是"catch these SDK exceptions instead of raw HTTPX exceptions"——如果你的 except 里还写着 httpx 的异常类,升级后它们不会再被命中。

3. python:错误码字符串化、重试校验与 parse 例外(v3.14.0 / v3.14.1)

错误码统一转字符串(#3532,v3.14.0)。带响应体的 API 错误对象(APIStatusError 及其子类)的 code 属性,从"原样透传"改为"统一转 str":404 变 '404'、0 变 '0'、空串保持空串、None 保持 None(官方测试逐例覆盖)。这是典型的静默行为变化——拿 code 与整数做全等比较的代码不会报错,只会永远失配:

except openai.APIStatusError as e:
    # v3.14.0 起 e.code 恒为 str 或 None
    if e.code == "insufficient_quota":  # 用字符串比较
        ...

max_retries 请求前预校验(#3867,v3.14.1)。max_retries 必须是非负整数:0 关闭重试;想放大重试预算就传大整数(官方测试直接用到 10 的 100 次方);None 抛 TypeError(提示信息改为"用 0 关闭重试或传大整数")、负数抛 ValueError——都在请求发出之前。同一条 commit 还收紧了重试循环本身:只捕获传输层请求异常,自定义 transport 或 hook 抛出的应用异常(包括任务执行器的取消信号)原样穿透;重试日志也从"Retrying request in N seconds"升级为带"retry i of N"进度。

parse 的 commentary 例外(#3861,v3.14.1)。client.responses.parse(..., text_format=YourModel) 之前会把所有文本输出项都尝试解析成 Pydantic 模型;现在带 phase 标记的输出项里,凡 phase 不是 final_answer 的(如 commentary),一律不做结构化解析、parsed 恒为 None——即使文本碰巧匹配 schema;refusal 也不会拿 commentary 文本当兜底答案去解析。output_text 行为不变(仍拼接包括 commentary 在内的全部文本),流式 text-done 事件同样适用此规则;phase 为 null 的旧输出保留原行为。

同版其他修正(release notes 原文措辞):为 vector store 文件轮询设上限(#3401)、PathLike 上传元组规范化(#3475)、空 item 后保留流索引(#3126)、output_text 处理 null 文本(#3019)、content filter 错误附带 completion(#3094)、OPENAI_LOG 新增 warning / error / critical 三档且非法值忽略(#3734,注意它只配置 openai 这一个 logger,HTTP 传输层日志要单独配)。

4. node:maxBufferedEvents 背压(#2748,v7.16.0)

Node 侧 WebSocket 流式迭代器此前缓冲无上限——消费慢的生产者迟早把内存顶爆,或者积压出天级延迟。v7.16.0 给每个迭代器加了独立的背压上限,官方文档(docs/responses.md)原文给出了完整语义:

import OpenAI from 'openai';
import { ResponsesWS } from 'openai/resources/responses/ws';

const client = new OpenAI();
const socket = new ResponsesWS(client);

// 每个迭代器独立缓冲;256 只是示例值,按你的处理能力定
for await (const event of socket.stream({ maxBufferedEvents: 256 })) {
  handle(event);
}

语义要点,全部出自文档原文:

  • 计数包含消息、原始数据、错误与生命周期记录(初始连接状态、重连中、关闭);
  • 下一条记录将超限时,该迭代器丢弃自己的积压、移除监听,后续 next() 以 WebSocketError 拒绝(错误信息为 WebSocket stream exceeded maxBufferedEvents (N))——close 记录也可以压爆满队列;
  • 共享 socket 与其他迭代器照常工作,不需要时自己关 socket;
  • 上限跨重连持续,也不会重启一个已失败的迭代器;
  • 限的是事件条数,不是字节或内存——一条大消息仍只算一条记录;
  • 不传该选项(包括直接 for await 迭代 socket 本体)维持无限缓冲;
  • 该选项对 beta Responses 与 Live 的 WebSocket 流同样可用(同条 commit 改了 live 与 beta 两处基类);
  • 参数校验在监听器挂上之前完成:非正整数或非 safe integer 直接抛 OpenAIError。

5. node:SSE 兜底与 WebSocket 凭据收紧(v7.17.0)

SSE 终止事件不再丢(#2726)。SSE 协议靠空行结束一条事件,但服务端偶尔会在流结束时省略最后一条事件的尾空行——此前解码器会把它丢掉,带 finish_reason 的终止 chunk 就这么消失了。v7.17.0 给解码器加了 flush():EOF 时把进行中的事件恰好补发一次;已经在空行处完结的记录不会重复投递(返回 null 即无进行中事件)。

函数式 API key 的 WebSocket 构造收紧(#2586)。apiKey 传函数(每次请求动态取 key)的客户端,此前开 WebSocket 可能在连接建立后才发现没有可用凭据;现在若无已解析的 key(此前请求解析过的可复用)也无调用方提供的凭据,构造在开连接之前即抛错。官方给出的出路:先发一次普通请求让 key 解析完成、在 WebSocket options 里传已解析的 Authorization 头、用兼容端点的自定义凭据头,或 Node ws 传输的 auth 选项。

同版其他条目(release notes 原文措辞):chat runner 保留 abort 原因(#2607)、realtime 保留原生 WebSocket 错误原因(#2715)、流式 runTools 拒绝未完结 turn(#2716)、zod strict schema 省略不可能的 optional 分支(#2751)、以及给 Responses API 增加 compaction 进度事件(#2749——本站另有专文拆解)。v7.16.0 同批还有:处理畸形 WebSocket 事件并改进缓冲(#2739)、callback 凭据每请求独立(#2744)、fallback abort 订阅以弱生命周期约束(#2745)、Live 转录确认后缀线性时间裁剪(#2740)等。

6. 升级注意事项:三条行为变化速查

变化谁会撞上怎么改
流消费异常类型变了(#3827)except 里写 httpx 异常类的流式代码改捕 APITimeoutError / APIConnectionError;断线续传逻辑自己做(SDK 不会替你重试流)
error.code 变字符串(#3532)拿 code 与整数比较的错误处理比较值改成字符串;None 分支不变
max_retries 校验前移(#3867)传 None、负数或浮点的调用方;依赖"传 None 报特定旧错误信息"的测试None 改 0;校验错误改在构造/with_options 时捕获
WS 凭据校验前移(#2586)函数式 apiKey 客户端直接开 WebSocket先解析 key 或显式传凭据头 / ws auth

一句话总结:这一批把"到运行中途才爆"的问题尽量前移到构造与请求发出之前,把"漏出来的传输层异常"规范化成可编程的 SDK 异常——都是朝着生产可控去的。

7. 常见错误与排查

  • 升级后流断线没进原来的 except 分支:异常类型规范化所致,把捕获列表换成 SDK 异常,调试时先看 __cause__。
  • 错误码比较永远为 false:code 已是字符串,整数比较改字符串比较。
  • 构造客户端时 max_retries 报错:这是新校验在请求前拦截,检查传参类型(整数、非负;关重试用 0)。
  • maxBufferedEvents 触发 WebSocketError:不是 socket 断了——共享连接还活着,是该迭代器积压超限被主动止损。调大上限或提高消费速度;其他迭代器与 socket 不受影响。
  • 函数式 key 客户端开 WebSocket 抛错:凭据解析前移到构造期,按第 5 节四条出路之一显式供凭据。
  • Python 侧找不到 compaction 进度事件:截至 v3.14.1 尚未落地,见 compaction 专文的跨包差异一节。

8. 下一步

关键要点

  • python v3.14.0(#3827,本批头牌):消费 Stream / AsyncStream 时,读超时抛 APITimeoutError、其他 httpx 请求失败抛 APIConnectionError,原异常挂在 __cause__;官方 README 明确流消费不会自动重试——重放请求可能把已交付的输出重复投递
  • python v3.14.0(#3532):APIStatusError 的 code 属性统一转字符串——404 变 '404'、0 变 '0'、空串保持空串、None 保持 None;拿 code 做整数比较的代码要改
  • python v3.14.1(#3867):max_retries 请求发出前即校验——非负整数才合法,0 关闭重试,None 抛 TypeError、负数抛 ValueError;重试循环只捕传输层异常,自定义 transport / hook 抛出的应用异常原样穿透
  • python v3.14.1(#3861):responses.parse 对 phase 不是 final_answer 的输出项(如 commentary)不再做结构化解析,parsed 恒为 None——即使文本碰巧匹配 schema;output_text 仍拼接全部文本
  • node v7.16.0(#2748):socket.stream({ maxBufferedEvents: 256 }) 给每个迭代器独立设背压上限;计数含消息、原始数据、错误与生命周期记录;溢出时该迭代器弃掉积压并对 next() 抛 WebSocketError,共享 socket 与其他迭代器不受影响;限制跨重连持续、计条数不记字节
  • node v7.17.0(#2726 / #2586):SSE 解码器在流结束时 flush 一次未完结事件——服务端省略末事件尾空行不再丢终止事件;函数式 API key 客户端若无已解析 key 或调用方凭据,WebSocket 构造在开连接前即抛错

常见问题

openai-python v3.14.0(2026-09-14)主打流式异常规范化(#3827),同批有错误码字符串化与 OPENAI_LOG 扩展;v3.14.1(09-15)主打 max_retries 预校验与 parse 的 commentary 例外。openai-node v7.16.0(09-15)主打 WebSocket 迭代器背压 maxBufferedEvents(#2748);v7.17.0(09-16)主打 compaction 进度事件与 SSE 终止事件兜底、WebSocket 凭据收紧。

官方参考

相关文章

订阅 GPTMap Weekly

每周一封邮件,精选 OpenAI 重要更新、深度解读与最佳实践。无广告,可随时退订。

提交后将在新标签页打开 Buttondown 完成订阅确认。

GPTMap Editorial发布于 2026-09-17 14 分钟阅读
测试环境(EEAT)
最后测试时间:2026-09-17
使用模型:openai-python v3.14.0 / v3.14.1;openai-node v7.16.0 / v7.17.0(SDK 行为层,模型无关)