Skip to content

Conversation

@nealvaidya
Copy link
Contributor

@nealvaidya nealvaidya commented Sep 8, 2025

Overview:

Previously, we defaulted to using the BasicReasoningParser if no reasoning parser was specified, which extracts anything between <think> </think> tags as being reasoning content. With this PR, when no reasoning parser is specified, all content is treated as "Normal" non-reasoning content. This aligns with the behavior of vllm and sglang.

Note

This PR retains the behavior that specifying a non-existent reasoning parser causes dynamo to fallback to the basic parser, rather than no parser. I think this is probably still the desired behavior.

Summary by CodeRabbit

  • New Features
    • Reasoning parsing is now optional in chat completion streaming. If no parser is configured, all output is treated as normal text with no reasoning content.
  • Documentation
    • Updated CLI help for --dyn-reasoning-parser across backends to clarify it’s optional and that no reasoning parsing occurs when omitted.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 8, 2025

Walkthrough

CLI help text for --dyn-reasoning-parser was clarified across backends to indicate optional usage. In Rust, DeltaGenerator.reasoning_parser was made optional, with guards added to skip reasoning parsing when not configured. Streaming postprocessing now treats all text as normal when no parser is present.

Changes

Cohort / File(s) Summary of Changes
CLI help: optional reasoning parser
components/backends/sglang/src/dynamo/sglang/args.py, components/backends/trtllm/src/dynamo/trtllm/utils/trtllm_utils.py, components/backends/vllm/src/dynamo/vllm/args.py
Updated help text for --dyn-reasoning-parser to state it is optional and that no reasoning parsing occurs if not provided; no logic or defaults changed.
DeltaGenerator: optional parser handling
lib/llm/src/protocols/openai/chat_completions/delta.rs
Changed reasoning_parser from ReasoningParserWrapper to Option<ReasoningParserWrapper>; updated constructor and flow to conditionally parse reasoning; added guards so text is treated as normal when no parser is configured.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor C as Client
  participant D as DeltaGenerator
  participant P as ReasoningParser (optional)
  participant PP as Postprocessor/Streamer

  C->>D: Provide delta chunk
  alt Parser configured
    D->>P: Parse delta into {normal_text, reasoning_content}
    P-->>D: Parsed parts
    D->>PP: Emit normal_text + reasoning_content (as reasoning)
  else No parser configured
    note right of D: Reasoning parsing skipped
    D->>PP: Emit delta as normal text only
  end
  PP-->>C: Streamed response chunk
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

A bunny taps keys with gentle care,
Optional parsers float in air.
When none are named, we hop on by,
Plain text streams—no need to try.
But if you call, we parse with cheer—
Carrots, code, and logs appear. 🥕✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
		  - name: "Undocumented Breaking Changes"
			  mode: "warning"
			  instructions: |
				  Flag potential breaking changes that are not documented:
				  1. Identify changes to public APIs/exports, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints (including removed/renamed items and changes to types, required params, return values, defaults, or behavior).
				  2. Ignore purely internal/private changes (e.g., code not exported from package entry points or marked internal).
				  3. Verify documentation exists: a "Breaking Change" section in the PR description and updates to CHANGELOG.md.

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The current pull request description only provides the Overview section, but the repository’s template also requires Details, Where should the reviewer start, and Related Issues sections to give reviewers context on the specific changes, files to inspect, and associated issue tracking. Without these sections, key information is missing and the PR cannot be properly evaluated. Please expand the description by adding a Details section that outlines the specific code changes, a Where should the reviewer start section pointing to the main files to review, and a Related Issues section listing any GitHub issues closed or addressed by this PR.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly captures the primary change of disabling the default reasoning parser and is clear, concise, and directly related to the main behavior adjustment introduced by this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
lib/llm/src/protocols/openai/chat_completions/delta.rs (2)

100-106: Treat empty/whitespace parser names as “unspecified”

Avoid enabling Basic parser when the configured name is an empty string. Trim and filter empties.

Apply this diff:

-        let reasoning_parser = options
-            .runtime_config
-            .reasoning_parser
-            .as_deref()
-            .map(|parser_name| ReasoningParserType::get_reasoning_parser_from_name(parser_name));
+        let reasoning_parser = options
+            .runtime_config
+            .reasoning_parser
+            .as_deref()
+            .map(str::trim)
+            .filter(|s| !s.is_empty())
+            .map(ReasoningParserType::get_reasoning_parser_from_name);

335-347: Simplify branch; remove is_some() check and Default dependency

You can rely solely on create_reasoning_content(...) to decide and avoid requiring ParserResult: Default. Also reduces branching.

Apply this diff:

-        // Handle reasoning parsing if enabled, otherwise treat all text as normal
-        let (normal_text, reasoning_content) = if self.reasoning_parser.is_some() {
-            let reasoning_parser_result = self
-                .create_reasoning_content(&delta.text, &delta.token_ids)
-                .unwrap_or_default();
-            (
-                reasoning_parser_result.get_some_normal_text(),
-                reasoning_parser_result.get_some_reasoning(),
-            )
-        } else {
-            // No reasoning parser configured, treat all text as normal
-            (delta.text, None)
-        };
+        // Handle reasoning parsing when configured; otherwise treat all text as normal
+        let (normal_text, reasoning_content) =
+            match self.create_reasoning_content(&delta.text, &delta.token_ids) {
+                Some(r) => (r.get_some_normal_text(), r.get_some_reasoning()),
+                None => (delta.text, None),
+            };
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fda1efe and a485124.

📒 Files selected for processing (4)
  • components/backends/sglang/src/dynamo/sglang/args.py (1 hunks)
  • components/backends/trtllm/src/dynamo/trtllm/utils/trtllm_utils.py (1 hunks)
  • components/backends/vllm/src/dynamo/vllm/args.py (1 hunks)
  • lib/llm/src/protocols/openai/chat_completions/delta.rs (4 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: nachiketb-nvidia
PR: ai-dynamo/dynamo#2750
File: lib/bindings/python/src/dynamo/reasoning_parser/basic_parser.py:24-48
Timestamp: 2025-08-27T20:27:44.446Z
Learning: In the BasicReasoningParser class in lib/bindings/python/src/dynamo/reasoning_parser/basic_parser.py, the self._in_reasoning state variable in the detect_and_parse_reasoning method is intentionally included as an example for tweakability rather than for strict functional consistency between one-shot and streaming parsing methods.
Learnt from: nachiketb-nvidia
PR: ai-dynamo/dynamo#2656
File: lib/llm/src/protocols/openai/chat_completions/delta.rs:320-327
Timestamp: 2025-08-22T19:55:41.608Z
Learning: There are two separate DeltaGenerator classes in the codebase: one for chat completions (lib/llm/src/protocols/openai/chat_completions/delta.rs with object "chat.completion.chunk") and one for text completions (lib/llm/src/protocols/openai/completions/delta.rs with object "text_completion"). They have different create_choice method signatures and serve different OpenAI API endpoints. The reasoning parsing functionality is only relevant to the chat completions DeltaGenerator.
📚 Learning: 2025-08-22T19:55:41.608Z
Learnt from: nachiketb-nvidia
PR: ai-dynamo/dynamo#2656
File: lib/llm/src/protocols/openai/chat_completions/delta.rs:320-327
Timestamp: 2025-08-22T19:55:41.608Z
Learning: There are two separate DeltaGenerator classes in the codebase: one for chat completions (lib/llm/src/protocols/openai/chat_completions/delta.rs with object "chat.completion.chunk") and one for text completions (lib/llm/src/protocols/openai/completions/delta.rs with object "text_completion"). They have different create_choice method signatures and serve different OpenAI API endpoints. The reasoning parsing functionality is only relevant to the chat completions DeltaGenerator.

Applied to files:

  • lib/llm/src/protocols/openai/chat_completions/delta.rs
📚 Learning: 2025-08-22T19:55:41.608Z
Learnt from: nachiketb-nvidia
PR: ai-dynamo/dynamo#2656
File: lib/llm/src/protocols/openai/chat_completions/delta.rs:320-327
Timestamp: 2025-08-22T19:55:41.608Z
Learning: The create_choice method exists on multiple different objects in the codebase. The DeltaGenerator::create_choice in lib/llm/src/protocols/openai/chat_completions/delta.rs has its own signature that was updated to include reasoning_content, but other objects in lib/llm/src/engines.rs have their own separate create_choice methods with different signatures that are not related to chat completions.

Applied to files:

  • lib/llm/src/protocols/openai/chat_completions/delta.rs
📚 Learning: 2025-09-08T21:18:43.464Z
Learnt from: nachiketb-nvidia
PR: ai-dynamo/dynamo#2936
File: lib/parsers/src/reasoning/granite_parser.rs:42-46
Timestamp: 2025-09-08T21:18:43.464Z
Learning: In GraniteReasoningParser in lib/parsers/src/reasoning/granite_parser.rs, the think_start_tokens and think_end_tokens are hardcoded in the constructor with fixed values, so unwrap() calls on these vectors are safe and won't panic.

Applied to files:

  • lib/llm/src/protocols/openai/chat_completions/delta.rs
🧬 Code graph analysis (1)
lib/llm/src/protocols/openai/chat_completions/delta.rs (2)
lib/bindings/python/rust/llm/local_model.rs (1)
  • reasoning_parser (76-78)
lib/parsers/src/reasoning/mod.rs (1)
  • get_reasoning_parser_from_name (171-187)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Build and Test - vllm
  • GitHub Check: Build and Test - dynamo
  • GitHub Check: pre-merge-rust (lib/runtime/examples)
  • GitHub Check: pre-merge-rust (lib/bindings/python)
  • GitHub Check: pre-merge-rust (.)
🔇 Additional comments (5)
components/backends/sglang/src/dynamo/sglang/args.py (1)

45-46: Help text clarification looks good

Accurately communicates the new default (no parsing when unspecified). Matches PR intent.

components/backends/vllm/src/dynamo/vllm/args.py (1)

117-118: Consistent optionality messaging

Help text now clearly reflects that parsing is optional; aligns with system behavior.

components/backends/trtllm/src/dynamo/trtllm/utils/trtllm_utils.py (1)

286-287: Clearer CLI help

Matches the new default (no parser ⇒ no reasoning parsing). Good consistency across backends.

lib/llm/src/protocols/openai/chat_completions/delta.rs (2)

67-69: Struct field made optional — correct direction

Making reasoning_parser an Option with doc explaining “None ⇒ no parsing” cleanly implements the new default.


201-203: Early return when no parser — correct

Short-circuiting on None avoids unnecessary work and matches the “no parser ⇒ no reasoning” behavior.

Copy link
Contributor

@nachiketb-nvidia nachiketb-nvidia left a comment

Choose a reason for hiding this comment

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

LGTM

@grahamking
Copy link
Contributor

/ok to test caf67d8

@nealvaidya nealvaidya merged commit 939ede8 into main Sep 9, 2025
15 of 16 checks passed
@nealvaidya nealvaidya deleted the nealv/no_default_reasoning branch September 9, 2025 18:12
tedzhouhk pushed a commit that referenced this pull request Sep 10, 2025
Signed-off-by: Neal Vaidya <[email protected]>
Signed-off-by: hongkuanz <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants