Skip to content

feat(reasoning_parser): add Mistral/Magistral reasoning parser - #1816

Open
slin1237 wants to merge 1 commit into
mainfrom
feat/mistral-reasoning-parser
Open

feat(reasoning_parser): add Mistral/Magistral reasoning parser#1816
slin1237 wants to merge 1 commit into
mainfrom
feat/mistral-reasoning-parser

Conversation

@slin1237

@slin1237 slin1237 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Description

Problem

SMG cannot extract reasoning from Mistral's Magistral models, which emit reasoning between [THINK] / [/THINK] markers.

Solution

Add a Magistral reasoning parser and route only the magistral model pattern to it — deliberately not matching broad mistral/mixtral, so non-reasoning Mistral models keep passthrough behavior.

Changes

  • Reasoning parser (crates/reasoning_parser/src/parsers/mistral.rs): [THINK] / [/THINK] markers via BaseReasoningParser (always_in_reasoning=false — the template does not prefill the start token).
  • Registration: register mistral; route pattern magistralmistral. Plain mistral-small / mixtral resolve to passthrough (asserted in tests).

Test Plan

  • cargo +nightly fmt --all — clean
  • cargo clippy -p reasoning-parser --all-targets -- -D warnings — clean
  • cargo test -p reasoning-parser — 82 passed, 0 failed. New tests cover complete extraction, whitespace preservation, truncated traces (no end token), streaming with the start/end marker split across chunk boundaries ([TH|INK], [/TH|INK]), passthrough, reset, and the factory routing (magistral → reasoning; plain mistral/mixtral → passthrough).
Checklist
  • cargo +nightly fmt passes
  • cargo clippy --all-targets -- -D warnings passes (reasoning-parser)
  • (Optional) Documentation updated

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a specialized reasoning parser for Mistral and Magistral family models to properly handle their reasoning format.
  • Tests

    • Added comprehensive test coverage for model routing and parsing behavior, including edge cases such as streaming scenarios and incomplete reasoning spans.

Magistral reasoning models emit reasoning between [THINK] and [/THINK] markers.
Adds a BaseReasoningParser-backed parser (always_in_reasoning=false) and routes
only the `magistral` pattern to it, so plain Mistral-Small / Mixtral
(non-reasoning) models still fall through to passthrough.

Signed-off-by: Simo Lin <linsimo.mark@gmail.com>
@slin1237
slin1237 requested a review from CatherineSue as a code owner June 22, 2026 12:25
@github-actions github-actions Bot added the reasoning-parser Reasoning parser changes label Jun 22, 2026
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2b1f36ad-8903-4923-9773-a4504a491724

📥 Commits

Reviewing files that changed from the base of the PR and between 6c3ce8e and c6d40c8.

📒 Files selected for processing (4)
  • crates/reasoning_parser/src/factory.rs
  • crates/reasoning_parser/src/lib.rs
  • crates/reasoning_parser/src/parsers/mistral.rs
  • crates/reasoning_parser/src/parsers/mod.rs

📝 Walkthrough

Walkthrough

Adds MistralParser, a new reasoning parser for Magistral models using [THINK]/[/THINK] delimiters with always_in_reasoning=false. It is wired into the parsers module, re-exported from the crate root, registered in ParserFactory under "mistral", and mapped to Magistral model IDs via a "magistral" pattern.

Changes

MistralParser for Magistral Reasoning

Layer / File(s) Summary
MistralParser struct, wiring, and public exports
crates/reasoning_parser/src/parsers/mistral.rs, crates/reasoning_parser/src/parsers/mod.rs, crates/reasoning_parser/src/lib.rs
MistralParser is defined as a wrapper around BaseReasoningParser using [THINK]/[/THINK] tokens with stream_reasoning=true and always_in_reasoning=false; the module and crate root re-export it publicly.
Factory registration and model-pattern routing
crates/reasoning_parser/src/factory.rs
ParserFactory::new() registers the "mistral" parser and adds a "magistral" model-pattern mapping; tests assert Magistral model IDs route to "mistral" and plain Mistral/Mixtral IDs fall back to "passthrough".
MistralParser unit tests
crates/reasoning_parser/src/parsers/mistral.rs
Tests cover full extraction, no-marker passthrough, truncated reasoning, streaming across chunk boundaries (including splits inside both markers), streaming passthrough, and reset behavior.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

reasoning-parser

Suggested reviewers

  • CatherineSue

Poem

🐇 Hop hop, a new [THINK] tag appears,
Magistral reasoning flows through the gears,
With always_in_reasoning set to false,
The parser decides when to pause—
No broad Mistral match, just the narrow path clear,
A factory wired with a tiny new ear! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding a new Mistral/Magistral reasoning parser. It is concise, specific, and clearly summarizes the primary contribution of the changeset.
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.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mistral-reasoning-parser

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

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a new MistralParser to support Mistral/Magistral reasoning models, which utilize [THINK] and [/THINK] markers. The parser is registered in the ParserFactory under the pattern "magistral", and comprehensive unit tests are added to verify correct routing and parsing behavior (ensuring plain Mistral models correctly fall back to passthrough). The review feedback suggests deriving Debug and Clone on the public MistralParser struct to improve usability and align with Rust API guidelines.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +18 to +20
pub struct MistralParser {
base: BaseReasoningParser,
}

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.

medium

To adhere to Rust API guidelines and general best practices, public structs should implement common traits like Debug and Clone whenever possible. Since BaseReasoningParser already implements both, we can easily derive them for MistralParser to improve usability and debuggability.

Suggested change
pub struct MistralParser {
base: BaseReasoningParser,
}
#[derive(Debug, Clone)]
pub struct MistralParser {
base: BaseReasoningParser,
}
References
  1. Prefer deriving 'Debug' using '#[derive(Debug)]' instead of manually implementing 'std::fmt::Debug' in Rust, unless manual implementation is necessary (e.g., when some fields do not implement 'Debug').

@claude claude Bot 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.

Looks good. Clean implementation following the established parser pattern. The magistral-only routing is correctly scoped so non-reasoning Mistral/Mixtral models aren't misrouted, and the negative test (test_plain_mistral_is_not_reasoning) locks that in. Good test coverage across streaming edge cases (split markers) and reset behavior.

0 🔴 Important · 0 🟡 Nit · 0 🟣 Pre-existing

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6d40c81d6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

&mut self,
text: &str,
) -> Result<ParserResult, ParseError> {
self.base.parse_reasoning_streaming_incremental(text)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Buffer partial end tags after reasoning text

When a streaming chunk ends with only the beginning of the end marker after some reasoning text, this delegation to BaseReasoningParser emits and clears the whole buffer instead of retaining the partial marker. For example [THINK]thought -> more[/TH -> INK]answer will emit more[/TH as reasoning, lose the buffered prefix, and then never recognize [/THINK], so the final answer stays hidden as reasoning. The new tests only cover a partial marker when it is the entire chunk; real chunk boundaries can split after content as well.

Useful? React with 👍 / 👎.

// Magistral is the reasoning member of the Mistral family. Match only
// `magistral` (not a broad `mistral`/`mixtral` substring) so plain
// Mistral-Small / Mixtral non-reasoning models are not misrouted here.
registry.register_pattern("magistral", "mistral");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route Ministral reasoning models to the parser

Auto-detection only registers the magistral substring, so official Mistral reasoning model IDs such as mistralai/Ministral-3-8B-Reasoning-2512 fall through to passthrough even though vLLM documents serving them with --reasoning-parser mistral (https://docs.vllm.ai/projects/recipes/en/latest/Mistral/Ministral-3-Reasoning.html). In deployments relying on model-name auto-detection, their [THINK]...[/THINK] blocks will be emitted as normal content; add a narrow ministral + reasoning style pattern rather than broad mistral.

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had any activity within 14 days. It will be automatically closed if no further activity occurs within 16 days. Leave a comment if you feel this pull request should remain open. Thank you!

@github-actions github-actions Bot added the stale PR has been inactive for 14+ days label Jul 6, 2026
@mergify

mergify Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Hi @slin1237, this PR has merge conflicts that must be resolved before it can be merged. Please rebase your branch:

git fetch origin main
git rebase origin/main
# resolve any conflicts, then:
git push --force-with-lease

@mergify mergify Bot added the needs-rebase PR has merge conflicts that need to be resolved label Jul 16, 2026
@github-actions github-actions Bot removed the stale PR has been inactive for 14+ days label Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-rebase PR has merge conflicts that need to be resolved reasoning-parser Reasoning parser changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant