Skip to content

feat: route Qwen3-Coder + DeepSeek-V4 tool calls through dynamo-parsers-v2 (DYN_ENABLE_EXPERIMENTAL_PARSERS_V2) - #10853

Merged
keivenchang merged 1 commit into
mainfrom
keivenchang/DIS-2228__qwen3-coder-tool-calls-v2
Jun 26, 2026
Merged

keivenchang merged 1 commit into
mainfrom
keivenchang/DIS-2228__qwen3-coder-tool-calls-v2

Conversation

@keivenchang

@keivenchang keivenchang commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Overview

Routes Qwen3-Coder and DeepSeek-V4 tool calls through the dynamo-parsers-v2 streaming parser, gated behind a new DYN_ENABLE_EXPERIMENTAL_PARSERS_V2 env flag. When the flag is set, both the batch and the streaming path bypass the v1 tool-call jail and use the v2 parser — which owns incremental tool-call emission and drops a parameter value truncated at EOF instead of guessing it. Off by default; the v1 jail / aggregate-finalize path is unchanged.

Changes

  • New DYN_ENABLE_EXPERIMENTAL_PARSERS_V2 flag (const + registry in environment_names.rs), read once via a cached LazyLock<bool>.
  • Generalized the Qwen3-Coder-only module qwen3_coder_v2.rs into a family-agnostic tool_parser_v2.rs: enabled(), supports_family() = {qwen3_coder, deepseek_v4}, and apply_stream / parse_complete built on dynamo_parsers_v2::create_tool_parser_for_family(family, …) returning Box<dyn ToolParser>. Adding a family is a one-line change here plus support in the crate.
  • Both dispatch sites gated on enabled() && supports_family(parser): streaming (preprocessor.rs) and batch (aggregator.rs).
  • Adds the published dynamo-parsers-v2 0.1.10 dep. The dynamo-parsers 2.1.2 bump merged separately in chore: bump dynamo-parsers to 2.1.2 #10899; this branch is rebased onto main on top of it.

Behavior

  • Flag unset (default): no change — every family uses the v1 jail.
  • Flag set: qwen3_coder and deepseek_v4 route through v2 for both batch and streaming.

Tests

  • New tool_parser_v2 unit tests: Qwen3-Coder incremental stream + truncated-at-EOF drop, plus a DeepSeek-V4 DSML incremental stream (reconstructs one get_weather(location=NYC) call, no markup leak).
  • cargo test -p dynamo-llm: 3 unit + 149 integration (test_jail 69, streaming 36, tool_choice 36, parallel 8) pass — the env-gating leaves the v1 path unchanged.

Enabling the experimental v2 path

Off by default. Set DYN_ENABLE_EXPERIMENTAL_PARSERS_V2 (truthy: 1/true/yes/on) at process startup to route the supported families (qwen3_coder, deepseek_v4) through the v2 parser instead of the v1 jail, for both batch and streaming. The flag is read once per process via LazyLock, so it's a startup switch (not toggleable mid-run). Other families, and non-auto tool_choice (required/named), always stay on v1.

In fixture / Rust tests — prefix the test run with the env var (applies to the whole test binary):

# v1 (default)
cargo test -p dynamo-llm --test postprocessor_parsing_stream tool_calls_qwen3_coder_auto_routes_through_experimental_gate
# v2 (experimental)
DYN_ENABLE_EXPERIMENTAL_PARSERS_V2=1 cargo test -p dynamo-llm --test postprocessor_parsing_stream tool_calls_qwen3_coder_auto_routes_through_experimental_gate

tool_calls_qwen3_coder_auto_routes_through_experimental_gate is the gate test: it routes a qwen3_coder + auto tool-call stream through the dispatch, so the same assertion exercises v1 with the flag off and v2 with it on. CI runs with the flag off.

In real Dynamo — export it for the frontend process:

DYN_ENABLE_EXPERIMENTAL_PARSERS_V2=1 python -m dynamo.frontend --http-port 8000

Related

DIS-2228 — Qwen3-Coder v2 parser + env-gated Dynamo routing

Summary by CodeRabbit

  • New Features

    • Added experimental support for improved tool-call parsing in streaming and non-streaming chat responses for select model families.
    • Introduced a new opt-in setting to enable the experimental parser path.
  • Bug Fixes

    • Reduced the chance of malformed tool-call output leaking into visible text.
    • Improved handling of incomplete responses at the end of a stream so tool calls are finalized more safely.

@keivenchang keivenchang self-assigned this Jun 23, 2026
@github-actions github-actions Bot added feat frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` labels Jun 23, 2026
@datadog-official

This comment has been minimized.

@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__bump-dynamo-parsers-2.0.1 branch from 84aaa30 to 2eb21b3 Compare June 23, 2026 01:25
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch from efd94c2 to 0c42524 Compare June 23, 2026 01:26
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__bump-dynamo-parsers branch from 2eb21b3 to 044c0ca Compare June 24, 2026 00:21
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch from 0c42524 to 2382afd Compare June 24, 2026 18:33
@keivenchang
keivenchang temporarily deployed to external_collaborator June 24, 2026 18:33 — with GitHub Actions Inactive
@keivenchang keivenchang changed the title feat: route Qwen3-Coder tool calls through dynamo-parsers-v2 (batch + streaming) feat: route Qwen3-Coder + DeepSeek-V4 tool calls through dynamo-parsers-v2 (DYN_ENABLE_EXPERIMENTAL_PARSERS_V2) Jun 24, 2026
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__bump-dynamo-parsers branch from 044c0ca to ce6c40c Compare June 24, 2026 18:45
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__bump-dynamo-parsers branch from ce6c40c to bd65cf7 Compare June 24, 2026 19:14
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch from 2382afd to 75b7b9b Compare June 24, 2026 19:21
@keivenchang
keivenchang temporarily deployed to external_collaborator June 24, 2026 19:21 — with GitHub Actions Inactive
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch from 75b7b9b to 84131c4 Compare June 24, 2026 19:33
@keivenchang
keivenchang temporarily deployed to external_collaborator June 24, 2026 19:33 — with GitHub Actions Inactive
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__bump-dynamo-parsers branch from bd65cf7 to 0159cef Compare June 24, 2026 19:48
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch from 84131c4 to 06833c0 Compare June 24, 2026 19:53
@keivenchang
keivenchang temporarily deployed to external_collaborator June 24, 2026 19:53 — with GitHub Actions Inactive
Base automatically changed from keivenchang/DIS-2228__bump-dynamo-parsers to main June 24, 2026 20:49
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch from 06833c0 to 9440d51 Compare June 24, 2026 21:04
@keivenchang
keivenchang temporarily deployed to external_collaborator June 24, 2026 21:04 — with GitHub Actions Inactive
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch from 9440d51 to d0747af Compare June 24, 2026 21:59
@keivenchang
keivenchang temporarily deployed to external_collaborator June 24, 2026 21:59 — with GitHub Actions Inactive
@keivenchang
keivenchang marked this pull request as ready for review June 24, 2026 22:08
@keivenchang
keivenchang requested a review from a team June 24, 2026 22:08
devin-ai-integration[bot]

This comment was marked as resolved.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds the parser-v2 dependency and environment flag, introduces a new OpenAI chat-completions tool parser module, routes supported tool-call flows through it, and adds unit and integration tests for the experimental path.

Changes

Experimental parser-v2 routing

Layer / File(s) Summary
Dependency and gate surface
Cargo.toml, lib/llm/Cargo.toml, lib/runtime/src/config/environment_names.rs, lib/llm/src/protocols/openai/chat_completions.rs
Adds dynamo-parsers-v2 to the workspace and llm dependencies, defines DYN_ENABLE_EXPERIMENTAL_PARSERS_V2, and exports tool_parser_v2.
Batch parser and family support
lib/llm/src/protocols/openai/chat_completions/tool_parser_v2.rs
Defines the supported families, the v2 enablement check, v1-to-v2 tool translation, and batch parsing into OpenAI tool calls plus normal text.
Streaming bypass and unit tests
lib/llm/src/protocols/openai/chat_completions/tool_parser_v2.rs
Implements the streaming v2 path with per-choice parser state, terminal flushing, and passthrough fallback, plus unit tests for incremental reconstruction and EOF truncation cases.
OpenAI routing and regression coverage
lib/llm/src/preprocessor.rs, lib/llm/src/protocols/openai/chat_completions/aggregator.rs, lib/llm/tests/postprocessor_parsing_stream.rs
Routes eligible tool-call streams through parser-v2 in preprocessing and final aggregation, and adds an integration test for the qwen3_coder Auto path.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly names the main change: experimental v2 routing for Qwen3-Coder and DeepSeek-V4 tool calls.
Description check ✅ Passed The description covers overview, changes, behavior, tests, and related issue info, though it lacks an explicit reviewer-start section.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands.

coderabbitai[bot]

This comment was marked as resolved.

@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch from d0747af to 0c2655e Compare June 24, 2026 23:19
@keivenchang
keivenchang temporarily deployed to external_collaborator June 24, 2026 23:19 — with GitHub Actions Inactive
…s-v2 behind DYN_ENABLE_EXPERIMENTAL_PARSERS_V2

Generalize the Qwen3-Coder v2 tool-call routing into a family-agnostic path and add DeepSeek-V4 (DSML). When DYN_ENABLE_EXPERIMENTAL_PARSERS_V2 is set, both the batch and streaming paths route supported families through dynamo_parsers_v2::create_tool_parser_for_family, bypassing the v1 jail; otherwise the v1 jail / aggregate-finalize path is unchanged. Renames qwen3_coder_v2.rs to tool_parser_v2.rs, adds the env flag (off by default), and depends on the published dynamo-parsers-v2 0.1.10 (dynamo-parsers 2.1.2).

Signed-off-by: Keiven Chang <keivenchang@users.noreply.github.com>
@keivenchang
keivenchang force-pushed the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch from 0c2655e to 9aad838 Compare June 24, 2026 23:39
@keivenchang
keivenchang temporarily deployed to external_collaborator June 24, 2026 23:39 — with GitHub Actions Inactive

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

Open in Devin Review

Comment thread lib/llm/src/protocols/openai/chat_completions/tool_parser_v2.rs
@keivenchang
keivenchang merged commit 1b37e82 into main Jun 26, 2026
101 checks passed
@keivenchang
keivenchang deleted the keivenchang/DIS-2228__qwen3-coder-tool-calls-v2 branch June 26, 2026 01:55

This branch was previously deployed

2 inactive deployments
GITLAB — 9aad8384 Deployed Jun 24, 2026 by copy-pr-bot[bot] via Trigger CI Pipeline #31964
external_collaborator — 9aad8384 Deployed Jun 24, 2026 by keivenchang via ok-to-test #228
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants