OpenInference TOOL spans drop tool record on non-dict tool args and leave tools_called output empty (Strands parity)
Repro
Two symmetric gaps vs deepeval/integrations/strands/instrumentator.py (hardened in #3103, mirrored to AgentCore in #3262), both in deepeval/integrations/openinference/instrumentator.py:
A. Non-dict tool args raise / poison the span. _extract_tool_call_from_tool_span in the OpenInference interceptor lacks the or {} + non-dict wrap that Strands has:
from deepeval.integrations.openinference.instrumentator import _extract_tool_call_from_tool_span
class S: attributes = {"openinference.span.kind": "TOOL", "tool.name": "t", "tool.parameters": "[1, 2, 3]"}
_extract_tool_call_from_tool_span(S()) # ValidationError: input_parameters should be a valid dictionary
# same input on Strands returns ToolCall(input_parameters={"input": [1, 2, 3]})
# 'null' yields input_parameters=None on OpenInference vs {} on StrandsThe raise is swallowed by on_end's try/except (debug log only), so the span loses its confident.span.tools_called record while staying otherwise silent (verified: a TOOL span with tool.parameters='[1, 2, 3]' and output.value set keeps SPAN_OUTPUT but records no tools_called).
B. tools_called[0].output left empty on TOOL spans. The OpenInference TOOL branch never writes back tc.output; Strands sets it from the tool output. A TOOL span with output.value set gets SPAN_OUTPUT but tools_called[0].output=None on OpenInference, while Strands records both.
Root cause
#3103 hardened only strands/instrumentator.py (its OpenInference hunk was a one-line setdefault guard); the sister function and TOOL-output block in openinference/instrumentator.py were left behind.
Options
a) Mirror the Strands hardening into OpenInference (or {} + non-dict wrap + tc.output write-back from output_text) — smallest, direction pre-approved by the #3103 merge.
b) Extract a shared helper across the three OTel interceptors — larger refactor, more review surface.
c) Do nothing — accept the silent tool-record loss on OpenInference TOOL spans.
I prefer (a) and will open a PR with a regression test in a new tests/test_integrations/test_openinference/test_span_interceptor.py (no such suite exists yet; modeled on the agentcore/strands harness with synthetic MagicMock spans, no instrumentor package needed).
Source: confident-ai/deepeval