Skip to content

(Openinference Migration: Langchain): Capture multimodal image content (OpenAI image_url and Anthropic image blocks) as Blob/Uri message parts. - #296

Open
rads-1996 wants to merge 9 commits into
open-telemetry:mainfrom
rads-1996:langchain-multimodal-images
Open

(Openinference Migration: Langchain): Capture multimodal image content (OpenAI image_url and Anthropic image blocks) as Blob/Uri message parts.#296
rads-1996 wants to merge 9 commits into
open-telemetry:mainfrom
rads-1996:langchain-multimodal-images

Conversation

@rads-1996

@rads-1996 rads-1996 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Description

Part of the langchain migration PRs - #272

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How has this been tested?

Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. List any relevant details for your test
configuration.

  • uv run tox -e py312-test-instrumentation-genai-langchain -- -q
  • uv run tox -e py312-test-instrumentation-genai-langchain-conformance -- -q
  • uv run --python 3.12 tox -e lint-instrumentation-genai-langchain

Checklist

See CONTRIBUTING.md
for the style guide, changelog guidance, and more.

  • Followed the style guidelines of this project
  • Changelog updated if the change requires an entry
  • Unit tests added
  • Documentation updated

@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Jul 21, 2026

Copy link
Copy Markdown

Pull request dashboard status

Waiting on the author · refreshed 2026-08-07 17:26 UTC

Two things need attention:

  • Required checks are failing — investigate the failures.
  • 4 review items — respond to each (e.g. link a commit, explain why not, ask a follow-up):
    • Inline threads: 1, 2, 3
    • Top-level threads: 4
Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Should this be with reviewers? Comment /dashboard route:reviewers to route it to them.
  • Anything wrong — including the routing? Report it with what you expected; it helps us improve the dashboard.

@rads-1996
rads-1996 marked this pull request as ready for review July 21, 2026 21:18
@rads-1996
rads-1996 requested a review from a team as a code owner July 21, 2026 21:18
Copilot AI review requested due to automatic review settings July 21, 2026 21:18
@rads-1996
rads-1996 marked this pull request as draft July 21, 2026 21:18

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

Pull request overview

Adds multimodal image support to the LangChain GenAI instrumentation by converting OpenAI image_url and Anthropic image blocks into Blob / Uri message parts, and extends the unit test suite to validate the new parsing behavior.

Changes:

  • Add multimodal image parsing helpers (_media_part, _image_from_url) to convert LangChain image blocks into Blob/Uri parts.
  • Extend callback-handler tests to cover OpenAI and Anthropic image content shapes (including data URIs and base64 sources).
  • Add a changelog fragment documenting the new capability.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
instrumentation/opentelemetry-instrumentation-genai-langchain/src/opentelemetry/instrumentation/genai/langchain/utils.py Adds image block parsing and base64 decoding to emit Blob/Uri message parts.
instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py Adds unit tests for data-URI, HTTP-URI, and Anthropic image/source parsing into message parts.
instrumentation/opentelemetry-instrumentation-genai-langchain/.changelog/296.added Documents the new multimodal image capture support.

@rads-1996
rads-1996 force-pushed the langchain-multimodal-images branch 3 times, most recently from aa26c4f to ed3949d Compare July 23, 2026 21:41
@rads-1996
rads-1996 force-pushed the langchain-multimodal-images branch 2 times, most recently from d72b64e to 501725d Compare July 30, 2026 16:54
@rads-1996
rads-1996 marked this pull request as ready for review July 30, 2026 17:20
@AgentGymLeader

Copy link
Copy Markdown
Contributor

@rads-1996 One thing I wanted to check on ordering. In on_chat_model_start, to_input_messages() runs before anything consults TelemetryHandler.should_capture_content(), so with the default NO_CONTENT the data URI and Anthropic base64 images still get decoded to bytes via _decode_base64.

That means a config that doesn't record content still copies large images. If that isn't intended, either skip the media decode when capture is off, or add a test asserting _decode_base64 isn't called under NO_CONTENT.

If the parse is meant to run unconditionally so the completion hook has something to work with, that's fine, I just wanted to confirm that's the assumption.

@rads-1996

Copy link
Copy Markdown
Contributor Author

@rads-1996 One thing I wanted to check on ordering. In on_chat_model_start, to_input_messages() runs before anything consults TelemetryHandler.should_capture_content(), so with the default NO_CONTENT the data URI and Anthropic base64 images still get decoded to bytes via _decode_base64.

That means a config that doesn't record content still copies large images. If that isn't intended, either skip the media decode when capture is off, or add a test asserting _decode_base64 isn't called under NO_CONTENT.

If the parse is meant to run unconditionally so the completion hook has something to work with, that's fine, I just wanted to confirm that's the assumption.

@AgentGymLeader Thanks for the callout. I have gated decoding and will skipped when the content capture mode is NO_CONTENT.

@rads-1996
rads-1996 force-pushed the langchain-multimodal-images branch from d9fe119 to a61da13 Compare August 4, 2026 22:16
@rads-1996
rads-1996 force-pushed the langchain-multimodal-images branch from 9ae1c71 to 0c19a81 Compare August 5, 2026 23:05

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can you please add conformance test? thanks!

@@ -0,0 +1 @@
(Openinference Migration: Langchain) - Capture multimodal image content (OpenAI ``image_url`` and Anthropic ``image`` blocks) as ``Blob``/``Uri`` message parts.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please update openai and anthropic instrumentations to hahve feature parity. Ok to do in a separate PR. Thanks!

def _decode_base64(data: str) -> bytes | None:
# Skip the decode entirely when message content is not being captured;
# the resulting bytes would never be emitted under ``NO_CONTENT``.
if get_content_capturing_mode() is ContentCapturingMode.NO_CONTENT:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this one actually checks env var and is now used on a hot path, please use handler.should_capture_content() somewhere up the stack and don't call this method when content capturing is disabled

return None


def _image_from_url(url: str) -> MessagePart | None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should we have this whole method in utils? the data:// is pretty generic approach, nothing specific to openai https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah make sense, I can then reuse it for openai and anthropic.

gen_ai_attributes as GenAIAttributes,
)
from opentelemetry.util.genai.types import (
Blob,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it's the first time we're using this type in this repo, mind renaming it to BlobPart? Same with Uri, can we rename it to UriPart - that's how they are called in semconv and it seems we're inconsistent - https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/non-normative/models.py

@rads-1996 rads-1996 Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sounds good. I will make changes in the utils. I believe the same change is needed for some other data classes as well. I think I will make a separate PR to address that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@lmolkova Blob and Uri both have previously been used in other instrumentations, Blob has been used in the anthropic and google-genai instrumentation and Uri in google-genai. I have a PR out which aligns the names with semconv - #365. Should we have backward compatibility aliases for downstream users with deprecation messages?

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.

5 participants