(Openinference Migration: Langchain): Capture multimodal image content (OpenAI image_url and Anthropic image blocks) as Blob/Uri message parts. - #296
Conversation
Pull request dashboard statusMerged · refreshed 2026-09-01 01:38 UTC Status above doesn't look right?
|
There was a problem hiding this comment.
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 intoBlob/Uriparts. - 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. |
aa26c4f to
ed3949d
Compare
d72b64e to
501725d
Compare
|
@rads-1996 One thing I wanted to check on ordering. In 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 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 |
9cd26fe to
1b98bc4
Compare
aaea6b5 to
d9fe119
Compare
9ae1c71 to
0c19a81
Compare
eb9e373 to
c57a7b7
Compare
f0b8684 to
837f846
Compare
…mage` blocks) as `Blob`/`Uri` message parts.
453d73b to
9719633
Compare
….changelog/296.added
Chat Completions `content` may be a plain string or a list of typed
content parts. `_prepare_input_messages` and `_prepare_output_messages`
gated content on `_is_text_part`, which only accepts `str` or an iterable
of `str`, so the list form was dropped entirely and such messages were
recorded in `gen_ai.input.messages` as `{"role": ..., "parts": []}`.
Convert each part instead, reusing the shared helpers from open-telemetry#296:
- `{"type": "text"}` -> `TextPart` (one per part)
- `{"type": "image_url"}` -> `image_from_url()`, so a `data:` URL becomes
a `BlobPart` carrying its mime type instead of a multi-MB `UriPart`
- `{"type": "input_audio"}` -> `BlobPart` via `decode_base64()`, which
validates the payload; `mp3` maps to `audio/mpeg`, not `audio/mp3`
- `{"type": "file"}` -> `FilePart` for `file_id`, or a document
`BlobPart` for the inline `file_data` data: URL
- `{"type": "refusal"}` -> `TextPart`
- unrecognized part types cost that part, not the whole message
`_prepare_output_messages` additionally records `choice.message.refusal`,
which a refused completion carries instead of `content`.
Plain-string content behaves exactly as before. A list of plain strings
now yields one part per string (previously the whole list was stringified
into one part). Mapping-shaped content is guarded, since iterating it
would have produced a text part per key.
Coverage runs through the instrumentation itself: three cassette-based
chat completions (multi-turn text parts, mixed multimodal parts, and a
refusal) assert the exported span's message attributes; a small unit
module pins the shapes that are awkward to record.
Implemented with Claude (Anthropic) assistance.
Fixes open-telemetry#357
Description
Part of the langchain migration PRs - #272
Type of change
Please delete options that are not relevant.
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.
Checklist
See CONTRIBUTING.md
for the style guide, changelog guidance, and more.