diff --git a/libs/core/langchain_core/tracers/core.py b/libs/core/langchain_core/tracers/core.py index 4f5a325ecea..75614e3c881 100644 --- a/libs/core/langchain_core/tracers/core.py +++ b/libs/core/langchain_core/tracers/core.py @@ -449,7 +449,7 @@ class _TracerCore(ABC): kwargs.update({"metadata": metadata}) if self._schema_format in {"original", "original+chat"}: - inputs = {"input": input_str} + inputs = inputs if isinstance(inputs, dict) else {"input": input_str} elif self._schema_format == "streaming_events": inputs = {"input": inputs} else: diff --git a/libs/core/tests/unit_tests/tracers/test_async_base_tracer.py b/libs/core/tests/unit_tests/tracers/test_async_base_tracer.py index 6bb93ee8539..829a917bc28 100644 --- a/libs/core/tests/unit_tests/tracers/test_async_base_tracer.py +++ b/libs/core/tests/unit_tests/tracers/test_async_base_tracer.py @@ -214,6 +214,40 @@ async def test_tracer_tool_run() -> None: assert tracer.runs == [compare_run] +@freeze_time("2023-01-01") +async def test_tracer_tool_run_preserves_structured_inputs() -> None: + """Structured `inputs` from `BaseTool.run` should not be flattened to `str`.""" + uuid = uuid4() + structured_inputs = {"command": "echo 'hello\nworld'", "timeout": None} + compare_run = Run( + id=str(uuid), + name="tool", + start_time=datetime.now(timezone.utc), + end_time=datetime.now(timezone.utc), + events=[ + {"name": "start", "time": datetime.now(timezone.utc)}, + {"name": "end", "time": datetime.now(timezone.utc)}, + ], + extra={}, + serialized={"name": "tool"}, + inputs=structured_inputs, + outputs={"output": "ok"}, + error=None, + run_type="tool", + trace_id=uuid, + dotted_order=f"20230101T000000000000Z{uuid}", + ) + tracer = FakeAsyncTracer() + await tracer.on_tool_start( + serialized={"name": "tool"}, + input_str=str(structured_inputs), + run_id=uuid, + inputs=structured_inputs, + ) + await tracer.on_tool_end("ok", run_id=uuid) + assert tracer.runs == [compare_run] + + @freeze_time("2023-01-01") async def test_tracer_nested_run() -> None: """Test tracer on a nested run.""" diff --git a/libs/core/tests/unit_tests/tracers/test_base_tracer.py b/libs/core/tests/unit_tests/tracers/test_base_tracer.py index 84e1218c9d1..c830bfeecc5 100644 --- a/libs/core/tests/unit_tests/tracers/test_base_tracer.py +++ b/libs/core/tests/unit_tests/tracers/test_base_tracer.py @@ -217,6 +217,40 @@ def test_tracer_tool_run() -> None: assert tracer.runs == [compare_run] +@freeze_time("2023-01-01") +def test_tracer_tool_run_preserves_structured_inputs() -> None: + """Structured `inputs` from `BaseTool.run` should not be flattened to `str`.""" + uuid = uuid4() + structured_inputs = {"command": "echo 'hello\nworld'", "timeout": None} + compare_run = Run( + id=str(uuid), + name="tool", + start_time=datetime.now(timezone.utc), + end_time=datetime.now(timezone.utc), + events=[ + {"name": "start", "time": datetime.now(timezone.utc)}, + {"name": "end", "time": datetime.now(timezone.utc)}, + ], + extra={}, + serialized={"name": "tool"}, + inputs=structured_inputs, + outputs={"output": "ok"}, + error=None, + run_type="tool", + trace_id=uuid, + dotted_order=f"20230101T000000000000Z{uuid}", + ) + tracer = FakeTracer() + tracer.on_tool_start( + serialized={"name": "tool"}, + input_str=str(structured_inputs), + run_id=uuid, + inputs=structured_inputs, + ) + tracer.on_tool_end("ok", run_id=uuid) + assert tracer.runs == [compare_run] + + @freeze_time("2023-01-01") def test_tracer_nested_run() -> None: """Test tracer on a nested run."""