wiki/b-reference/platform-core/automation.md
2026-06-02 09:42:12 +02:00

235 lines
12 KiB
Markdown

<!-- status: canonical -->
<!-- lastReviewed: 2026-04-23 -->
<!-- verifiedAgainst: gateway (codebase audit 2026-04-07, post Automation Unification) -->
# Automation (Graphical Editor)
## Ueberblick
PowerOn hat ein **konsolidiertes Automatisierungssystem** basierend auf dem Feature `graphicalEditor`. Die frueheren separaten Systeme (Automation v1, Automation2) wurden entfernt; der gesamte Workflow-Lebenszyklus laeuft ueber ein einheitliches Datenmodell und eine gemeinsame **Unified Action Library** (`workflows/methods/` + `ActionExecutor`).
**Architektur-Saeulen:**
1. **AI Service** -- Agent mit Toolbox-Registry, Streaming, Neutralisierung, Failover
2. **Graphical Editor** -- n8n-Style Flow-Builder mit AI Chat, UDB, Tracing, Versioning
3. **Consolidated Scheduler** -- `WorkflowScheduler` mit inkrementellem Sync (v1-Patterns)
4. **Unified Action Library** -- Method/Action-Bibliothek mit Toolbox-Zuordnung
**Leitentscheide:**
- **Typed Pick-not-Push (2026-04):** Action-Nodes füllen Parameter nur noch über explizite `DataRef` / Static / SystemVar (`resolveParameterReferences`). Laufzeit-Wire-Handover (`applyWireHandover`) ist entfernt. `executeGraph` ruft `materializeConnectionRefs` (leere `connectionReference` → Ref auf `connection.id`) und `validateGraph` mit **harter** Port-Typprüfung (Ausnahme: Ziel-Port nur `Transit` = untypisierter Sink). Upstream-Pfade: `POST/GET …/upstream-paths` und Agent-Tools `listUpstreamPaths` / `bindNodeParameter`.
- Legacy-Features `automation` und `automation2` entfernt (Gateway + Frontend)
- Greenfield-DB `poweron_graphicaleditor` (keine Migration von Altdaten)
- v1-Scheduler-Patterns (inkrementell, `eventId`, `replaceExisting`, Stale-Removal) in konsolidierten Scheduler uebernommen
- `app.py` als generischer Entry-Point ohne feature-spezifische Imports
---
## Datenmodell (Greenfield DB `poweron_graphicaleditor`)
| Modell | Datei | Beschreibung |
|--------|-------|-------------|
| `AutoWorkflow` | `features/graphicalEditor/datamodelFeatureGraphicalEditor.py` | Workflow mit `graph`, `invocations`, `active`, `eventId`, Template-Felder (`isTemplate`, `templateScope`, `sharedReadOnly`) |
| `AutoVersion` | gleiche Datei | Immutabler Graph-Snapshot: Status `draft`/`published`/`archived` |
| `AutoRun` | gleiche Datei | Ausfuehrung: Status `running`/`paused`/`completed`/`failed`, `nodeOutputs`, `context`, `runEnvelope` |
| `AutoStepLog` | gleiche Datei | Pro-Node-Log: `inputSnapshot`, `output`, `startedAt`, `completedAt`, `durationMs`, `tokensUsed`, `retryCount`, `error` |
| `AutoTask` | gleiche Datei | Human-Task bei Pause: Status `pending`/`completed`/`rejected`/`expired` |
**Invarianten:**
- Pro Workflow hoechstens **eine** `PUBLISHED` Version; Scheduler bindet an die veroeffentlichte Version
- `templateScope`: `user` | `instance` | `mandate` | `system` -- steuert Sichtbarkeit und RBAC
- System-Templates (`templateScope=system`, `mandateId=None`) werden im Bootstrap erstellt
### Workflow-Vorlagen
| Scope | Sichtbarkeit | Editierbar | Erstellt durch |
|-------|-------------|------------|----------------|
| `user` | Nur Ersteller | Ja | User ("Als Vorlage speichern") |
| `instance` | Alle User der Feature-Instanz | Nein (read-only) | Scope-Erweiterung |
| `mandate` | Alle User des Mandanten | Nein (read-only) | Scope-Erweiterung |
| `system` | Plattformweit | Nein (read-only) | Bootstrap (`_bootstrapSystemTemplates`) |
---
## Scheduler (`WorkflowScheduler`)
**Datei:** `workflows/scheduler/mainScheduler.py`
Konsolidierter Scheduler mit v1-Patterns:
| Aspekt | Implementierung |
|--------|----------------|
| Sync-Muster | Inkrementell: nur geaenderte Jobs re-registrieren (`eventId` Tracking) |
| Stale-Removal | Jobs fuer geloeschte/deaktivierte Workflows werden entfernt |
| Re-Registration | `replaceExisting=True` fuer idempotente Updates |
| Active-Check | Vor Execution: Workflow `active` und Entry-Point `enabled` pruefen |
| Cron-Parsing | Einfache Intervalle (*/N) als Sekunden; komplexe Cron via `parse_cron_to_kwargs` |
| Thread-Bridge | `setMainLoop()` fuer async Handler aus Scheduler-Threads |
| Delayed Sync | 5s nach Start fuer spaete DB-Verfuegbarkeit |
| Change-Callback | `callbackRegistry` fuer `graphicalEditor.workflow.changed` |
**Lifecycle:**
- Start: `mainGraphicalEditor.onStart()` -> `startScheduler(eventUser)`
- Stop: `mainGraphicalEditor.onStop()` -> `stopScheduler()`
- Manual Sync: `POST /api/workflows/{instanceId}/schedule-sync` -> `syncNow()`
- Main Loop: `app.py` setzt `setSchedulerMainLoop(main_loop)` beim Start
**Run-Failure-Handling:**
- In-App Notification fuer Workflow-Creator
- Messaging-Subscription (`GraphicalEditorRunFailed`) fuer E-Mail-Benachrichtigung
- Event-Emission (`graphicalEditor.run.failed`)
---
## Execution Engine
**Datei:** `workflows/automation2/executionEngine.py`
| Feature | Details |
|---------|---------|
| Graph-Execution | Topologische Sortierung, Branch-Erkennung (ifElse/switch), Loop-Support |
| Pause/Resume | `PauseForHumanTaskError`, `PauseForEmailWaitError` mit Loop-State-Persistenz |
| Retry | Pro-Node Retry-Policy (`retryMaxAttempts`, `retryDelaySeconds`) |
| Step-Logging | `AutoStepLog` mit `inputSnapshot` fuer alle Pfade (inkl. Loops, Skipped) |
| SSE Live-Push | `_emitStepEvent()` sendet Step-Updates an `EventManager`-Queue (`run-trace-{runId}`) |
| Run-Events | `run_complete` / `run_failed` Events am Ende der Execution |
**InputSnapshot-Abdeckung:**
- Regulaere Nodes: Outputs aller Vorgaenger-Nodes
- Loop-Header (`flow.loop`): Vorgaenger-Outputs
- Loop-Body: `_loopItem`, `_loopIndex` + Vorgaenger-Outputs
- Loop-Resume: `_loopItem`, `_loopIndex` + Vorgaenger-Outputs
- Skipped Nodes: `_skipReason: "inactive_branch"` + Vorgaenger-Outputs
---
## Node-Types
Definiert in `features/graphicalEditor/nodeDefinitions/`:
| Kategorie | Nodes |
|-----------|-------|
| **Trigger** | `trigger.manual`, `trigger.schedule`, `trigger.webhook`, `trigger.form`, `trigger.event` |
| **Flow** | `flow.ifElse`, `flow.switch`, `flow.loop`, `flow.merge`, `flow.delay` |
| **Input** | `input.humanTask`, `input.emailWait` |
| **AI** | `ai.prompt`, `ai.classify`, `ai.extract`, `ai.summarize` |
| **Email** | `email.checkEmail`, `email.sendEmail`, `email.draftEmail` |
| **SharePoint** | `sharepoint.listFiles`, `sharepoint.readFile`, `sharepoint.upload` |
| **ClickUp** | `clickup.searchTasks`, `clickup.createTask`, `clickup.updateTask` |
| **File** | `file.create` |
| **Data** | `data.transform`, `data.filter`, `data.aggregate` |
| **Trustee** | `trustee.refreshAccountingData`, `trustee.extractFromFiles`, `trustee.processDocuments`, `trustee.syncToAccounting` |
---
## Toolbox Registry
**Datei:** `serviceCenter/services/serviceAgent/toolboxRegistry.py`
Thematische Tool-Gruppierungen fuer den AI Agent:
| Toolbox | Tools | Default | Requires Connection |
|---------|-------|---------|-------------------|
| `core` | 20 (readFile, listFiles, webSearch, writeFile, ...) | Ja | -- |
| `ai` | 9 (summarizeContent, generateImage, executeCode, ...) | Ja | -- |
| `datasources` | 8 (browseDataSource, searchDataSource, ...) | Ja | -- |
| `email` | 5 (sendMail, outlook_readEmails, ...) | Nein | `microsoft` |
| `sharepoint` | 3 (sharepoint_findDocuments, ...) | Nein | `microsoft` |
| `clickup` | 3 (clickup_searchTasks, ...) | Nein | `clickup` |
| `jira` | 3 (jira_connect, ...) | Nein | `jira` |
| `workflow` | 9 (readWorkflowGraph, addNode, ...) | Nein | -- (featureCode: graphicalEditor) |
| `trustee` | 1 (trustee_refreshAccountingData) | Nein | -- (featureCode: trustee) |
**`requestToolbox` Meta-Tool:**
- Agent kann zur Laufzeit inaktive Toolboxes anfordern
- Handler in `mainServiceAgent._registerRequestToolbox()`
- `agentLoop.py` refresht `toolDefinitions` nach erfolgreichem Aufruf
- Nur inaktive Toolboxes werden als Enum-Optionen angeboten
**Aktivierung:**
- `_activateToolboxes` in `mainServiceAgent.py` prueft User-Connections
- Toolboxes ohne `requiresConnection` sind immer verfuegbar
- Tools aus inaktiven Toolboxes werden aus der Registry entfernt
---
## RBAC
Feature-Code: `graphicalEditor`
| Template-Rolle | UI-Zugriff | Daten-Zugriff |
|----------------|-----------|---------------|
| `graphicalEditor-viewer` | Dashboard, Workflows, Tasks, Templates (view) | Read: Mandate |
| `graphicalEditor-user` | + Editor | CRUD: Mandate |
| `graphicalEditor-admin` | Alle UI/Resource | CRUD: Mandate |
Rollen werden beim Feature-Start via `_syncTemplateRolesToDb()` synchronisiert (inkl. Mandate-Instanz-Rollen).
---
## API-Endpunkte (Auswahl)
| Methode | Pfad | Beschreibung |
|---------|------|-------------|
| POST | `/{instanceId}/execute` | Workflow ausfuehren |
| GET | `/{instanceId}/runs/{runId}/stream` | SSE Live-Tracing fuer einen Run |
| POST | `/{instanceId}/schedule-sync` | Scheduler manuell synchronisieren |
| POST | `/{instanceId}/{workflowId}/chat/stream` | AI Chat SSE Stream |
| GET | `/{instanceId}/node-types` | Verfuegbare Node-Types |
| GET/POST | `/{instanceId}/workflows` | Workflows CRUD |
| GET/POST | `/{instanceId}/templates` | Templates CRUD |
| POST | `/{instanceId}/workflows/{workflowId}/versions/draft` | Draft-Version erstellen |
| POST | `/{instanceId}/workflows/{workflowId}/versions/{versionId}/publish` | Version veroeffentlichen |
---
## System-Automatisierung (Meine Sicht)
Mandatsuebergreifende Uebersicht im Frontend: Route **`/automations`** (`ui-nyla` → `AutomationsDashboardPage`), Eintrag unter **Meine Sicht** in der Navigation.
| Tab | Inhalt |
|-----|--------|
| **Dashboard** | Metriken (`GET /api/system/workflow-runs/metrics`), Runs-Tabelle mit Backend-Paginierung (`GET /api/system/workflow-runs`) |
| **Workflows** | Liste aller nicht-Template-Workflows aus der Greenfield-DB, gefiltert nach Mandatszugehoerigkeit; Enrichment mit Mandats- und Instanz-Label; pro Zeile **`canEdit` / `canDelete` / `canExecute`** (Sysadmin: alles; Mandats-Admin fuer betroffenes Mandat: alles; sonst keine Mutationsaktionen im UI) |
**Gateway:** `routes/routeWorkflowDashboard.py`, Router-Prefix **`/api/system/workflow-runs`**.
| Methode | Pfad | Beschreibung |
|---------|------|--------------|
| GET | `/api/system/workflow-runs` | Runs (offset/limit, RBAC) |
| GET | `/api/system/workflow-runs/metrics` | Aggregierte Kennzahlen |
| GET | `/api/system/workflow-runs/{runId}/steps` | Step-Logs fuer einen Run |
| GET | `/api/system/workflow-runs/workflows` | Workflows mandatsuebergreifend (Query: `active`, `mandateId`, `pagination` als JSON) |
Instanzspezifische Verwaltung unveraendert unter **`/api/workflows/{instanceId}/...`** (`routeFeatureGraphicalEditor.py`).
---
## Schluessel-Dateien
| Bereich | Pfade (`platform-core/modules/`) |
|---------|--------------------------|
| Feature-Definition | `features/graphicalEditor/mainGraphicalEditor.py` |
| API-Routes | `features/graphicalEditor/routeFeatureGraphicalEditor.py` |
| Interface | `features/graphicalEditor/interfaceFeatureGraphicalEditor.py` |
| Datenmodell | `features/graphicalEditor/datamodelFeatureGraphicalEditor.py` |
| Node-Definitionen | `features/graphicalEditor/nodeDefinitions/` |
| Execution Engine | `workflows/automation2/executionEngine.py` |
| System-Runs / zentrale Workflow-Liste | `routes/routeWorkflowDashboard.py` |
| Scheduler | `workflows/scheduler/mainScheduler.py` |
| Toolbox Registry | `serviceCenter/services/serviceAgent/toolboxRegistry.py` |
| Action Library | `workflows/methods/`, `workflows/processing/` (`ActionExecutor`, `methodDiscovery`) |
| Agent Tools | `serviceCenter/services/serviceAgent/` (coreTools, actionToolAdapter, workflowTools) |
| Bootstrap | `interfaces/interfaceBootstrap.py` (System-Templates, Billing) |
---
## Regeln / Invarianten
1. **Eine Action Library:** Aenderungen an Methods/Actions betreffen Workspace, Graph-Editor und Agent-Tools gleichzeitig -- `actionId`-Stabilitaet beachten.
2. **RBAC strikt trennen:** Mandantenrollen vs Feature-Instanz-Rollen nicht mischen; Permissions ueber AccessRules.
3. **Scheduler vs Editor:** Ausfuehrung und Zeitplanung gehoeren zum Scheduler; der Graph ist Version-gebunden (Draft vs Published).
4. **Tool-Skalierung:** Toolbox-Konzept mit `requestToolbox` Meta-Tool; connection-basierte Verfuegbarkeit.
5. **`app.py` generisch:** Keine feature-spezifischen Imports in `app.py`; Feature-Lifecycle in `mainGraphicalEditor.onStart/onStop`.
6. **Neutralisierung / KI:** Plattformweit gelten die zentralen KI-Datenpfade; Automation nutzt dieselben Services.
7. **Bootstrap idempotent:** `initBootstrap` mit `_bootstrapDone` Flag; System-Templates, Billing, Stripe-Prices.