Repository navigation
Conversation
… no thought, as llama.cpp does (#1865) After a prompt that opens no thought, llama.cpp (cd959bae9) takes a thought only where the reply starts with the start tag; every other thinking tag is content. llamadart's handlers extracted thoughts anywhere (extractThinking), so a bare `</think>`, a `<think>` after text, or a second `</think>` after a leading thought became reasoning, and the stream, which split tags as they arrived, could disagree with the final parse and repeat text. - `ChatFormatTraits.takesThoughtOnlyAtReplyStart` marks the formats verified against the oracle; their rendered parse runs inside `withThoughtOnlyAtReplyStart`, which switches `extractThinking` to the leading-thought rule for every handler call site. - The stream ends such a leading thought at its first end tag and streams the rest as content, as it already did for Seed-OSS and PEG replies. - `closed_thought_upstream.json` gains oracle cases for one vendored template per format (five templates vendored at b10549, identical at cd959bae9). Refs #1874
Contributor
|
Chat app preview for Space: https://huggingface.co/spaces/leehack/llamadart-chat-pr-1882 |
This was referenced Nov 10, 2026
leehack
added a commit
that referenced
this pull request
Nov 10, 2026
leehack
added a commit
that referenced
this pull request
Nov 10, 2026
Owner
Author
|
Landed on main via batch integration PR #1895 (rebase merge, main |
2 of 3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
After a prompt that opens no thought, llama.cpp
cd959bae9(native v0.9.0) takes a thought only where the reply starts with the start tag and keeps every other thinking tag as content. llamadart's handlers took thoughts anywhere (extractThinking), so a bare</think>, a<think>after text, or a second</think>after a leading thought became reasoning, and the stream could end differently from the final parse and repeat text in both channels.takesThoughtOnlyAtReplyStart, set only on formats verified against the oracle: generic, content-only, Llama 3, GLM 4.5, Laguna, LFM2, Kimi K2, DeepSeek R1 / V3.2 / V4, Granite, MiniCPM5, Mistral Nemo, Apertus, Hunyuan V3, Hermes and Qwen3-Coder XML.parseRendered(and the engine) runs the handler insidewithThoughtOnlyAtReplyStart, a zone flag that switchesextractThinkingto the leading-thought rule. The flag reaches everyextractThinkingcall site (handlers,parseXmlToolCalls, the incomplete-call check) without changing the publicChatTemplateHandlersignatures.ChatTemplateEngine.parse(no render, so it can't tell what the prompt opened), forced-open and thinking-off-closed prompts, DeepSeek V3, and formats without the trait.Fixes #1865 (items 1 and 2). Refs #1874.
Production-readiness scope
<think>thought as content, and llamadart still takes it as reasoning (as before; recorded inclosed_thought_upstream.md).Completeness checklist
parseRenderedDartdoc, both guides, both changelogs,llamadart-chat-streamingskill.)PR type guidance
extractThinking, which takes a thought at any<think>and treats an end tag before any start tag as ending a prompt-opened thought; only Hermes, Qwen3-Coder XML and DeepSeek V3 got the Streaming a bare </think> after a prompt with no open thought duplicates text and depends on chunking #1858 bare-end-tag rule, and none got the mid-reply rule. The stream split tags as they arrived, while the final parse recursed over the whole reply, so the final reconciliation re-sent text. Regression tests: oracle fixture cases (below), failing on base for every new entry (370 failures) and passing on head.Test Plan
dart run tool/prepare_workspace.dartdart format --output=none --set-exit-if-changed .dart analyzedart test -p vm -j 1 --exclude-tags local-only(local -j 1 run: onlywindows_registry_qualification_test(6 cases) fails, identically on basef78cafa97on this macOS host)dart test -p chrome --exclude-tags local-only(4660 passed)coverage-libmatrix row: see Matrix Evidence./tool/testing/run_template_parity_suites.sh(all five stages pass);dart run tool/testing/check_platform_boundaries.dart;dart test -p vm test/unit/tooling/package_skills_test.dart; oracle sweeps and mutation run below.Matrix Evidence
cd959bae9run_template_parity_suites.sh; sweeps below; fixture cases recorded from the oracleUpstream comparison (llama.cpp
cd959bae9oracle,common_chat_peg_parse+common_chat_session)Corpora over all 76 upstream
models/templates(thinking on/off x kwargs x 5 tool modes) and the vendoredtest/fixtures/templates: the #1858 bare end-tag corpus extended with #1865 shapes (finished + partial), the #1525 thinking corpus, and a format-native tool-call corpus. Each finished row compares the oracle session withparseRenderedand with the stream at whole, per-char, 2-char, 3-char and every 2-split chunking.Note: the 12 rows are partial Command R+ replies (
</think>), where llama.cpp returns empty content for every partial reply of that template and rejects every finished one; the base matched by chance.Every finished row of a trait format after a prompt that opens no thought now streams as it parses. Remaining parse differences in those formats are all outside #1865: a leading thought where llama.cpp parses no reasoning for the template, Apriel 1.6 and IFM K2 Horizon (dedicated reasoning-prefix parsers upstream), Hermes' untagged JSON call, Apriel 1.5 and Qwen3-Coder tool-call shapes (#1531). Remaining stream differences are forced-open or thinking-off-closed prompts and non-trait formats, unchanged from base.
Per format (prompts that open no thought, #1865 + #1525 upstream corpora, finished rows):
Durable tests
test/fixtures/closed_thought_upstream.jsonno_thought_templates: one vendored template per trait format (Gemma 2, SmolLM3, Llama 3.2, GLM-4.6, Laguna XS.2, LFM2-8B-A1B, Kimi K2, R1 Distill Qwen 32B, V3.2, V4, Granite 3.3, MiniCPM5, Mistral Nemo, Apertus, Hy3) plus new cases for the Streaming a bare </think> after a prompt with no open thought duplicates text and depends on chunking #1858 entries, recorded from the oracle (finished, without tools, and partial).closed_thought_upstream_testchecks the parse and every chunking against them, and now comparesupstream_without_toolswith a render without tools, which covers content-only.b10549(identical atcd959bae9): DeepSeek V3.2, V4, Granite 3.3, Laguna XS.2, Mistral Nemo. Their caps are pinned from Dart detection, and the forced-open grammar test now lists DeepSeek V3.2/V4 and Laguna with the required-call grammars that take any text before the calls.thinking_utils_test: the leading-thought rule (ASCII-only leading whitespace, unended thought, escapes, custom tags, forced-open untouched).closed_thought_upstream_test: only trait formats switch rule, and only for rendered parses.Mutation check
19 of 20 mutants killed; per-format trait removal killed for all 17 formats. Survivor P3 (apply the rule after a forced-open prompt) is equivalent: every trait handler passes
thinkingForcedOpentoextractThinking, which ignores the rule for a forced-open thought (U5 kills that).promptOpensNoThought(public parse, thinking-off-closed)Evidence: oracle harness, sweep rows (base and head) and summaries in the maintainer evidence folder
llamadart-evidence/issue-1865/.Review Notes
High-risk regression review
cd959bae9oracle over all 76 upstream templates and the vendored templates; no model weights needed for a parser-only change.Independent audit
Exact head
a6ea0316b6913b247fe4a744d0ebbfa152088a2dagainst basef78cafa97: PASS, 0 blocking, 0 PR-caused P1, 0 threads. All 1026 fixture values re-derived from a llama.cppcd959bae9oracle; 131-template corpus shows no match→mismatch, publicparseidentical, forced-open/thinking-off renders unchanged, 678 stream rows fixed; zone approach judged sound; exclusions and changed expectations justified; Qwen3-0.6B GGUF and.litertlmoutputs identical base vs head. 31/35 mutants killed (survivors equivalent or a P3 gap). Coverage 93.66%. Readiness evidence v2: local run exits 2. Non-blocking items tracked on #1861.