[Bug]: Anthropic /v1/messages erases OpenAI Responses refusal blocks into empty content array

该问题发生在通过 LiteLLM 将 Anthropic Messages 格式请求转发到 OpenAI Responses 后端时,上游返回的 refusal 拒绝内容块在协议转换中被丢弃,导致客户端收到空 content 数组且 stop_reason 为 end_turn。优先检查 LiteL

快速结论:该问题发生在通过 LiteLLM 将 Anthropic Messages 格式请求转发到 OpenAI Responses 后端时,上游返回的 refusal 拒绝内容块在协议转换中被丢弃,导致客户端收到空 content 数组且 stop_reason 为 end_turn。优先检查 LiteLLM 的 Responses 到 Anthropic 消息转换逻辑是否处理了 refusal 内容类型。

适用环境:LiteLLM 1.99.0 及当前 main 分支(验证于 litellm_internal_staging 分支 commit c8635ecc);配置 openai/mockresp 或 openai/responses 作为上游模型;通过 Anthropic /v1/messages 兼容端点访问。

最快修复方案:暂无确认的一步修复方案。该问题在 Issue 关闭时未被官方修复,属于协议转换层的功能缺失。

注意事项:任何 workaround(如自定义回调、请求改写)均未在 Issue 中得到验证,需自行测试;该问题不涉及崩溃或报错,而是静默数据丢失,容易被业务逻辑误判为“正常空回复”。

问题场景

用户通过 LiteLLM Proxy 的 /v1/messages 端点(Anthropic 兼容接口)调用 OpenAI Responses 后端模型。当上游模型返回标准的 refusal 内容块({"type": "refusal", "refusal": "..."})时,LiteLLM 将响应转换为 Anthropic Messages 格式,但错误地把 refusal 块丢弃,给客户端返回一个空数组 content 和 end_turn 停止原因。客户端业务逻辑因此无法区分“模型正常无输出”和“模型拒绝请求”。

报错原文

[Bug]: Anthropic /v1/messages erases OpenAI Responses refusal blocks into empty content array

// upstream response:
{"type": "message", "role": "assistant", "content": [{"type": "refusal", "refusal": "REFUSALPROBE cannot help"}]}

// LiteLLM response to client:
{
  "id": "resp_...",
  "type": "message",
  "role": "assistant",
  "model": "openai-responses-model",
  "content": [],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": {"input_tokens": 1, "output_tokens": 1}
}

原因分析

问题出在 LiteLLM 的协议转换层:当 LiteLLM 将 OpenAI Responses 响应转换为 Anthropic Messages 格式时,只处理了 output_textmessage.content 等常见字段,没有针对 Responses API 的 refusal 内容类型做映射。refusal 块被当作未知结构跳过,导致 content 数组为空,且 stop_reason 未从上游的 refusal 状态推导出来。可能原因还包括:

  • 转换逻辑中未遍历 Responses output 数组中所有 content 类型,仅提取了 text 类型。
  • 对于 stop_reason 的映射不完全,上游 refusal 状态未能映射到 Anthropic 的 refusal 停止原因。

环境排查

  • 确认 LiteLLM 版本为 1.99.0 或当前 main 分支(Issue 验证于 litellm_internal_staging commit c8635ecc)。
  • 确认代理配置中模型指向 OpenAI Responses 端点(如 openai/mockresp,api_base 指向 /v1/responses 兼容服务)。
  • 确认上游返回的 JSON 结构确实是 {"type": "refusal", "refusal": "..."},而不是普通文本或错误字段。
  • 检查是否有其他中间层(如自定义回调、请求改写插件)修改了上游响应。

解决步骤

  1. 复现问题:在 LiteLLM 代理配置中添加上游 mock 服务(如 openai/mockresp),用 curl 发送 Anthropic /v1/messages 请求,确收返回体为空 content。
  2. 查看 LiteLLM 源码中 Responses 转 Anthropic 的转换文件,搜索对 refusal 字段的处理逻辑,确认是否缺失。
  3. 如果使用旧版本,先升级到包含最新提交的版本(如 litellm_internal_staging 分支对应版本),排除已修复的可能。若新版仍复现,确认是未修复的开放问题。
  4. 可优先尝试:在 LiteLLM 配置中启用 litellm.modify_params=True 或相关实验性标志,看是否影响转换行为(未在 Issue 中验证,仅推测)。
  5. 如果业务急需区分 refusal 场景,可考虑在上游 OpenAI Responses 调用处加一个包装层:将 refusal 内容改写为普通文本块并附带特殊前缀(如 [REFUSAL]),让 LiteLLM 按普通文本透传,但这会改变原始的 stop_reason 语义。
  6. 关注上游 Issue #39721 的后续进展,或在其评论中补充复现证据,推动官方修复转换逻辑。

验证方法

修复后,重新发送相同的 Anthropic /v1/messages 请求,检查返回 JSON:content 数组应包含至少一个文本块(如 {"type": "text", "text": "REFUSALPROBE cannot help"}),且 stop_reason 应为 refusal 或包含拒绝含义的状态,而非空的 end_turn

参考来源

BerriAI/litellm #39721

GamsGo AI

AI 工具推荐

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

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

了解 GamsGo AI

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

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 22099

发表回复

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