[Bug]: /v1/messages streaming delays message_start until the model’s thinking pass finishes, even with no fallbacks configured

这个报错通常出现在 LiteLLM 代理以 /v1/messages 流式转发带 adaptive thinking 的 Bedrock/Anthropic 模型时:即使配置里没有 fallback, message_start 和 content_block_start 也会被 Router 缓冲

快速结论:这个报错通常出现在 LiteLLM 代理以 /v1/messages 流式转发带 adaptive thinking 的 Bedrock/Anthropic 模型时:即使配置里没有 fallback,message_start 和 content_block_start 也会被 Router 缓冲到模型思考结束后才发出。优先排查 LiteLLM 版本、是否为 stream=True 的 anthropic_messages 调用,以及代理日志中是否出现 Available Model Group Fallbacks=None。

适用环境:Issue 中已确认的场景为 LiteLLM 代理(涉及 v1.100.0、v1.100.1、v1.101.0,v1.99.0 与 v1.99.1 不含该包装逻辑),请求路径为 /v1/messages 且 stream=True,模型为 adaptive-thinking 的 Bedrock 模型,以及 anthropic/claude-opus-5 走 api.anthropic.com 的部署。未确认 Python、CUDA、显卡等环境信息,不要自行补写。

最快修复方案:暂无确认的一步修复方案。Issue 中提出的方向是:当请求的模型组没有任何可用 fallback(general、context-window、content-policy 或 default)时,跳过 Router._aanthropic_messages_streaming_iterator 的 lifecycle 缓冲,直接透传上游流;该修复需要与 Bedrock invoke reader 上已有的 httpx chunk_size=1024 修复一起生效,才能让 message_start 之后的 content_block_start 也实时转发。可优先尝试在受影响版本上回归验证,但不要将未合入的建议补丁当作已发布修复。

注意事项:仅关闭 Router 缓冲不够,content_block_start 仍可能被 httpx 按 1024 字节边界切分而延迟;Issue 也指出 fallback 存在性判断会漏掉 order/weighted failover 配置。评论中还指出,即使无法透传 lifecycle 帧,pre-content 的 ping 也不应被丢弃,因为一旦 message_start 进入缓冲,后续 keepalive 会被丢弃,客户端在思考窗口内收不到任何字节,无法区分“慢请求”和“断连”。Issue 结束时并未确认是什么终止了约 759 秒的窗口,因此不要把该时长当成固定超时结论。

问题场景

用户在 LiteLLM 代理上通过 /v1/messages 发起流式请求,后端为带 adaptive thinking 的 Bedrock 模型(例如 thinking: {"type": "adaptive"} 的 Claude Sonnet 5),或为通过 api.anthropic.com 的 anthropic/claude-opus-5 部署。请求本身没有配置任何 fallback,但客户端在模型整个 thinking pass 期间看不到任何 SSE 数据,message_start 和 content_block_start 直到 thinking 结束才到达。同样的请求绕过代理直连 Bedrock 时,约两秒内就能收到 message_start。

报错原文

[Bug]: /v1/messages streaming delays message_start until the model's thinking pass finishes, even with no fallbacks configured

Available Model Group Fallbacks=None

原因分析

最可能的原因是 Router._aanthropic_messages_streaming_iterator 对所有流式 /v1/messages 响应无条件包装,把 message_start、content_block_start 以及被缓冲帧之后的 ping 先缓存起来,直到第一个 content_block_delta 才释放。这样做的本意是让 mid-stream provider error 能在 fallback 部署上重试,避免一个 SSE 流上出现两个重叠的 message 生命周期。

但对 adaptive-thinking 模型,Bedrock 的第一个 content_block_delta 要等整个 thinking pass 结束才到,因此这些不携带内容、也不属于 fallback 需重做范围的 lifecycle 帧被一起扣住。Issue 明确说明该行为在没有任何 fallback 部署时也会发生,所以不是 fallback 逻辑真的在保护什么。评论进一步指出,该包装在每个 stream=True 的 anthropic_messages 调用上都会执行,没有配置或环境开关可以关闭;同时 message_start 进入缓冲后,后续 ping 会被丢弃,客户端在等待窗口内收到零字节,而不是延迟的 ping。

另一层可能原因是 httpx 的字节分块器按固定 1024 字节切片,不关心 event-stream 帧边界,导致 content_block_start 的帧可能被切开,剩余部分留在 httpx 内部等待更多字节。即使只修 Router 缓冲,message_start 约两秒可出,content_block_start 仍会等到 thinking pass 结束。

环境排查

  • 确认 LiteLLM 代理版本:v1.100.0 开始包含相关包装逻辑,v1.100.1 与 v1.101.0 仍受影响;v1.99.0、v1.99.1 与引入提交对比为 diverged、behind_by: 289。
  • 确认请求路径为 /v1/messages 且 stream=True,并确认调用的是 anthropic_messages。
  • 确认配置中是否真的存在 fallbacks、context_window_fallbacks、content_policy_fallbacks 或 default_fallbacks;评论中的复现配置四者皆无。
  • 检查代理日志中是否出现 Available Model Group Fallbacks=None。
  • 确认 Bedrock invoke reader 上的 httpx chunk_size=1024 相关修复是否已包含在所用提交中。
  • 确认后端模型是否为 adaptive-thinking 模型,以及 output_config.effort 与 max_tokens 的设置。
  • 检查 LiteLLM_SpendLogs 中的 completionStartTime 与总耗时组合:Issue 中受影响行呈现约 0 秒 TTFB 与数分钟总耗时并存。
  • Issue 未提供 Python、CUDA、PyTorch、显卡或操作系统信息,相关项目不要作为排查依据。

解决步骤

  1. 先确认问题是否确实发生在 Router 包装层:查看代理日志是否存在 Available Model Group Fallbacks=None,并确认该模型组没有配置任何 fallback。若存在 fallback,Issue 中描述的“无 fallback 却仍被缓冲”的前提不成立,需要另行分析。
  2. 对比直连上游与走代理的流式行为。若直连 Bedrock 约两秒内收到 message_start,而代理在模型思考期间只发 keepalive 或完全无字节,则符合本 Issue 现象。
  3. 检查所用版本是否落在受影响范围(v1.100.0 及以上,且仍包含无条件包装逻辑的版本)。在未合入官方修复前,不要仅靠调整 fallback 配置来绕过,因为该包装没有配置或环境开关可关闭。
  4. 如果要在本地验证 Issue 提出的修复方向,可优先尝试:在无任何 fallback 可解析到当前模型组时,跳过 lifecycle 缓冲,直接透传源流;同时确保 Bedrock reader 使用按 event-stream 帧边界读取的方式,而不是固定 1024 字节切片。两处需要一起验证。
  5. 若暂时无法透传 lifecycle 帧,可优先尝试让 pre-content 的 ping 继续通过,而不是在缓冲期间丢弃,这样至少能把不可观测的挂起变回可观测的慢请求。
  6. 在等待修复期间,如需区分慢请求和断连,可结合 LiteLLM_SpendLogs.completionStartTime 判断上游首个 chunk 是否早已到达,而不要只依赖客户端侧 SSE 心跳。

验证方法

用同一个 adaptive-thinking 请求分别走代理与直连上游:直连约两秒内收到 message_start;修复后代理也应在上游发出 message_start 后尽快转发,并且 content_block_start 不再等到 thinking pass 结束。进一步确认在整个 thinking 窗口内,客户端能持续收到字节或 keepalive,而不是从 message_start 之后到第一个 content_block_delta 之间零字节。若同时关注性能指标,可对比 LiteLLM_SpendLogs 中 completionStartTime 与总耗时的关系,确认不再出现约 0 秒 TTFB 配数分钟总耗时的行。

参考来源

BerriAI/litellm #39431

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 26297

发表回复

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