11"""Tests for Langfuse adapter."""
22
33from datetime import datetime , timedelta
4- from types import SimpleNamespace
54from typing import Any , Dict , List , Optional
65from unittest .mock import Mock
76
@@ -63,26 +62,29 @@ def _create_mock_trace(
6362 trace_id : str , input_data : Any = None , output_data : Any = None , observations : Optional [List ] = None
6463):
6564 """Helper to create mock trace objects"""
66- return SimpleNamespace (
67- id = trace_id , input = input_data , output = output_data , observations = observations or [], tags = [], metadata = {}
65+ # Ensure observations have metadata attribute
66+ obs_with_metadata = []
67+ for obs in observations or []:
68+ if hasattr (obs , "metadata" ):
69+ obs_with_metadata .append (obs )
70+ else :
71+ # Add metadata to existing observation
72+ obs_dict = obs .__dict__ if hasattr (obs , "__dict__" ) else {}
73+ obs_dict ["metadata" ] = getattr (obs , "metadata" , {})
74+ obs_with_metadata .append (Mock (** obs_dict ))
75+
76+ return Mock (
77+ id = trace_id , input = input_data , output = output_data , observations = obs_with_metadata , tags = [], metadata = {}
6878 )
6979
7080
7181def _create_mock_traces_response (traces : List [Dict [str , Any ]]):
7282 """Helper to create mock traces list response"""
7383 trace_objects = []
7484 for trace_data in traces :
75- trace_objects .append (SimpleNamespace (** trace_data ))
76-
77- return SimpleNamespace (
78- data = trace_objects ,
79- meta = SimpleNamespace (
80- page = 1 ,
81- total_pages = 1 ,
82- total_items = len (trace_objects ), # Add total_items to match real API
83- limit = 100 ,
84- ),
85- )
85+ trace_objects .append (Mock (** trace_data ))
86+
87+ return Mock (data = trace_objects , meta = Mock (page = 1 , total_pages = 1 , total_items = len (trace_objects ), limit = 100 ))
8688
8789
8890@pytest .fixture
@@ -111,8 +113,7 @@ def test_basic_trace_conversion():
111113 input_data = {"messages" : [{"role" : "user" , "content" : "What's the weather?" }]},
112114 output_data = {"messages" : [{"role" : "assistant" , "content" : "It's sunny!" }]},
113115 )
114-
115- result = convert_trace_to_evaluation_row (trace ) # pyright: ignore[reportArgumentType]
116+ result = convert_trace_to_evaluation_row (trace )
116117
117118 assert result is not None
118119 assert len (result .messages ) == 2
@@ -150,7 +151,7 @@ def test_trace_with_tool_calls():
150151 },
151152 )
152153
153- result = convert_trace_to_evaluation_row (trace , include_tool_calls = True ) # pyright: ignore[reportArgumentType]
154+ result = convert_trace_to_evaluation_row (trace , include_tool_calls = True )
154155
155156 assert result is not None
156157 assert result .tools is not None
@@ -170,22 +171,27 @@ def test_trace_with_tool_calls():
170171def test_trace_conversion_with_span_name ():
171172 """Test trace conversion with specific span name"""
172173 # Mock observations with spans and generations
173- observations = [
174- SimpleNamespace (id = "span1" , name = "judge" , type = "SPAN" , metadata = {}),
175- SimpleNamespace (
176- id = "gen1" ,
177- name = "generation" ,
178- type = "GENERATION" ,
179- parent_observation_id = "span1" ,
180- input = {"messages" : [{"role" : "user" , "content" : "Judge this" }]},
181- output = {"messages" : [{"role" : "assistant" , "content" : "Good response" }]},
182- start_time = datetime .now (),
183- metadata = {},
184- ),
185- ]
174+ span_mock = Mock ()
175+ span_mock .id = "span1"
176+ span_mock .name = "judge"
177+ span_mock .type = "SPAN"
178+ span_mock .metadata = {}
179+ span_mock .parent_observation_id = None
180+
181+ gen_mock = Mock ()
182+ gen_mock .id = "gen1"
183+ gen_mock .name = "generation"
184+ gen_mock .type = "GENERATION"
185+ gen_mock .parent_observation_id = "span1"
186+ gen_mock .input = {"messages" : [{"role" : "user" , "content" : "Judge this" }]}
187+ gen_mock .output = {"messages" : [{"role" : "assistant" , "content" : "Good response" }]}
188+ gen_mock .start_time = datetime .now ()
189+ gen_mock .metadata = {}
190+
191+ observations = [span_mock , gen_mock ]
186192
187193 trace = _create_mock_trace ("trace_span" , observations = observations )
188- result = convert_trace_to_evaluation_row (trace , span_name = "judge" ) # pyright: ignore[reportArgumentType]
194+ result = convert_trace_to_evaluation_row (trace , span_name = "judge" )
189195
190196 assert result is not None
191197 assert len (result .messages ) == 2
@@ -197,17 +203,17 @@ def test_empty_trace_returns_none():
197203 """Test that empty traces return None"""
198204 trace = _create_mock_trace ("empty_trace" , input_data = None , output_data = None )
199205
200- result = convert_trace_to_evaluation_row (trace ) # pyright: ignore[reportArgumentType]
206+ result = convert_trace_to_evaluation_row (trace )
201207
202208 assert result is None
203209
204210
205211def test_malformed_trace_returns_none ():
206212 """Test that malformed traces are handled gracefully"""
207213 # Trace with missing required attributes
208- trace = SimpleNamespace (id = "malformed" ) # Missing input/output
214+ trace = Mock (id = "malformed" ) # Missing input/output
209215
210- result = convert_trace_to_evaluation_row (trace ) # pyright: ignore[reportArgumentType]
216+ result = convert_trace_to_evaluation_row (trace )
211217
212218 assert result is None
213219
@@ -315,7 +321,7 @@ def test_extract_messages_from_various_formats():
315321 input_data = {"messages" : [{"role" : "user" , "content" : "Hello" }]},
316322 output_data = {"messages" : [{"role" : "assistant" , "content" : "Hi" }]},
317323 )
318- messages1 = extract_messages_from_trace (trace1 ) # pyright: ignore[reportArgumentType]
324+ messages1 = extract_messages_from_trace (trace1 )
319325 assert len (messages1 ) == 2
320326 assert messages1 [0 ].role == "user"
321327 assert messages1 [1 ].role == "assistant"
@@ -324,7 +330,7 @@ def test_extract_messages_from_various_formats():
324330 trace2 = _create_mock_trace (
325331 "trace2" , input_data = {"prompt" : "What is AI?" }, output_data = {"content" : "AI is artificial intelligence" }
326332 )
327- messages2 = extract_messages_from_trace (trace2 ) # pyright: ignore[reportArgumentType]
333+ messages2 = extract_messages_from_trace (trace2 )
328334 assert len (messages2 ) == 2
329335 assert messages2 [0 ].role == "user"
330336 assert messages2 [0 ].content == "What is AI?"
@@ -337,7 +343,7 @@ def test_extract_messages_from_various_formats():
337343 input_data = [{"role" : "user" , "content" : "List format" }],
338344 output_data = [{"role" : "assistant" , "content" : "Response" }],
339345 )
340- messages3 = extract_messages_from_trace (trace3 ) # pyright: ignore[reportArgumentType]
346+ messages3 = extract_messages_from_trace (trace3 )
341347 assert len (messages3 ) == 2
342348 assert messages3 [0 ].content == "List format"
343349 assert messages3 [1 ].content == "Response"
0 commit comments