Repository navigation
fix(server): resolve a namespaced tool call to the offered tool (replaces #1248) - #1254
Merged
Merged
Conversation
This package's tool instructions carry a namespaced example — `example_tool_name.example_function_name` — so a model offered a bare name sometimes answers with a qualified one. OpenCode rejects the call outright: "Model tried to call unavailable tool 'glob.glob'. Available tools: bash, edit, glob, grep, read, write." The namespace is the model's spelling, not the caller's, and a client can only dispatch a name it offered. When the qualified name is not on offer but its final segment is, the call names that tool and is now resolved to it. Anything else is left exactly as the model spelled it, so a caller keeps reporting the unavailable tool it already knows how to report rather than being handed a target it never offered. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 0190e2eb-abe4-451f-b36d-44a035a99b7e
…ix ambiguity Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Aug 18, 2026
Making reasoning visible spans #1197, not just #1224 — the buffered path filters independently
#1256
Closed
justinchuby
added a commit
that referenced
this pull request
Aug 18, 2026
CI's format lane had fallen behind — `cargo fmt --all -- --check` was failing on `main` at `7d669c76`. The drift came from several changes landing in the same window: the session-state unification (#1255), the server stack (#1197/#1249/#1250/#1254), and the native CUDA vision kernels (#1251). **No logic changes.** This is `cargo fmt --all` and nothing else — 7 source files, plus two `.squad/decisions/inbox` notes that were already sitting uncommitted in the tree. Verified locally: `cargo fmt --all -- --check` exits clean on this branch. Co-authored-by: justinchuby <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1254 +/- ##
==========================================
- Coverage 79.95% 79.92% -0.03%
==========================================
Files 360 360
Lines 158518 158605 +87
Branches 158518 158605 +87
==========================================
+ Hits 126741 126771 +30
- Misses 27160 27213 +53
- Partials 4617 4621 +4
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
🔴 Benchmark Regression DetectedComparison of criterion micro-benchmarks: PR head vs merge-base, measured on the same runner in the same job (base first → PR second).
Visual flags: Host infoWhat this cannot catch
|
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.
Replacement for #1248, rebased onto current
mainafter #1197/#1224/#1236 landed as squashes (the stacked base diverged). The original functional commit plus two added tests, re-validated on top of the merged stack.Symptom
A model driven with flat tool names (
bash,edit,glob,grep,read,write) emits a namespaced call such asglob.glob, which the client rejects: it can only dispatch a name it offered.Cause (as claimed by the PR, and what I could and could not confirm)
The caller offers flat tool definitions over the API. The instruction text the model reads is rendered by the package's own chat template, which the PR says assumes namespaced names, takes
name.split('.')[0], and renders# Valid recipients: "self", "glob.*", ..., "user".— so the model follows a grammar that is wrong for a flat offer and writesglob.glob.I could not inspect that template with my own eyes. It ships inside the model package (Muse Glimmer / harmony-style), which is not in this repository or in my local model set; searching the repo and local packages for the
split('.')/ "Valid recipients" grammar found nothing. What I can confirm from the server side corroborates the addressing scheme: the ATEM parser recognisesto=functions.<name>channels and<atem:invoke name="...">blocks (extract_atem_tool_calls,ATEM_USER_CHANNEL = "to=user<|message|>"), so a namespaced recipient likefunctions.globproducing a call namedglob.globis consistent with the diagnosis.Template fix vs dispatch fix — my conclusion: resolving at dispatch is the right layer, and this is a considered choice, not merely the first available. The template belongs to the model author and is rendered in the jinja sandbox; the server cannot rewrite an arbitrary third-party package's template at runtime, and the namespaced grammar is not universally wrong (it is correct for a caller that offers namespaced tools — only a flat offer breaks it). The server is the only component that sees both the caller's offer and the model's emitted call, so it is the only place that can reconcile them without editing model files. A template correction for flat offers would still be worthwhile in the package, but it cannot be the server's runtime responsibility.
Fix
When a parsed call names a tool that is not on offer but whose final
.-segment exactly equals an offered tool's whole name, the call is resolved to that offered tool; anything else is left exactly as the model spelled it. Applied at the three call sites that already have the request in scope (buffered, streaming, and the logprob path), viaParsedAssistantOutput::aligned_to/align_tool_calls.Ambiguity is impossible by construction, not merely avoided: the resolution target is always the single final segment string, and it resolves only if that exact string is an offered (unique) name — there is never a choice between two candidates. When two offered tools merely share a final segment (
fs.read,net.read) and the model writes a third namespace (svc.read), the bare segmentreadis offered by neither, so the call is left alone for the caller to reject.Verification (mine, on this branch atop the merged stack)
cargo clippy -p onnx-genai-server --all-targets -- -D warningsclean.align_tool_callsto a no-op madea_namespaced_call_resolves_to_the_offered_tooland the new full-parse-path test returnglob.globinstead ofglob.an_offered_name_is_left_alonego RED (fs.read→read), which is exactly what that test protects.a_shared_final_segment_is_not_resolved_arbitrarily(the ambiguity case above) anda_namespaced_atem_call_dispatches_through_the_full_parse_path, which drives a raw<atem:invoke name="glob.glob">turn through the realparse_assistant_output→align_tool_callspath and asserts the dispatched name isglob.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com