diff --git a/.claude/README.md b/.claude/README.md index 36b294e..3c91fc5 100644 --- a/.claude/README.md +++ b/.claude/README.md @@ -12,6 +12,7 @@ Dieser Ordner ist der **primäre Orientierungspunkt** für Claude Code / Cursor- | 2 | **`rules/DOCUMENTATION.md`** – Ablage- und Dokumentationsregeln | | 3 | `rules/ARCHITECTURE.md`, `rules/CODING_RULES.md`, `rules/LESSONS_LEARNED.md` | | 4 | Issue-Landkarte: **`.claude/docs/GITEA_ISSUES_INDEX.md`** | +| 5 | **Universal CSV Import** (Modul/Executor/Vorlagen): **`docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md`** (unter `.claude/`) | Themen mit UI/Nav/PWA: siehe `../docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md` (im **Projekt**-`docs/`, nicht hier). diff --git a/.claude/docs/GITEA_ISSUES_INDEX.md b/.claude/docs/GITEA_ISSUES_INDEX.md index ca04bda..3b711b6 100644 --- a/.claude/docs/GITEA_ISSUES_INDEX.md +++ b/.claude/docs/GITEA_ISSUES_INDEX.md @@ -1,6 +1,6 @@ # Gitea Issues – Landkarte (Auswertung) -**Quelle:** Gitea `Lars/mitai-jinkendo`, Stand **2026-04-08** (Abfrage `state=all`). +**Quelle:** Gitea `Lars/mitai-jinkendo`, Stand **2026-04-09** (Abfrage `state=all`, ergänzt: #71). **URL:** http://192.168.2.144:3000/Lars/mitai-jinkendo/issues Dieses Dokument ist ein **Orientierungs-Index** für Agenten und Entwickler. Verbindliches Tracking bleibt **in Gitea**; hier: Kategorien, Dubletten-Hinweise, grobe Prioritätseinschätzung. @@ -88,7 +88,6 @@ Dieses Dokument ist ein **Orientierungs-Index** für Agenten und Entwickler. Ver | # | Titel | |---|--------| | 15 | [FEAT-002] Quality-Filter für KI-Auswertungen & Charts integrieren | -| 21 | [FEATURE] Universeller CSV-Parser mit lernbarem Feldmapping | | 36 | BUG-009: Trainingstyp-Erstellung führt zu Internal Server Error | --- diff --git a/.claude/docs/README.md b/.claude/docs/README.md index 713510a..eb9d192 100644 --- a/.claude/docs/README.md +++ b/.claude/docs/README.md @@ -54,6 +54,7 @@ _Dieser Ordner `.claude/docs/` ist per `.gitignore`-Ausnahme **versioniert** (Sp | Platzhalter / Registry | `technical/PLACEHOLDER_REGISTRY_FRAMEWORK.md`, `technical/PLACEHOLDER_DEVELOPMENT_GUIDE.md` | `backend/placeholder_registrations/`, `backend/placeholder_resolver.py` | | Dashboard-Lab-Widgets | `technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md` | Widget-Katalog + Registrierung (siehe Guide) | | Training Profiler / Resolver | `technical/TRAINING_PROFILE_RESOLVER_LAYER1.md`, `functional/TRAINING_TYPE_PROFILES.md` | Resolver-Module wie im Guide genannt | +| Universal CSV Import | `technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` | `backend/csv_parser/`, `routers/csv_import.py`, `routers/admin_csv_templates.py` | | Mitgliedschaft / Features | `technical/MEMBERSHIP_SYSTEM.md`, `architecture/FEATURE_ENFORCEMENT.md` | `backend/auth.py`, Feature-Logging, Router mit Enforcement | --- @@ -111,6 +112,7 @@ _Dieser Ordner `.claude/docs/` ist per `.gitignore`-Ausnahme **versioniert** (Sp | `PROFILE_REFERENCE_VALUES.md` | Profil-Referenzwerte | | `TRAINING_PROFILE_RESOLVER_LAYER1.md` | Training-Resolver Schicht 1 | | `TRAINING_TYPE_PROFILES_TECHNICAL.md` | Trainingsprofile technisch | +| `UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` | Universal CSV: Registry, Executor, Vorlagen, Agent-Checkliste | | `V9D_PHASE2_VITALS_SLEEP.md` | v9d Vitalwerte/Schlaf (Release-Bezug) | --- @@ -174,4 +176,4 @@ Siehe [`audit/README.md`](./audit/README.md). --- -**Letzte Aktualisierung:** 8. April 2026 (Struktur-Index, Duplikatbereinigung, Abgleich-Hinweise) +**Letzte Aktualisierung:** 9. April 2026 (Universal CSV Agent-Guide, Abgleich-Tabelle) diff --git a/.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md b/.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md new file mode 100644 index 0000000..5bdcea0 --- /dev/null +++ b/.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md @@ -0,0 +1,63 @@ +# Universal CSV Import – Agent-Leitfaden + +**Stand:** 2026-04-09 · **Kontext:** Issue #21 (Universeller CSV-Parser), Prod-Migrationen u. a. 051–053. + +Dieses Dokument ist **normativ für Agenten**, die ein neues Import-Zielmodul anlegen oder bestehende Import-Pfade (Executor, Vorlagen, DB) ändern. + +--- + +## 1. Architektur (Kurz) + +| Komponente | Pfad / Rolle | +|------------|----------------| +| Modul-Definitionen | `backend/csv_parser/module_registry.py` (`MODULE_DEFINITIONS`) | +| Typ-/Einheiten-Konvertierung | `backend/csv_parser/type_converter.py`, `field_units.py` | +| Zeilen-Aggregation (z. B. Ernährung pro Tag) | `backend/csv_parser/import_row_processing.py` | +| Import-Ausführung | `backend/csv_parser/executor.py` | +| Fehlertexte / Transaktions-Hinweise | `backend/csv_parser/import_errors.py` (`enrich_row_error`) | +| Admin-Systemvorlagen | `backend/routers/admin_csv_templates.py` | +| Nutzer-Import (Profil-Mappings) | `backend/routers/csv_import.py` | +| Vorlagen-Validierung (strukturell + Sample) | `backend/csv_parser/template_validator.py` (`validate_csv_template`) | + +**Single Source of Truth** für erlaubte Zielfelder, Typen und Duplikat-Keys ist **`module_registry.py`**. Keine parallele Feldliste in Routern duplizieren. + +--- + +## 2. Checkliste: Neues Zielmodul + +1. **`MODULE_DEFINITIONS`** um Eintrag erweitern: `table`, `fields` (Typen `date` / `datetime` / `float` / `int` / `string`), `duplicate_key`, `duplicate_strategy`, ggf. `derive_date_from_datetime_field`, `import_mode` (Spezialpfade wie Schlaf). +2. **DB:** Migration nur nach Projektregel (`backend/migrations/NNN_*.sql`). Spaltenbreiten/Typen so wählen, dass importierte Werte (z. B. kJ→kcal, große Energiebeträge) **keinen NUMERIC-Overflow** verursachen. +3. **`source` / CHECK-Constraints:** Wenn die Zieltabelle `source` hat, muss der Wert **`csv`** (oder der vereinbarte Import-Tag) in der DB erlaubt sein (Migration anpassen, nicht nur App-Code). +4. **Executor:** Einfügen/Aktualisieren in `executor.py` nur über bestehende Muster (ein Cursor, **kein** verschachteltes `get_db()` im gleichen Request). Bei mehreren Zeilen pro Transaktion: bei **Zeilenfehlern** SAVEPOINT pro Zeile nutzen (siehe Activity-Pattern), damit die Transaktion nicht dauerhaft abgebrochen ist. +5. **Trainingstyp / FK-Auflösung:** DB-Zugriffe für abhängige Entitäten (z. B. `get_training_type_for_activity_with_cursor`) **mit dem gleichen Cursor** wie der Import – keine zweite Connection aus dem Importpfad. +6. **Vorlagen:** System-Templates in Migration/Seed pflegen (`csv_field_mappings`, `is_system=true`). `type_conversions` und `source_unit` dort setzen, wo Einheiten aus Exporten abweichen (z. B. Apple kJ). +7. **Validierung:** Neue/angepasste Admin-Vorlagen müssen **`validate_csv_template`** passieren (Create/Update liefert bei Fehlern **422** mit `validation`). Tests für Randfälle ergänzen (`tests/test_template_validator.py` o. ä.). +8. **API / Frontend:** Neue Admin-Endpunkte in `main.py` registrieren; Frontend **nur** über `api.js`. Bei strukturierten FastAPI-Fehlern (`detail` als Objekt/Liste) bestehende Hilfen (`formatFastApiDetail`) nutzen. + +--- + +## 3. Checkliste: Bestehendes Modul ändern + +- Schema-Änderung: Migration + ggf. **`module_registry`**-Felder anpassen. +- Neue Spalte im Import: Executor-Mapping, optional `type_conversions` / Validator. +- Änderung an Duplikatlogik: `duplicate_key` / `ON CONFLICT`-Pfad im Executor prüfen. +- Datums-/Zeit-Parsing: **`type_converter`** – ISO-Daten `YYYY-MM-DD` konsistent (**`dayfirst=False`**), Zeiten `HH:MM` ohne Sekunden unterstützen wo nötig. + +--- + +## 4. Bekannte Einschränkungen (Follow-up in Gitea) + +- Admin **„Format prüfen“** kann `import_row_processing` derzeit weglassen; volle Parität mit dem gespeicherten Template erst beim Speichern / echten Import. +- Nutzer-Mappings (Copy aus Systemvorlage) laufen nicht automatisch durch **`validate_csv_template`** – Tracking: **Gitea #71** (http://192.168.2.144:3000/Lars/mitai-jinkendo/issues/71). + +--- + +## 5. Verwandte Regeln + +- `.claude/rules/ARCHITECTURE.md` – Router, DB, `source`-Tracking +- `.claude/rules/CODING_RULES.md` – Kurzverweis Universal CSV +- `.claude/rules/DOCUMENTATION.md` – Ablage technischer Specs + +--- + +**Version:** 1.0 diff --git a/.claude/rules/ARCHITECTURE.md b/.claude/rules/ARCHITECTURE.md index e715af3..d5e4e3b 100644 --- a/.claude/rules/ARCHITECTURE.md +++ b/.claude/rules/ARCHITECTURE.md @@ -216,8 +216,11 @@ updated_at TIMESTAMP DEFAULT NOW() Tabellen die Daten aus externen Quellen empfangen brauchen: ```sql source VARCHAR(50) DEFAULT 'manual' --- Werte: 'manual' | 'apple_health' | 'garmin' | 'withings' +-- Werte u. a.: 'manual' | 'apple_health' | 'garmin' | 'withings' | 'csv' ``` +Importe über den **Universal CSV**-Pfad setzen `source = 'csv'`, sofern die Tabelle ein `source`-Feld hat; CHECK-Constraints und Migrationen müssen diesen Wert erlauben. + +**Agent-Pflicht bei neuen Import-Zielen oder Executor-Änderungen:** `.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` Manuelle Einträge (`source = 'manual'`) haben IMMER Vorrang bei Reimport: ```sql diff --git a/.claude/rules/CODING_RULES.md b/.claude/rules/CODING_RULES.md index a703ec1..542bc53 100644 --- a/.claude/rules/CODING_RULES.md +++ b/.claude/rules/CODING_RULES.md @@ -39,6 +39,13 @@ from slowapi import Limiter def sensitive(request: Request, ...): ``` +### 6. Universal CSV Import / Admin-Vorlagen +Neues **Import-Zielmodul**, Änderungen an **`csv_parser`**, Executor, DB-`source`/`CHECK`, oder System-CSV-Vorlagen: + +- Pflichtlektüre und Checkliste: **`.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md`** +- Keine zweite DB-Connection im Importpfad; Zeilenfehler ohne „aborted transaction“ (SAVEPOINT-Muster wo nötig) +- Admin Create/Update von Systemvorlagen: Validierung über `validate_csv_template` nicht umgehen + ## Frontend ### 1. api.js für alle API-Calls diff --git a/CLAUDE.md b/CLAUDE.md index f234606..01e648f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,6 +8,7 @@ > | Coding-Regeln | `.claude/rules/CODING_RULES.md` | > | Lessons Learned | `.claude/rules/LESSONS_LEARNED.md` | > | **Gitea-Landkarte (lokal gepflegt)** | **`.claude/docs/GITEA_ISSUES_INDEX.md`** | +> | **Universal CSV Import** (neues Modul / Executor / Vorlagen) | **`.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md`** | > | **GUI / IA / Admin / Nav / PWA-Leiste** | **`docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md`** | > | **Dashboard-Lab-Widgets** (Katalog, Registrierung, `config`) | **`.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md`** | > | **Agent-Einstieg** | **`.claude/README.md`** | @@ -98,6 +99,12 @@ frontend/src/ **Branch:** develop **Nächster Schritt:** Frontend Chart Integration → Testing → Prod Deploy v0.9i +### Updates (09.04.2026 - Universal CSV Import, Prod-Migration abgeschlossen) + +- **Agent-Leitfaden:** `.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` (Checkliste für neue Import-Module, Executor, Vorlagen, `source=csv`, SAVEPOINT-/Cursor-Regeln) +- **Regeln:** Verweise in `.claude/rules/ARCHITECTURE.md` (§3.2 `source`), `.claude/rules/CODING_RULES.md` (§6) +- **Follow-ups:** **Gitea #71** – Dry-Run inkl. `import_row_processing`, Nutzer-Mapping-Validierung, Fehler-Hints in der Import-UI ([Issue](http://192.168.2.144:3000/Lars/mitai-jinkendo/issues/71)) + ### GUI / Informationsarchitektur (Abnahme dieser Iteration, 2026-04-05) Admin-Bereich (`AdminShell`, Hub-Routen), Hauptnavigation inkl. **Ziele** (`config/appNav.js`), Einstellungen nur aktives Profil + E-Mail, KI-Analyse Ergebnis in rechter Spalte, **PWA** Bottom-Nav inkl. iOS Safe Area. Zentrale Agent-Doku: **`docs/issues/GUI_IA_ADMIN_NAV_2026-04-05.md`**. Responsive-Epic **Gitea #30:** Phasenplan `docs/issues/PHASE_PLAN_RESPONSIVE_UI.md` — **P7 Kern erledigt**, **P8** (Regression/Abnahme) ausstehend; Issue bewusst **nicht** geschlossen. diff --git a/backend/version.py b/backend/version.py index c90e6c0..bb05fce 100644 --- a/backend/version.py +++ b/backend/version.py @@ -7,8 +7,8 @@ Semantic Versioning: MAJOR.MINOR.PATCH - PATCH: Bugfix, kleine Änderung, Refactor """ -APP_VERSION = "0.9p" -BUILD_DATE = "2026-04-09" +APP_VERSION = "0.9q" +BUILD_DATE = "2026-04-11" DB_SCHEMA_VERSION = "20260409c" # 048/049 vitals_baseline.source csv + SAVEPOINT Import MODULE_VERSIONS = { @@ -29,13 +29,26 @@ MODULE_VERSIONS = { "exportdata": "1.1.0", "importdata": "1.0.0", "membership": "2.1.0", - "workflow": "0.6.0", # Phase 4: End Node Template Engine + "workflow": "0.7.0", # Part 3: Inline Prompts (reference + inline mode) "app_dashboard": "1.11.0", # Entitlements: DB-Override widget→features (AND), sonst Katalog "csv_import": "0.3.2", # Import-Fehler: enrich_row_error / freundlichere 500-Hinweise "admin_csv_templates": "0.3.0", # POST /validate + Speichern nur bei valid (422 + warnings in Response) } CHANGELOG = [ + { + "version": "0.9q", + "date": "2026-04-11", + "changes": [ + "Workflow Engine Part 3: Inline Prompts", + "Frontend: Radio Buttons (Reference/Inline), InlineTemplateEditor Component", + "Frontend: Placeholder Picker für Inline-Templates, Cursor-Position Tracking", + "Backend: load_prompt_template() unterstützt inline_template", + "Backend: WorkflowNode.inline_template Feld hinzugefügt", + "Serialization: inline_template speichern/laden in graph_data", + "Validation: Prüft dass entweder prompt_slug ODER inline_template gesetzt", + ], + }, { "version": "0.9p", "date": "2026-04-09", diff --git a/backend/workflow_executor.py b/backend/workflow_executor.py index e238479..a516727 100644 --- a/backend/workflow_executor.py +++ b/backend/workflow_executor.py @@ -18,7 +18,7 @@ import json from jinja2 import Environment, ChainableUndefined, TemplateError from workflow_models import ( - WorkflowGraph, NodeExecutionState, ExecutionResult, + WorkflowGraph, WorkflowNode, NodeExecutionState, ExecutionResult, NodeStatus, NormalizedSignal, FallbackStrategy, SignalStatus, EndNodeOutputMode ) @@ -278,9 +278,10 @@ async def execute_node( # Analysis Nodes if node.type == "analysis": - # 1. Lade Prompt - prompt_template = await load_prompt_template(node.prompt_slug, context) - logger.debug(f"Node {node.id}: Loaded prompt '{node.prompt_slug}'") + # 1. Lade Prompt (Part 3: inline_template support) + prompt_template = await load_prompt_template(node, context) + source_type = "inline" if node.inline_template else "reference" + logger.debug(f"Node {node.id}: Loaded prompt from {source_type}") # 2. Parse question_augmentations questions = [] @@ -812,39 +813,64 @@ def _has_active_incoming_edge(node, graph: WorkflowGraph, context: Dict[str, Any return False -async def load_prompt_template(prompt_slug: str, context: Dict[str, Any]) -> str: +async def load_prompt_template(node: WorkflowNode, context: Dict[str, Any]) -> str: """ - Lädt Prompt-Template aus DB und resolved Platzhalter. + Lädt Prompt-Template aus DB (reference mode) oder direkt vom Node (inline mode). + + Part 3: Inline Prompts - Unterstützt zwei Modi: + - Reference Mode: prompt_slug → Template aus ai_prompts Tabelle + - Inline Mode: inline_template → Template direkt vom Node Args: - prompt_slug: Slug des Prompts (z.B. "pipeline_body") + node: WorkflowNode mit prompt_slug ODER inline_template context: {"variables": {"name": "Lars", ...}, "profile_id": "..."} Returns: Resolved prompt template + Raises: + HTTPException: Wenn weder prompt_slug noch inline_template gesetzt + Beispiel: - >>> template = await load_prompt_template("pipeline_body", {"profile_id": "123"}) + >>> node = WorkflowNode(id="n1", prompt_slug="pipeline_body") + >>> template = await load_prompt_template(node, {"profile_id": "123"}) >>> "{{name}}" not in template True """ from placeholder_resolver import get_placeholder_example_values, get_placeholder_catalog from prompt_executor import resolve_placeholders + from fastapi import HTTPException - with get_db() as conn: - cur = get_cursor(conn) - cur.execute( - "SELECT template FROM ai_prompts WHERE slug = %s AND active = true", - (prompt_slug,) + # Mode 1: Inline Template (NEU) + if node.inline_template: + logger.debug(f"Node {node.id}: Using inline template ({len(node.inline_template)} chars)") + template = node.inline_template + + # Mode 2: Reference (bestehend) + elif node.prompt_slug: + logger.debug(f"Node {node.id}: Loading prompt '{node.prompt_slug}' from DB") + with get_db() as conn: + cur = get_cursor(conn) + cur.execute( + "SELECT template FROM ai_prompts WHERE slug = %s AND active = true", + (node.prompt_slug,) + ) + row = cur.fetchone() + if not row: + raise HTTPException(status_code=404, detail=f"Prompt not found: {node.prompt_slug}") + template = row['template'] + + # Mode 3: Error - weder inline noch reference + else: + raise HTTPException( + status_code=400, + detail=f"Node {node.id}: Either prompt_slug or inline_template required" ) - row = cur.fetchone() - if not row: - raise ValueError(f"Prompt not found: {prompt_slug}") - - template = row['template'] # Resolve Placeholders using modern prompt_executor method profile_id = context.get("profile_id") + if not profile_id: + raise HTTPException(status_code=400, detail="profile_id required in context") # Build variables dict with ALL registered placeholders variables = {} @@ -852,14 +878,51 @@ async def load_prompt_template(prompt_slug: str, context: Dict[str, Any]) -> str try: # Get all placeholder values from registry processed_placeholders = get_placeholder_example_values(profile_id) + logger.info(f"🔍 DEBUG: Loaded {len(processed_placeholders)} placeholders from registry") + logger.info(f"🔍 DEBUG: Sample keys (first 3): {list(processed_placeholders.keys())[:3]}") + # Remove {{ }} from keys (placeholder_resolver returns them with wrappers) cleaned_placeholders = { - key.replace('{{', '').replace('}}', ''): value + key.replace('{{', '').replace('}}', '').strip(): value for key, value in processed_placeholders.items() } + logger.info(f"🔍 DEBUG: Cleaned keys (first 3): {list(cleaned_placeholders.keys())[:3]}") + logger.info(f"🔍 DEBUG: Sample values: name={cleaned_placeholders.get('name')}, age={cleaned_placeholders.get('age')}, geschlecht={cleaned_placeholders.get('geschlecht')}") + variables.update(cleaned_placeholders) except Exception as e: - logger.warning(f"Failed to load placeholders for workflow: {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: @@ -868,13 +931,22 @@ async def load_prompt_template(prompt_slug: str, context: Dict[str, Any]) -> str catalog = None logger.warning(f"Failed to load placeholder catalog for workflow: {e}") + logger.info(f"🔍 DEBUG: Template before resolution:\n{template[:200]}...") + logger.info(f"🔍 DEBUG: Variables dict has {len(variables)} entries") + # Resolve with modern executor + debug_info = {} resolved = resolve_placeholders( template=template, variables=variables, + debug_info=debug_info, catalog=catalog ) + logger.info(f"🔍 DEBUG: Resolved placeholders: {debug_info.get('resolved_placeholders', {})}") + logger.info(f"🔍 DEBUG: Unresolved placeholders: {debug_info.get('unresolved_placeholders', [])}") + logger.info(f"🔍 DEBUG: Template after resolution:\n{resolved[:200]}...") + return resolved diff --git a/backend/workflow_models.py b/backend/workflow_models.py index 12134c9..5b92126 100644 --- a/backend/workflow_models.py +++ b/backend/workflow_models.py @@ -190,7 +190,8 @@ class WorkflowNode(BaseModel): position: Optional[Position] = Field(None, description="Position im visuellen Editor") # ANALYSIS-Knoten - prompt_slug: Optional[str] = Field(None, description="Slug des auszuführenden Prompts") + prompt_slug: Optional[str] = Field(None, description="Slug des auszuführenden Prompts (reference mode)") + inline_template: Optional[str] = Field(None, description="Inline-Prompt-Template (inline mode, Part 3)") question_augmentations: Optional[List[QuestionAugmentation]] = Field(None, description="Fragenergänzungen (knotengebunden, überschreiben Prompt-Defaults)") # LOGIC-Knoten diff --git a/frontend/src/components/ConfirmDialog.jsx b/frontend/src/components/ConfirmDialog.jsx new file mode 100644 index 0000000..221f99a --- /dev/null +++ b/frontend/src/components/ConfirmDialog.jsx @@ -0,0 +1,106 @@ +/** + * ConfirmDialog Component + * + * Modal confirmation dialog + * + * Props: + * - message: string + * - onConfirm: callback when confirmed + * - onCancel: callback when cancelled + * - confirmText: string (default: 'OK') + * - cancelText: string (default: 'Abbrechen') + * - type: 'warning' | 'danger' | 'info' (default: 'warning') + */ +export function ConfirmDialog({ + message, + onConfirm, + onCancel, + confirmText = 'OK', + cancelText = 'Abbrechen', + type = 'warning' +}) { + const colors = { + warning: '#FFC107', + danger: 'var(--danger)', + info: 'var(--accent)' + } + + return ( +
{'{{ placeholder_name }}'} für dynamische Werte
+