Skip to content

fix: reasoning forwarding#413

Open
tianmu-li wants to merge 2 commits into
mlcommons:mainfrom
tianmu-li:fix/reasoning_forwarding
Open

fix: reasoning forwarding#413
tianmu-li wants to merge 2 commits into
mlcommons:mainfrom
tianmu-li:fix/reasoning_forwarding

Conversation

@tianmu-li

Copy link
Copy Markdown
Collaborator

What does this PR do?

Current endpoint sends reasoning_content, which is accepted by sglang and normalized to reasoning internally for vllm, but some router frameworks (e.g.: vllm_router) drops reasoning_content and only accepts reasoning. This PR sends both reasoning and reasoning_content to the server regardless of which one is present in the dataset and rejects if both are present.

Type of change

  • Bug fix
  • New feature
  • Documentation update
  • Refactor/cleanup

Related issues

Testing

  • Tests added/updated
  • All tests pass locally
  • Manual testing completed

Checklist

  • Code follows project style
  • Pre-commit hooks pass
  • Documentation updated (if needed)

@tianmu-li
tianmu-li requested review from a team and Copilot July 14, 2026 18:21
@github-actions

Copy link
Copy Markdown

MLCommons CLA bot All contributors have signed the MLCommons CLA ✍️ ✅

@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 support for the reasoning field as an alias for reasoning_content across agentic inference datasets and OpenAI chat messages. It adds validation to ensure both fields are not provided simultaneously, updates the message adapter and types, and includes unit tests. Feedback is provided regarding the resolution of the reasoning value in _chat_message_from_dict, where using the or operator could incorrectly discard empty string values.

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.


def _chat_message_from_dict(msg: dict) -> "ChatMessage":
"""Build a ChatMessage from a dict, forwarding all supported fields."""
reasoning_value = msg.get("reasoning_content") or msg.get("reasoning")

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

Using the or operator here can lead to unexpected behavior if reasoning_content is an empty string "" (which is a falsy value in Python). If reasoning_content is "" and reasoning is None or not present, reasoning_value will incorrectly evaluate to None instead of preserving the empty string.\n\nTo make this more robust, explicitly check for None using a conditional expression.

Suggested change
reasoning_value = msg.get("reasoning_content") or msg.get("reasoning")
reasoning_value = msg.get("reasoning_content") if msg.get("reasoning_content") is not None else msg.get("reasoning")

Copilot AI 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.

Pull request overview

This PR addresses interoperability between OpenAI-compatible backends/routers by treating reasoning_content (SGLang/DeepSeek) and reasoning (vLLM) as aliases, so requests can reach routers that only accept one of the two.

Changes:

  • Add reasoning to the OpenAI ChatMessage msgspec type and forward both reasoning and reasoning_content in outgoing requests.
  • Enforce an alias policy in the agentic dataset builder to reject rows that contain both fields.
  • Add/update unit tests and update the agentic dataset README to document the new field.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/inference_endpoint/openai/types.py Adds reasoning field to ChatMessage schema.
src/inference_endpoint/openai/openai_msgspec_adapter.py Forwards reasoning alias fields when building request messages.
src/inference_endpoint/dataset_manager/agentic_inference_dataset.py Rejects dataset rows containing both reasoning aliases and includes reasoning in extracted message fields.
tests/unit/openai/test_msgspec_adapter.py Adds request-side tests for reasoning alias forwarding.
tests/unit/dataset_manager/test_agentic_inference_dataset.py Adds dataset validation tests for reasoning alias policy.
examples/10_Agentic_Inference/README.md Documents reasoning as an allowed dataset field.
Comments suppressed due to low confidence (1)

src/inference_endpoint/dataset_manager/agentic_inference_dataset.py:182

  • When building the per-row message dict, missing values coming from a PyArrow-backed DataFrame can be pd.NA (not None and not a float NaN). Those should be filtered out, otherwise pd.NA may end up in the emitted message payload.
                val = row.get(key)
                if val is not None and not (isinstance(val, float) and pd.isna(val)):
                    msg[key] = val

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 50 to 53
def _chat_message_from_dict(msg: dict) -> "ChatMessage":
"""Build a ChatMessage from a dict, forwarding all supported fields."""
reasoning_value = msg.get("reasoning_content") or msg.get("reasoning")
return ChatMessage(
Comment on lines +171 to +176
[
({"reasoning_content": "thinking via sglang"}, "thinking via sglang"),
({"reasoning": "thinking via vllm"}, "thinking via vllm"),
({"reasoning_content": "sglang", "reasoning": "vllm"}, "sglang"),
],
ids=["reasoning_content", "reasoning", "reasoning_content_precedence"],
Comment on lines +199 to +200


Comment on lines 148 to +163
for _, row in sorted_group.iterrows():
role = row.get("role")
reasoning_content = row.get("reasoning_content")
reasoning = row.get("reasoning")
if (
reasoning_content is not None
and not (
isinstance(reasoning_content, float) and pd.isna(reasoning_content)
)
and reasoning is not None
and not (isinstance(reasoning, float) and pd.isna(reasoning))
):
raise InputValidationError(
f"conversation {row.get('conversation_id')!r} turn {row.get('turn')} "
"has both 'reasoning_content' and 'reasoning' values"
)
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@ca91f61). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #413   +/-   ##
=======================================
  Coverage        ?   80.22%           
=======================================
  Files           ?      129           
  Lines           ?    17288           
  Branches        ?        0           
=======================================
  Hits            ?    13870           
  Misses          ?     3418           
  Partials        ?        0           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

3 participants