diff --git a/packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/span_utils.py b/packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/span_utils.py index 09d365daaf..92e99b2a90 100644 --- a/packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/span_utils.py +++ b/packages/opentelemetry-instrumentation-langchain/opentelemetry/instrumentation/langchain/span_utils.py @@ -195,7 +195,10 @@ def set_request_params(span, kwargs, span_holder: SpanHolder): tool_defs = [] for tool in tools: tool_function = tool.get("function", tool) - tool_def = {"name": tool_function.get("name")} + tool_def = { + "type": tool.get("type") or "function", + "name": tool_function.get("name"), + } if tool_function.get("description"): tool_def["description"] = tool_function.get("description") params_val = tool_function.get("parameters", tool.get("input_schema")) @@ -248,7 +251,10 @@ def set_chat_request( if functions: tool_defs = [] for function in functions: - tool_def = {"name": function.get("name")} + tool_def = { + "type": "function", + "name": function.get("name"), + } if function.get("description"): tool_def["description"] = function.get("description") if function.get("parameters"): diff --git a/packages/opentelemetry-instrumentation-langchain/tests/test_tool_definitions.py b/packages/opentelemetry-instrumentation-langchain/tests/test_tool_definitions.py new file mode 100644 index 0000000000..cdb0126404 --- /dev/null +++ b/packages/opentelemetry-instrumentation-langchain/tests/test_tool_definitions.py @@ -0,0 +1,94 @@ +import json +from unittest.mock import Mock, patch + +from opentelemetry.instrumentation.langchain.span_utils import ( + set_chat_request, + set_request_params, +) +from opentelemetry.semconv._incubating.attributes import ( + gen_ai_attributes as GenAIAttributes, +) + + +def _span_attributes(span: Mock) -> dict: + return {call.args[0]: call.args[1] for call in span.set_attribute.call_args_list} + + +def test_tool_definitions_preserve_source_type(): + span = Mock() + span.is_recording.return_value = True + span_holder = Mock() + kwargs = { + "invocation_params": { + "tools": [ + { + "type": "custom_tool", + "function": { + "name": "get_weather", + "description": "Get the weather", + "parameters": {"type": "object"}, + }, + } + ] + } + } + + set_request_params(span, kwargs, span_holder) + + tool_definitions = json.loads(_span_attributes(span)[GenAIAttributes.GEN_AI_TOOL_DEFINITIONS]) + assert tool_definitions == [ + { + "type": "custom_tool", + "name": "get_weather", + "description": "Get the weather", + "parameters": {"type": "object"}, + } + ] + + +def test_tool_definitions_default_to_function_type(): + span = Mock() + span.is_recording.return_value = True + span_holder = Mock() + kwargs = { + "invocation_params": { + "tools": [ + { + "name": "get_weather", + "description": "Get the weather", + "input_schema": {"type": "object"}, + } + ] + } + } + + set_request_params(span, kwargs, span_holder) + + tool_definitions = json.loads(_span_attributes(span)[GenAIAttributes.GEN_AI_TOOL_DEFINITIONS]) + assert tool_definitions[0]["type"] == "function" + + +def test_legacy_function_definitions_include_function_type(): + span = Mock() + span.is_recording.return_value = True + span_holder = Mock() + kwargs = { + "invocation_params": { + "functions": [ + { + "name": "get_weather", + "description": "Get the weather", + "parameters": {"type": "object"}, + } + ] + } + } + + with patch( + "opentelemetry.instrumentation.langchain.span_utils.should_send_prompts", + return_value=True, + ): + set_chat_request(span, {}, [], kwargs, span_holder) + + tool_definitions = json.loads(_span_attributes(span)[GenAIAttributes.GEN_AI_TOOL_DEFINITIONS]) + assert tool_definitions[0]["type"] == "function"