Skip to content

[Bug] 图片预处理缺少字节体积兜底:合规图片原样直通 + 历史图片不重新处理,导致 413 request_too_large #10089

Description

@MostimaBridges

问题描述

在 v4.28.1 中向多模态模型发送大尺寸图片时,请求体超出服务商上限,返回 413 request_too_large,且以 All chat models failed 形式抛出,无法从错误得知原因是图片过大。

实测请求体 42838045 字节,按 base64 膨胀 4/3 反推图片数据约 32 MB。复核 v4.28.1 代码后,图片预处理链路存在字节体积盲区,链路如下。

1. 预处理只判定像素长边,不判定文件体积

astrbot/core/utils/media_utils.py_convert_image_bytes_sync()

if (
    image.format in {"PNG", "JPEG"}
    and image.getexif().get(274, 1) == 1
    and max(image.size) <= max_size
):
    return source_bytes

任何 PNG/JPEG、EXIF 方向正常、长边不超过 max_size 的图片都会被字节级原样返回,不存在绝对体积上限。

实测(当前代码,全部默认配置 image_compress_enabled=truemax_size=1280):

1280x1280 PNG   src=6561745 bytes  ->  out=6561745 bytes  passthrough=True
4000x3000 JPEG  src=23735999 bytes ->  out=23735999 bytes passthrough=True

第一行说明:不修改任何配置,一张 1280×1280 的高熵 PNG 就有 6.26 MiB,base64 后约 8.7 MB,已超过 5 MB 请求体上限。第二行对应 CUA 沙箱模式(见第 3 点)。

2. 组装请求时只做 base64 编码,不再缩放

astrbot/core/provider/entities.pyProviderRequest.assemble_context() 注释为 "Read image references without resizing or transcoding",直接 resolve_image_ref_to_base64_data(...).to_data_url() 内联为 base64 data URI。

3. CUA 沙箱模式等效关闭缩放

astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py

if cua_pixel_mode:
    # CUA pixel tools read coordinates 1:1 on stills, so the
    # still-image resize is lifted; ...
    max_size = 1_000_000

由于判定比较的是长边像素,任何真实图片都远小于 1e6,等效于对所有静图关闭缩放。此时仅当图片超过 _CUA_IMAGE_WARN_BYTES(5 MB,恰好等于服务商图片上限)时输出一条 warning,不采取任何实际措施:

CUA session sends N image(s) larger than 5 MB (largest X MB) without resize;
this may exceed provider image upload limits.

意味着代码已经知晓该阈值,但仍会把超限图片发出去。

4. 历史消息中的图片不参与预处理,请求体随会话累积

assemble_context() 的代码注释指出内联的 data URI 会同时进入持久化历史("must also reach providers and persisted history as portable data URIs"),history_saver.py / internal.py 通过 dump_messages_with_checkpoints(all_messages) 将模型实际看到的消息写入会话历史,下一轮 astr_main_agent.py 又直接 req.contexts = json.loads(req.conversation.history) 原样送入请求。

同时 image_input.py 的模块注释声明:

"""Prepare current local-agent input; history and tool results are not inputs here."""

即历史图片不会被重新预处理。两者叠加后,会话中每新增一张直通的大图,其 base64 都会被之后每一轮请求重复携带,请求体随会话单调增长,可达数十 MB 量级(本次观测到 42.8 MB)。

5. 413 后遍历全部回退模型,错误不可读

astrbot/core/agent/runners/tool_loop_agent_runner.py 会以同一份超大请求体依次尝试所有 fallback provider。413 属请求体积问题,回退模型无法缓解,最终只抛出 All chat models failed: APIStatusError: 413

如何复现

  1. 使用内置 Agent,provider_settings.image_compress_enabledimage_compress_options.max_size 保持默认(true / 1280)。
  2. 准备满足「PNG 或 JPEG + EXIF 方向正常 + 长边不超过 1280」但体积显著大于 5 MB 的高熵图片(例如用随机内容编码的 1280×1280 RGBA PNG,约 6.26 MiB)。
  3. 通过任一带图片的消息平台适配器发送;在同一会话中重复发送若干张此类图片。
  4. 请求体随历史累积增大,最终返回 413,并抛出 All chat models failed

不依赖消息平台的最小验证(直接调用内部函数):

python -c "import io,os; from PIL import Image; \
from astrbot.core.utils.media_utils import _convert_image_bytes_sync; \
b=io.BytesIO(); Image.frombytes('RGBA',(1280,1280),os.urandom(1280*1280*4)).save(b,'PNG'); \
src=b.getvalue(); out=_convert_image_bytes_sync(src,1280,95); \
print(len(src), len(out), out is src)"

输出 6561745 6561745 True,即未做任何处理。

期望行为

  • 预处理增加绝对字节上限:无论尺寸是否「合规」,超过阈值即强制降质或缩放。现有 MODEL_IMAGE_PNG_FALLBACK_MAX_BYTES 已对带透明度的 PNG 做过 1 MB 回退,可沿用同一思路。
  • 历史消息中的图片在发送前也应有体积校验,或提供会话级清理手段,避免请求体随会话单调增长。
  • 识别 413 / request_too_large 后不再遍历回退模型,改为触发图片降级重发,或抛出可读错误。
  • CUA 沙箱下对超过体积阈值的图片提供可操作选项(例如允许打破像素 1:1 换取可发送),而非仅打印 warning。

相关 issue

#7791 报告了「图片压缩无效,大于 2048*2048 的图无法处理」,由 #7807 修复;但该修复针对的是像素尺寸绕过(compress_image() 仅在文件大于 1 MB 时触发,导致尺寸超限但体积小的图绕过缩放)。本 issue 属于字节体积维度,未被覆盖。

环境

  • AstrBot 版本:v4.28.1
  • 操作系统:Windows
  • 部署方式:Windows 源码部署(uv / venv)
  • 消息平台适配器:Onebot v11(NapCat)

错误日志

LLM 响应错误: All chat models failed: APIStatusError: Error code: 413 -
{'detail': {'error': {'message': 'Request body is 42838045 bytes, over the 5242880 byte limit',
'type': 'invalid_request_error', 'code': 'request_too_large'}}}

检查清单

  • 我已在 Issue 列表中搜索过相关问题仍无法解决,或该问题从未被报告过。
  • 我已尝试过禁用所有插件,排除了可能是因插件导致的问题。
  • 我报告的问题与 AstrBot 本体相关,而非在报告某一插件的问题。
  • 我已阅读并同意本项目的贡献者行为准则。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions