一个装了几年都没出过问题的命令行工具,8月中旬突然在全新安装时集体报错。Simon Willison维护的LLM——一个用来从命令行调用大模型的Python工具——在8月21日紧急放出0.32.1版本,靠一行依赖版本锁定把安装恢复正常。发布说明写得云淡风轻,但翻看项目的issue记录会发现,这行锁定是排除了两条更彻底的修复路径之后,才不得不选的权宜之计。

一个传递依赖,说断就断

LLM本身从没直接声明依赖httpx这个HTTP客户端库,它一直是靠openai这个官方Python SDK顺带装进来的。这种写法在Python生态里很常见:你不用的库,只要你依赖的库用了,pip就会替你悄悄装上。

麻烦出在8月12日,OpenAI Python SDK发布3.0.0版本,默认把HTTP客户端换成了pydantic团队做的httpx2,不再自动安装老的httpx包。LLM的依赖链条一下子断了一环,用户执行pip install时,httpx压根没装进环境,程序一启动就是ModuleNotFoundError

四次故障处理,两次被放弃 #1608 发现断裂 fresh install 失败被报告 #1609 只加httpx 测试模拟 仍会失效 #1621 迁移测试到 httpx2 尝试放弃 #1630 当天开关 锁定openai<3 拍板发布

两次尝试失败,才轮到锁版本

issue #1608先报了故障。项目维护者最初的想法很直接——issue #1609提议给LLM直接加上httpx依赖,问题不就解决了。

但很快发现没那么简单:LLM的测试套件用pytest-httpx来模拟API调用,这套模拟工具是绑定httpx底层实现写的。openai换成httpx2之后,模拟根本拦不住新的流量,测试请求会真的打到OpenAI的正式接口上。只加一个httpx包,装是能装上,测试却是坏的。

于是有了issue #1621,尝试把整套测试基础设施迁移到httpx2,从根子上解决问题。这条路走到一半被放弃了——测试迁移涉及的改动比想象中大,短期内做不完整。

8月21日当天,issue #1630开了又关,最终方案定了下来:不追求一步到位,先把openai依赖锁定在3.0以下,让环境里继续带着老版本的httpx,先把安装问题堵上。0.32.1就是这个决定的产物,pyproject.toml里那行openai>=2.32.0改成了openai>=2.32.0,<3

锁定不是治愈,只是拖了时间

这行版本锁定解决了眼下的报错,但没有解决问题的根。LLM的包元数据依然没有把httpx列为自己的显式依赖——它能正常工作,纯粹是因为被锁死的openai版本还在传递安装httpx。

看起来修好了,其实只是绕开 0.32.1做了什么 锁定 openai<3 借旧版间接保留httpx fresh install恢复正常 没做的事 httpx未成为显式依赖 测试仍绑定旧httpx实现 openai后续2.x若微调,故障可复发
  • 风险.如果openai在2.x系列里再发一个补丁版本调整了传递安装行为,同样的ModuleNotFoundError完全可能重演,用户届时需要手动pip install httpx兜底。
补丁解决的是症状,不是病灶。

这不是LLM一家的孤例。任何Python项目只要没把间接用到的库写进自己的依赖声明里,都可能在某天上游一次看似内部的调整后突然裂开。OpenAI SDK这次换HTTP客户端,波及的显然不止一个下游项目——凡是靠openai库间接引入httpx做二次封装的CLI工具、网关和agent框架,都值得回头检查一下自己的依赖清单里,是不是也藏着类似的"没写但用了"。

发布说明里提到,即将到来的0.33版本会正式把LLM从httpx切到httpx2,届时测试基础设施能不能跟着彻底迁移过去,才是真正检验这次故障有没有被治好的时刻。在那之前,0.32.1更像一块暂时按住伤口的纱布。