Skip to content

Start Seed-OSS tool calls at the first line-broken scope, as llama.cpp does - #1886

Closed
leehack wants to merge 4 commits into
mainfrom
docs/1542-seed-oss-divergences
Closed

leehack wants to merge 4 commits into
mainfrom
docs/1542-seed-oss-divergences

Conversation

@leehack

@leehack leehack commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Maintainer decisions on #1542: Seed-OSS tool-call start matches llama.cpp (cd959bae9) ("Match llama.cpp (Recommended)"); an unknown tool and a missing required argument stay lenient and documented ("Keep lenient, document").
  • Behavior change (Seed-OSS only): calls start at the first <seed:tool_call> followed by \n anywhere in the reply, as llama.cpp does; text before it, including a <seed:tool_call> not followed by \n, is content. The tool-call grammar (auto and ToolChoice.required) now requires "<seed:tool_call>\n", matching llama.cpp's "<seed:tool_call>\n" space, and the compact lazy trigger <seed:tool_call><function= is gone.
  • Implementation: XmlToolCallFormat.seedOss.scopeStart is now '<seed:tool_call>\n' (one Seed-only hunk in the shared xml_tool_call_format.dart; no overlap with #1885's hunks), so the parse, stream gate and grammar share it. The handler's first-tag gate is removed.
  • Docs: template-engine-internals.md, fixture notes, both changelog bullets.

Refs #1542, #1531.

Production-readiness scope

  • User-facing scope: <seed:tool_call><function=... (or a space/tab/CRLF after the tag) is content, not a call; a line-broken scope after such text or a compact scope is now found (708a852 returned no call; main returned the compact one too). A later scope without \n after valid calls stays content after them (main returned it as a call; llama.cpp fails the reply). Unknown tools and missing arguments: rejected for ToolDefinition.fromJsonSchema tools, returned as calls otherwise; documented.
  • Supported platforms/paths: All (shared Dart template parsing and GBNF).
  • Unsupported or intentionally unavailable paths: None.
  • Out of scope / follow-ups: Pre-existing divergences left as on main: content before calls is trimmed (llama.cpp keeps a trailing \n); a first line-broken scope holding no valid call keeps the reply as content where llama.cpp errors; two calls with parallelToolCalls: false are both returned where llama.cpp errors.

Completeness checklist

  • Declared scope is fully implemented.
  • Unsupported combinations: N/A.
  • Docs and changelog updated.
  • Regression coverage, failing first at 708a852: multi-scope parse, scope tag inside a thought, later compact scope, end-of-output hold in the stream, trigger list, compiled-grammar rejection of compact/space scopes (auto, parallel and required). Mutation pass: 7/7 killed (table below).
  • Validation registry: N/A, no capability change.
  • Security/privacy review: nothing sensitive.
  • Follow-ups: the pre-existing divergences above, under Reasoning parity follow-ups from #1524/#1525 audit: mutants X2/X7, Ling <think> in args, partial-tag drop #1531.

PR type guidance

  • Bugfix PR: re-audit of 708a852 found (B1) the grammar and lazy trigger still admitted <seed:tool_call><function= that the parse discarded, so ToolChoice.required could end with 0 calls, and (B2) calls were decided by the first tag instead of the first line-broken scope. Root cause for both: Seed's scope start lacked the \n llama.cpp requires.

Test Plan

  • dart run tool/prepare_workspace.dart (tree clean after)
  • dart format --output=none --set-exit-if-changed .
  • dart analyze: no issues
  • dart run tool/testing/check_platform_boundaries.dart
  • dart test -p vm -j 1 --exclude-tags local-only (macOS, isolated TMPDIR): 11964 passed; 6 failures only in windows_registry_qualification_test.dart (pwsh not installed on this host, unrelated)
  • GBNF e2e specialized_tool_grammar_validation_e2e_test.dart --run-skipped with llama.cpp cd959bae9 test-gbnf-validator: 79 passed
  • dart test -p chrome: N/A, parsing is platform-independent; CI runs it
  • coverage-lib: left to CI on main
  • ./tool/docs/build_site.sh --port 61136: no broken internal links

Matrix Evidence

Matrix row Scope covered Platform / model / backend Result Evidence / notes
structured-output-adversarial Seed-OSS parse, stream, auto/required/parallel grammar acceptance and rejection, upstream parity VM, llama.cpp cd959bae9 oracle and GBNF validator PASS Oracle below; GBNF e2e 79/79
Upstream parity Auditor's 24-case harness × thinking × parallel × whole/bytewise (192) VM 76 strict mismatches, 0 regressions llamadart-evidence/pr-1886/oracle/fix-new/
high-risk-exact-head-independent-qa Exact head — Pending Re-audit requested

Oracle (strict / categorised) vs llama.cpp cd959bae9:

Build Match Different calls llama.cpp errors Content trim only Reasoning with thinking off Other content
main 77b01da 52 88 28 8 8 8
708a852 116 16 28 8 16 8
this head 116 0 28 24 16 8

No case matching at main or 708a852 mismatches now. The 16 "different calls" at 708a852 (compact-then-valid, mention-then-valid) now return llama.cpp's calls, differing only by the existing content trim. ToolChoice.required grammar: compact and space-after-tag scopes are now invalid in the compiled grammar (both thinking modes); the line-broken scope is valid.

Mutants (oracle/fix-new/mutants.log):

Mutant Result
Seed scope start without \n Killed (7 tests incl. GBNF e2e)
Compact lazy trigger restored Killed
Trigger without \n Killed
708a852 first-tag gate restored Killed
First-tag gate on raw output (M6) Killed
Last scope instead of first (M5) Killed (52)
Later compact scope accepted Killed

Review Notes

  • Independent review status: audit at 154fed33 failed on doc accuracy; re-audit at 708a852 failed (B1, B2); this head addresses both plus the non-blocking end-of-output, M6 and later-scope items. Re-audit pending.
  • CI status / head SHA: green (CI run; 32 pass, 9 skipped) / 67d1651

High-risk regression review

  • Classification: high-risk (structuredOutput)
  • Implementation task: this PR
  • Independent blocking QA task: pending re-audit
  • Exact head / current base: 67d1651 / 77b01da
  • Production-branch deletion, bypass, or miswire proof: mutants above
  • Affected-family real-model/artifact evidence: llama.cpp cd959bae9 oracle and GBNF validator on the vendored Seed-OSS template; no model weights
  • Explicit unavailable-family or other N/A evidence: Seed-OSS weights not used; parser/grammar-level evidence only
  • Known PR-caused P1 regressions: 0
  • Unresolved review threads: 0

@leehack leehack changed the title Document Seed-OSS tool-call leniencies as intentional divergences Match llama.cpp on the Seed-OSS scope start tag and document the remaining leniencies Nov 10, 2026
…p does

Seed's scope start is now <seed:tool_call>\n, so the parse starts calls at
the first line-broken scope anywhere in the reply, the grammar (also with
ToolChoice.required) requires the line break, and the compact lazy trigger
is gone. A later scope without the line break stays content.
@leehack leehack changed the title Match llama.cpp on the Seed-OSS scope start tag and document the remaining leniencies Start Seed-OSS tool calls at the first line-broken scope, as llama.cpp does Nov 10, 2026
leehack added a commit that referenced this pull request Nov 10, 2026
@leehack

leehack commented Nov 10, 2026

Copy link
Copy Markdown
Owner Author

Landed on main via batch integration PR #1895 (rebase merge, main ba4e0e1b; tree identical to the audited integration head 5ffd1224).

@leehack leehack closed this Nov 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Chat app preview removed for leehack/llamadart-chat-pr-1886.

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