Commit Graph

22 Commits

Author SHA1 Message Date
73963e7140 fix: ImportError - normalize_signal_value does not exist
All checks were successful
Deploy Development / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Root Cause:
- Tried to import normalize_signal_value from normalization_engine
- Function does not exist (only normalize_decision_signal)
- Caused 500 Internal Server Error on workflow execution

Backend workflow_executor.py:
- Changed import: normalize_signal_value → normalize_decision_signal
- normalize_decision_signal returns NormalizedSignal (not dict)
- Use returned object directly (no .get() calls)
- Simplified logic

Fix:
```python
# BEFORE (broken):
normalized = normalize_signal_value(...)
normalized_signals.append(NormalizedSignal(..., normalized.get('status')))

# AFTER (working):
normalized_signal = normalize_decision_signal(...)
normalized_signals.append(normalized_signal)
```

Issue: 500 Internal Server Error on workflow execution
Version: 0.9p (workflow module)
Part 3: End Node Template Engine - Import Fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 21:17:34 +02:00
de5b8cbf15 fix: CRITICAL - Use question ID (not type) for LLM communication
All checks were successful
Deploy Development / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
ROOT ARCHITECTURAL CHANGE:
Multiple questions with same type are now supported!

Problem:
- question_augmenter used q.type as LLM key
- If two questions had type="unsicherheit":
  - LLM saw duplicate keys: "- unsicherheit: [ja/nein]"
  - Could only answer one
  - Signals were ambiguous

Solution:
- Use question.id as LLM key (unique by design)
- Keep type for normalization logic
- Map id → type internally

Backend question_augmenter.py:
- format_question_list() now uses q.id as key
- Format: "- **q21**: [ja/nein]  # Question text"
- Question text as comment for LLM context

Backend workflow_executor.py:
- Removed type→id mapping (no longer needed)
- decision_signals now keyed by id (from LLM)
- Build id→type catalog for normalization
- NormalizedSignal.question_type stores id (not type!)
- End Node template: signal_{id} directly available

Flow:
1. Questions sent to LLM: "- q21: [ja/nein]  # Ist Protein unsicher?"
2. LLM answers: "- q21: nein"
3. Normalization: id→type lookup for spectrum/rules
4. Template: {{ node_4.signal_q21 }} = "nein"

Example (TWO unsicherheit questions):
Questions:
- q21: type=unsicherheit, question="Ist Protein unsicher?"
- q22: type=unsicherheit, question="Ist Energie unsicher?"

LLM Prompt:
```
## Entscheidungsfragen
- **q21**: [ja/nein]  # Ist Protein unsicher?
- **q22**: [ja/nein]  # Ist Energie unsicher?
```

LLM Response:
```
- q21: nein
- q22: ja
```

Template:
```
{{ node_4.signal_q21 }} → "nein"
{{ node_4.signal_q22 }} → "ja"
```

BREAKING CHANGE:
- Old workflows with decision_signals keyed by type will break
- Need to re-execute workflows after update

Issue: Cannot have multiple questions with same type
Version: 0.9p (workflow module)
Part 3: End Node Template Engine - ARCHITECTURAL FIX

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 21:13:50 +02:00
29a3dbceb5 fix: Simplified signal→ID mapping (direct lookup)
All checks were successful
Deploy Development / deploy (push) Successful in 49s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
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 <noreply@anthropic.com>
2026-04-09 21:09:17 +02:00
3b4902dc11 fix: CRITICAL - Use question ID in placeholders, not type
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Root Cause:
- Multiple questions with same type (e.g. "unsicherheit") created duplicate placeholders
- {{ node_4.signal_unsicherheit }} could refer to q21 OR q22
- Later signal overwrote earlier one in template context

Solution:
- Placeholders now use question ID: {{ node_4.signal_q21 }}
- Unique even with multiple questions of same type

Frontend PlaceholderPicker.jsx:
- Changed placeholder: signal_${questionType} → signal_${questionId}
- Changed placeholder: question_${questionType} → question_${questionId}
- Description shows both: "q21 (unsicherheit): Question text"

Backend workflow_executor.py:
- Build question_type → question_id mapping from graph
- Map normalized_signals (by type) to question IDs
- Handles duplicate types with index tracking
- Creates signal_${id} and question_${id} in template context

Example:
Questions configured:
- q21: type="unsicherheit", question="Ist Protein unsicher?"
- q22: type="unsicherheit", question="Ist Energie unsicher?"

Placeholders generated:
- {{ node_4.signal_q21 }} → "nein"
- {{ node_4.signal_q22 }} → "ja"
- {{ node_4.question_q21 }} → "Ist Protein unsicher?"
- {{ node_4.question_q22 }} → "Ist Energie unsicher?"

Issue: Duplicate question types cause placeholder conflicts
Version: 0.9p (workflow module)
Part 3: End Node Template Engine - CRITICAL FIX

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 21:01:24 +02:00
3e93dbbc89 fix: Placeholder field name mismatch + debug logging
All checks were successful
Deploy Development / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
Root Cause:
- PlaceholderPicker used q.id for signal placeholders
- Backend template context used question_type
- Placeholders never matched → empty values

Frontend PlaceholderPicker.jsx:
- Changed signal_${q.id} → signal_${q.type} (matches backend)
- Added question_${q.type} placeholders (question texts)
- New category: "Workflow - Questions"

Backend workflow_executor.py:
- Added extensive debug logging for template context
- Logs all signal_* and question_* keys + values
- Helps diagnose template rendering issues

Example:
- Question configured with type="kalorienbilanz"
- Frontend now shows: {{ node_4.signal_kalorienbilanz }}
- Frontend now shows: {{ node_4.question_kalorienbilanz }}
- Backend creates: template_context['node_4']['signal_kalorienbilanz']
- Should match and render correctly

Issue: Signal placeholders show empty values
Version: 0.9p (workflow module)
Part 3: End Node Template Engine - Field Name Fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 20:49:45 +02:00
76b4b36617 feat: End Node template placeholders + clean output display
All checks were successful
Deploy Development / deploy (push) Successful in 51s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Three major improvements for workflow templates:

1. **Normalized Signal Placeholders:**
   - Signals now available as {{ node_4.signal_kalorienbilanz }}
   - Uses normalized_value (not raw decision_signals)
   - Enables structured decision-based outputs

2. **Question Text Placeholders:**
   - Question texts available as {{ node_4.question_kalorienbilanz }}
   - Extracted from workflow graph (question_augmentations)
   - Allows displaying questions alongside answers

3. **Clean End Node Output:**
   - End Node output no longer duplicated with "## node_4" headers
   - aggregate_results() detects End Nodes via graph.nodes
   - Only shows final template-rendered output
   - Backward compatible: Falls back to combined_analysis if no End Node

Backend workflow_executor.py:
- execute_end_node(): Added normalized signals to template context
- execute_end_node(): Added question texts to template context
- execute_workflow(): Added graph to context for End Node access
- aggregate_results(): Signature change to accept graph
- aggregate_results(): Detects End Nodes and uses only their output

Frontend WorkflowResultViewer.jsx:
- Now uses aggregated.analysis_core (primary output)
- Removed fallback to combined_analysis (was showing duplicates)

Example Template:
```jinja2
**Frage:** {{ node_4.question_kalorienbilanz }}
**Antwort:** {{ node_4.signal_kalorienbilanz }}

---
{{ node_4.analysis_core }}
```

Issue: Signal placeholders empty, question texts unavailable, duplicate output
Version: 0.9p (workflow module)
Part 3: End Node Template Engine - Complete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 20:45:08 +02:00
856a82ec1d fix: Frontend-Backend field name mismatch for workflow questions
All checks were successful
Deploy Development / deploy (push) Successful in 57s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Root Cause:
- Frontend serialized as "questions"
- Backend expected "question_augmentations"
- Analysis Nodes WITH questions configured sent empty array to backend
- Questions were never added to LLM prompt

Frontend workflowSerializer.js:
- Serialization: questions → question_augmentations (Backend field name)
- Deserialization: question_augmentations → questions (Frontend data object)
- Backward compatible: Falls back to "questions" for old workflows

Backend workflow_executor.py:
- Removed incorrect load_prompt_questions() function (was a misunderstanding)
- Back to original logic: Only use node.question_augmentations
- Simplified normalization logging

Impact:
- Analysis Node questions are now correctly sent to backend
- Questions augment the base prompt as intended
- LLM receives structured questions
- Decision signals are generated and accessible as placeholders

Example:
- Node configures question with id="q21"
- Signal becomes accessible as {{ node_2.signal_q21 }}
- Can be used in Logic Nodes and End Node templates

Issue: Workflow questions not sent to LLM (field name mismatch)
Version: 0.9p (workflow module)
Part 3: End Node Template Engine - Critical Fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 18:28:54 +02:00
b17bec3340 fix: Load base prompt questions in workflow (Hybrid Model)
All checks were successful
Deploy Development / deploy (push) Successful in 54s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Backend workflow_executor.py:
- New function: load_prompt_questions() loads questions from base prompt
- execute_node() now implements Hybrid Model correctly:
  * IF node has question_augmentations → use those (override)
  * ELSE load questions from referenced base prompt (fallback)
- Normalization now uses `questions` variable (not node.question_augmentations)
- This fixes base prompts having questions that were ignored in workflows

Root Cause:
- Phase 1 Hybrid Model was incomplete
- Node-specific questions worked, but base prompt questions were ignored
- augment_prompt_with_questions() was only called when node.question_augmentations existed

Impact:
- Analysis Nodes WITHOUT custom questions now use base prompt questions
- LLM receives proper question augmentation
- Decision signals are generated and normalized correctly

Issue: Workflow questions not sent to LLM
Version: 0.9p (workflow module)
Part 3: End Node Template Engine - Critical Fix

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 18:18:08 +02:00
857c55aeb8 fix: Workflow placeholder resolution + complete catalog display
All checks were successful
Deploy Development / deploy (push) Successful in 51s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 16s
Backend workflow_executor.py:
- load_prompt_template() now uses modern resolve_placeholders() from prompt_executor
- Calls get_placeholder_example_values() to populate ALL registered placeholders
- Passes catalog for |d modifier support
- Fixes issue where basis prompts had empty/null placeholder values in workflows

Backend placeholder_resolver.py:
- get_placeholder_catalog() now includes ALL placeholders from PLACEHOLDER_MAP
- Uncategorized placeholders added to "Sonstiges" category
- Fixes discrepancy: 111 total placeholders but only ~30 shown in picker

Root Cause:
- Workflow used old resolve_placeholders() (only PLACEHOLDER_MAP, no variables)
- Isolated execution used modern resolve_placeholders() (full variables dict)
- Catalog excluded non-registry placeholders from PLACEHOLDER_MAP

Impact:
- All placeholders now resolve correctly in workflow execution
- PlaceholderPicker shows all 111+ placeholders (not just registry ones)

Version: 0.9p (workflow module)
Part 3: End Node Template Engine - Bug Fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 18:10:04 +02:00
fac76c28da fix: Handle None workflow_id in success path
All checks were successful
Deploy Development / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Also use 'N/A' placeholder in ExecutionResult when workflow_id is None
(when using graph_data directly instead of workflow_definitions).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 07:28:30 +02:00
6016eec250 fix: Add ON CONFLICT to workflow_executions insert
All checks were successful
Deploy Development / deploy (push) Successful in 47s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Prevents duplicate key violation when save_execution_state is called
multiple times with the same execution_id (e.g., during error handling).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 07:26:10 +02:00
c95b4e185d fix: Edge format normalization and nullable workflow_id
All checks were successful
Deploy Development / deploy (push) Successful in 55s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Fixes:
1. Edge Format Mismatch:
   - graph_data uses React Flow format (source/target)
   - WorkflowEdge expects backend format (from/to)
   - Added normalization in parse_workflow_graph()

2. UUID Validation Error:
   - workflow_id can be None when using graph_data (Phase 5)
   - save_execution_state now accepts Optional[str]
   - ExecutionResult uses "N/A" placeholder when None

Changes:
- workflow_engine.py: normalize edges before Pydantic validation
- workflow_executor.py: Optional[str] for workflow_id parameter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 07:22:32 +02:00
fe28cce921 fix: Workflow executor graph parsing and error handling
All checks were successful
Deploy Development / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 14s
Fixes:
- graph_data was incorrectly json.dumps() encoded (should stay as dict)
- workflow_id=None in error handler caused ValidationError
- parse_workflow_graph expects Dict, not str

Changes:
- Use graph_dict directly instead of json.dumps(graph_data)
- Set workflow_id="" when None in error handler

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 07:18:43 +02:00
b888f5d3c8 feat: Phase 4 - End Node Template Engine (v0.9n)
All checks were successful
Deploy Development / deploy (push) Successful in 52s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Backend:
- workflow_models.py: EndNodeOutputMode enum (AUTO, TEMPLATE)
- workflow_executor.py: execute_end_node() with Jinja2 rendering
- Template Context: {{node_id.analysis_core}}, {{node_id.decision_signals.key}}
- Conditional Rendering: {% if node_id %} for optional paths
- AUTO Mode: Backward compatible (concatenates all analyses)
- TEMPLATE Mode: Custom Jinja2 templates with placeholders

Features:
- Access node results: {{node_id.analysis_core}}
- Access signals: {{node_id.decision_signals.relevanz}}
- Optional paths: {% if node_id %}...{% endif %}
- Default values: {{node_id|default("N/A")}}

Version: 0.9n
Module: workflow 0.6.0
Konzept: konzept_workflow_engine_konsolidated.md (Section 11)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 07:07:49 +02:00
dc59596f01 feat: Phase 5 - Visual Workflow Editor (Option B)
Some checks failed
Deploy Development / deploy (push) Failing after 1s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 14s
Backend (Mini-Backend 1-2h):
- Migration 016: ai_prompts.graph_data JSONB column
- workflow_executor: graph_data parameter support (backward-compatible)
- prompt_executor: execute_workflow_prompt uses graph_data

Frontend (Main effort 25-35h):
- WorkflowCanvas: React Flow wrapper component
- 5 Custom Nodes: Start, End, Analysis, Logic, Join
- 4 Config Panels: QuestionAugmentation, LogicExpression, Fallback, Join
- workflowValidation: Structural + logical validation
- workflowSerializer: Canvas ↔ JSONB conversion
- WorkflowEditorPage: Main orchestration (420 LOC)
- Route: /workflow-editor/:id
- CSS: workflowEditor.css (300 LOC)

Architecture:
- Option B: ai_prompts.type='workflow' (not separate table)
- panels/ subdirectory for clean separation
- WorkflowCanvas reusable component
- User GUI identical (Workflows = Prompts)
- Backward-compatible (type='pipeline' unchanged)

Version: v0.9m → v0.9n (Phase 5 complete)
Module: workflow 0.5.0 → 0.6.0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 17:56:00 +02:00
c607cd1833 fix: Convert joined signals Dict to List for NodeExecutionState
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 22s
NodeExecutionState expects normalized_signals as List[NormalizedSignal],
but join_evaluator returns Dict[str, NormalizedSignal].

Fix: Convert dict to list before returning NodeExecutionState.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 12:33:58 +02:00
e2a132353d feat: Phase 4 - Join Nodes and Path Consolidation
All checks were successful
Deploy Development / deploy (push) Successful in 45s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
Backend Implementation (v0.9m, workflow 0.5.0):
- join_evaluator.py (394 lines): Join-Strategie-Evaluator
  - evaluate_join_node(): Hauptlogik für Join-Node Execution
  - Join-Strategien: wait_all, wait_any, best_effort
  - Skip-Handling: ignore_skipped, use_placeholder, require_minimum
  - Result Consolidation: merge analysis_cores, combine signals
  - Partial Execution: korrekte Behandlung von SKIPPED/FAILED Pfaden

- workflow_executor.py: execute_join_node() Integration
  - BFS-Traversierung erweitert für Join-Nodes
  - NodeExecutionState List → Dict Konvertierung für Signale
  - Signal-Name-Kollisionen via node_id Präfix gelöst

Testing (49 Tests passing):
- test_phase4_join_nodes.py: 18 neue Unit Tests
  - Join-Strategien (wait_all, wait_any, best_effort)
  - Skip-Handling (ignore, placeholder)
  - Result Consolidation (merge, combine)
  - Partial Execution (mixed status paths)
  - Helper Functions (collect, check, merge, combine)

- Backward Compatibility: 31 Phase 2/3 Tests (alle passing)
  - test_phase2_workflow_executor.py: 1 Test aktualisiert
  - test_phase3_logic_evaluator.py: 20 Tests unverändert

Konzept: konzept_workflow_engine_konsolidated.md (Sektion 8.8)
Anforderungsanalyse: phase4_anforderungsanalyse.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 12:27:31 +02:00
2ce0874dcb feat: Phase 3 - Logic Nodes + Conditional Branching
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Backend:
- logic_evaluator.py (NEU, 307 Zeilen): Deterministischer Logic Evaluator
  - Vergleichsoperatoren: EQ, NEQ, IN, NOT_IN, GT, LT, GTE, LTE, CONTAINS
  - Logische Operatoren: AND, OR, NOT mit Verschachtelung
  - Resolve signal references (node_id.question_type)
  - Error handling für UNCLEAR/INVALID/NOT_DECIDABLE Signale

- workflow_executor.py (ERWEITERT):
  - execute_logic_node(): Bedingungen evaluieren, Pfade aktivieren/deaktivieren
  - execute_workflow(): BFS-Traversierung mit Edge-Activation statt Sequential
  - _apply_fallback(): 4 Fallback-Strategien (CONSERVATIVE_SKIP, DEFAULT_PATH, UNCERTAINTY_PATH, DOCUMENT_ONLY)
  - _has_active_incoming_edge(): Prüft ob Node erreichbar ist
  - _get_edges_by_label(): Findet then/else/uncertainty Pfade

- workflow_models.py (ERWEITERT):
  - LogicOperator.CONTAINS hinzugefügt

- version.py: 0.9k → 0.9l, workflow 0.3.0 → 0.4.0

Tests:
- test_phase3_logic_evaluator.py (NEU): 20 Unit Tests (alle passing)
  - Comparison operators (EQ, NEQ, IN, GT, LT, CONTAINS)
  - Logical operators (AND, OR, NOT)
  - Nested expressions
  - Error handling (missing refs, UNCLEAR/INVALID signals)

- test_phase2_workflow_executor.py (AKTUALISIERT): 11 Tests (alle passing)
  - execute_node() graph parameter hinzugefügt (Phase 3 requirement)
  - test_execute_node_unknown_type: logic → join (logic jetzt implementiert)

- test_phase3_workflow_branching.py (NEU): Integration Tests vorbereitet
  - Erfordert vollständige DB-Mock-Strategie (wird in E2E-Test nachgeholt)

Phase 2 Backward Compatibility:  Alle Phase 2 Tests bestehen weiterhin

Konzept: .claude/task/Workflow_engine_prompting_engine/konzept_workflow_engine_konsolidated.md
Anforderungsanalyse: .claude/task/Workflow_engine_prompting_engine/phase3_anforderungsanalyse.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-04 08:02:22 +02:00
c588372f3a fix: Hybrid model - node-specific question spectrums override catalog (Phase 1 requirement)
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 21s
2026-04-03 21:49:13 +02:00
585f189b13 fix: Remove extra_vars parameter from resolve_placeholders call - function doesn't support it yet
All checks were successful
Deploy Development / deploy (push) Successful in 48s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 13s
2026-04-03 21:44:39 +02:00
ac2e7cf5bb fix: Use dict keys for all RealDictCursor row access in Phase 2 code
All checks were successful
Deploy Development / deploy (push) Successful in 50s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 15s
2026-04-03 21:36:44 +02:00
1f8791f4dd feat: Phase 2 - Normalisierung + Workflow Executor
All checks were successful
Deploy Development / deploy (push) Successful in 44s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s
Backend:
- normalization_engine.py (200 Zeilen): Synonym-Mapping, 5 Statuswerte
  * normalize_decision_signal(): Kaskade (exact → case → synonym → invalid)
  * apply_synonym_mapping(): DB-basierte Synonyme (case-insensitive)
  * normalize_all_signals(): Batch-Processing gegen Katalog
  * load_question_catalog(): Lädt normalization_rules aus DB
- workflow_executor.py (440 Zeilen): Sequenzielle Workflow-Ausführung
  * execute_workflow(): Traversiert DAG in topologischer Reihenfolge
  * execute_node(): Führt analysis nodes aus (start/end = no-op)
  * aggregate_results(): Kombiniert analysis_core + normalized_signals
  * save_execution_state(): Persistiert in workflow_executions
- workflow_models.py: Erweitert um Phase 2 Models
  * SignalStatus Enum (valid, normalized, unclear, invalid, not_decidable)
  * NormalizedSignal (question_type, raw_value, normalized_value, status)
  * NodeExecutionState (node_id, status, analysis_core, normalized_signals)
  * ExecutionResult (execution_id, workflow_id, status, node_states, aggregated_result)
- workflow_engine.py: Neue Funktion get_execution_order()
  * Flattened topological sort für sequenzielle Execution
  * Phase 7: Wird zu levels (parallele Execution)
- prompt_executor.py: execute_workflow_prompt() Implementierung
  * Ruft workflow_executor.execute_workflow() auf
  * Konvertiert ExecutionResult zu API-Response
- routers/workflows.py (230 Zeilen): Workflow Execution API
  * POST /api/workflows/{id}/execute (mit enable_debug)
  * GET /api/workflows/executions/{id} (lädt gespeicherten State)
  * GET /api/workflows (listet alle aktiven Workflows)
  * GET /api/workflows/{id} (lädt einzelnen Workflow mit Graph)
- main.py: Router-Registrierung (workflows.router)

Tests:
- test_phase2_normalization.py (17 Tests): Alle Normalisierungs-Szenarien
  * Exact match, case-insensitive, synonym mapping, invalid, whitespace
  * Batch-Normalisierung, not_in_catalog, mixed validity
- test_phase2_workflow_executor.py (10 Tests): Executor + Aggregation
  * aggregate_results mit verschiedenen Konstellationen
  * execute_node für start/end/analysis/unknown
  * Integration mit question_augmenter + result_container_parser

Alle 27 Unit-Tests bestanden.

version: 0.9k (backend)
module:  workflow 0.3.0

Konzept: .claude/task/Workflow_engine_prompting_engine/anforderungsanalyse_umsetzungsplan.md (Phase 2)
2026-04-03 21:20:23 +02:00