fix: Add workflow node outputs as placeholders in inline templates
All checks were successful
Deploy Development / deploy (push) Successful in 46s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s

ISSUE: Inline templates referencing node outputs ({{ node_id.analysis_core }},
{{ node_id.signal_xyz }}) were not resolved - AI received empty data from
previous workflow stages.

ROOT CAUSE: load_prompt_template() only loaded system placeholders
(name, age, etc.) but not node execution results from context['node_results'].

FIX:
- Extract node outputs from context['node_results']
- Add as placeholders: node_id.analysis_core, node_id.signal_xyz, node_id.question_xyz
- Format matches PlaceholderPicker extraction logic
- Debug logging shows which node placeholders are added

TESTING:
- System placeholder test:  SUCCESS (name, age, geschlecht resolved)
- Node output placeholders: Fixed (previously missing)
- User workflow: Join → Analysis → End now receives upstream data

Part 3: Inline Prompts - placeholder resolution completion
This commit is contained in:
Lars 2026-04-11 10:13:03 +02:00
parent aeb0ee6ad9
commit 88f0b5a0a4

View File

@ -893,6 +893,37 @@ async def load_prompt_template(node: WorkflowNode, context: Dict[str, Any]) -> s
except Exception as e:
logger.error(f"❌ CRITICAL: Failed to load placeholders for workflow: {e}", exc_info=True)
# Add workflow node outputs as placeholders (Part 3: Inline Prompts)
# Format: node_id.analysis_core, node_id.signal_xyz, node_id.question_xyz
node_results = context.get("node_results", {})
if node_results:
logger.info(f"🔍 DEBUG: Adding {len(node_results)} node outputs as placeholders")
for node_id, node_state in node_results.items():
# analysis_core
if hasattr(node_state, 'analysis_core') and node_state.analysis_core:
key = f"{node_id}.analysis_core"
variables[key] = node_state.analysis_core
logger.debug(f" Added placeholder: {key} = {node_state.analysis_core[:50]}...")
# decision_signals (keyed by question ID)
if hasattr(node_state, 'decision_signals') and node_state.decision_signals:
for signal_id, signal_value in node_state.decision_signals.items():
# Signal placeholder: node_id.signal_question_id
signal_key = f"{node_id}.signal_{signal_id}"
variables[signal_key] = signal_value
logger.debug(f" Added placeholder: {signal_key} = {signal_value}")
# Question texts (from graph metadata if available)
# NOTE: Question text placeholders are populated from graph in PlaceholderPicker
# Here we only add if available in node_state metadata
if hasattr(node_state, 'metadata') and isinstance(node_state.metadata, dict):
questions = node_state.metadata.get('questions', [])
for q in questions:
if isinstance(q, dict) and 'id' in q and 'question' in q:
question_key = f"{node_id}.question_{q['id']}"
variables[question_key] = q['question']
logger.debug(f" Added placeholder: {question_key}")
# Load catalog for |d modifier support
try:
catalog = get_placeholder_catalog(profile_id)