open-telemetry / open-telemetry/opentelemetry-python-genai
genai-openai: multimodal content-part arrays are dropped from captured messages (including the text part)
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-openai1.1b0 (also reproduced on currentmain)opentelemetry-util-genai1.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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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