langchain - ✅(Solved) Fix Improve BaseMessage.pretty_repr for multimodal content [5 pull requests, 2 comments, 2 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#36365Fetched 2026-04-08 01:52:51
View on GitHub
Comments
2
Participants
2
Timeline
11
Reactions
0
Timeline (top)
cross-referenced ×4commented ×2labeled ×2referenced ×2

Currently, the pretty_repr method in BaseMessage (used for pretty_print()) only handles string content fully. When a message contains a list of multimodal content blocks (e.g., text and image_url), it defaults to printing the raw list and dictionaries, which is difficult for developers to read during debugging.

I've already developed a fix that adds human-readable formatting for text, image, audio, video, and reasoning blocks.

Error Message

Error Message and Stack Trace (if applicable)

Root Cause

Currently, the pretty_repr method in BaseMessage (used for pretty_print()) only handles string content fully. When a message contains a list of multimodal content blocks (e.g., text and image_url), it defaults to printing the raw list and dictionaries, which is difficult for developers to read during debugging.

I've already developed a fix that adds human-readable formatting for text, image, audio, video, and reasoning blocks.

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.

PR fix notes

PR #36364: fix(core): improve message pretty_repr and cleanup output parser

Description (problem / solution / changelog)

Resolves #36365

Description

This PR addresses two "Developer Experience" (DX) improvements in langchain-core:

  1. Multimodal pretty_repr: Previously, the pretty_repr method used by pretty_print() printed multimodal content (images, audio, video, reasoning) as raw Python dictionaries. I've updated it to iterate through content blocks and format them into a human-readable representation.
  2. Standardization: Renamed the parameter completion to text in OutputParser.parse_with_prompt, resolving a long-standing legacy TODO.

Tagging & Labels

#core #fix #dx #new-contributor

Verification

Manually verified HumanMessage.pretty_print() with a list of multimodal content blocks (images and reasoning). The output is now formatted cleanly instead of showing raw dictionaries.

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +39/-2)
  • libs/core/langchain_core/output_parsers/base.py (modified, +3/-4)

PR #36370: fix(core): improve BaseMessage.pretty_repr for multimodal content

Description (problem / solution / changelog)

Description

Fixes #36365

Currently, BaseMessage.pretty_repr only handles string content elegantly. When a message contains a list of multimodal content blocks, it defaults to printing the raw python dict payloads, which is difficult for developers to parse during debugging/logging.

This PR iterates through the dicts and cleanly formats textual representations for text, reasoning, image, image_url, video, and audio block types instead.

Testing

Added three test groups under tests/unit_tests/messages/test_base.py to ensure pretty_repr accurately handles combinations of list[str | dict] layouts without regressions to standard string payloads.


Disclaimer: This contribution was developed with assistance from an AI coding agent.

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +55/-2)
  • libs/core/tests/unit_tests/messages/test_base.py (added, +46/-0)

PR #36372: Improve BaseMessage.pretty_repr for multimodal content

Description (problem / solution / changelog)

Description

Currently, the pretty_repr method in BaseMessage (used for pretty_print()) only handles string content fully. When a message contains a list of multimodal content blocks (e.g., text and image_url), it defaults to printing the raw list and dictionaries, which is difficult for developers to read during debugging.

Changes

This PR improves the pretty_repr method to handle multimodal content blocks:

  • Added _format_content_for_pretty_repr helper method to handle list content
  • Added _format_content_block method to format individual content blocks with human-readable output
  • Support for the following block types:
    • text - Displays the text content directly
    • reasoning - Formats with <thinking> tags
    • image / image_url - Shows URL, file_id, or base64 indicator with mime_type
    • audio - Shows URL, file_id, or base64 indicator with mime_type
    • video - Shows URL, file_id, or base64 indicator with mime_type
    • file - Shows URL, file_id, or base64 indicator with mime_type
    • text-plain - Shows truncated text content
    • tool_call - Shows tool name, ID, and arguments
    • non_standard - Shows the provider-specific value

Before

After

Backward Compatibility

  • String content continues to work exactly as before
  • List content with string elements is handled correctly
  • Unknown block types fall back to showing the block type and content

Fixes #36365

Changed files

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

PR #35402: feat(core): enrich pretty_repr for multimodal content blocks

Description (problem / solution / changelog)

Summary

  • Fixes pretty_repr() rendering raw Python list.__repr__() when content is a list (original fix from #34875)
  • Extends rendering to produce human-readable output for multimodal block types (fixes #36365):
    • image_url (OpenAI legacy format): [Image: <url>, detail=<detail>]
    • image / video / audio / file: shows URL, file_id, or base64 summary (base64 (N bytes, mime_type))
    • reasoning: inline text prefixed with [Reasoning]:
    • text-plain: rendered as plain text (same as text)
    • Unknown block types still fall back to [type] placeholder

A new private _render_block helper handles single-block dispatch, keeping _render_content_blocks clean.

Why this approach

The existing PR already handled string/text blocks with a _render_content_blocks function. Extending it with a _render_block dispatcher is the minimal change needed — no new public API, no breaking changes, and each block type's rendering is straightforward pattern-matching on the type key.

Areas requiring careful review

  • The image_url branch handles OpenAI's legacy nested format {"type": "image_url", "image_url": {"url": "..."}} — distinct from the LangChain v1 image block with a top-level url key
  • The detail field on image_url blocks is forwarded in the rendered string (e.g. [Image: https://..., detail=high])
  • reasoning blocks show their text inline; empty/missing reasoning field renders as [Reasoning]

Test plan

  • All 24 unit tests pass (pytest tests/unit_tests/messages/test_base.py)
  • Existing tests updated to expect [Image: <url>] instead of [image_url]
  • New tests cover: image_url with/without detail, image with url/file_id/base64/no-source, audio, video, file, reasoning, text-plain
  • ruff check passes

Note: This PR was developed with AI assistance (Claude Sonnet 4.6 via Claude Code).

🤖 Generated with Claude Code

Changed files

  • .github/ISSUE_TEMPLATE/bug-report.yml (modified, +2/-0)
  • .github/ISSUE_TEMPLATE/feature-request.yml (modified, +4/-0)
  • .github/PULL_REQUEST_TEMPLATE.md (modified, +8/-3)
  • .github/actions/uv_setup/action.yml (modified, +1/-1)
  • .github/dependabot.yml (modified, +40/-12)
  • .github/images/logo-dark.svg (modified, +6/-25)
  • .github/images/logo-light.svg (modified, +6/-25)
  • .github/pr-file-labeler.yml (removed, +0/-128)
  • .github/scripts/check_diff.py (modified, +15/-2)
  • .github/scripts/get_min_versions.py (modified, +1/-1)
  • .github/scripts/pr-labeler-config.json (added, +84/-0)
  • .github/scripts/pr-labeler.js (added, +271/-0)
  • .github/workflows/_refresh_model_profiles.yml (added, +202/-0)
  • .github/workflows/_release.yml (modified, +14/-10)
  • .github/workflows/_test.yml (modified, +2/-2)
  • .github/workflows/auto-label-by-package.yml (modified, +3/-0)
  • .github/workflows/check_diffs.yml (modified, +1/-67)
  • .github/workflows/codspeed.yml (added, +109/-0)
  • .github/workflows/integration_tests.yml (modified, +2/-2)
  • .github/workflows/pr_labeler.yml (added, +212/-0)
  • .github/workflows/pr_labeler_backfill.yml (added, +130/-0)
  • .github/workflows/pr_labeler_file.yml (removed, +0/-28)
  • .github/workflows/pr_labeler_title.yml (removed, +0/-44)
  • .github/workflows/pr_lint.yml (modified, +12/-1)
  • .github/workflows/refresh_model_profiles.yml (modified, +25/-73)
  • .github/workflows/require_issue_link.yml (added, +308/-0)
  • .github/workflows/tag-external-contributions.yml (removed, +0/-148)
  • .github/workflows/tag-external-issues.yml (added, +205/-0)
  • .github/workflows/v03_api_doc_build.yml (modified, +5/-2)
  • .mcp.json (modified, +5/-1)
  • AGENTS.md (modified, +5/-5)
  • CLAUDE.md (modified, +5/-5)
  • CONTRIBUTING.md (removed, +0/-15)
  • README.md (modified, +43/-35)
  • libs/README.md (modified, +4/-1)
  • libs/core/Makefile (modified, +11/-7)
  • libs/core/langchain_core/_api/deprecation.py (modified, +1/-1)
  • libs/core/langchain_core/_security/_ssrf_protection.py (modified, +48/-11)
  • libs/core/langchain_core/cross_encoders.py (added, +18/-0)
  • libs/core/langchain_core/language_models/base.py (modified, +18/-0)
  • libs/core/langchain_core/language_models/chat_models.py (modified, +30/-4)
  • libs/core/langchain_core/language_models/llms.py (modified, +6/-6)
  • libs/core/langchain_core/messages/base.py (modified, +70/-2)
  • libs/core/langchain_core/messages/block_translators/openai.py (modified, +50/-0)
  • libs/core/langchain_core/outputs/chat_result.py (modified, +3/-2)
  • libs/core/langchain_core/outputs/llm_result.py (modified, +3/-2)
  • libs/core/langchain_core/runnables/graph_mermaid.py (modified, +5/-0)
  • libs/core/langchain_core/tracers/base.py (modified, +22/-3)
  • libs/core/langchain_core/tracers/langchain.py (modified, +30/-7)
  • libs/core/langchain_core/utils/function_calling.py (modified, +2/-2)
  • libs/core/langchain_core/utils/pydantic.py (modified, +6/-1)
  • libs/core/langchain_core/version.py (modified, +1/-1)
  • libs/core/pyproject.toml (modified, +1/-1)
  • libs/core/scripts/lint_imports.sh (modified, +3/-2)
  • libs/core/tests/unit_tests/language_models/chat_models/test_base.py (modified, +147/-0)
  • libs/core/tests/unit_tests/messages/test_base.py (added, +206/-0)
  • libs/core/tests/unit_tests/test_ssrf_protection.py (modified, +19/-0)
  • libs/core/tests/unit_tests/test_tools.py (modified, +17/-0)
  • libs/core/tests/unit_tests/tracers/test_langchain.py (modified, +149/-21)
  • libs/core/tests/unit_tests/utils/test_pydantic.py (modified, +19/-0)
  • libs/core/uv.lock (modified, +116/-118)
  • libs/langchain/Makefile (modified, +3/-2)
  • libs/langchain/langchain_classic/agents/mrkl/output_parser.py (modified, +1/-3)
  • libs/langchain/langchain_classic/agents/output_parsers/react_single_input.py (modified, +1/-3)
  • libs/langchain/langchain_classic/retrievers/document_compressors/cross_encoder.py (modified, +2/-15)
  • libs/langchain/pyproject.toml (modified, +4/-4)
  • libs/langchain/tests/README.md (removed, +0/-3)
  • libs/langchain/tests/unit_tests/agents/output_parsers/test_react_single_input.py (modified, +32/-0)
  • libs/langchain/tests/unit_tests/agents/test_mrkl_output_parser.py (modified, +30/-0)
  • libs/langchain/tests/unit_tests/test_pytest_config.py (modified, +1/-1)
  • libs/langchain/uv.lock (modified, +343/-292)
  • libs/langchain_v1/Makefile (modified, +12/-8)
  • libs/langchain_v1/langchain/__init__.py (modified, +1/-1)
  • libs/langchain_v1/langchain/agents/factory.py (modified, +43/-7)
  • libs/langchain_v1/langchain/agents/middleware/__init__.py (modified, +3/-0)
  • libs/langchain_v1/langchain/agents/middleware/_redaction.py (modified, +19/-1)
  • libs/langchain_v1/langchain/chat_models/base.py (modified, +2/-0)
  • libs/langchain_v1/pyproject.toml (modified, +2/-2)
  • libs/langchain_v1/tests/cassettes/test_inference_to_native_output[True].yaml.gz (modified, +0/-0)
  • libs/langchain_v1/tests/cassettes/test_inference_to_tool_output[True].yaml.gz (modified, +0/-0)
  • libs/langchain_v1/tests/cassettes/test_strict_mode[True].yaml.gz (modified, +0/-0)
  • libs/langchain_v1/tests/unit_tests/agents/middleware/implementations/test_pii.py (modified, +52/-0)
  • libs/langchain_v1/tests/unit_tests/agents/test_response_format.py (modified, +1/-1)
  • libs/langchain_v1/tests/unit_tests/chat_models/test_chat_models.py (modified, +1/-1)
  • libs/langchain_v1/tests/unit_tests/conftest.py (modified, +20/-0)
  • libs/langchain_v1/uv.lock (modified, +280/-236)
  • libs/model-profiles/Makefile (modified, +46/-9)
  • libs/model-profiles/langchain_model_profiles/cli.py (modified, +7/-0)
  • libs/model-profiles/scripts/lint_imports.sh (added, +18/-0)
  • libs/model-profiles/tests/unit_tests/test_cli.py (modified, +64/-0)
  • libs/model-profiles/uv.lock (modified, +108/-108)
  • libs/partners/anthropic/Makefile (modified, +13/-8)
  • libs/partners/anthropic/langchain_anthropic/_version.py (modified, +1/-1)
  • libs/partners/anthropic/langchain_anthropic/chat_models.py (modified, +215/-278)
  • libs/partners/anthropic/langchain_anthropic/data/_profiles.py (modified, +140/-2)
  • libs/partners/anthropic/langchain_anthropic/middleware/prompt_caching.py (modified, +121/-13)
  • libs/partners/anthropic/pyproject.toml (modified, +3/-3)
  • libs/partners/anthropic/scripts/lint_imports.sh (modified, +3/-2)
  • libs/partners/anthropic/tests/integration_tests/test_chat_models.py (modified, +29/-12)
  • libs/partners/anthropic/tests/unit_tests/middleware/test_prompt_caching.py (modified, +233/-200)

PR #36736: fix(core): improve BaseMessage.pretty_repr for multimodal content

Description (problem / solution / changelog)

Description

This PR improves BaseMessage.pretty_repr() to handle multimodal content blocks with human-readable output instead of falling back to raw Python dict representations.

Before:

================================ Human Message =================================

[{'type': 'image_url', 'url': 'https://example.com/image.png'}, {'type': 'text', 'text': 'Describe this image'}]

After:

================================ Human Message =================================

[image: https://example.com/image.png]
Describe this image

Changes

  • Added _format_content() helper function that handles different content block types:
    • text blocks: rendered as plain text
    • image/image_url blocks: rendered as [image: url] or [image: base64 media_type]
    • audio blocks: rendered as [audio: base64 media_type] or [audio]
    • video blocks: rendered as [video: base64 media_type] or [video]
    • reasoning blocks: rendered as [reasoning: text] or [reasoning]
    • Unknown block types: fall back to str(block)
  • Updated BaseMessage.pretty_repr() to use _format_content() instead of directly printing self.content
  • Removed the # TODO: handle non-string content. comment since this is now handled
  • Added 17 unit tests covering all content block types

Testing

  • Added tests/unit_tests/messages/test_pretty_repr.py with 17 test cases
  • All existing 215 message tests still pass
  • ruff check passes with no errors

Fixes #36365


🤖 Generated with Claude Code

Changed files

  • libs/core/langchain_core/messages/base.py (modified, +66/-2)
  • libs/core/tests/unit_tests/messages/test_pretty_repr.py (added, +173/-0)

Code Example

from langchain_core.messages import HumanMessage
msg = HumanMessage(content=[
    {"type": "text", "text": "What is in this image?"},
    {"type": "image_url", "image_url": {"url": "https://example.com/image.png"}}
])
# Currently outputs raw Python dictionaries
print("Current Output:")
msg.pretty_print()

---
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)

from langchain_core.messages import HumanMessage
msg = HumanMessage(content=[
    {"type": "text", "text": "What is in this image?"},
    {"type": "image_url", "image_url": {"url": "https://example.com/image.png"}}
])
# Currently outputs raw Python dictionaries
print("Current Output:")
msg.pretty_print()

Error Message and Stack Trace (if applicable)

Description

Currently, the pretty_repr method in BaseMessage (used for pretty_print()) only handles string content fully. When a message contains a list of multimodal content blocks (e.g., text and image_url), it defaults to printing the raw list and dictionaries, which is difficult for developers to read during debugging.

I've already developed a fix that adds human-readable formatting for text, image, audio, video, and reasoning blocks.

Code Example

from langchain_core.messages import HumanMessage

# Current behavior outputs raw dict:
msg = HumanMessage(content=[{"type": "text", "text": "Hi"}, {"type": "image_url", "image_url": {"url": "..."}}])
msg.pretty_print() 
# -> Output: [{'type': 'text', ...}, ...]


### System Info

System Information
------------------
> OS:  Windows
> OS Version:  10.0.26200
> Python Version:  3.13.6 (tags/v3.13.6:4e66535, Aug  6 2025, 14:36:00) [MSC v.1944 64 bit (AMD64)]

Package Information
-------------------

Optional packages not installed
-------------------------------
> langsmith
> deepagents
> deepagents-cli

extent analysis

Fix Plan

To improve the readability of the pretty_print() method for multimodal content blocks, we need to modify the pretty_repr method in BaseMessage to handle different content types.

Here are the steps:

  • Update the pretty_repr method to check the type of each content block.
  • Add specific formatting for text, image, audio, video, and reasoning blocks.
  • Use recursion to handle nested content blocks.

Code Changes

class BaseMessage:
    # ...

    def pretty_repr(self):
        if isinstance(self.content, str):
            return self.content
        elif isinstance(self.content, list):
            formatted_content = []
            for block in self.content:
                if block["type"] == "text":
                    formatted_content.append(f"Text: {block['text']}")
                elif block["type"] == "image_url":
                    formatted_content.append(f"Image: {block['image_url']['url']}")
                # Add more conditions for other content types
                else:
                    formatted_content.append(str(block))
            return "\n".join(formatted_content)
        else:
            return str(self.content)

    def pretty_print(self):
        print(self.pretty_repr())

Verification

To verify the fix, create a HumanMessage with multimodal content and call the pretty_print() method:

msg = HumanMessage(content=[
    {"type": "text", "text": "What is in this image?"},
    {"type": "image_url", "image_url": {"url": "https://example.com/image.png"}}
])
msg.pretty_print()

This should output:

Text: What is in this image?
Image: https://example.com/image.png

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 Improve BaseMessage.pretty_repr for multimodal content [5 pull requests, 2 comments, 2 participants]