Skip to content

馃悰 Bug Report: mistralai event mode loses prompt events when a ToolMessage is present聽#4523

Description

@chrikrah

In event mode, a ToolMessage in the conversation costs the mistralai instrumentation its prompt events.
_emit_message_events at
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py:398
binds role and content in two branches and has no else:

for message in messages:
    if isinstance(message, (UserMessage, AssistantMessage, SystemMessage)):
        role = message.role
        content = message.content
    elif isinstance(message, dict):
        role = message.get("role", "unknown")
        content = message.get("content")
    emit_event(MessageEvent(content=content, role=role or "unknown"), event_logger)

mistralai.models exports ToolMessage alongside those three, so a tool result matches neither branch.

Wrong behaviour, measured through an instrumented chat.complete with the repository's own
instrument_with_content and log_exporter fixtures, Python 3.10.21, mistralai 1.x, at 332599a:

$ python -m pytest tests/test_zz_toolmsg_probe.py -q -s
PROBE tool-first prompt events: []
PROBE swallowed: UnboundLocalError: local variable 'content' referenced before assignment
PROBE user-first prompt events: [('gen_ai.user.message', 'what is 2+2?'), ('gen_ai.user.message', 'what is 2+2?')]

A tool result after a user message repeats that user message, because the loop variables survive the
iteration, and the tool content never appears. A tool result first aborts the loop on iteration one, so the
genuine user prompt is lost too, and @dont_throw swallows it at debug level. _emit_choice_events is a
separate call from _handle_response at line 473, so the response event still lands: the span shows an
answer with no question in it.

Expected: a ToolMessage emits its own event with role="tool", and a message type nobody anticipated emits
one event carrying whatever role it has, never a copy of its predecessor.

Every claim above came from that run plus reading the module at 332599a. On Python 3.11 and later the same
failure reads "cannot access local variable 'content'". I did not check the other instrumentations in the
monorepo for the same pattern. The only ToolMessage hit in the tracker is the closed LangChain report #2938,
and the legacy-attribute path has the mirror gap, which #4509 is currently narrowing for dicts.

Would you take ToolMessage in the tuple, or an else that reads getattr(message, "role", None) and
getattr(message, "content", None) so the next SDK type cannot do this again?

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions