Skip to content

fix(clarify): drop maxLength from the choices schema so llama.cpp can parse the grammar - #131286

Merged
unsupportedpastels merged 1 commit into
NousResearch:mainfrom
charan-rathore:fix/clarify-maxlength-grammar
Oct 4, 2026
Merged

unsupportedpastels merged 1 commit into
NousResearch:mainfrom
charan-rathore:fix/clarify-maxlength-grammar

Conversation

@charan-rathore

@charan-rathore charan-rathore commented Oct 2, 2026 •

Copy link
Copy Markdown

Every request to a llama.cpp server fails with HTTP 400 "failed to parse grammar" because the clarify tool schema carries maxLength: 8000, and llama.cpp rejects bounded repetition of 2000 or more.

Closes #131278.

What happened

The clarify tool's choices items schema set maxLength to 8000 (commit a4c31d5). llama.cpp's json-schema-to-grammar converter expands maxLength/minLength/maxItems/minItems into bounded repetitions and rejects anything at 2000 or more. The tool list is sent on every request, so every request to a llama.cpp server fails with "Failed to initialize samplers: failed to parse grammar", whatever the user asked. The reporter's bisect (see Credit) pinned the cutoff at 1999 working and 2000 failing.

Change

This PR touches two files.

  • tools/clarify_tool.py: the choices items schema no longer sets maxLength. The runtime over-limit check on choices is untouched, so the limit is still enforced.
  • tests/tools/test_schema_sanitizer.py: two new tests, test_clarify_schema_has_no_unbounded_repetition_keyword (the clarify schema carries no length bound of 2000 or more) and test_registered_tool_schemas_stay_inside_llama_cpp_repetition_limit (no registered built-in tool ships a length or count bound of 2000 or more).

tools/schema_sanitizer.py is not changed. An earlier version of this PR also extended the grammar-failure recovery strip there; that was removed to keep the PR narrow.

Credit

Verification

Ran on Python 3.12 at 13c84d3: tests/tools/test_schema_sanitizer.py and tests/tools/test_clarify_tool.py, 68 passed.

  • Not run: the full suite, ruff, and a real llama.cpp server.
  • Diff is two files: the schema line and the two new tests.

Maintainer notes

@whyyagswhy

Copy link
Copy Markdown

Verified at f9032fe69a: test_schema_sanitizer.py passes 40/40.

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/tools Tool registry, model_tools, toolsets area/local-models Local model inference/runtimes: llama.cpp, Ollama, LM Studio, MLX/vLLM, GGUF, VRAM and offload labels Oct 2, 2026
@charan-rathore
charan-rathore force-pushed the fix/clarify-maxlength-grammar branch from f9032fe to 33bd60a Compare October 3, 2026 13:01
@charan-rathore

Copy link
Copy Markdown
Author

I refreshed this branch on the latest main, and the CI workflows are waiting for a maintainer to approve the run since I am a first-time contributor. Could someone approve the workflow run so CI can validate the change? Thanks.

@ehz0ah ehz0ah left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The direct clarify schema change addresses #131278, and the focused suites pass. I found one cross-provider regression in the broader recovery change: expanding the shared strip helper removes supported length and item bounds from every xAI Responses request. I also confirmed that the reported llama.cpp error text does not yet enter this recovery path on current main; PR #130076 covers that classifier change separately. Details are inline.

Comment thread tools/schema_sanitizer.py Outdated
_STRIP_ON_RECOVERY_KEYS = frozenset({"pattern", "format"})
# ``pattern``/``format`` use a regex subset llama.cpp lacks; ``maxLength``/``minLength``/``maxItems``/
# ``minItems`` expand into bounded repetitions that its converter rejects at >= 2000 (#131278).
_STRIP_ON_RECOVERY_KEYS = frozenset({"pattern", "format", "maxLength", "minLength", "maxItems", "minItems"})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Bug] (blocking) This set also controls proactive xAI sanitization, not only llama.cpp recovery. Both _build_codex_kwargs() and _CodexCompletionsAdapter._build_responses_kwargs() call strip_pattern_and_format() on every xAI Responses request before any rejection. Expanding the shared set therefore removes valid minItems and maxItems constraints, including schemas that require exactly two coordinates, even though those call sites only document xAI rejecting pattern and format. A request that previously told Grok to emit exactly two coordinates is now sent without either bound, so malformed arrays can reach the handler. Please keep bounded-repetition stripping confined to the llama.cpp recovery path, or otherwise preserve the bounds that xAI supports.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks, you're right: the shared strip set is also used proactively on xAI Responses, so widening it would drop valid bounds there. I narrowed this PR to the clarify schema change that fixes #131278 and removed the recovery-set change (pushed as 13c84d3). The classifier gap you noted is covered separately by #130076.

Comment thread tests/tools/test_schema_sanitizer.py Outdated
"b": {"type": "array", "items": {"type": "string", "maxLength": 2000}, "maxItems": 5000, "minItems": 1},
},
}}}]
result, stripped = strip_pattern_and_format(tools)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Suggestion] (non-blocking) This direct helper test bypasses the classifier that gates production recovery. The reported 400 says Failed to initialize samplers: failed to parse grammar, but current main classifies that exact text as non-retryable format_error, so turn_recovery never calls this new stripping behavior. PR #130076 would add that match, but it is still open. An end-to-end regression from the reported error through classification and recovery would make this dependency explicit and prevent the helper-only test from going green while the real recovery path remains inactive.

@ehz0ah ehz0ah left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approval conclusion (formal approval unavailable)

English verdict: APPROVE at exact head 13c84d3. The earlier cross-provider blocker is fixed, and I found no remaining blocking issue.

Motivation

Issue #131278 is a real regression. The base clarify schema sends choices.items.maxLength as 8000, while llama.cpp rejects bounded repetitions at 2000 or above before it generates any text. The issue isolates the 1999/2000 boundary, and a second Ollama user independently confirmed that removing this one keyword restores normal requests.

Approach

This revision fixes the constraint at its source. CLARIFY_SCHEMA no longer advertises maxLength for each choice, while _normalize_questions still rejects choice text longer than MAX_CHOICE_CHARS before any UI callback runs. That keeps the existing 8,000-character application limit without sending a schema keyword that breaks the provider.

The previous review at 33bd60a2 found that the broader recovery change also stripped valid bounds from xAI requests. That code is no longer in this diff. tools/schema_sanitizer.py is untouched, so the cross-provider regression is resolved.

Changes

The production change is one focused schema edit in tools/clarify_tool.py. The tests add two invariants: the clarify schema must stay below llama.cpp's repetition limit, and no registered built-in tool may introduce another bound at or above that limit. The second check matters because Hermes sends the selected registered tool schemas with model requests; one incompatible schema can reject an otherwise unrelated turn.

The positive path now sends the clarify tool without maxLength, so compatible local servers can build the grammar and process the request. The negative path remains owned by Hermes: if the model produces a choice longer than 8,000 characters, _normalize_questions returns the existing tool error instead of passing it to a UI surface.

Risk to main

I found no blocking issue. The exact head passed 68 repository tests across test_schema_sanitizer.py and test_clarify_tool.py. A separate review probe passed on this head and failed on the parent with maxLength=8000, which confirms that the test distinguishes the regression. The patch also merges cleanly into current upstream/main at 158fd63, and that composed tree passed the same 68 tests. git diff --check passed.

GitHub currently reports no hosted checks or commit statuses. I also did not run a real llama.cpp server locally, so the strongest end-to-end evidence remains the two independent reports on #131278.

One non-blocking housekeeping note: the PR description still lists the removed tools/schema_sanitizer.py change and the old validation totals. Please refresh it before merge so it matches this narrowed two-file diff.

Overall assessment

This is now a small, proportional fix at the correct boundary. It restores llama.cpp compatibility, preserves runtime validation, and avoids changing shared provider sanitization.

@unsupportedpastels unsupportedpastels 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.

Thanks @charan-rathore — clean, well-scoped fix, and the regression test that scans every registered tool schema is a nice guard against this coming back.

Verified locally against a llama.cpp server built before ggml-org/llama.cpp#26613:

clarify → choices.items Result
no maxLength (this PR) ✅ 200
maxLength: 8000 (main) ❌ 400 failed to parse grammar
maxLength: 2000 ❌ 400
maxLength: 1999 ✅ 200

The 8000-char cap is still enforced at runtime in clarify_tool.py, so behavior is unchanged for every other provider. CI is green. Merging — thanks again!

Infographic

llama-cpp-grammar-fix

@unsupportedpastels
unsupportedpastels merged commit cf6dca8 into NousResearch:main Oct 4, 2026
47 checks passed
@charan-rathore
charan-rathore deleted the fix/clarify-maxlength-grammar branch October 4, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/local-models Local model inference/runtimes: llama.cpp, Ollama, LM Studio, MLX/vLLM, GGUF, VRAM and offload comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/tools Tool registry, model_tools, toolsets P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: clarify tool schema maxLength: 8000 breaks every request on llama.cpp servers ("Failed to initialize samplers: failed to parse grammar")

5 participants