# Copyright (c) 2025 Patrick Motsch # Context node definitions — structural extraction without AI plus # generic key/value, merge, filter and transform helpers. from modules.shared.i18nRegistry import t from modules.features.graphicalEditor.nodeDefinitions.flow import CONTEXT_MERGE_ACTION_RESULT_DATA_PICK_OPTIONS _CONTEXT_INPUT_SCHEMAS = [ "Transit", "ActionResult", "AiResult", "MergeResult", "FormPayload", "DocumentList", "EmailList", "TaskList", "FileList", "LoopItem", "UdmDocument", ] CONTEXT_NODES = [ { "id": "context.extractContent", "category": "context", "label": t("Inhalt extrahieren"), "description": t( "Extrahiert Inhalt ohne KI. Ergebnis einheitlich wie KI-Schritte: `response` " "(gesammelter Klartext), strukturierte JSON-Unterlage in `documents[0]`, " "einzelne Bilder als eigene Dokumente `extract_media_*` (nur im Workflow, ohne Eintrag unter „Meine Dateien“) — " "Auswahl im Daten-Picker wie bei `ai.process`." ), "parameters": [ {"name": "documentList", "type": "str", "required": True, "frontendType": "hidden", "description": t("Dokumentenliste (via Wire oder DataRef)"), "default": "", "graphInherit": {"port": 0, "kind": "documentListWire"}}, { "name": "contentFilter", "type": "str", "required": False, "frontendType": "select", "frontendOptions": { "options": [ {"value": "all", "label": t("Alles (Text, Tabellen, Bilder)")}, {"value": "textOnly", "label": t("Nur Text und Tabellen")}, {"value": "imagesOnly", "label": t("Nur Bilder")}, {"value": "noImages", "label": t("Alles ausser Bilder")}, ] }, "default": "all", "description": t( "Welche Parts im Handover behalten werden. " "all = alle Typgruppen inkl. Bilder; " "textOnly = ausschliesslich Text-, Tabellen- und Struktur-Parts; " "imagesOnly = ausschliesslich Bild-Parts; " "noImages = alle Parts ausser Bildern (weiter als textOnly: " "auch kuenftige Nicht-Bild-Typen bleiben erhalten)." ), }, ], "inputs": 1, "outputs": 1, "inputPorts": {0: {"accepts": ["DocumentList", "Transit", "LoopItem"]}}, "outputPorts": { 0: { "schema": "ActionResult", # Authoritative DataPicker paths (same idea as ``parameters`` for configuration). # Frontend uses only this list — no schema expansion merge for this port. "dataPickOptions": [ { "path": ["documents", 0, "documentData"], "pickerLabel": t("Gesamter Inhalt"), "detail": t( "Strukturiertes Handover als JSON inklusive aller Textteile " "und Verweisen auf ausgelagerte Bilder." ), "recommended": True, "type": "Any", }, { "path": ["response"], "pickerLabel": t("Nur Text"), "detail": t( "Verketteter Klartext aus allen erkannten Textteilen." ), "recommended": True, "type": "str", }, { "path": ["imageDocumentsOnly"], "pickerLabel": t("Nur Bilder"), "detail": t( "Nur die extrahierten Bilddokumente als Liste, ohne JSON-Handover." ), "recommended": False, "type": "List[ActionDocument]", }, { "path": ["documents"], "pickerLabel": t("Alle Dateitypen"), "detail": t( "Alle Ausgabedokumente nacheinander: JSON-Handover und Bilder." ), "recommended": False, "type": "List[ActionDocument]", }, ], } }, "meta": {"icon": "mdi-file-tree-outline", "color": "#00897B", "usesAi": False}, "_method": "context", "_action": "extractContent", }, { "id": "context.mergeContext", "category": "context", "label": t("Kontext zusammenführen"), "description": t( "Führt eine Liste von Ergebnissen zu einem einzigen Kontext zusammen. " "Wähle als Datenquelle die Option Alle Schleifen-Ergebnisse einer Schleife, " "um alle Iterationsergebnisse in einem Datensatz zu vereinen." ), "parameters": [ { "name": "dataSource", "type": "Any", "required": True, "frontendType": "dataRef", "description": t( "Datenquelle: Liste von Einträgen zum Zusammenführen " "(z. B. Schleife → Alle Schleifen-Ergebnisse)" ), }, ], "inputs": 1, "outputs": 1, "inputPorts": {0: {"accepts": _CONTEXT_INPUT_SCHEMAS}}, "outputPorts": { 0: {"schema": "ActionResult", "dataPickOptions": CONTEXT_MERGE_ACTION_RESULT_DATA_PICK_OPTIONS} }, "injectUpstreamPayload": True, # Same contract as transformContext: picker paths like ``merged`` / ``first`` must match # ``nodeOutputs`` (see actionNodeExecutor ``surfaceDataAsTopLevel``); merge payloads live in ``data``. "surfaceDataAsTopLevel": True, "meta": {"icon": "mdi-call-merge", "color": "#7B1FA2", "usesAi": False}, "_method": "context", "_action": "mergeContext", }, { "id": "context.transformContext", "category": "context", "label": t("Kontext transformieren"), "description": t( "Verändert die Struktur des eingehenden Datenstroms. " "Operationen pro Mapping: 'rename' (Key umbenennen), 'cast' (Typ konvertieren), " "'nest' (mehrere Felder unter neuem Objekt zusammenfassen), " "'flatten' (verschachteltes Objekt auf oberste Ebene heben), " "'compute' (neues Feld aus Template-/{{...}}-Ausdruck berechnen). " "Jedes Mapping definiert: 'sourceField' (Eingangspfad / Ausdruck), " "'outputField' (Ausgabe-Key), 'operation' und 'type' (Zieltyp). " "Das Ergebnis ist ein neues Objekt — der ursprüngliche Datenstrom " "wird nicht automatisch weitergegeben (ausser 'passthroughUnmapped: true')." ), "parameters": [ { "name": "mappings", "type": "list", "required": True, "frontendType": "mappingTable", "default": [], "description": t( "Liste von Mapping-Einträgen. Jeder Eintrag: " "sourceField (DataRef-Pfad oder Ausdruck), " "outputField (Ziel-Key im Output), " "operation (rename | cast | nest | flatten | compute), " "type (str | int | bool | float | object | list — für cast), " "expression (für compute: Template oder Ausdruck, z.B. '{{firstName}} {{lastName}}')." ), }, { "name": "passthroughUnmapped", "type": "bool", "required": False, "frontendType": "checkbox", "default": False, "description": t( "Alle nicht gemappten Felder des Eingangs zusätzlich in den Output übernehmen." ), }, { "name": "flattenDepth", "type": "int", "required": False, "frontendType": "number", "default": 1, "description": t("Tiefe für flatten-Operation (1 = eine Ebene, -1 = vollständig)"), }, ], "inputs": 1, "outputs": 1, "inputPorts": {0: {"accepts": _CONTEXT_INPUT_SCHEMAS}}, "outputPorts": { 0: { "schema": { "kind": "fromGraph", "parameter": "mappings", "nameField": "outputField", "schemaName": "Transform_dynamic", }, "dynamic": True, "deriveFrom": "mappings", "deriveNameField": "outputField", } }, "injectUpstreamPayload": True, "surfaceDataAsTopLevel": True, "meta": {"icon": "mdi-swap-horizontal", "color": "#EF6C00", "usesAi": False}, "_method": "context", "_action": "transformContext", }, ]