From 5dbd57e7d2c26e4158e8161172e13dfba8ceb3c7 Mon Sep 17 00:00:00 2001 From: Hassieb Pakzad <68423100+hassiebp@users.noreply.github.com> Date: Thu, 3 Jul 2025 18:54:00 +0200 Subject: [PATCH 1/2] fix(langchain): do not stringify metadata unnecessarily --- langfuse/_client/attributes.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/langfuse/_client/attributes.py b/langfuse/_client/attributes.py index 623506e69..d531d242b 100644 --- a/langfuse/_client/attributes.py +++ b/langfuse/_client/attributes.py @@ -170,6 +170,10 @@ def _flatten_and_serialize_metadata( metadata_attributes[prefix] = _serialize(metadata) else: for key, value in metadata.items(): - metadata_attributes[f"{prefix}.{key}"] = _serialize(value) + metadata_attributes[f"{prefix}.{key}"] = ( + value + if isinstance(value, str) or isinstance(value, int) + else _serialize(value) + ) return metadata_attributes From 03fed0d6b4d5b3f4ce49a76b24439149d76f2af9 Mon Sep 17 00:00:00 2001 From: Hassieb Pakzad <68423100+hassiebp@users.noreply.github.com> Date: Fri, 4 Jul 2025 10:54:46 +0200 Subject: [PATCH 2/2] fix tests --- tests/test_core_sdk.py | 2 +- tests/test_otel.py | 39 ++++++++++++++++----------------------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/tests/test_core_sdk.py b/tests/test_core_sdk.py index b519d5877..9d1acae85 100644 --- a/tests/test_core_sdk.py +++ b/tests/test_core_sdk.py @@ -59,7 +59,7 @@ async def update_generation(i, langfuse: Langfuse): # Verify metadata observation = generation_obs[0] assert observation.name == str(i) - assert observation.metadata["count"] == f"{i}" + assert observation.metadata["count"] == i def test_flush(): diff --git a/tests/test_otel.py b/tests/test_otel.py index acabc4896..8434f3703 100644 --- a/tests/test_otel.py +++ b/tests/test_otel.py @@ -346,9 +346,9 @@ def test_span_attributes(self, langfuse_client, memory_exporter): output_data = json.loads( attributes[LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT] ) - metadata_data = json.loads( - attributes[f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.session"] - ) + metadata_data = attributes[ + f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.session" + ] # Verify attribute values assert input_data == {"prompt": "Test prompt"} @@ -425,9 +425,7 @@ def test_trace_update(self, langfuse_client, memory_exporter): tags = list(attributes[LangfuseOtelSpanAttributes.TRACE_TAGS]) input_data = json.loads(attributes[LangfuseOtelSpanAttributes.TRACE_INPUT]) - metadata = json.loads( - attributes[f"{LangfuseOtelSpanAttributes.TRACE_METADATA}.trace-meta"] - ) + metadata = attributes[f"{LangfuseOtelSpanAttributes.TRACE_METADATA}.trace-meta"] # Check attribute values assert sorted(tags) == sorted(["tag1", "tag2"]) @@ -501,11 +499,9 @@ def test_complex_scenario(self, langfuse_client, memory_exporter): ) # Parse metadata - proc_metadata = json.loads( - proc["attributes"][ - f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.step" - ] - ) + proc_metadata = proc["attributes"][ + f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.step" + ] assert proc_metadata == "processing" # Parse input/output JSON @@ -980,12 +976,14 @@ def test_complex_metadata_serialization(self): assert ( f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.key2" in simple_result ) - assert simple_result[ - f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.key1" - ] == _serialize("value1") - assert simple_result[ - f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.key2" - ] == _serialize(123) + assert ( + simple_result[f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.key1"] + == "value1" + ) + assert ( + simple_result[f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.key2"] + == 123 + ) # Test case 3: Nested dict (will be flattened in current implementation) nested_dict = { @@ -1037,7 +1035,7 @@ def test_complex_metadata_serialization(self): # The nested structures are serialized as JSON strings assert json.loads(complex_result[level1_key]) == complex_dict["level1"] - assert complex_result[sibling_key] == _serialize("value") + assert complex_result[sibling_key] == "value" def test_nested_metadata_updates(self): """Test that nested metadata updates don't overwrite unrelated keys.""" @@ -2130,11 +2128,6 @@ async def async_task(parent_span, task_id): ) assert output["result"] == f"Task {i} completed" - metadata = self.verify_json_attribute( - task_span, f"{LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.task_id" - ) - assert metadata == i - # Verify main span output main_output = self.verify_json_attribute( main, LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT