Skip to content

fix(core): avoid LM Studio grammar failure - #10275

Open
mredbishop wants to merge 2 commits into
QwenLM:mainfrom
mredbishop:fix/lmstudio-grammar-schema
Open

fix(core): avoid LM Studio grammar failure#10275
mredbishop wants to merge 2 commits into
QwenLM:mainfrom
mredbishop:fix/lmstudio-grammar-schema

Conversation

@mredbishop

Copy link
Copy Markdown

What this PR does

Removes the length constraint from review finding summaries in the tool schema.

Why it is needed

LM Studio using llama.cpp fails before generation when this nested schema constraint is present. Removing it keeps the review tool available and allows new conversations to start normally.

Reviewer Test Plan

How to verify

Use Qwen Code with an LM Studio OpenAI-compatible endpoint and start a new conversation. A simple prompt such as Hi qwen should receive a response instead of a grammar parsing error.

Evidence (Before & After)

Before: LM Studio returned Failed to initialize samplers: failed to parse grammar.

After: the same request succeeds when the summary length constraint is absent.

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment (optional)

Tested with Qwen Code 0.22.2 against LM Studio’s OpenAI-compatible API using a Qwen 27B model.

Risk & Scope

  • Main risk or tradeoff: review finding summaries are no longer capped at 2,000 characters.
  • Not validated / out of scope: other LM Studio and llama.cpp versions.
  • Breaking changes / migration notes: none.

Linked Issues

None.

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: real and observed, not theoretical — this is the same failure as #10065 (Failed to initialize samplers: failed to parse grammar — LM Studio serving a local Qwen model, plain chat requests 400 before any generation). That issue carries a full reproduction with a direct-API control, was independently reproduced by @yiliang114 against a controlled endpoint, and other users report the same on LM Studio and llama-swap. Your before/after on 0.22.2 matches it. One note: the PR says "Linked Issues: None" — it should link #10065 so both threads stay connected.

Direction: aligned — local models through the OpenAI-compatible endpoint are a supported path, and this failure blocks every conversation on grammar-based backends (LM Studio, llama-swap), so it is a P2-level blocker for those users. No comparable entry in peer-CLI changelogs, but the area is clearly relevant.

Size: core path (packages/core/src/tools/) — 1 production line removed, 15 test lines added. Far below any threshold; no maintainer awareness needed on size.

Approach: the diff itself is minimal and focused — one constraint removed, one regression test, no drive-by edits. But before the code review, one substantive question about whether this is the right constraint to remove. The #10065 analysis fingered oneOf/anyOf combinators (the agent tool's fork_turns, record_artifact metadata) rather than maxLength — and @yiliang114's controlled reproduction hit the 400 with schemas carrying maxLength 65536/10000/8000/4096 still in the request. After this PR the same schema keeps five other maxLength constraints (64, 4096, 4000, 64, 1000), and eight other tool schemas use maxLength too — so if maxLength is what the llama.cpp grammar converter rejects, this fix looks incomplete; if it is the combinators, this fix does not touch them. Your before/after says that in your LM Studio build removing this one constraint made conversations work, and that deserves to be taken seriously — but CI cannot reproduce it (no LM Studio available), and it sits in tension with the analysis on record. Coordination matters here too: @yiliang114 is already working the #10065 fix (#10080 is open for the empty-allowlist half, with the schema side presumably following).

Risk: no elevated risk signals — neither changed file matches the revert-correlated paths.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题:真实且已观测到,不是理论问题——这与 #10065 是同一个故障(Failed to initialize samplers: failed to parse grammar——LM Studio 加载本地 Qwen 模型,纯聊天请求在生成前即返回 400)。该 issue 有完整复现和直连 API 对照,@yiliang114 已在受控端点上独立复现,另有用户在 LM Studio 和 llama-swap 上报告了同样问题。你在 0.22.2 上的 before/after 与之吻合。一点提醒:PR 里写的是"Linked Issues: None"——应当关联 #10065,让两条线保持互通。

方向:对齐——OpenAI 兼容端点跑本地模型是受支持的路径,而这个故障会让基于 grammar 的后端(LM Studio、llama-swap)的每一次对话都失败,对这些用户属于 P2 级阻断。同类 CLI 的 changelog 中没有直接对应条目,但该领域显然相关。

规模:核心路径(packages/core/src/tools/)——删除 1 行生产代码,新增 15 行测试。远低于任何阈值,无需因规模提请维护者关注。

方案:diff 本身最小且聚焦——删一个约束、加一个回归测试、无顺手改动。但在进入代码审查前,有一个实质性问题:删的是不是对的约束?#10065 的分析指向的是 oneOf/anyOf 组合器(agent 工具的 fork_turnsrecord_artifact 的 metadata),而不是 maxLength——且 @yiliang114 的受控复现显示,在 maxLength 65536/10000/8000/4096 的 schema 仍在请求中时就已经触发 400。本 PR 之后,同一个 schema 仍保留另外五个 maxLength(64、4096、4000、64、1000),另外还有八个工具的 schema 也使用 maxLength——如果 llama.cpp 的 grammar 转换器拒绝的是 maxLength,那这个修复不完整;如果拒绝的是组合器,这个修复又没有触及它们。你的 before/after 表明在你的 LM Studio 构建上删掉这一个约束后对话即可正常工作,这值得认真对待——但 CI 无法复现(没有 LM Studio 环境),而且与现有记录在案的分析存在张力。协调也很重要:@yiliang114 正在推进 #10065 的修复(#10080 处理空白名单那一半,schema 一侧的修复预计随后跟上)。

风险:无升级风险信号——两个改动文件均未命中与 revert 相关的高风险路径。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at 4bdf969ff4547eb48245e2885ddde5026cf17651 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code review

My independent take before reading the diff: given the #10065 record, I would have started by capturing the request body LM Studio rejects and bisecting the tool set (the permissions.deny workaround documented there makes that cheap), then fixed whatever construct the grammar converter actually rejects — likely at the OpenAI-compatible wire path, since the suspected combinators (oneOf/anyOf/pattern) and maxLength both recur across the tool surface. The PR is more minimal than that: one constraint removed, one test added.

Judged on its own terms, the diff is clean:

  • No runtime regression possible. Nothing ever enforced the 2,000-char cap: validateToolParamValues checks emptiness and control characters, not length; summary flows through trimmed; the ReportedFinding type and the review artifact's Finding interface carry no length limit; shortSummary compression is separate. No docs or snapshots reference the cap. The constraint was purely model-facing guidance.
  • The test is valid. parametersJsonSchema is the real getter on the tool's schema, the no-arg constructor matches, and the assertion pins the removal against regressions. Collocated, convention-following.
  • No drive-by edits, no AGENTS.md violations.

The open question is not code quality but sufficiency — whether this is the construct the llama.cpp grammar converter rejects (Stage 1 covers it). Not verified: that the grammar failure is gone after this change on any environment beyond the author's (macOS only, per the Tested-on table); CI has no LM Studio to reproduce it. That sentence is the trigger, so the remedy goes with it:

Sandboxed verification could settle half of this: @qwen-code /verify as a sponsored run (the author has no write access, so a maintainer's comment approves the head — it carries a pre-execution risk screen and a full workspace wipe; read its report with the same skepticism as the fork's own logs) — it can A/B the wire request and prove the schema delta actually reaches the provider. What it cannot settle is the other half: whether llama.cpp accepts the resulting grammar — no LM Studio exists in any sandbox, so that stays with whoever has the real endpoint.

CI test evidence

The PR's own CI has not run: both pull_request workflows (Qwen Code CI, Security Checks) sit at action_required — fork PRs need a maintainer to approve the workflow run before any suite executes on this commit. Everything below is the current state, not a green signal; the table updates in place once CI settles.

Check Conclusion
Qwen Code CI ⏸️ action_required (fork workflow approval needed)
Security Checks ⏸️ action_required (fork workflow approval needed)
precheck-pr / precheck ✅ success
label ✅ success

The added unit test (does not constrain finding summaries in its tool schema) has therefore never executed anywhere yet — not in CI, and unattended triage does not run PR code.

中文说明

代码审查

读 diff 之前的独立判断:结合 #10065 的记录,我会先抓取 LM Studio 实际拒绝的请求体,并对工具集做二分(该 issue 记录的 permissions.deny 方案让这步成本很低),然后修复 grammar 转换器真正拒绝的结构——很可能要在 OpenAI 兼容的下发路径上修,因为疑似的组合器(oneOf/anyOf/pattern)和 maxLength 都在工具面中反复出现。这个 PR 比那更精简:删一个约束、加一个测试。

就其本身而言,diff 是干净的:

  • 不可能引入运行时回归。 这个 2000 字符上限从未被执行过:validateToolParamValues 只检查空值和控制字符,不检查长度;summary 仅做 trim 后透传;ReportedFinding 类型和 review 工件的 Finding 接口都没有长度限制;shortSummary 的压缩是独立逻辑。文档和快照中也没有引用该上限。这个约束纯粹是面向模型的引导。
  • 测试有效。 parametersJsonSchema 是工具 schema 上真实存在的 getter,无参构造函数匹配,断言把"移除"这一状态钉死以防回归。测试与源码同目录,符合约定。
  • 无顺手改动,无 AGENTS.md 违规。

悬而未决的不是代码质量,而是充分性——这是否正是 llama.cpp grammar 转换器拒绝的结构(见 Stage 1)。未验证:除作者自身环境(按 Tested-on 表仅 macOS)之外,该 grammar 故障在本改动后是否消失;CI 没有 LM Studio 可以复现。这句话本身就是触发条件,所以补救手段一并给出:

沙箱验证可以解决其中一半:以 sponsored run 方式使用 @qwen-code /verify(作者无写权限,需维护者评论批准该 head——运行前有预执行风险筛查和完整工作区清理;请像对待 fork 自身 CI 日志一样审慎阅读其报告)——它可以对请求体做 A/B 对比,证明 schema 变化确实到达了提供方。它无法解决另一半:llama.cpp 是否接受结果 grammar——任何沙箱里都没有 LM Studio,那只能留给拥有真实端点的人。

CI 测试证据

该 PR 自身的 CI 尚未运行:两个 pull_request 工作流(Qwen Code CISecurity Checks)均处于 action_required 状态——fork PR 需要维护者先批准工作流运行,套件才能在该提交上执行。下表是当前状态,不是绿色信号;CI 落定后表格会原地更新。

(表格见上方英文部分)

新增的单元测试(does not constrain finding summaries in its tool schema)因此尚未在任何地方执行过——CI 没有跑,无人值守的 triage 也不运行 PR 代码。

Qwen Code · qwen3.8-max

Reviewed at 4bdf969ff4547eb48245e2885ddde5026cf17651 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 3/5 — clean, runtime-safe one-line fix for a real observed failure, but whether it removes the construct the grammar converter actually rejects is unresolved, it overlaps maintainer work already in flight on #10065, and no CI has ever run on this commit — needs a human call.

Stepping back: the problem is real and multiply corroborated — #10065 carries a full reproduction with a direct-API control, @yiliang114 reproduced it independently against a controlled endpoint, and users hit it on both LM Studio and llama-swap. Judged as code, this diff is the smallest it could be: one advisory constraint removed, one regression test, and I verified nothing at runtime ever depended on the cap (validateToolParamValues never checked length; the ReportedFinding type and the review artifact carry no limit; no docs or snapshots reference it). If I had to maintain this in six months, the change itself is blameless.

What keeps this from an approval is not the code but the fit:

  1. It may be treating the wrong construct. The LM Studio 0.4.21: Qwen Code request fails with "failed to parse grammar" even with no MCP servers and tools.core=[] #10065 analysis fingered oneOf/anyOf combinators (the agent tool's fork_turns, record_artifact metadata) — both still present on main and still present in v0.22.2, which the author tested. After this PR, the same schema keeps five other maxLength constraints and eight other tool schemas keep theirs (up to 65536). Either story — maxLength is the poison, or the combinators are — leaves this fix unexplained or incomplete. The author's before/after is the one piece of evidence pointing at this specific constraint, and it deserves respect, but it was produced in a single environment, is not reproducible in CI, and contradicts the analysis on record.
  2. The owner of this problem is already working it. @yiliang114 said on LM Studio 0.4.21: Qwen Code request fails with "failed to parse grammar" even with no MCP servers and tools.core=[] #10065 that the fix PR would link back there; fix(core): honor empty core tool allowlists #10080 (the empty-allowlist half) is open and the schema side presumably follows. Merge as-is, merge as a stopgap, or fold into the planned schema fix is a call for whoever holds the LM Studio 0.4.21: Qwen Code request fails with "failed to parse grammar" even with no MCP servers and tools.core=[] #10065 plan — and the PR currently says "Linked Issues: None", so the two threads are not even connected.
  3. No CI evidence exists on this commit. Both fork workflows sit at action_required, so even the added unit test has never executed. (For the record: the approval guardrail is clear — this is a fix, not a fork refactor — and the pending-run count technically reads zero because the two pull_request runs are completed as action_required; approving on that reading would attest to a suite that never ran, so no approval and no approve-on-green marker.)

⏸️ Deferring to @yiliang114 — you reproduced #10065 and own the fix path, so this is yours to call. Suggested path: (1) connect this PR to #10065 and decide whether the one-line removal is a useful stopgap or should wait for the schema-side fix; (2) approve the fork's workflow runs so CI (including the new test) lands on 4bdf969f; (3) if you want the behavioural claim settled, a before/after against a real LM Studio — or a captured request body showing the grammar delta — is the only evidence that would close it. Re-trigger @qwen-code /triage after any of that and I'll take it from there.

中文说明

置信度:3/5 —— 对一个真实观测到的故障而言,这是干净且运行时安全的一行修复;但它移除的是否正是 grammar 转换器实际拒绝的结构仍未解决,与 #10065 上正在进行的维护者工作重叠,且该提交从未跑过 CI——需要人来拍板。

退一步看:问题是真实的且有多方佐证——#10065 有完整复现和直连 API 对照,@yiliang114 在受控端点上独立复现,用户在 LM Studio 和 llama-swap 上都遇到了。就代码本身而言,这个 diff 已经是最小形态:删一个纯引导性约束、加一个回归测试;我也核实了运行时从未依赖这个上限(validateToolParamValues 从不检查长度,ReportedFinding 类型和 review 工件都没有长度限制,文档和快照也没有引用)。如果六个月后由我维护,这个改动本身无可指摘。

阻止批准的不是代码,而是契合度:

  1. 它可能修错了结构。 LM Studio 0.4.21: Qwen Code request fails with "failed to parse grammar" even with no MCP servers and tools.core=[] #10065 的分析指向 oneOf/anyOf 组合器(agent 工具的 fork_turnsrecord_artifact 的 metadata)——这两者在当前 main 和作者测试所用的 v0.22.2 上都存在。本 PR 之后,同一 schema 仍保留另外五个 maxLength,另有八个工具的 schema 也保留着各自的 maxLength(最高 65536)。无论哪个故事成立——maxLength 是毒源,或组合器是毒源——这个修复要么无法解释、要么不完整。作者的 before/after 是唯一指向这个特定约束的证据,值得认真对待,但它来自单一环境、CI 无法复现,且与记录在案的分析相矛盾。
  2. 该问题的负责人正在处理。 @yiliang114LM Studio 0.4.21: Qwen Code request fails with "failed to parse grammar" even with no MCP servers and tools.core=[] #10065 中表示修复 PR 会关联回该 issue;fix(core): honor empty core tool allowlists #10080(空白名单那一半)已开启,schema 一侧的修复预计随后跟上。直接合并、作为临时缓解合并、还是并入计划中的 schema 修复,应由掌握 LM Studio 0.4.21: Qwen Code request fails with "failed to parse grammar" even with no MCP servers and tools.core=[] #10065 全局的人决定——而本 PR 目前写着"Linked Issues: None",两条线甚至尚未连通。
  3. 该提交没有任何 CI 证据。 两个 fork 工作流都处于 action_required,连新增的单元测试都从未执行过。(备忘:批准护栏本身是清晰的——这是 fix 而非 fork refactor;pending 计数技术上为零,因为两个 pull_request 运行以 action_required 状态"完成";基于这种读数去批准等于为一个从未运行的套件背书,因此不批准、也不发出 approve-on-green 标记。)

⏸️ 转交 @yiliang114 —— #10065 由您复现、修复路径由您掌握,此事应由您定夺。建议路径:(1)把本 PR 与 #10065 连通,判断这一行删除是有用的临时缓解,还是应等待 schema 一侧的修复;(2)批准 fork 的工作流运行,让 CI(含新测试)落在 4bdf969f 上;(3)如需落实行为声明,在真实 LM Studio 上做 before/after——或提供一份展示 grammar 差异的抓取请求体——是唯一能闭环的证据。之后重新触发 @qwen-code /triage,我会继续跟进。

Qwen Code · qwen3.8-max

Reviewed at 4bdf969ff4547eb48245e2885ddde5026cf17651 · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially reviewed — gaps disclosed. Suggestions are inline. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here.

Not explored to full depth (tool budget reached): "agent 6b": end-to-end verification of whether LM Studio/llama.cpp accepts the post-change schema with the remaining nested maxLength constraints (requires a live LM Studio….

Not reviewed: reverse audit — stopped before round 1 by the review time budget.

Not reviewed: verification — a verifier ran and opened its brief, but no agent was launched with the prompt the CLI built — the launch was written by hand, and the posted findings cannot be counted as verified against it.

— qwen3.8-max via Qwen Code /review (v0.22.2)

Comment on lines 163 to 165
summary: {
type: 'string',
maxLength: 2000,
description: 'One sentence stating the defect.',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] This removes the 2,000-character cap from the schema that BaseDeclarativeTool.validateToolParams validates against (Ajv enforces maxLength), so client-side enforcement of summary length is dropped for every provider, not just the LM Studio endpoint this targets. Verified by probe against the real tool and validator:

summary len=2500: PR schema -> ACCEPTED | base schema -> REJECTED

The codebase already has a layer built for exactly this class of OpenAI-compatible gateway breakage: relaxSchemaForFunctionCalling (packages/core/src/utils/schemaConverter.ts), applied to tool schemas in convertGeminiToolsToOpenAI, whose design note (PR 7315) says the constraint is relaxed on the wire only while client-side validateToolParams still enforces the full source schema. Also note the five sibling constraints in this same item schema (id 64, file 4096, failureScenario 4000, category 64, outcomeNote 1000, plus maxItems: 50) remain, so the change neither preserves the cap where it works nor closes the nested-constraint class that caused this failure.

Consider keeping maxLength: 2000 here and stripping it only at the wire layer for OpenAI-compatible endpoints (extending relaxSchemaForFunctionCalling per the PR 7315 pattern), or moving the cap into validateToolParamValues so client-side enforcement survives — and updating the new test to pin the wire shape accordingly. If the wire-relaxation route is taken, please add the converter/schemaConverter test asserting the relaxed report_findings wire schema carries no maxLength on summary while the source schema keeps it, and confirm it goes red when the relaxation step is removed.

— qwen3.8-max via Qwen Code /review (v0.22.2)

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partially reviewed — gaps disclosed. Suggestions are inline. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here.

Not reviewed: reverse audit — stopped before round 1 by the review time budget.

Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:

  • packages/core/src/tools/report-findings.ts:163 — [probe] compaction invariant comment falsified — the retention bound now rides entirely on MAX_RETAINED_AGENT_FIELD_CHARS (measured eviction boundary 22,642/22,643)

— qwen3.8-max via Qwen Code /review (v0.22.2)

summary: {
type: 'string',
maxLength: 2000,
description: 'One sentence stating the defect.',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R1-1: Still standing — this code is unchanged since the previous round. Removing maxLength: 2000 from the source schema drops client-side (Ajv) enforcement of summary length for every provider, not just the LM Studio endpoint this targets: BaseDeclarativeTool.validateToolParams validates exactly this schema, and the wire-only relaxation layer relaxSchemaForFunctionCalling (packages/core/src/utils/schemaConverter.ts) — the PR 7315 pattern of relaxing constraints on the wire while keeping client-side enforcement — was not used.

The five sibling constraints in this same item schema (id 64, file 4096, failureScenario 4000, category 64, outcomeNote 1000) plus maxItems: 50 remain, so the change neither preserves the cap where it works nor closes the nested-constraint class that caused the LM Studio grammar failure. Round 1 verified by probe against the real tool and validator: summary len=2500: PR schema -> ACCEPTED | base schema -> REJECTED.

Consider keeping maxLength: 2000 here and stripping it only at the wire layer for OpenAI-compatible endpoints (extending relaxSchemaForFunctionCalling per the PR 7315 pattern), or moving the cap into validateToolParamValues so client-side enforcement survives — and updating the new test to pin the wire shape accordingly. If the wire-relaxation route is taken, add the converter/schemaConverter test asserting the relaxed report_findings wire schema carries no maxLength on summary while the source schema keeps it, and confirm it goes red when the relaxation step is removed.

— qwen3.8-max via Qwen Code /review (v0.22.2)

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.

2 participants