open-telemetry / open-telemetry/opentelemetry-python-genai

genai-openai: multimodal content-part arrays are dropped from captured messages (including the text part)

Open Beginner friendly
#521 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
39
Forks
63
Avg merge
1d 15h
Merged PRs (30d)
175

Description

Describe your environment

  • opentelemetry-instrumentation-genai-openai 1.1b0 (also reproduced on current main)
  • opentelemetry-util-genai 1.1b0
  • openai SDK 2.x, Python 3.12
  • Content capture opted in: OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=SPAN_ONLY

What happened?

When a chat completion request uses the OpenAI content-part array form for multimodal messages, e.g.

messages = [
    {"role": "user", "content": [
        {"type": "text", "text": "What is in this image?"},
        {"type": "image_url", "image_url": {"url": "https://example.com/cat.png"}},
    ]},
]

the captured gen_ai.input.messages records the user message with empty parts — the text part is silently dropped along with the image part:

[{"role":"user","parts":[]}]

What did you expect to see?

At minimum the text part should be preserved. Ideally non-text parts are represented too (there is already a GenericPart type in opentelemetry-util-genai documented for exactly this: provider-specific part types that don't map to the standard semconv part types).

Root cause

_prepare_input_messages in instrumentation/opentelemetry-instrumentation-genai-openai/src/opentelemetry/instrumentation/genai/openai/utils.py only appends a part when _is_text_part(content) is true, and _is_text_part accepts only str or an iterable of str:

def _is_text_part(content: Any) -> bool:
    return isinstance(content, str) or (
        isinstance(content, Iterable)
        and all(isinstance(part, str) for part in content)
    )

A content-part array (list of dicts / SDK part models) fails this check, and there is no fallback branch, so the message ends up with no parts at all.

Additional context

This is a regression in observability coverage for anyone migrating from third-party OpenAI instrumentations, which serialized the full content-part array. I have a fix ready (map text parts to TextPart, preserve other part types as GenericPart) and will open a PR referencing this issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in instrumentation/opentelemetry-instrumentation-genai-openai/src/opentelemetry/instrumentation/genai/openai/utils.py, focusing on _prepare_input_messages and _is_text_part, then inspect the GenericPart type in opentelemetry-util-genai. Done means content-part arrays no longer produce empty parts: text content is captured and non-text content is represented where supported.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
observability
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
78/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.