Skip to content

fix(compress): accept JSON-string content in parseCompressInput - #176

Merged
ranxianglei merged 1 commit into
masterfrom
2026-08-19_compress-json-string-content
Aug 21, 2026
Merged

fix(compress): accept JSON-string content in parseCompressInput#176
ranxianglei merged 1 commit into
masterfrom
2026-08-19_compress-json-string-content

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

背景

非严格工具 provider(vLLM openai-completions,supportsStrictTools:false)会把嵌套数组参数字符串化:模型调用 compress 时传的 contentJSON 字符串而非数组。

此前 parseCompressInputArray.isArray(obj.content) 为 false → 走 toRange(obj)(顶层无 startId/endId/summary)→ null → 静默解析出 0 个 range,压缩从不执行(只有一条 warn 日志,无任何报错)。

改动

src/compress-tool.tsparseCompressInput 在数组判断前,若 content 是字符串则先 JSON.parse(非法 JSON → warn + 0 ranges,不崩溃)。与 billion-context-pi PR #164(normalizeRanges)对称。

tests/basic.test.ts 增加 JSON 字符串形式的回归测试。

验证

  • npm run typecheck(tsc --noEmit --project tsconfig.build.json):clean
  • npm test:464 pass / 0 fail

不合并,等 review。

Non-strict-tool providers (e.g. vLLM openai-completions,
supportsStrictTools:false) stringify nested array arguments, so the model
may send compress.content as a JSON string instead of an array. Previously
Array.isArray(obj.content) was false and toRange(obj) returned null, so the
call silently parsed 0 ranges and compression never ran.

parseCompressInput now JSON.parse's a string content before the array check
(invalid JSON -> warn + 0 ranges, no crash), mirroring the fix in
billion-context-pi PR #164 (normalizeRanges in src/compress-tool.ts).

Adds a regression test for the JSON-string form.
@ranxianglei

Copy link
Copy Markdown
Owner Author

LGTM — verified against all five call sites (stream.ts, stream-openai.ts, stream-responses.ts, compress-loop-responses.ts, loop/core.ts) and the new regression tests pass (25/25 in tests/basic.test.ts). Mirrors the sibling fix in billion-context-pi.

One non-blocking note: if content is a JSON string of a single object (e.g. '{"startId":"m1","endId":"m2","summary":"x"}'), JSON.parse succeeds but the result isn't an array, so the fallback reads startId/endId/summary from the top-level obj (line 70) — which only carries content — and you get 0 ranges. Same result as before the patch, so no regression; just flagging that the lenient path only covers array strings. If you want the single-object hallucination to work too, one line: after the parse, if (content && typeof content === "object" && !Array.isArray(content)) content = [content];. The schema declares content as an array, so I'd merge as-is.

@ranxianglei
ranxianglei merged commit 653989b into master Aug 21, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant