From 29a3dbceb5bef3e488d02015ddf85a2f30551130 Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 9 Apr 2026 21:09:17 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Simplified=20signal=E2=86=92ID=20mapping?= =?UTF-8?q?=20(direct=20lookup)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root Cause: - Previous index-based mapping assumed signals come in same order as questions - But LLM response order can differ from question configuration order - Led to signal values being assigned to wrong question IDs Old Logic (BUGGY): 1. Build question_type → [list of IDs] 2. Track index per type 3. Get Nth ID from list → Assumes LLM answers in question definition order ❌ New Logic (CORRECT): 1. Build question_type → question_id (direct mapping) 2. For each signal: lookup type → get ID → Order-independent ✅ Backend workflow_executor.py: - Removed index tracking (type_counts) - Direct lookup: question_type_to_id[signal.question_type] - Added ERROR log if duplicate question types found - Added INFO log for each mapped signal (debugging) Important: - Each question MUST have a UNIQUE type - If two questions share same type: ERROR logged - System designed for unique types (LLM can't answer duplicates) Example Debug Output: ``` Mapped signal: protein_ausreichend → signal_q21 = 'nein' Mapped signal: kohlenhydrate_strategie → signal_q1775... = 'von Proteinen' ``` Issue: Signal values assigned to wrong question IDs Version: 0.9p (workflow module) Part 3: End Node Template Engine - Signal Mapping Fix Co-Authored-By: Claude Opus 4.6 --- backend/workflow_executor.py | 42 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/backend/workflow_executor.py b/backend/workflow_executor.py index eee6080..cb3bcb9 100644 --- a/backend/workflow_executor.py +++ b/backend/workflow_executor.py @@ -603,8 +603,8 @@ def execute_end_node( "status": node_state.status.value if node_state.status else "unknown", } - # Build question_type → question_id mapping from workflow graph - question_id_map = {} + # Build direct question_type → question_id mapping + question_type_to_id = {} if graph: workflow_node = next((n for n in graph.nodes if n.id == node_id), None) if workflow_node and workflow_node.question_augmentations: @@ -613,35 +613,31 @@ def execute_end_node( q_type = q_dict.get('type') q_id = q_dict.get('id') if q_type and q_id: - # Multiple questions with same type → use ID - if q_type not in question_id_map: - question_id_map[q_type] = [] - question_id_map[q_type].append(q_id) + # WICHTIG: Wenn mehrere Fragen den gleichen type haben, ist das ein Fehler! + if q_type in question_type_to_id: + logger.error( + f"DUPLICATE question type '{q_type}'! " + f"First ID: {question_type_to_id[q_type]}, Second ID: {q_id}. " + f"Each question MUST have a UNIQUE type!" + ) + question_type_to_id[q_type] = q_id - # Add normalized signals as {{node_id.signal_ID}} (not type!) + # Add normalized signals as {{node_id.signal_ID}} if node_state.normalized_signals: - # Track which types we've seen (for duplicate detection) - type_counts = {} - for signal in node_state.normalized_signals: # Convert NormalizedSignal object to dict if needed signal_dict = signal.model_dump() if hasattr(signal, 'model_dump') else signal q_type = signal_dict['question_type'] - # Determine which ID to use for this signal - if q_type in question_id_map: - # Get the Nth ID for this type (handles duplicates) - type_idx = type_counts.get(q_type, 0) - type_counts[q_type] = type_idx + 1 - - if type_idx < len(question_id_map[q_type]): - q_id = question_id_map[q_type][type_idx] - signal_key = f"signal_{q_id}" - node_context[signal_key] = signal_dict['normalized_value'] or signal_dict['raw_value'] - else: - logger.warning(f"No question_id found for signal type={q_type} index={type_idx}") + # Direct lookup: question_type → question_id + if q_type in question_type_to_id: + q_id = question_type_to_id[q_type] + signal_key = f"signal_{q_id}" + signal_value = signal_dict['normalized_value'] or signal_dict['raw_value'] + node_context[signal_key] = signal_value + logger.info(f"Mapped signal: {q_type} → {signal_key} = '{signal_value}'") else: - logger.warning(f"No question_id mapping for type={q_type}") + logger.warning(f"No question_id found for signal type='{q_type}' (available types: {list(question_type_to_id.keys())})") # Add question texts as {{node_id.question_ID}} if graph: