llama.cpp b11871 改为在处理前拒绝嵌套过深的 JSON 请求。运营服务器的开发者可以减少因单个请求出错而导致整个服务器退出的情况,深度超过阈值时会收到 HTTP 400 响应。

1. 递归处理导致服务器停止的原因

问题出在读取 JSON 后,沿内部结构反复调用同一函数的递归处理中。沿着层层嵌套的结构深入时,如果耗尽了处理 HTTP 请求所用的内存空间,即栈,整个服务器就可能退出。

根据发行说明,定义输入格式的工具模式请求,在 macOS 上从几百层起、在 Linux 上从约 1万 层起就会导致服务器退出。

更改提案(PR)中的以下代码是复现步骤,会向服务器发送深度设为 1000 的工具定义。运行此示例可能导致旧版服务器退出;说明指出,macOS 在该深度下会退出,Linux 则需将深度提高到 30000。

llama-server -hf LiquidAI/LFM2.5-VL-450M-GGUF -hff LFM2.5-VL-450M-F16.gguf --port 8080 &
python3 -c "d=1000; s='{\"type\": \"array\", \"items\": '*d+'{\"type\": \"string\"}'+'}'*d; print('{\"messages\": [{\"role\": \"user\", \"content\": \"hi\"}], \"max_tokens\": 1, \"tools\": [{\"type\": \"function\", \"function\": {\"name\": \"f\", \"description\": \"d\", \"parameters\": {\"type\": \"object\", \"properties\": {\"x\": '+s+'}}}}]}')" > deep.json
curl -s localhost:8080/v1/chat/completions -H 'Content-Type: application/json' --data-binary @deep.json

2. 超过 128 层就在处理前拒绝

这次更改会先检查解析后的 JSON 深度,以防后续递归处理过度深入;解析即把字符串读取为结构的过程。根据 PR 的实现说明,检查函数自身的递归调用也被限制为最多 129 次。

发行说明指出,超过 128 层的输入会收到 HTTP 400。调用方无需再确认服务器是否已退出,而是会收到请求因深度过大而被拒绝的响应。

检查范围包括聊天请求中的工具定义和工具调用参数、传给工具的执行值、模板变量、向模板填入值的项目以及模式。补全请求的模式和状态管理请求的整个正文也会接受检查。嵌套过深的 gemma4 工具结果不会被解释为结构化结果,而是保留为文本。

这样既能维持正常范围内的请求流程,又能阻断深层输入耗尽服务器栈的路径。另一方面,超过 128 层的 JSON 将不再被处理,依赖此类深度的调用方需要简化请求结构。

3. 原有代码与应用更改后的响应差异

以下是 PR 作者在 macOS M3 Ultra 上比较原有代码与应用更改后版本的结果。200 表示正常处理,400 表示因深度超限被拒绝;在测量的请求类型中,应用更改后的版本从 130 层起返回 400。

表:各嵌套层数的 HTTP 响应

条件:macOS M3 Ultra,比较 PR 更改前后的版本。状态管理使用 d1-omni,工具调用参数为字符串输入。

单位:HTTP 状态码。— 表示服务器退出,未收到响应。

原有代码与应用更改后的响应差异
嵌套层数更改前的状态管理更改前的工具参数更改前的响应格式更改前的工具调用参数应用更改后的全部类型
100200200200200200
120200200200200200
130200200200200400
700500——200400
1000500——200400
2000————400
10000————400
30000————400
1000000————400

更改前,100 层和 120 层的响应也与应用更改后的版本相同。状态管理请求在 700 层和 1000 层时记录的 500 并非服务器退出,而是批大小错误,即一次处理的数据批次大小错误。

PR 还补充,在 Linux 上,原有模式请求从 1万 层起、状态和参数请求从 3万 层起会导致退出,而应用更改后的版本从 130 层起返回 400。

4. 检查开销与尚未覆盖的范围

PR 作者在 M3 Max 上测量了检查开销。0.33MB 聊天请求耗时 0.02ms,短于 JSON 解析的 1.4ms;10MB 请求耗时 0.54ms(解析为 35ms)。含 100万 个整数的数组耗时 3.8ms(解析为 41ms)。这些数值来自 PR 在该环境下的测量;输入越大,检查耗时也越长,但报告中的三个案例均短于解析耗时。

PR 指出,macOS 上的部分模板即使不到 128 层也可能导致栈溢出。对于以助手消息结尾的请求,在模型响应前预先放入的预填充输入,以及由嵌套以外原因引发的 JSON 模式转换递归问题,也不在本次更改范围内。

b11871 的核心在于,将深层输入引发的失败从整个服务器退出转变为单个请求被拒绝,从而减轻运维负担。