Unrestricted caching keyed by generated types causes memory leak in multi-threaded regimes

当你在多线程环境(例如 Web 服务)中反复调用 OpenAI.responses.parse 并传入 Pydantic 模型作为 text_format 时,出现过 Unrestricted caching keyed by generated types causes memory leak i

快速结论:当你在多线程环境(例如 Web 服务)中反复调用 OpenAI.responses.parse 并传入 Pydantic 模型作为 text_format 时,出现过 Unrestricted caching keyed by generated types causes memory leak in multi-threaded regimes,内存随请求持续增长。优先升级 openaiv3.16.2 或更高版本

适用环境:Issue 中确认的环境为 macOS、Python v3.12.11、openai v1.107.0;使用 responses.parsetext_format 为 Pydantic BaseModel。该问题与具体模型和用户输入无关,任何模型均可复现。

最快修复方案:升级到 openai>=3.16.2。该版本由 #3088 修复,维护者在多线程复现场景下验证:原先每次新建线程池都会增长的缓存 TypeAdapter 数量已保持稳定。

注意事项:Issue 中曾有一个临时 workaround:将 openai._models.TypeAdapter 替换为 lru_cache() 包裹的版本以限制缓存大小(上限 128),但评论者明确说明这会让缓存机制失效,属于“非最优”方案。在升级到 v3.16.2 之后不应再使用该 monkey patch。请以官方修复版本为准,不要长期依赖 workaround。

问题场景

用户在调用 OpenAI Python SDK 的 client.responses.parse() 时,传入 text_format=MyClass(一个 Pydantic BaseModel 子类)来校验结构化输出。单线程循环调用时内存变化很小,但在多线程场景下(例如标准 Web 服务器每个请求开一条线程,或每次新建 ThreadPoolExecutor),进程内存随每次请求持续增长,形成内存泄漏。Issue 报告者用 psutil 打印 RSS,并用 threading.Thread 方式复现了内存增长。

报错原文

Unrestricted caching keyed by generated types causes memory leak in multi-threaded regimes

原因分析

responses.parse 在校验响应时,会为传入的 text_format(Issue 中记为 MyClass)动态生成类型 ParsedResponseOutputMessage[MyClass],并把该类型交给 pydantic.TypeAdapter 的一个无界 lru_cache。在多线程环境下,Pydantic 会重新生成该类型,导致每次运行的 hash 都不同,缓存条目不断累积,从而造成缓存无限增长。评论者进一步验证:使用固定的 BaseModel 响应格式、并反复新建 ThreadPoolExecutor(max_workers=16),进程级缓存的 TypeAdapter 数量随 wave 递增(initial: 2 → wave 1: 36 → wave 2: 62 → wave 3: 88 → wave 4: 116)。顺序调用会复用相同条目,只有新线程池会触发新的运行时泛型标识。

环境排查

  • 确认 openai 版本:低于 v3.16.2 均可能受影响,Issue 报告版本为 v1.107.0。
  • 确认调用方式:是否使用 client.responses.parse(),且 text_format 为 Pydantic BaseModel
  • 确认并发模型:是否在多线程 / Web 服务器请求处理中反复调用,是否每次请求新建线程或线程池。
  • 确认 Python 版本:Issue 环境为 Python v3.12.11。
  • 确认操作系统:Issue 环境为 macOS(该问题与操作系统无关,多线程即可复现)。
  • 观察内存:可用 psutil.Process().memory_info().rss 打印 RSS,确认是否随请求持续增长。

解决步骤

  1. openai 升级到 v3.16.2 或更高版本,该版本包含 #3088 的修复。
  2. 升级后重新运行多线程复现场景,观察内存是否仍然持续增长。
  3. 如果你此前使用了评论中提供的 workaround(覆写 openai._models.TypeAdapteropenai._models.lru_cache()(openai._models._TypeAdapter)),在升级到 v3.16.2 后应移除该 monkey patch,避免与实际修复冲突。
  4. 如果因故暂时无法升级,可优先尝试该 workaround,但需知它只是限制缓存大小(128),并非根治,会使缓存机制失效。

验证方法

在多线程环境下反复调用 client.responses.parse(),持续打印进程 RSS 或缓存 TypeAdapter 数量。升级到 v3.16.2 后,官方验证结果为:先前每次新建线程池都会增长的缓存 TypeAdapter 数量现已保持稳定;顺序调用依旧复用相同条目,新建线程池不再触发无界增长。

参考来源

openai/openai-python #2672

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 24505

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注