diff --git a/python/instrumentation/openinference-instrumentation-openai-agents/src/openinference/instrumentation/openai_agents/_processor.py b/python/instrumentation/openinference-instrumentation-openai-agents/src/openinference/instrumentation/openai_agents/_processor.py index 60a56aa706..47406aa018 100644 --- a/python/instrumentation/openinference-instrumentation-openai-agents/src/openinference/instrumentation/openai_agents/_processor.py +++ b/python/instrumentation/openinference-instrumentation-openai-agents/src/openinference/instrumentation/openai_agents/_processor.py @@ -350,7 +350,23 @@ def _get_attributes_from_response_custom_tool_call_output_param( if (call_id := obj.get("call_id")) is not None: yield f"{prefix}{MessageAttributes.MESSAGE_TOOL_CALL_ID}", call_id if (output := obj.get("output")) is not None: - yield f"{prefix}{MessageAttributes.MESSAGE_CONTENT}", output + if isinstance(output, str): + yield f"{prefix}{MESSAGE_CONTENT}", output + elif isinstance(output, list): + for i, item in enumerate(output): + if item["type"] == "input_text": + yield ( + f"{prefix}{MESSAGE_CONTENTS}.{i}.{MESSAGE_CONTENT_TEXT}", + item["text"] or "", + ) + elif item["type"] == "input_image": + # TODO: handle the input image type for the tool input + pass + elif item["type"] == "input_file": + # TODO: handle the input file type for the tool input + pass + elif TYPE_CHECKING: + assert_never(item["type"]) def _get_attributes_from_function_call_output( @@ -359,7 +375,24 @@ def _get_attributes_from_function_call_output( ) -> Iterator[tuple[str, AttributeValue]]: yield f"{prefix}{MESSAGE_ROLE}", "tool" yield f"{prefix}{MESSAGE_TOOL_CALL_ID}", obj["call_id"] - yield f"{prefix}{MESSAGE_CONTENT}", obj["output"] + if (output := obj.get("output")) is not None: + if isinstance(output, str): + yield f"{prefix}{MESSAGE_CONTENT}", output + elif isinstance(output, list): + for i, item in enumerate(output): + if item["type"] == "input_text": + yield ( + f"{prefix}{MESSAGE_CONTENTS}.{i}.{MESSAGE_CONTENT_TEXT}", + item["text"] or "", + ) + elif item["type"] == "input_image": + # TODO: handle the input image type for the tool input + pass + elif item["type"] == "input_file": + # TODO: handle the input file type for the tool input + pass + elif TYPE_CHECKING: + assert_never(item["type"]) def _get_attributes_from_generation_span_data( diff --git a/python/instrumentation/openinference-instrumentation-openai-agents/tests/test_span_attribute_helpers.py b/python/instrumentation/openinference-instrumentation-openai-agents/tests/test_span_attribute_helpers.py index 983b8b28ce..db9fd38d0f 100644 --- a/python/instrumentation/openinference-instrumentation-openai-agents/tests/test_span_attribute_helpers.py +++ b/python/instrumentation/openinference-instrumentation-openai-agents/tests/test_span_attribute_helpers.py @@ -409,8 +409,8 @@ def test_get_attributes_from_response_function_tool_call_param( "output": "", }, { - "message.content": "", "message.role": "tool", + "message.content": "", "message.tool_call_id": "123", }, id="empty_output", @@ -421,12 +421,23 @@ def test_get_attributes_from_response_function_tool_call_param( "output": None, }, { - "message.content": None, "message.role": "tool", "message.tool_call_id": "123", }, id="none_output", ), + pytest.param( + { + "call_id": "123", + "output": [{"type": "input_text", "text": "result"}], + }, + { + "message.role": "tool", + "message.contents.0.message_content.text": "result", + "message.tool_call_id": "123", + }, + id="functional_call_output", + ), ], ) def test_get_attributes_from_function_call_output(