langchain - ✅(Solved) Fix _extract_reasoning_from_additional_kwargs should ignore empty strings [16 pull requests, 14 comments, 12 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
langchain-ai/langchain#36194Fetched 2026-04-08 01:21:37
View on GitHub
Comments
14
Participants
12
Timeline
47
Reactions
0

in libs/core/langchain_core/messages/base.py, there is a function named _extract_reasoning_from_additional_kwargs:

def _extract_reasoning_from_additional_kwargs(
    message: BaseMessage,
) -> types.ReasoningContentBlock | None:
    """Extract `reasoning_content` from `additional_kwargs`.

    Handles reasoning content stored in various formats:
    - `additional_kwargs["reasoning_content"]` (string) - Ollama, DeepSeek, XAI, Groq

    Args:
        message: The message to extract reasoning from.

    Returns:
        A `ReasoningContentBlock` if reasoning content is found, None otherwise.
    """
    additional_kwargs = getattr(message, "additional_kwargs", {})

    reasoning_content = additional_kwargs.get("reasoning_content")
    if reasoning_content is not None and isinstance(reasoning_content, str):
        return {"type": "reasoning", "reasoning": reasoning_content}

    return None

If reasoning_content is an empty str '', the function will return an empty ReasoningContentBlock.

I'm using reasoning ChatModel(eg. ChatTongyi). These providers still attach reasoning_content with empty string even the reasoning stage is finished.

So if I set LC_OUTPUT_VERSION=v1, the content_blocks will be mess -- A series of alternating reasoning and content blocks, where the reasoning blocks are empty.

<img width="1044" height="301" alt="Image" src="https://github.com/user-attachments/assets/60ee73e0-6b89-4b70-aa67-84a2597d908a" />

You could certainly blame the model provider, but I believe infrastructure should provide fallback handling for edge cases.

Error Message

Error Message and Stack Trace (if applicable)

Root Cause

in libs/core/langchain_core/messages/base.py, there is a function named _extract_reasoning_from_additional_kwargs:

def _extract_reasoning_from_additional_kwargs(
    message: BaseMessage,
) -> types.ReasoningContentBlock | None:
    """Extract `reasoning_content` from `additional_kwargs`.

    Handles reasoning content stored in various formats:
    - `additional_kwargs["reasoning_content"]` (string) - Ollama, DeepSeek, XAI, Groq

    Args:
        message: The message to extract reasoning from.

    Returns:
        A `ReasoningContentBlock` if reasoning content is found, None otherwise.
    """
    additional_kwargs = getattr(message, "additional_kwargs", {})

    reasoning_content = additional_kwargs.get("reasoning_content")
    if reasoning_content is not None and isinstance(reasoning_content, str):
        return {"type": "reasoning", "reasoning": reasoning_content}

    return None

If reasoning_content is an empty str '', the function will return an empty ReasoningContentBlock.

I'm using reasoning ChatModel(eg. ChatTongyi). These providers still attach reasoning_content with empty string even the reasoning stage is finished.

So if I set LC_OUTPUT_VERSION=v1, the content_blocks will be mess -- A series of alternating reasoning and content blocks, where the reasoning blocks are empty.

<img width="1044" height="301" alt="Image" src="https://github.com/user-attachments/assets/60ee73e0-6b89-4b70-aa67-84a2597d908a" />

You could certainly blame the model provider, but I believe infrastructure should provide fallback handling for edge cases.

Fix Action

Fix / Workaround

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Other Dependencies

aiohttp: 3.13.3 blockbuster: 1.5.26 click: 8.3.1 cloudpickle: 3.1.2 croniter: 6.0.0 cryptography: 46.0.5 dataclasses-json: 0.6.7 grpcio: 1.78.0 grpcio-health-checking: 1.78.0 grpcio-tools: 1.78.0 httpx: 0.28.1 httpx-sse: 0.4.3 jsonpatch: 1.33 jsonschema-rs: 0.29.1 langgraph: 1.1.3 langgraph-checkpoint: 4.0.0 numpy: 2.4.2 openai: 2.24.0 opentelemetry-api: 1.39.1 opentelemetry-exporter-otlp-proto-http: 1.39.1 opentelemetry-sdk: 1.39.1 orjson: 3.11.7 packaging: 26.0 protobuf: 6.33.5 pydantic: 2.12.5 pydantic-settings: 2.13.1 pyjwt: 2.11.0 pytest: 9.0.2 python-dotenv: 1.2.1 pyyaml: 6.0.3 PyYAML: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 sqlalchemy: 2.0.47 SQLAlchemy: 2.0.47 sse-starlette: 2.1.3 starlette: 0.52.1 structlog: 25.5.0 tenacity: 9.1.4 tiktoken: 0.12.0 truststore: 0.10.4 typing-extensions: 4.15.0 uuid-utils: 0.14.0 uvicorn: 0.41.0 watchfiles: 1.1.1 xxhash: 3.6.0 zstandard: 0.25.0

PR fix notes

PR #36197: fix(core): ignore empty reasoning_content in message extraction

Description (problem / solution / changelog)

Fixes https://github.com/langchain-ai/langchain/issues/36194

Treat empty or whitespace-only reasoning_content as None to prevent empty reasoning blocks in output.

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +4/-1)
  • libs/core/tests/unit_tests/messages/test_utils.py (modified, +39/-1)

PR #36203: core[patch]: ignore empty/whitespace reasoning_content in _extract_reasoning_from_additional_kwargs

Description (problem / solution / changelog)

Fixes: #36194

Problem

Some providers (e.g. ChatTongyi) attach an empty reasoning_content string even after the reasoning stage is finished. With LC_OUTPUT_VERSION=v1, this creates alternating empty reasoning blocks and content blocks in the output.

Fix

Changed the guard from:

if reasoning_content is not None and isinstance(reasoning_content, str):

to:

if isinstance(reasoning_content, str) and reasoning_content.strip():

This treats empty ("") and whitespace-only (" ") strings as no reasoning content, matching the expected behavior that empty reasoning blocks should not be emitted.

Testing

  • Added unit test test_extract_reasoning_ignores_empty_string covering:
    • Empty string → returns None
    • Whitespace-only string → returns None
    • None value → returns None
    • Valid non-empty string → returns ReasoningContentBlock
  • Verified all cases pass locally

Changes

  • libs/core/langchain_core/messages/base.py: one-line condition change
  • libs/core/tests/unit_tests/messages/block_translators/test_groq.py: new test function

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/block_translators/test_groq.py (modified, +35/-0)

PR #36206: fix(core): ignore empty strings in _extract_reasoning_from_additional_kwargs

Description (problem / solution / changelog)

Fixes #36194

Change if reasoning_content is not None to if reasoning_content in _extract_reasoning_from_additional_kwargs so that empty strings are treated the same as None — no empty ReasoningContentBlock is created.

Added a unit test that verifies AIMessage(content="hi", additional_kwargs={"reasoning_content": ""}) produces no reasoning block.

How I verified: make test in libs/core — all tests pass.

  • make format
  • make lint
  • make test

Twitter: @ LinkedIn: https://linkedin.com/in/

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +8/-0)

PR #36207: fix(core): ignore empty reasoning_content in _extract_reasoning_from_additional_kwargs

Description (problem / solution / changelog)

Summary

Fixes #36194

When model providers (e.g., ChatTongyi) attach an empty string "" to reasoning_content in additional_kwargs after the reasoning stage finishes, _extract_reasoning_from_additional_kwargs incorrectly creates empty ReasoningContentBlock entries. With LC_OUTPUT_VERSION=v1, this produces a series of alternating empty reasoning blocks and content blocks.

The fix changes the guard from if reasoning_content is not None to if reasoning_content, which is falsy for both None and "". This is a one-line change in the condition.

This contribution was made with assistance from an AI coding agent (Claude).

Changes

  • libs/core/langchain_core/messages/base.py: change truthiness check to exclude empty strings
  • libs/core/tests/unit_tests/messages/test_ai.py: add regression test for empty reasoning_content

Test plan

  • Added test_content_blocks_empty_reasoning_content_ignored verifying that AIMessage with reasoning_content: "" produces no reasoning block
  • Existing test_content_blocks_reasoning_extraction continues to pass (non-empty reasoning is still extracted)

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +13/-0)

PR #36218: fix(core): ignoring empty reasoning strings

Description (problem / solution / changelog)

Fixes #36194

Added ignoring empty reasoning strings and added unit tests to test the change.

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +19/-0)
  • libs/text-splitters/langchain_text_splitters/sentence_transformers.py (modified, +5/-3)

PR #36219: fix(core): ignoring empty reasoning strings

Description (problem / solution / changelog)

Fixes #36194

Added ignoring empty reasoning strings and added unit tests to test the change.

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +19/-0)

PR #36274: fix(core): ignore empty reasoning_content strings

Description (problem / solution / changelog)

Summary

Ignores empty-string reasoning_content values when extracting reasoning blocks from additional_kwargs in langchain-core.

Without this guard, providers that emit reasoning_content="" can produce empty reasoning blocks in content_blocks, which matches the behavior reported in #36194.

Why this change

The existing helper treats any string, including "", as valid reasoning content. That creates a spurious {"type": "reasoning", "reasoning": ""} block even though there is no actual reasoning payload to preserve.

This patch keeps the existing behavior for non-empty strings and only skips the empty-string edge case.

Tests

  • Added a regression assertion in libs/core/tests/unit_tests/messages/test_ai.py covering additional_kwargs={"reasoning_content": ""}.
  • Ran:
    • python -m uv sync --group test
    • python -m uv run --group test pytest tests/unit_tests/messages/test_ai.py -k reasoning_extraction
    • python -m uv run --group lint ruff check langchain_core/messages/base.py tests/unit_tests/messages/test_ai.py

Review notes

  • The behavior change is intentionally narrow: empty strings are ignored, but whitespace-only strings are still preserved as provided.

AI assistance disclosure

This PR was prepared with AI agent assistance, then manually validated by running the targeted LangChain core test and lint commands listed above.

Closes #36194.

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +8/-0)

PR #36289: fix: skip empty reasoning_content strings

Description (problem / solution / changelog)

Description

Fixes #36194

Empty reasoning_content strings were creating empty ReasoningContentBlock objects, causing unnecessary empty reasoning blocks in message content.

Changes

  • Modified _extract_reasoning_from_additional_kwargs to check for non-empty string (if reasoning_content and isinstance(...)) instead of just checking for not None
  • This prevents empty strings from being converted to reasoning blocks

Test

Before fix:

message.additional_kwargs = {"reasoning_content": ""}
# Would create: {"type": "reasoning", "reasoning": ""}

After fix:

message.additional_kwargs = {"reasoning_content": ""}
# Returns None, no empty block created

Checklist

  • I confirm that this pull request is my own work and that I have the right to submit it under the project's license

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +2/-1)

PR #36326: fix(core): ignore empty strings in _extract_reasoning_from_additional_kwargs

Description (problem / solution / changelog)

Summary

The function _extract_reasoning_from_additional_kwargs was returning a ReasoningContentBlock even when reasoning_content was an empty string, causing issues with reasoning models (e.g., ChatTongyi) that attach empty reasoning_content="" after the reasoning stage completes.

Problem

When using LC_OUTPUT_VERSION=v1, the content_blocks would contain alternating empty reasoning blocks and content blocks, creating a messy output.

Fix

Add .strip() check to ignore empty/whitespace-only strings.

Testing

Added unit tests covering:

  • Empty string returns None
  • Whitespace-only string returns None
  • Valid reasoning returns the expected block

Fixes #36194

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/block_translators/test_groq.py (modified, +29/-0)

PR #36328: fix(core): ignore empty strings in _extract_reasoning_from_additional_kwargs

Description (problem / solution / changelog)

Summary

  • Fix a bug in _extract_reasoning_from_additional_kwargs that returned a ReasoningContentBlock even when reasoning_content was an empty string
  • Add .strip() check to ignore empty/whitespace-only reasoning_content strings
  • Add unit tests for empty string, whitespace-only, and valid content cases

Problem

The function was returning a ReasoningContentBlock even when reasoning_content was an empty string, causing issues with reasoning models (e.g., ChatTongyi) that attach reasoning_content="" after the reasoning stage completes.

When using LC_OUTPUT_VERSION=v1, this led to messy content_blocks with alternating empty reasoning blocks and content blocks.

Fix

# Before
if reasoning_content is not None and isinstance(reasoning_content, str):
    return {"type": "reasoning", "reasoning": reasoning_content}

# After
if (
    reasoning_content is not None
    and isinstance(reasoning_content, str)
    and reasoning_content.strip()
):
    return {"type": "reasoning", "reasoning": reasoning_content}

Test Plan

  • Added test_extract_reasoning_ignores_empty_string() - verifies empty string returns None
  • Added test_extract_reasoning_ignores_whitespace_only() - verifies whitespace-only returns None
  • Added test_extract_reasoning_preserves_valid_content() - verifies valid content is preserved
  • make lint passed
  • All tests pass locally

Fixes #36194

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +5/-1)
  • libs/core/tests/unit_tests/messages/block_translators/test_groq.py (modified, +35/-0)

PR #36329: fix(core): ignore empty string reasoning_content in additional_kwargs

Description (problem / solution / changelog)

Fixes #36194.

Problem

Models like DeepSeek and Tongyi emit reasoning_content="" on intermediate stream chunks before the actual reasoning text arrives. The existing guard:

if reasoning_content is not None and isinstance(reasoning_content, str):

treats an empty string as valid, producing a ReasoningContentBlock with reasoning="". Downstream consumers receive spurious empty reasoning blocks on every partial chunk.

Fix

Tighten the check to a plain truthiness test:

if reasoning_content and isinstance(reasoning_content, str):

Empty strings are now filtered out. None and non-string values are unaffected. Non-empty strings continue to produce a ReasoningContentBlock as before.

Testing

Added test_convert_to_openai_messages_empty_reasoning_content_ignored to tests/unit_tests/messages/test_utils.py asserting that a message carrying additional_kwargs={"reasoning_content": ""} produces no reasoning block in the converted output.

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_utils.py (modified, +16/-0)

PR #36504: fix(core): skip empty reasoning_content strings in _extract_reasoning_from_additional_kwargs

Description (problem / solution / changelog)

Summary

  • Fix _extract_reasoning_from_additional_kwargs in libs/core/langchain_core/messages/base.py to ignore empty strings for reasoning_content
  • Some model providers (e.g. ChatTongyi) attach reasoning_content="" to additional_kwargs even after the reasoning stage is finished, causing alternating empty ReasoningContentBlock and content blocks
  • Added a truthiness check (and reasoning_content) so empty strings are treated the same as None
  • Added test case for the empty string scenario

Closes #36194

Test plan

  • Added unit test in libs/core/tests/unit_tests/messages/test_ai.py verifying that reasoning_content="" does not produce a reasoning content block
  • Existing tests continue to pass (reasoning extraction with non-empty strings still works)

🤖 Generated with Claude Code

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +10/-0)

PR #36520: fix(core): ignore empty reasoning_content in additional_kwargs extraction

Description (problem / solution / changelog)

Summary

  • Adds a truthiness check for reasoning_content in _extract_reasoning_from_additional_kwargs so empty strings ("") are treated the same as absent values
  • Prevents empty ReasoningContentBlock entries from cluttering content_blocks

Motivation

Some model providers (e.g. ChatTongyi, and potentially others) attach reasoning_content="" to additional_kwargs after the reasoning stage finishes. The current check reasoning_content is not None and isinstance(reasoning_content, str) passes for empty strings, producing empty reasoning blocks.

With LC_OUTPUT_VERSION=v1, this causes alternating empty reasoning and text blocks:

[{"type": "reasoning", "reasoning": ""}, {"type": "text", ...}, {"type": "reasoning", "reasoning": ""}, ...]

Changes

  • libs/core/langchain_core/messages/base.py: Added and reasoning_content truthiness check to the existing condition (1 character change)
  • libs/core/tests/unit_tests/messages/test_ai.py: Added test case for empty reasoning_content string in test_content_blocks_reasoning_extraction

Testing

  • Added a test case that verifies AIMessage with reasoning_content="" in additional_kwargs produces only a text block (no empty reasoning block)
  • Existing test for non-empty reasoning_content continues to pass

Disclaimer

AI agents (Claude Code) assisted with this contribution — investigating the issue, tracing the code path, and drafting the fix.

Fixes #36194

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +11/-0)

PR #36522: fix(core): ignore empty reasoning_content in additional_kwargs extraction

Description (problem / solution / changelog)

Summary

  • Adds a truthiness check for reasoning_content in _extract_reasoning_from_additional_kwargs so empty strings ("") are treated the same as absent values
  • Prevents empty ReasoningContentBlock entries from cluttering content_blocks

Motivation

Some model providers (e.g. ChatTongyi, and potentially others) attach reasoning_content="" to additional_kwargs after the reasoning stage finishes. The current check reasoning_content is not None and isinstance(reasoning_content, str) passes for empty strings, producing empty reasoning blocks.

With LC_OUTPUT_VERSION=v1, this causes alternating empty reasoning and text blocks:

[{"type": "reasoning", "reasoning": ""}, {"type": "text", ...}, {"type": "reasoning", "reasoning": ""}, ...]

Changes

  • libs/core/langchain_core/messages/base.py: Added and reasoning_content truthiness check to the existing condition
  • libs/core/tests/unit_tests/messages/test_ai.py: Added test case for empty reasoning_content string in test_content_blocks_reasoning_extraction

Testing

  • Added a test case that verifies AIMessage with reasoning_content="" in additional_kwargs produces only a text block (no empty reasoning block)
  • Existing test for non-empty reasoning_content continues to pass

Disclaimer

AI agents (Claude Code) assisted with this contribution.

Fixes #36194

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +11/-0)

PR #36639: fix(core): skip empty string in _extract_reasoning_from_additional_kwargs

Description (problem / solution / changelog)

Description

Fixes #36194

When a model returns additional_kwargs["reasoning_content"] = "" (empty string) — which Ollama and some OpenAI-compatible providers do when not actively reasoning — _extract_reasoning_from_additional_kwargs was creating a spurious ReasoningContentBlock with reasoning=''. This block would be prepended to content_blocks, creating noise in downstream processing and causing tools that expect non-empty reasoning to behave unexpectedly.

Root cause: the guard condition was if reasoning_content is not None, which allows empty strings through.

Fix: change to a truthiness check (if reasoning_content), which is falsy for both None and '''. This is the same pattern used throughout the codebase for optional string fields.

Changes

  • : 1-character fix in
  • : added a regression test for the empty-string case inside the existing test function

Testing

All existing tests pass; new test case explicitly asserts that reasoning_content='' does not produce a reasoning block.

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +11/-0)

PR #36665: fix(core): ignore empty reasoning_content in content block extraction

Description (problem / solution / changelog)

Summary

  • When a provider sends reasoning_content: "", skip it instead of creating a ReasoningContentBlock with empty reasoning
  • One-line condition change: is not None → truthiness check

Fixes #36194

Test plan

  • Added test case for empty string reasoning_content in test_content_blocks_reasoning_extraction
  • Existing reasoning extraction tests still pass

AI assistance (Claude Code) was used. All changes reviewed and validated by the submitting human.

🤖 Generated with Claude Code

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +1/-1)
  • libs/core/tests/unit_tests/messages/test_ai.py (modified, +9/-0)

Code Example

no need

---



---

def _extract_reasoning_from_additional_kwargs(
    message: BaseMessage,
) -> types.ReasoningContentBlock | None:
    """Extract `reasoning_content` from `additional_kwargs`.

    Handles reasoning content stored in various formats:
    - `additional_kwargs["reasoning_content"]` (string) - Ollama, DeepSeek, XAI, Groq

    Args:
        message: The message to extract reasoning from.

    Returns:
        A `ReasoningContentBlock` if reasoning content is found, None otherwise.
    """
    additional_kwargs = getattr(message, "additional_kwargs", {})

    reasoning_content = additional_kwargs.get("reasoning_content")
    if reasoning_content is not None and isinstance(reasoning_content, str):
        return {"type": "reasoning", "reasoning": reasoning_content}

    return None
RAW_BUFFERClick to expand / collapse

Checked other resources

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Related Issues / PRs

No response

Reproduction Steps / Example Code (Python)

no need

Error Message and Stack Trace (if applicable)

Description

in libs/core/langchain_core/messages/base.py, there is a function named _extract_reasoning_from_additional_kwargs:

def _extract_reasoning_from_additional_kwargs(
    message: BaseMessage,
) -> types.ReasoningContentBlock | None:
    """Extract `reasoning_content` from `additional_kwargs`.

    Handles reasoning content stored in various formats:
    - `additional_kwargs["reasoning_content"]` (string) - Ollama, DeepSeek, XAI, Groq

    Args:
        message: The message to extract reasoning from.

    Returns:
        A `ReasoningContentBlock` if reasoning content is found, None otherwise.
    """
    additional_kwargs = getattr(message, "additional_kwargs", {})

    reasoning_content = additional_kwargs.get("reasoning_content")
    if reasoning_content is not None and isinstance(reasoning_content, str):
        return {"type": "reasoning", "reasoning": reasoning_content}

    return None

If reasoning_content is an empty str '', the function will return an empty ReasoningContentBlock.

I'm using reasoning ChatModel(eg. ChatTongyi). These providers still attach reasoning_content with empty string even the reasoning stage is finished.

So if I set LC_OUTPUT_VERSION=v1, the content_blocks will be mess -- A series of alternating reasoning and content blocks, where the reasoning blocks are empty.

<img width="1044" height="301" alt="Image" src="https://github.com/user-attachments/assets/60ee73e0-6b89-4b70-aa67-84a2597d908a" />

You could certainly blame the model provider, but I believe infrastructure should provide fallback handling for edge cases.

System Info

System Information

OS: Linux OS Version: #19-Ubuntu SMP PREEMPT_DYNAMIC Fri Mar 6 14:02:58 UTC 2026 Python Version: 3.13.7 (main, Mar 3 2026, 12:19:54) [GCC 15.2.0]

Package Information

langchain_core: 1.2.13 langchain: 1.2.13 langchain_community: 0.4.1 langsmith: 0.7.3 langchain_classic: 1.0.1 langchain_openai: 1.1.10 langchain_text_splitters: 1.1.1 langgraph_api: 0.7.63 langgraph_cli: 0.4.14 langgraph_runtime_inmem: 0.26.0 langgraph_sdk: 0.3.6

Optional packages not installed

langserve

Other Dependencies

aiohttp: 3.13.3 blockbuster: 1.5.26 click: 8.3.1 cloudpickle: 3.1.2 croniter: 6.0.0 cryptography: 46.0.5 dataclasses-json: 0.6.7 grpcio: 1.78.0 grpcio-health-checking: 1.78.0 grpcio-tools: 1.78.0 httpx: 0.28.1 httpx-sse: 0.4.3 jsonpatch: 1.33 jsonschema-rs: 0.29.1 langgraph: 1.1.3 langgraph-checkpoint: 4.0.0 numpy: 2.4.2 openai: 2.24.0 opentelemetry-api: 1.39.1 opentelemetry-exporter-otlp-proto-http: 1.39.1 opentelemetry-sdk: 1.39.1 orjson: 3.11.7 packaging: 26.0 protobuf: 6.33.5 pydantic: 2.12.5 pydantic-settings: 2.13.1 pyjwt: 2.11.0 pytest: 9.0.2 python-dotenv: 1.2.1 pyyaml: 6.0.3 PyYAML: 6.0.3 requests: 2.32.5 requests-toolbelt: 1.0.0 sqlalchemy: 2.0.47 SQLAlchemy: 2.0.47 sse-starlette: 2.1.3 starlette: 0.52.1 structlog: 25.5.0 tenacity: 9.1.4 tiktoken: 0.12.0 truststore: 0.10.4 typing-extensions: 4.15.0 uuid-utils: 0.14.0 uvicorn: 0.41.0 watchfiles: 1.1.1 xxhash: 3.6.0 zstandard: 0.25.0

extent analysis

Fix Plan

To address the issue of empty ReasoningContentBlock instances being returned when reasoning_content is an empty string, we need to modify the _extract_reasoning_from_additional_kwargs function to handle this edge case. Here are the steps:

  • Modify the condition to check for empty strings
  • Return None when reasoning_content is an empty string

Code Changes

def _extract_reasoning_from_additional_kwargs(
    message: BaseMessage,
) -> types.ReasoningContentBlock | None:
    """Extract `reasoning_content` from `additional_kwargs`.

    Handles reasoning content stored in various formats:
    - `additional_kwargs["reasoning_content"]` (string) - Ollama, DeepSeek, XAI, Groq

    Args:
        message: The message to extract reasoning from.

    Returns:
        A `ReasoningContentBlock` if reasoning content is found, None otherwise.
    """
    additional_kwargs = getattr(message, "additional_kwargs", {})

    reasoning_content = additional_kwargs.get("reasoning_content")
    if reasoning_content is not None and isinstance(reasoning_content, str) and reasoning_content.strip() != "":
        return {"type": "reasoning", "reasoning": reasoning_content}

    return None

Verification

To verify that the fix worked, you can test the _extract_reasoning_from_additional_kwargs function with different inputs, including empty strings. The function should return None when reasoning_content is an empty string.

Extra Tips

  • Make sure to test the function thoroughly to ensure it handles all possible edge cases.
  • Consider adding additional logging or error handling to help diagnose any issues that may arise.
  • If you're using a version control system, be sure to commit the changes and update the relevant documentation.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

langchain - ✅(Solved) Fix _extract_reasoning_from_additional_kwargs should ignore empty strings [16 pull requests, 14 comments, 12 participants]