From e799edbae4420aabdcaca16d4965e6269a9cd1fe Mon Sep 17 00:00:00 2001 From: Lars Date: Thu, 26 Mar 2026 12:44:28 +0100 Subject: [PATCH] feat: expert mode + stage outputs in value table (Issue #47) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FEATURE: Experten-Modus πŸ”¬ - Toggle-Button in Wertetabelle - Normal: Nur gefΓΌllte Werte anzeigen - Experten: Alle Platzhalter inkl. leere/technische - Anzeige: "(+X ausgeblendet)" wenn Werte gefiltert - Button-Style: Accent wenn aktiv FILTER: Leere Werte ausblenden (Normal-Modus) - Filtert: '', 'nicht verfΓΌgbar', '[Nicht verfΓΌgbar]' - Zeigt nur relevante Nutzer-Daten - Experten-Modus zeigt alles FEATURE: Stage-Outputs in Wertetabelle βœ… ROOT CAUSE: stage_N_key Platzhalter hatten keine Werte - Stage-Outputs (z.B. stage_1_body) sind Basis-Analysen-Ergebnisse - Wurden nicht in cleaned_values gefunden (nur statische Platzhalter) FIX: - Collect stage outputs aus result.debug.stages[].output - Store als stage_N_key dict - Lookup: erst stage_outputs, dann cleaned_values - Description: "Output aus Stage X (Basis-Analyse)" - JSON-Werte automatisch serialisiert BEISPIEL Pipeline-Wertetabelle: β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ πŸ“Š Verwendete Werte (8) (+3 ausgeblendet) πŸ”¬β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ weight_aktuell β”‚ 85.2 kg β”‚ Gewicht β”‚ β”‚ β”‚ β”‚ stage_1_body β”‚ {"bmi":...β”‚ Output...β”‚ β”‚ ← Stage output! β”‚ β”‚ stage_1_nutr... β”‚ {"kcal"...β”‚ Output...β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ AKTIVIERUNG Experten-Modus: 1. Analyse ΓΆffnen 2. "πŸ“Š Verwendete Werte" aufklappen 3. Button "πŸ”¬ Experten-Modus" klicken 4. Zeigt alle Platzhalter (auch leere stage outputs) version: 9.8.0 (feature) module: prompts 2.3.0, insights 1.6.0 --- backend/routers/prompts.py | 40 +++++++++++++------ frontend/src/pages/Analysis.jsx | 68 +++++++++++++++++++++++++-------- 2 files changed, 82 insertions(+), 26 deletions(-) diff --git a/backend/routers/prompts.py b/backend/routers/prompts.py index 257914c..814c126 100644 --- a/backend/routers/prompts.py +++ b/backend/routers/prompts.py @@ -856,6 +856,19 @@ async def execute_unified_prompt( elif result['type'] == 'pipeline': # Pipeline: collect from all stages stages_debug = result['debug'].get('stages', []) + + # First, collect stage outputs (outputs from base prompts in each stage) + stage_outputs = {} + for stage_debug in stages_debug: + stage_num = stage_debug.get('stage', 0) + stage_output = stage_debug.get('output', {}) + if isinstance(stage_output, dict): + for output_key, output_value in stage_output.items(): + # Store stage outputs (e.g., stage_1_body) + placeholder_key = f"stage_{stage_num}_{output_key}" + stage_outputs[placeholder_key] = output_value + + # Collect all resolved placeholders from prompts for stage_debug in stages_debug: for prompt_debug in stage_debug.get('prompts', []): resolved_keys = [] @@ -867,20 +880,25 @@ async def execute_unified_prompt( for key in resolved_keys: if key not in metadata['placeholders']: # Avoid duplicates - # Get full untruncated value - value = cleaned_values.get(key, '') + # Get value: first try stage outputs, then cleaned_values + value = stage_outputs.get(key, cleaned_values.get(key, '')) - # Find description in catalog - desc = None - for cat_items in catalog.values(): - matching = [item for item in cat_items if item['key'] == key] - if matching: - desc = matching[0].get('description', '') - break + # For stage output placeholders, add special description + if key.startswith('stage_'): + desc = f"Output aus Stage {key.split('_')[1]} (Basis-Analyse)" + else: + # Find description in catalog + desc = None + for cat_items in catalog.values(): + matching = [item for item in cat_items if item['key'] == key] + if matching: + desc = matching[0].get('description', '') + break + desc = desc or '' metadata['placeholders'][key] = { - 'value': value, - 'description': desc or '' + 'value': value if isinstance(value, str) else json.dumps(value, ensure_ascii=False), + 'description': desc } # Save to database with metadata diff --git a/frontend/src/pages/Analysis.jsx b/frontend/src/pages/Analysis.jsx index 3021c2c..90dd29f 100644 --- a/frontend/src/pages/Analysis.jsx +++ b/frontend/src/pages/Analysis.jsx @@ -24,6 +24,7 @@ function InsightCard({ ins, onDelete, defaultOpen=false, prompts=[] }) { const showOnlyValues = isBasePrompt && isJsonOutput && Object.keys(placeholdersRaw).length > 0 const [showValues, setShowValues] = useState(showOnlyValues) // Auto-expand for base prompts with JSON + const [expertMode, setExpertMode] = useState(false) // Show empty/technical placeholders // Find matching prompt to get display_name const prompt = prompts.find(p => p.slug === ins.scope) @@ -31,8 +32,20 @@ function InsightCard({ ins, onDelete, defaultOpen=false, prompts=[] }) { // Use already-parsed metadata const metadata = metadataRaw - const placeholders = placeholdersRaw + const allPlaceholders = placeholdersRaw + + // Filter placeholders: In normal mode, hide empty values + const placeholders = expertMode + ? allPlaceholders + : Object.fromEntries( + Object.entries(allPlaceholders).filter(([key, data]) => { + const val = data.value || '' + return val.trim() !== '' && val !== 'nicht verfΓΌgbar' && val !== '[Nicht verfΓΌgbar]' + }) + ) + const placeholderCount = Object.keys(placeholders).length + const hiddenCount = Object.keys(allPlaceholders).length - placeholderCount return (
@@ -83,20 +96,45 @@ function InsightCard({ ins, onDelete, defaultOpen=false, prompts=[] }) { {/* Value Table */} {placeholderCount > 0 && (
-
setShowValues(!showValues)} - style={{ - cursor: 'pointer', - fontSize: 12, - color: 'var(--text2)', - fontWeight: 600, - display: 'flex', - alignItems: 'center', - gap: 6 - }} - > - {showValues ? : } - πŸ“Š Verwendete Werte ({placeholderCount}) +
+
setShowValues(!showValues)} + style={{ + cursor: 'pointer', + fontSize: 12, + color: 'var(--text2)', + fontWeight: 600, + display: 'flex', + alignItems: 'center', + gap: 6 + }} + > + {showValues ? : } + πŸ“Š Verwendete Werte ({placeholderCount}) + {hiddenCount > 0 && !expertMode && ( + + (+{hiddenCount} ausgeblendet) + + )} +
+ + {showValues && Object.keys(allPlaceholders).length > 0 && ( + + )}
{showValues && (