diff --git a/analytics/trace_comparison_rules/src/langfuse_adapter.py b/analytics/trace_comparison_rules/src/langfuse_adapter.py index 4baca19..cf0fa0d 100644 --- a/analytics/trace_comparison_rules/src/langfuse_adapter.py +++ b/analytics/trace_comparison_rules/src/langfuse_adapter.py @@ -70,8 +70,23 @@ async def load_trace(self, file_path: str) -> TraceIR: # Create trace IR trace = TraceIR(trace_id=trace_id) - # Extract task formulation from top-level input field - trace.task_formulation = data.get("input", {}).get("intent") + # Extract task formulation from top-level input field. + # Old format (pre-PR#33): {"intent": "...", "task_name": "...", ...} + # New format (post-PR#33): list of LangChain message dicts — LangGraph + # callback is now the trace root, so input is the raw message list. + # LangChain message `content` may be a list (multi-modal blocks), so we + # only assign when the extracted value is a string; downstream code + # calls .strip() / concatenation on task_formulation. + raw_input = data.get("input") + candidate: Any = None + if isinstance(raw_input, dict): + candidate = raw_input.get("intent") + elif isinstance(raw_input, list): + candidate = next( + (m.get("content") for m in raw_input if isinstance(m, dict) and m.get("role") == "user"), + None, + ) + trace.task_formulation = candidate if isinstance(candidate, str) else None # Get observations list observations = data.get("observations", [])