diff --git a/TOPICS.md b/TOPICS.md index 5e61803..f30790d 100644 --- a/TOPICS.md +++ b/TOPICS.md @@ -1,4 +1,4 @@ - + # Themen-Index für AI-Kontext @@ -32,15 +32,32 @@ Lade immer zuerst diese Datei. Dann gezielt die passende(n) Referenz-Datei(en). | Thema | Datei | Wann laden | |-------|-------|------------| | Neutralisierung | b-reference/platform/neutralization.md | Datenschutz, AI-Call-Pipeline, Failsafe | -| RBAC | b-reference/platform/rbac.md | Berechtigungen, Mandate- und Feature-Rollen | +| RBAC | b-reference/platform/rbac.md | 4-Stufen-Modell, Template-Rollen, Resolution, Datenmodell | +| Datenbank-Architektur | b-reference/platform/database-architecture.md | Interface-Pattern, Connector, Auto-Init, DB-Liste | | Navigation | b-reference/platform/navigation.md | Menü-Struktur, Admin-Seiten, API | +## Aktive Arbeiten (c-work) + +| Thema | Datei | Wann laden | +|-------|-------|------------| +| Automation Unification | c-work/1-plan/2026-04-automation-unification.md | Refactoring v1/v2/Workspace | +| Web Image Search | c-work/1-plan/2026-03-web-image-search.md | WEB_SEARCH_MEDIA Feature | + ## Prozess & Betrieb | Thema | Datei | Wann laden | |-------|-------|------------| | Coding-Regeln | d-guides/coding-conventions.md | Naming, Patterns, Anti-Patterns | | Testing-Strategie | d-guides/testing-strategy.md | Testpyramide, AC-Format, Test-Pfade | -| Deployment | d-guides/deployment.md | Release-Prozess, Environments | | Dev-Setup | d-guides/dev-setup.md | Lokale Umgebung starten | +| Secrets-Verschluesselung | d-guides/encrypt-env-secrets.md | Env-Dateien verschluesseln | +| Google OAuth | d-guides/google-oauth-setup.md | OAuth Auth/Data Apps einrichten | +| Security-Migration | d-guides/security-migration-guide.md | JWT Cookie Migration | | Doc-Sync Cursor-Rule | d-guides/doc-sync.mdc | Cursor-Regel installieren, Doku-Workflow | + +## Compliance & Sicherheit + +| Thema | Datei | Wann laden | +|-------|-------|------------| +| Sicherheitsuebersicht | e-compliance/security-overview.md | Kunden/Audit: DSGVO, RBAC, Verschluesselung | +| Neutralisierung Detail | e-compliance/neutralisierung-detail.md | Technische Neutralisierungs-Flows | diff --git a/a-strategy/investor/investor-doc-2025-10.md b/a-strategy/investor/2025-10-investor-detail.md similarity index 100% rename from a-strategy/investor/investor-doc-2025-10.md rename to a-strategy/investor/2025-10-investor-detail.md diff --git a/a-strategy/investor/images/doc_platform_01_platform_overview.jpg b/a-strategy/investor/images/platform-01-platform-overview.jpg similarity index 100% rename from a-strategy/investor/images/doc_platform_01_platform_overview.jpg rename to a-strategy/investor/images/platform-01-platform-overview.jpg diff --git a/a-strategy/investor/images/doc_platform_02_customer_story.jpg b/a-strategy/investor/images/platform-02-customer-story.jpg similarity index 100% rename from a-strategy/investor/images/doc_platform_02_customer_story.jpg rename to a-strategy/investor/images/platform-02-customer-story.jpg diff --git a/a-strategy/investor/images/doc_platform_03_workflow_system.jpg b/a-strategy/investor/images/platform-03-workflow-system.jpg similarity index 100% rename from a-strategy/investor/images/doc_platform_03_workflow_system.jpg rename to a-strategy/investor/images/platform-03-workflow-system.jpg diff --git a/a-strategy/investor/images/doc_platform_04_microservice_architecture.jpg b/a-strategy/investor/images/platform-04-microservice-architecture.jpg similarity index 100% rename from a-strategy/investor/images/doc_platform_04_microservice_architecture.jpg rename to a-strategy/investor/images/platform-04-microservice-architecture.jpg diff --git a/a-strategy/investor/images/doc_platform_05_rbac_system.jpg b/a-strategy/investor/images/platform-05-rbac-system.jpg similarity index 100% rename from a-strategy/investor/images/doc_platform_05_rbac_system.jpg rename to a-strategy/investor/images/platform-05-rbac-system.jpg diff --git a/a-strategy/investor/images/doc_platform_06_ui_architecture.jpg b/a-strategy/investor/images/platform-06-ui-architecture.jpg similarity index 100% rename from a-strategy/investor/images/doc_platform_06_ui_architecture.jpg rename to a-strategy/investor/images/platform-06-ui-architecture.jpg diff --git a/a-strategy/investor/images/doc_platform_07_big_picture_and_future_vision.jpg b/a-strategy/investor/images/platform-07-big-picture-and-future-vision.jpg similarity index 100% rename from a-strategy/investor/images/doc_platform_07_big_picture_and_future_vision.jpg rename to a-strategy/investor/images/platform-07-big-picture-and-future-vision.jpg diff --git a/a-strategy/investor/doc_platform_big_picture.css b/a-strategy/investor/platform-big-picture.css similarity index 100% rename from a-strategy/investor/doc_platform_big_picture.css rename to a-strategy/investor/platform-big-picture.css diff --git a/a-strategy/investor/logo2.png b/a-strategy/investor/poweron-logo.png similarity index 100% rename from a-strategy/investor/logo2.png rename to a-strategy/investor/poweron-logo.png diff --git a/a-strategy/investor/doc_referenzen.html b/a-strategy/investor/referenzen.html similarity index 100% rename from a-strategy/investor/doc_referenzen.html rename to a-strategy/investor/referenzen.html diff --git a/a-strategy/investor/doc_user_main.html b/a-strategy/investor/user-main.html similarity index 100% rename from a-strategy/investor/doc_user_main.html rename to a-strategy/investor/user-main.html diff --git a/a-strategy/market/AI-Extraktion und BUHA-Sync.html b/a-strategy/market/ai-extraktion-buha-sync.html similarity index 100% rename from a-strategy/market/AI-Extraktion und BUHA-Sync.html rename to a-strategy/market/ai-extraktion-buha-sync.html diff --git a/a-strategy/market/langdock-markteinschaetzung.md b/a-strategy/market/langdock-markteinschaetzung.md index cea69f3..ddd52f1 100644 --- a/a-strategy/market/langdock-markteinschaetzung.md +++ b/a-strategy/market/langdock-markteinschaetzung.md @@ -1,4 +1,4 @@ -# Markteinschaetzung: Langdock (Berlin) vs. PowerON +# Markteinschaetzung: Langdock (Berlin) vs. PowerOn **Dokument:** Markt- und Wettbewerbsanalyse mit strategischer Empfehlung **Stand:** 23. Maerz 2026 @@ -10,9 +10,9 @@ **Langdock** ist eine horizontale Enterprise-KI-Adoptionsplattform (Chat, Workflows, Agents, Integrationen, API) mit Fokus auf sichere, modellagnostische KI fuer das gesamte Unternehmen. Sitz Berlin, tausende Kunden, schnelles Wachstum. -**PowerON** positioniert sich auf poweron.swiss als *"The Swiss Pocket Knife of AI for SMEs"* -- eine universelle KI-Plattform fuer Schweizer KMU mit DSGVO-Compliance und Schweizer Hosting. Im Code steckt deutlich mehr: eine mandantenfaehige SaaS-Plattform mit RBAC, Feature-Instanzen, Billing/Subscription, vertikalen Fachmodulen (Treuhand, Immobilien, CommCoach) und einem einzigartigen **Daten-Neutralisierungs-Feature**. +**PowerOn** positioniert sich auf poweron.swiss als *"The Swiss Pocket Knife of AI for SMEs"* -- eine universelle KI-Plattform fuer Schweizer KMU mit DSGVO-Compliance und Schweizer Hosting. Im Code steckt deutlich mehr: eine mandantenfaehige SaaS-Plattform mit RBAC, Feature-Instanzen, Billing/Subscription, vertikalen Fachmodulen (Treuhand, Immobilien, CommCoach) und einem einzigartigen **Daten-Neutralisierungs-Feature**. -**Kernbefund:** Die oeffentliche Positionierung von PowerON ueberschneidet sich staerker mit Langdock als noetig. Das Produkt hat genuegend einzigartige Tiefe, um sich klar abzugrenzen -- die Botschaft muss geschaerft werden. +**Kernbefund:** Die oeffentliche Positionierung von PowerOn ueberschneidet sich staerker mit Langdock als noetig. Das Produkt hat genuegend einzigartige Tiefe, um sich klar abzugrenzen -- die Botschaft muss geschaerft werden. --- @@ -44,7 +44,7 @@ --- -## 3. PowerON -- Oeffentliche Positionierung (poweron.swiss) +## 3. PowerOn -- Oeffentliche Positionierung (poweron.swiss) ### 3.1 Claim und Zielgruppe @@ -86,7 +86,7 @@ Im Code existiert dafuer ein eigenes Feature-Modul (`neutralization`) mit Playgr --- -## 4. PowerON -- Realitaet im Code (ueber die Homepage hinaus) +## 4. PowerOn -- Realitaet im Code (ueber die Homepage hinaus) Die Codebase zeigt erheblich mehr Tiefe als die Website kommuniziert: @@ -115,13 +115,13 @@ Die Codebase zeigt erheblich mehr Tiefe als die Website kommuniziert: ### 4.3 Gap: Homepage vs. Code -Die Website zeigt PowerON als **generische KI-Plattform**. Der Code offenbart eine **spezialisierte SaaS mit Branchen-Features, Abrechnungslogik und tiefer Mandantenfaehigkeit** -- davon erfaehrt der Besucher von poweron.swiss aktuell nichts. +Die Website zeigt PowerOn als **generische KI-Plattform**. Der Code offenbart eine **spezialisierte SaaS mit Branchen-Features, Abrechnungslogik und tiefer Mandantenfaehigkeit** -- davon erfaehrt der Besucher von poweron.swiss aktuell nichts. --- -## 5. Ueberschneidungen PowerON vs. Langdock +## 5. Ueberschneidungen PowerOn vs. Langdock -| Thema | Langdock | PowerON | +| Thema | Langdock | PowerOn | |-------------------------|-----------------------------------|-----------------------------------------------| | Firmenweite KI-Nutzung | Kerngeschaeft | Workspace, Chatbot, Teamsbot | | Chat & Konversationen | Chat-Produkt | Chatbot + Workspace-Editor | @@ -131,13 +131,13 @@ Die Website zeigt PowerON als **generische KI-Plattform**. Der Code offenbart ei | Integrationen | Viele Standard-Integrationen | M365, Google, Jira, ClickUp, SharePoint, DB | | Governance / Security | EU-Hosting, DSGVO | Schweizer Hosting, DSGVO, RBAC, Neutralisierung| -**Die generische KI-Schicht ueberschneidet sich stark.** Wer nur "ChatGPT im Unternehmen" sucht, sieht PowerON und Langdock in der gleichen Kategorie -- wobei Langdock dort durch Marktgroesse und Bekanntheit dominiert. +**Die generische KI-Schicht ueberschneidet sich stark.** Wer nur "ChatGPT im Unternehmen" sucht, sieht PowerOn und Langdock in der gleichen Kategorie -- wobei Langdock dort durch Marktgroesse und Bekanntheit dominiert. --- -## 6. Klare Unterschiede (PowerON-Vorteile) +## 6. Klare Unterschiede (PowerOn-Vorteile) -| Dimension | PowerON | Langdock | +| Dimension | PowerOn | Langdock | |----------------------------|----------------------------------------------|---------------------------------| | **Daten-Neutralisierung** | 5-Stufen-Prozess, eigenes Feature | Nicht vorhanden | | **Schweizer Hosting** | 100% Schweiz, lokale Datensouveraenitaet | EU (nicht CH-spezifisch) | @@ -151,13 +151,13 @@ Die Website zeigt PowerON als **generische KI-Plattform**. Der Code offenbart ei ### 7.1 Kernproblem der aktuellen Positionierung -Die Homepage positioniert PowerON als **generische KI-Plattform** -- genau dort, wo Langdock (und Microsoft Copilot, und Google Gemini for Workspace, und dutzende weitere) bereits mit massiv mehr Ressourcen, Brand und Kundenstamm unterwegs sind. +Die Homepage positioniert PowerOn als **generische KI-Plattform** -- genau dort, wo Langdock (und Microsoft Copilot, und Google Gemini for Workspace, und dutzende weitere) bereits mit massiv mehr Ressourcen, Brand und Kundenstamm unterwegs sind. Ein Drei-Personen-Team kann dieses Rennen nicht auf der generischen Ebene gewinnen. Jede Stunde, die in "wir sind auch ein KI-Chat mit Workflows" investiert wird, ist eine Stunde, in der Langdock mit tausenden Kunden und Millionen-Revenue den gleichen Pitch perfektioniert. ### 7.2 Empfohlene Richtung: Vertikale KI-SaaS mit Neutralisierungs-Moat -**PowerON sollte sich als vertikale, mandantenfaehige KI-SaaS positionieren -- nicht als generische KI-Plattform.** +**PowerOn sollte sich als vertikale, mandantenfaehige KI-SaaS positionieren -- nicht als generische KI-Plattform.** Die empfohlene Positionierung stuetzt sich auf drei Saeulen: @@ -165,7 +165,7 @@ Die empfohlene Positionierung stuetzt sich auf drei Saeulen: Die Daten-Neutralisierung ist das Feature, das kein Wettbewerber in dieser Form hat. Statt es als eines von fuenf technischen Features aufzulisten, sollte es das **zentrale Verkaufsargument** werden: -- *"PowerON ist die einzige KI-Plattform, bei der LLMs niemals Ihre sensiblen Daten sehen -- garantiert durch einen 5-Stufen-Neutralisierungsprozess."* +- *"PowerOn ist die einzige KI-Plattform, bei der LLMs niemals Ihre sensiblen Daten sehen -- garantiert durch einen 5-Stufen-Neutralisierungsprozess."* - Besonders relevant fuer: Treuhand, Kanzleien, Gesundheitswesen, Finanzdienstleister, oeffentliche Verwaltung. - Alle anderen Features (Chat, Automation, Workspace) werden **unter dem Neutralisierungs-Dach** vermarktet, nicht daneben. @@ -173,7 +173,7 @@ Die Daten-Neutralisierung ist das Feature, das kein Wettbewerber in dieser Form Der Code enthaelt bereits Treuhand, Immobilien und CommCoach. Diese Module gehoeren sichtbar auf die Website und in den Pitch: -- Dedizierte Landing Pages pro Branche: "PowerON fuer Treuhandbueros", "PowerON fuer Immobilienverwaltungen". +- Dedizierte Landing Pages pro Branche: "PowerOn fuer Treuhandbueros", "PowerOn fuer Immobilienverwaltungen". - Jede Branchenseite zeigt den konkreten Workflow: Dokument rein, Neutralisierung, KI-Verarbeitung, Ergebnis zurueck -- mit branchenspezifischem Vokabular. - Der Feature-Store und das Mandantenmodell sind dabei die **technische Grundlage**, nicht der Marketing-Lead. @@ -198,7 +198,7 @@ Der Code enthaelt bereits Treuhand, Immobilien und CommCoach. Diese Module gehoe ### 7.4 Wovon ich abrate 1. **Breites Integrations-Rennen:** Langdock hat Dutzende Integrationen; dieses Rennen zu gewinnen ist mit kleinem Team unrealistisch. Besser: die vorhandenen Integrationen (M365, SharePoint, Google) **tief und zuverlaessig** halten und pro Branche gezielt erweitern (z. B. ABACUS/Bexio fuer Treuhand). -2. **"KI-Chat fuer alle":** Das ist Langdocks Kernmarkt. PowerON gewinnt nicht, indem es den gleichen Pitch macht, sondern indem es den Pitch **spezifischer** macht. +2. **"KI-Chat fuer alle":** Das ist Langdocks Kernmarkt. PowerOn gewinnt nicht, indem es den gleichen Pitch macht, sondern indem es den Pitch **spezifischer** macht. 3. **Feature-Breite vor Feature-Tiefe:** Lieber drei Branchen exzellent bedienen als zehn Features halb fertig zeigen (vgl. `features.feature5.title`-Platzhalter auf der Homepage). ### 7.5 Mittelfristiger Zielzustand (6-12 Monate) @@ -212,13 +212,13 @@ Der Code enthaelt bereits Treuhand, Immobilien und CommCoach. Diese Module gehoe ## 8. Zusammenfassung in einem Satz -> PowerON sollte aufhoeren, Langdocks Spiel zu spielen ("generische KI fuer alle"), und stattdessen das eigene Spiel spielen: **sichere, neutralisierte KI fuer regulierte Schweizer Branchen** -- ein Markt, den Langdock strukturell nicht adressiert. +> PowerOn sollte aufhoeren, Langdocks Spiel zu spielen ("generische KI fuer alle"), und stattdessen das eigene Spiel spielen: **sichere, neutralisierte KI fuer regulierte Schweizer Branchen** -- ein Markt, den Langdock strukturell nicht adressiert. --- ## 9. Quellen und Methodik -- **PowerON:** Homepage poweron.swiss (Stand Maerz 2026) sowie Codeanalyse gateway und frontend_nyla. +- **PowerOn:** Homepage poweron.swiss (Stand Maerz 2026) sowie Codeanalyse gateway und frontend_nyla. - **Langdock:** Oeffentliche Website langdock.com und Produktunterseiten, Presseartikel (Stand Maerz 2026). - Keine vertraulichen Materialien verwendet. Fuer verbindliche Pricing- und SLA-Vergleiche ist Herstellerkontakt noetig. diff --git a/a-strategy/market/langdock-markteinschaetzung-poweron.pdf b/a-strategy/market/langdock-markteinschaetzung.pdf similarity index 100% rename from a-strategy/market/langdock-markteinschaetzung-poweron.pdf rename to a-strategy/market/langdock-markteinschaetzung.pdf diff --git a/a-strategy/market/playground-marketing-pitch.md b/a-strategy/market/playground-marketing-pitch.md index fc9a71d..8285154 100644 --- a/a-strategy/market/playground-marketing-pitch.md +++ b/a-strategy/market/playground-marketing-pitch.md @@ -30,7 +30,7 @@ 1) **🔒 Neutralizer (Datenschutz-First)** - **Automatische Anonymisierung** sensibler Daten vor der KI-Verarbeitung - **DSGVO-konform**, transparente Regeln – du bestimmst, was neutralisiert wird - - **Validierungs-Roadmap**: Level 1 Datenschutz jetzt; Level 2 kontextueller IP-Schutz folgt mit externem Validat/Cert (ETH/HSG/Partner) + - **Validierungs-Roadmap**: Level 1 Datenschutz jetzt; Level 2 kontextueller IP-Schutz folgt mit externem Validator/Zertifizierung (ETH/HSG/Partner) 2) **♾️ Unlimitierte Verarbeitung** - **Keine Token-Limits** – Dokumente jeder Grösse verarbeiten diff --git a/a-strategy/product-strategy.md b/a-strategy/product-strategy.md index 552c978..5bf5fb4 100644 --- a/a-strategy/product-strategy.md +++ b/a-strategy/product-strategy.md @@ -106,7 +106,7 @@ PowerOn ist eine **Multi-Agent-KI-Plattform für Enterprise-Workflows** mit inte ### 2.2 Alleinstellungsmerkmale (USP) #### **1. Integrierter Datenschutz-Neutralisierer** -- **Einzigartig**: Erste Plattform mit eingebautem Datenschutz-Schutz +- **Einzigartig**: Erste Plattform mit eingebautem Datenschutz-Neutralisierer - **Vorteil**: Unternehmen können KI nutzen ohne Datenschutzrisiken - **Differenzierung**: Kein Konkurrent bietet diese Funktionalität @@ -169,7 +169,7 @@ PowerOn ist eine **Multi-Agent-KI-Plattform für Enterprise-Workflows** mit inte - **3 Workflow-Modi**: - **Learning-Mode**: Iterative Plan-Act-Observe-Refine-Schleife - **Actionplan-Mode**: Batch-Planung mit sequenzieller Ausführung - - **Automation-Mode**: Automatisierte vordefinierte Workflosw + - **Automation-Mode**: Automatisierte vordefinierte Workflows - **Adaptive Learning Engine**: Lernt aus Validierungsergebnissen und passt Prompts an - **Intelligente Task-Planung**: Automatische Aufschlüsselung komplexer Anfragen in ausführbare Schritte - **Echtzeit-Validierung**: Kontinuierliche Qualitätskontrolle und automatische Verbesserungen @@ -701,7 +701,7 @@ Wir sind aktuell im Monat 10 des Projekts und haben noch 2 Monate bis zur Seed-R #### **KI-Services** - **OpenAI**: GPT-4, DALL-E, Whisper - **Anthropic**: Claude, Constitutional AI -- **Google**: PaLM, Bard, Vertex AI +- **Google**: Gemini, Vertex AI - **Azure**: Cognitive Services, OpenAI Service --- diff --git a/a-strategy/product-vision.md b/a-strategy/product-vision.md new file mode 100644 index 0000000..0375b07 --- /dev/null +++ b/a-strategy/product-vision.md @@ -0,0 +1,71 @@ + + + +# PowerOn PORTA -- Produktvision + +## Was ist PowerOn PORTA? + +PowerOn PORTA ist eine **Multi-Agent-KI-Plattform für Enterprise-Workflows** mit integriertem Datenschutz-Neutralisierer. Die Plattform ermöglicht Unternehmen, KI-Transformation zu beschleunigen, ohne Datenschutzrisiken einzugehen. + +**PORTA** = **P**owerful **O**rchestration & **R**eal-**T**ime **A**utomation + +## Value Proposition + +| Problem | PowerOn-Lösung | +|---------|---------------| +| 80% der Unternehmen können ChatGPT/Copilot nicht nutzen wegen Datenschutz | Integrierter Datenschutz-Neutralisierer: sensible Daten verlassen nie das Unternehmen | +| Token-Limits bei grossen Dokumenten | Unlimitierte Verarbeitung durch intelligente Chunking-Strategien | +| Isolierte KI-Services arbeiten nicht zusammen | Einheitliche Plattform mit nahtloser Integration aller Services | +| Komplexe, unvorhersehbare Pricing-Modelle | Transparentes Pauschalpricing pro Monat/Jahr | +| End-to-End-Workflows sind Expertensache | Schrittweise Integration, beginnend mit einfachen Use Cases | + +## Zielkunden + +- **Primär:** Mittelständische Unternehmen (100-2'000 MA) in datenschutz-sensitiven Branchen +- **Branchen:** Finanzdienstleistungen, Treuhand, Immobilien, Professional Services, Healthcare +- **Geografie:** DACH (initial), EU + US (Expansion) + +## Alleinstellungsmerkmale (USPs) + +1. **Datenschutz-Neutralisierer:** Einzige Plattform mit integrierter Echtzeit-Neutralisierung, die sensible Daten vor dem Verlassen des Unternehmens anonymisiert und nach Verarbeitung reanonymisiert. + +2. **Multi-Agent-Orchestrierung:** Spezialisierte KI-Agenten pro Aufgabe (Chat, Workflow, Voice, RAG, Automation), koordiniert durch eine zentrale Engine mit 40+ Tools. + +3. **Modellunabhängigkeit:** Integration mit Anthropic, OpenAI, Mistral, Perplexity, Tavily, Private LLM -- kein Vendor Lock-in, immer das beste Modell pro Aufgabe. + +4. **Feature-Store-Architektur:** Mandanten aktivieren Features modular (Workspace, Automation, CommCoach, Trustee etc.) -- skaliert von Einzelanwendung bis Full-Suite. + +5. **Voice-First:** Integrierte STT/TTS in den meisten Sprachen, nahtlos im Workflow nutzbar. + +## Technologie-Stack (Kurzübersicht) + +| Komponente | Technologie | +|-----------|-------------| +| Frontend Nyla | React/TypeScript, Vite | +| Gateway | FastAPI, Python, PostgreSQL, pgvector | +| Private LLM | Python, Ollama | +| Teams Bot | TypeScript/Node.js | + +Detaillierte Architektur: → `b-reference/product.md` + +## Umsatzmodell + +- **SaaS-Abonnement:** Pauschal pro Monat/Jahr, Features modular zubuchbar +- **Ziel-Bruttomarge:** 75-85% nach Skalierung +- **Preismodell:** Nicht nutzungsbasiert pro Token, sondern unlimitiert innerhalb des Abonnements + +## Strategische Phasen + +| Phase | Zeitraum | Fokus | +|-------|---------|-------| +| Launch | 2025-2026 | Core-Plattform, 3-5 Referenzkunden, DACH | +| Growth | 2026-2027 | Feature-Expansion, branchenspezifische Templates, Partner | +| Scale | 2027+ | Internationalisierung, Agenten-Marktplatz, Branchenlösungen | + +## Verweise + +- Strategie & PMF: → `a-strategy/product-strategy.md` +- Roadmap: → `a-strategy/roadmap.md` +- Marktanalyse: → `a-strategy/market/` +- Investor-Zusammenfassung: → `a-strategy/investor/investor-summary.md` +- Komponentenübersicht: → `b-reference/product.md` diff --git a/a-strategy/roadmap-2026-02/20260224_PowerOn_Preismodell.pdf b/a-strategy/roadmap-2026-02/2026-02-24-preismodell.pdf similarity index 100% rename from a-strategy/roadmap-2026-02/20260224_PowerOn_Preismodell.pdf rename to a-strategy/roadmap-2026-02/2026-02-24-preismodell.pdf diff --git a/a-strategy/roadmap-2026-02/20260224_PowerOn_Roadmap.pdf b/a-strategy/roadmap-2026-02/2026-02-24-roadmap.pdf similarity index 100% rename from a-strategy/roadmap-2026-02/20260224_PowerOn_Roadmap.pdf rename to a-strategy/roadmap-2026-02/2026-02-24-roadmap.pdf diff --git a/a-strategy/roadmap-2026-02/roadmap_presentation.html b/a-strategy/roadmap-2026-02/roadmap-presentation.html similarity index 100% rename from a-strategy/roadmap-2026-02/roadmap_presentation.html rename to a-strategy/roadmap-2026-02/roadmap-presentation.html diff --git a/a-strategy/roadmap.md b/a-strategy/roadmap.md index cceccd4..4c9da3f 100644 --- a/a-strategy/roadmap.md +++ b/a-strategy/roadmap.md @@ -17,7 +17,7 @@ Dieses Dokument beschreibt die vorhandenen Features der PowerOn-Plattform, das a PowerOn ist eine **Multi-Mandanten-Plattform**: -- **Mandatenmodell:** Jede Organisation/Kunde wird als Mandant abgebildet. +- **Mandantenmodell:** Jede Organisation/Kunde wird als Mandant abgebildet. - **Datenisolation:** Daten eines Mandanten sind strikt von anderen Mandanten getrennt. - **Zugehörigkeitsprüfung:** Bei jedem Zugriff wird geprüft, ob der Nutzer dem Mandanten angehört (via `UserMandate`). - **Request-Kontext:** `mandateId` und `featureInstanceId` werden über Header (`X-Mandate-Id`, `X-Feature-Instance-Id`) übergeben. diff --git a/b-reference/gateway/automation.md b/b-reference/gateway/automation.md index fa559fe..4e4e4b9 100644 --- a/b-reference/gateway/automation.md +++ b/b-reference/gateway/automation.md @@ -4,21 +4,19 @@ # Automation -## Überblick +## Ueberblick -PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified Action Library** (`workflows/methods/` + `ActionExecutor`) teilen, aber unterschiedliche **Persistenz**, **Scheduler** und **Execution Engines** nutzen. Die Business Spec beschreibt eine **Ziel-Architektur**: konsolidierte Plattform mit klarer Trennung Mandant / Feature / Feature-Instanz, skalierbarer **Toolbox**-Schicht für Agent-Tools und einem **vereinheitlichten Workflow-Datenmodell** (Draft/Published, Runs, Tracing). +PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified Action Library** (`workflows/methods/` + `ActionExecutor`) teilen, aber unterschiedliche **Persistenz**, **Scheduler** und **Execution Engines** nutzen. Die Business Spec beschreibt eine **Ziel-Architektur**: konsolidierte Plattform mit klarer Trennung Mandant / Feature / Feature-Instanz, skalierbarer **Toolbox**-Schicht fuer Agent-Tools und einem **vereinheitlichten Workflow-Datenmodell** (Draft/Published, Runs, Tracing). **Leitentscheide (Business Spec):** -- Keine Migration von Altdaten für Automation v1 (nicht produktiv); v1 bleibt bis zur Ablösung. -- **Vier Säulen:** AI Service (Agent, Streaming, Neutralisierung, Failover), Graphical Editor (Flow + künftig Editor-Chat/UDB), Automation Scheduler (Cron/Events/Webhooks), **Unified Action Library** mit Toolboxes. -- **Inkonsistenzen heute:** zwei Workflow-Datenwelten (v1 vs Automation2), zwei DB-Schemas, zwei Scheduler-Patterns, zwei Engines — Ausrichtung über Zielmodell und Übernahme bewährter v1-Scheduler-Patterns in Richtung v2. +- Keine Migration von Altdaten fuer Automation v1 (nicht produktiv); v1 bleibt bis zur Abloesung. +- **Vier Saeulen:** AI Service (Agent, Streaming, Neutralisierung, Failover), Graphical Editor (Flow + kuenftig Editor-Chat/UDB), Automation Scheduler (Cron/Events/Webhooks), **Unified Action Library** mit Toolboxes. +- **Inkonsistenzen heute:** zwei Workflow-Datenwelten (v1 vs Automation2), zwei DB-Schemas, zwei Scheduler-Patterns, zwei Engines -- Ausrichtung ueber Zielmodell und Uebernahme bewaehrter v1-Scheduler-Patterns in Richtung v2. --- -## Business Spec (Zusammenfassung) - -**Ist-Systeme:** +## Ist-Systeme (Gateway, Stand 2026-04-05) | System | Kurzbeschreibung | |--------|------------------| @@ -26,41 +24,9 @@ PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified | **Automation2** | n8n-Style Graph; Run/Task; Pause/Resume; Human-in-the-loop; weniger robuster Scheduler-Sync als v1. | | **AI Workspace** | Chat-Agent mit vielen Tools + RAG; kein persistiertes Workflow-Modell wie im Graph-Editor. | -**Gemeinsame technische Basis:** Alle genannten Pfade führen über **`ActionExecutor.executeAction()`** zur Method/Action Library; der Workspace-Agent mappt `dynamicMode`-Actions via **`ActionToolAdapter`** auf Tools; Automation2-Nodes tragen `_method` / `_action`. +**Gemeinsame technische Basis:** Alle genannten Pfade fuehren ueber **`ActionExecutor.executeAction()`** zur Method/Action Library; der Workspace-Agent mappt `dynamicMode`-Actions via **`ActionToolAdapter`** auf Tools; Automation2-Nodes tragen `_method` / `_action`. -**Toolbox-Konzept (Ziel):** Statt alle Tools flach zu exponieren, **thematische Toolboxes** (`core`, `ai`, `email`, `sharepoint`, `workflow`, …) mit Meta-Tool **`requestToolbox`** — Agent startet schlank, fordert Spezial-Toolboxes nach Bedarf; **`availableToolboxes`** ist pro Feature/RBAC/Connections filterbar. - -**Sub-Agents:** Datenintensive Features (z. B. Trustee) kapseln Schema-Wissen in einem **Feature Data Agent**; Main-Agent sieht ein aggregiertes Tool (`queryFeatureInstance`), nicht Dutzende Tabellen-Tools. - -**Agent-Run (State Machine, konzeptionell):** `IDLE` → `THINKING` → bei Tool-Calls `EXECUTING_TOOLS` → optional `TOOLBOX_ESCALATION` / `SUB_AGENT_CALL` → `COMPLETED` / `FAILED` / `CANCELLED` (Limits: `maxRounds`, `maxCostCHF`). - -**RBAC (Kurz):** Zwei getrennte Template-Systeme — **System-Template-Rollen** (Mandant: admin/user/viewer) vs **Feature-Template-Rollen** (Instanz: z. B. workspace-admin); keine Vermischung. Auflösung: höchste Priorität gewinnt bei DATA; View OR über Rollen der Top-Priorität; Item-Spezifität exact > prefix > generic. - ---- - -## Datenmodell (Zusammenfassung) - -**Gateway-Schichten (Data Model Doc):** Frontend → **Features** (instanzgebunden) → **Services** (request-scoped) → **Shared Infrastructure** (`workflows/methods`, `workflows/processing`, `workflows/automation2`, `interfaces`, `aicore`, `datamodels`, `security`). - -**Feature-Container-Pattern:** `features//` mit `main*.py` (FEATURE_CODE, UI/RESOURCE/TEMPLATE_ROLES), `routeFeature*.py`, `interfaceFeature*.py`, `datamodelFeature*.py`, optional `nodeDefinitions/`, `service*/`. Discovery über `registry.py` / `ServiceHub` ohne manuelles Wiring sämtlicher Router. - -**Ziel-Entitäten (vereinheitlichtes Workflow-Modell, Spec):** - -| Entität | Zweck | -|---------|--------| -| **Workflow** | Metadaten, Mandant/Instanz, `active`, optional `eventId` (Scheduler-Job), Zeiger auf aktuelle Version | -| **WorkflowVersion** | Immutabler Snapshot: `graph` (Nodes/Connections), `invocations` (Trigger: manual, schedule, webhook, …), Status **draft / published / archived** | -| **WorkflowRun** | Eine Ausführung: Status (pending → running → completed/failed/paused/cancelled), Trigger-Metadaten, `nodeOutputs`, Pause-/Resume-Kontext, Kostenfelder | -| **RunStepLog** | Pro Node: Input-Snapshot, Output, Dauer, Fehler, Token-Nutzung | -| **HumanTask** | Menschliche Zwischenaufgabe bei Pause (approval/form); Status pending → completed/cancelled/expired | - -**Invarianten (Zielmodell):** Pro Workflow höchstens **eine** `PUBLISHED` Version; Scheduler bindet an die veröffentlichte Version. Pause z. B. durch Human-Task-Nodes oder E-Mail-Warten; Resume mit geliefertem Ergebnis. - -**Toolbox-Datenmodell (Ziel):** `ToolboxDefinition` (id, label i18n, `tools[]`, optional `requiresConnection`), `ToolboxRegistry`, erweiterte `AgentConfig` mit `initialToolboxes` / `availableToolboxes`. - ---- - -## Automation v1 vs v2 +### Automation v1 vs v2 (Ist-Vergleich) | Aspekt | Automation v1 | Automation2 (v2) | |--------|----------------|------------------| @@ -68,20 +34,74 @@ PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified | DB / Schema | `poweron_automation` | `poweron_automation2` | | Scheduler | Inkrementell, Callback `automation.changed`, `eventId` auf `AutomationDefinition` | Full re-register, Callback `automation2.workflow.changed` | | Engine | `WorkflowManager` + `WorkflowProcessor` + `modeAutomation` | `executionEngine.executeGraph` | -| Stärken | Bewährtes Scheduling, Execution-Logs | Branching, Loops, Pause/Resume, visuelles Editing | +| Staerken | Bewaehrtes Scheduling, Execution-Logs | Branching, Loops, Pause/Resume, visuelles Editing | -**Konsolidierungsrichtung (Spec):** v1-Scheduler-Muster (inkrementell, Job-Handle, Reload+`active`-Check vor Run, capped Logs, `sysCreatedBy` für Kontext) in die v2-Welt übernehmen; einheitliches **Workflow/Version/Run**-Konzept langfristig über beide Welten. +**Konsolidierungsrichtung (Spec):** v1-Scheduler-Muster (inkrementell, Job-Handle, Reload+`active`-Check vor Run, capped Logs, `sysCreatedBy` fuer Kontext) in die v2-Welt uebernehmen; einheitliches **Workflow/Version/Run**-Konzept langfristig ueber beide Welten. --- -## Schlüssel-Dateien +## Datenmodell + +### Ist: Konkrete Modelle (Gateway, Stand 2026-04-05) + +| Modell | Datei | DB | Beschreibung | +|--------|-------|-----|-------------| +| `AutomationDefinition` | `features/automation/datamodelFeatureAutomation.py` | `poweron_automation` | v1 Template mit `schedule`, `placeholders`, `eventId` | +| `ChatWorkflow` | `datamodels/datamodelChat.py` | `poweron_chat` | v1 Execution-Artefakt (Status, Tasks, Messages) | +| `Automation2Workflow` | `features/automation2/datamodelFeatureAutomation2.py` | `poweron_automation2` | v2 Graph mit `active`, inline `graph`, `invocations` | +| `Automation2WorkflowRun` | gleiche Datei | `poweron_automation2` | v2 Run: Status `running`/`paused`/`completed`/`failed`, `nodeOutputs`, `context` | +| `Automation2HumanTask` | gleiche Datei | `poweron_automation2` | Human-Task: Status `pending`/`completed`/`rejected` | + +**Hinweis:** Es gibt **keine** `WorkflowVersion`-Tabelle -- v2 speichert den Graph direkt in `Automation2Workflow`. Tracing ist `nodeOutputs` im Run, nicht ein separates `RunStepLog`-Entity. + +### Ziel: Vereinheitlichtes Workflow-Modell (Spec -- nicht implementiert) + +| Entitaet | Zweck | Heute approximiert durch | +|----------|-------|--------------------------| +| **Workflow** | Metadaten, Mandant/Instanz, `active`, `eventId`, Zeiger auf aktuelle Version | `Automation2Workflow` (ohne Versioning) | +| **WorkflowVersion** | Immutabler Snapshot: `graph`, `invocations`, Status draft/published/archived | *nicht vorhanden* (Graph inline in Workflow) | +| **WorkflowRun** | Ausfuehrung mit Status, Trigger, `nodeOutputs`, Kostenfelder | `Automation2WorkflowRun` (ohne Kostenfelder, Statusmenge abweichend) | +| **RunStepLog** | Pro Node: Input, Output, Dauer, Fehler, Token | *nicht vorhanden* (approximiert durch `nodeOutputs`) | +| **HumanTask** | Menschliche Aufgabe bei Pause; Status pending/completed/cancelled/expired | `Automation2HumanTask` (Status: `pending`/`completed`/`rejected`, kein `expired`) | + +**Invarianten (Zielmodell):** Pro Workflow hoechstens **eine** `PUBLISHED` Version; Scheduler bindet an die veroeffentlichte Version. + +### Toolbox-Datenmodell (Ziel -- nicht im Gateway implementiert) + +`ToolboxDefinition` (id, label i18n, `tools[]`, optional `requiresConnection`), `ToolboxRegistry`, erweiterte `AgentConfig` mit `initialToolboxes` / `availableToolboxes`. + +> Kein Match im Gateway-Code fuer `requestToolbox`, `ToolboxDefinition` oder `availableToolboxes` (Stand 2026-04-05). + +### Gateway-Schichten + +Frontend -> **Features** (instanzgebunden) -> **Services** (request-scoped) -> **Shared Infrastructure** (`workflows/methods`, `workflows/processing`, `workflows/automation2`, `interfaces`, `aicore`, `datamodels`, `security`). + +**Feature-Container-Pattern:** `features//` mit `main*.py` (FEATURE_CODE, UI/RESOURCE/TEMPLATE_ROLES), `routeFeature*.py`, `interfaceFeature*.py`, `datamodelFeature*.py`, optional `nodeDefinitions/`, `service*/`. Discovery ueber `registry.py` / `ServiceHub`. + +--- + +## Ziel-Konzepte (Business Spec, nicht implementiert) + +**Toolbox-Konzept:** Statt alle Tools flach zu exponieren, **thematische Toolboxes** (`core`, `ai`, `email`, `sharepoint`, `workflow`, ...) mit Meta-Tool **`requestToolbox`** -- Agent startet schlank, fordert Spezial-Toolboxes nach Bedarf; **`availableToolboxes`** ist pro Feature/RBAC/Connections filterbar. + +**Sub-Agents:** Datenintensive Features (z. B. Trustee) kapseln Schema-Wissen in einem **Feature Data Agent**; Main-Agent sieht ein aggregiertes Tool (`queryFeatureInstance`), nicht Dutzende Tabellen-Tools. + +**Agent-Run (State Machine, konzeptionell -- nicht im Code):** `IDLE` -> `THINKING` -> bei Tool-Calls `EXECUTING_TOOLS` -> optional `TOOLBOX_ESCALATION` / `SUB_AGENT_CALL` -> `COMPLETED` / `FAILED` / `CANCELLED` (Limits: `maxRounds`, `maxCostCHF`). + +**RBAC (Kurz):** Zwei getrennte Template-Systeme -- **System-Template-Rollen** (Mandant: admin/user/viewer) vs **Feature-Template-Rollen** (Instanz: z. B. workspace-admin); keine Vermischung. + +--- + +## Schluessel-Dateien | Bereich | Typische Pfade (`gateway/modules/`) | |---------|-------------------------------------| | Action Library | `workflows/methods/`, `workflows/processing/` (`ActionExecutor`, `methodDiscovery`) | | Automation v1 Feature | `features/automation/` | -| Automation2 Feature | `features/automation2/`, `workflows/automation2/` | -| Agent ↔ Actions | `serviceCenter/services/serviceAgent/` (u. a. Tool-Registrierung, `ActionToolAdapter`) | +| Automation v1 Runtime | `workflows/automation/` (`mainWorkflow.py`, `subAutomationSchedule.py`) | +| Automation2 Feature | `features/automation2/` | +| Automation2 Runtime | `workflows/automation2/` (`executionEngine.py`, `subAutomation2Schedule.py`) | +| Agent <-> Actions | `serviceCenter/services/serviceAgent/` (Tool-Registrierung, `ActionToolAdapter`) | | Feature-Discovery | `system/registry.py`, `serviceHub/__init__.py` | | RBAC | `interfaces/interfaceRbac.py`, `security/`, Rollen/AccessRules in Datamodels | @@ -89,9 +109,9 @@ PowerOn hat **mehrere Automatisierungs-Spuren**, die sich die **gleiche Unified ## Regeln / Invarianten -1. **Eine Action Library:** Änderungen an Methods/Actions betreifen Workspace, Automation v1, Automation2 und Agent-Tools gleichzeitig — Kompatibilität und `actionId`-Stabilität beachten. -2. **RBAC strikt trennen:** Mandantenrollen vs Feature-Instanz-Rollen nicht mischen; Permissions über AccessRules und Prioritätsregeln. -3. **Scheduler vs Editor:** Ausführung und Zeitplanung gehören zur **Scheduler-Säule**; der Graph ist **Version**-gebunden (Draft vs Published). -4. **Tool-Skalierung:** Flache Tool-Listen skalieren nicht; Toolbox + `requestToolbox` + connection-basierte Verfügbarkeit sind das vorgesehene Gegenmittel. -5. **Zielmodell vs Ist:** `Workflow` / `WorkflowVersion` / `WorkflowRun` in der Spec sind **Zielarchitektur** — Abgleich mit produktivem Schema bei Implementierungsarbeiten erforderlich. +1. **Eine Action Library:** Aenderungen an Methods/Actions betreffen Workspace, Automation v1, Automation2 und Agent-Tools gleichzeitig -- Kompatibilitaet und `actionId`-Stabilitaet beachten. +2. **RBAC strikt trennen:** Mandantenrollen vs Feature-Instanz-Rollen nicht mischen; Permissions ueber AccessRules und Prioritaetsregeln. +3. **Scheduler vs Editor:** Ausfuehrung und Zeitplanung gehoeren zur **Scheduler-Saeule**; der Graph ist **Version**-gebunden (Draft vs Published). +4. **Tool-Skalierung:** Flache Tool-Listen skalieren nicht; Toolbox + `requestToolbox` + connection-basierte Verfuegbarkeit sind das vorgesehene Gegenmittel (Ziel, nicht implementiert). +5. **Zielmodell vs Ist:** `Workflow` / `WorkflowVersion` / `WorkflowRun` in der Spec sind **Zielarchitektur**. Ist-Modelle siehe oben (`Automation2Workflow`, `Automation2WorkflowRun` etc.). Abgleich bei Implementierungsarbeiten erforderlich. 6. **Neutralisierung / KI:** Plattformweit gelten die zentralen KI-Datenpfade (siehe `workflow.md` / Compliance); Automation nutzt dieselben Services wie der Rest des Gateways. diff --git a/b-reference/gateway/billing.md b/b-reference/gateway/billing.md index cceb717..1fa678c 100644 --- a/b-reference/gateway/billing.md +++ b/b-reference/gateway/billing.md @@ -1,4 +1,4 @@ - + @@ -24,27 +24,20 @@ Ohne gültige, zahlungswirksame Subscription (soweit implementiert) sollen **nur - **Mandant** ist die zentrale Kostenstelle; Benutzer können mehreren Mandanten angehören — Verbrauch wird pro Mandant getrennt erfasst. - Kosten entstehen an **AICore-Connectoren** (z. B. `anthropic`, `openai`, `perplexity`, `tavily`, `internal`); die erlaubte Provider-Liste ist dynamisch (Model Registry / Plugins). -### Abrechnungsmodelle (`BillingSettings.billingModel`) +### Abrechnungsmodell -| Modell | Kostenstelle | Kurzbeschreibung | -|--------|--------------|------------------| -| `PREPAY_MANDATE` | Mandant | Gemeinsames Guthaben für alle User des Mandanten | -| `PREPAY_USER` | User im Mandanten-Kontext | Eigenes Guthaben pro User; automatisches Startguthaben v. a. für **Root-Mandant** / Bootstrap (`defaultUserCredit`, z. B. 5 CHF) | -| `UNLIMITED` | — | Keine Guthabenlimitierung (interne Mandanten) | - -- **`CREDIT_POSTPAY`** ist entfernt; gespeicherte Legacy-Werte werden beim Lesen auf **`PREPAY_MANDATE`** normalisiert (ohne separate DB-Migration). -- **Audit-Hinweis:** Im Gateway-Kontext wird der operative Fokus als **PREPAY_MANDATE** (kein PREPAY_USER im Sinne des Standard-Mandanten-Produkts) geführt; **Root** bleibt im Konzept mit **PREPAY_USER** und Startguthaben beschrieben. +Im Gateway gibt es aktuell **kein `billingModel`-Feld** auf `BillingSettings`. Das operative Modell ist faktisch **PREPAY_MANDATE** (gemeinsames Mandantenkonto). Konzeptionell waren weitere Modelle (`PREPAY_USER`, `UNLIMITED`, `CREDIT_POSTPAY`) vorgesehen — diese sind im Code nicht implementiert. ### Kernentitäten -- **`BillingAccount`:** Guthaben (`balance` CHF), optional `userId` bei `PREPAY_USER`, `accountType` MANDATE | USER, `warningThreshold`, `enabled`. +- **`BillingAccount`:** Guthaben (`balance` CHF), `mandateId`, optional `userId`, `warningThreshold`, `enabled`. Kein explizites `accountType`-Feld im Code — Mandant vs. User wird über Vorhandensein von `userId` unterschieden. - **`BillingTransaction`:** `CREDIT` | `DEBIT` | `ADJUSTMENT`; Referenzen (`referenceType` / `referenceId`, `workflowId`, `featureInstanceId`, `aicoreProvider`). Mandanten-Kontext über **`BillingAccount.mandateId`** — Chat-Modelle bleiben user-owned ohne `mandateId` in `ChatWorkflow`; Zuordnung für Statistiken über Transaktionen. -- **`BillingSettings`:** Modell, `defaultUserCredit`, `warningThresholdPercent`, `notifyEmails`, `notifyOnWarning`; Erweiterung **`stripeCustomerId`** (Organisation = Mandant, nicht einzelne Subscription). +- **`BillingSettings`:** `warningThresholdPercent`, `notifyEmails`, `notifyOnWarning`, `stripeCustomerId` (Organisation = Mandant). Zusätzlich: `autoRechargeEnabled`, `rechargeAmountCHF`, `rechargeThresholdCHF` (Auto-Recharge) sowie Storage-Felder (`storageLimitGB`, `STORAGE_PRICE_PER_GB_CHF`). - **`UsageStatistics`:** Aggregationen nach Periode, Provider und Feature (Konzept; Auswertung aus Transaktionen). ### Preise und Währung -- Verrechnung in **CHF**; Plugin-seitig typisch **`calculatePriceCHF()`** mit dokumentiertem Aufschlag (Konzept: z. B. 50 % auf Provider-Kosten), Werte in den AICore-Plugins gepflegt. +- Verrechnung in **CHF**; zentraler Aufschlag via `calculatePriceWithMarkup()` in `mainServiceBilling.py` mit `BILLING_MARKUP_PERCENT = 400` (Multiplikator ×5 auf Provider-Kosten). **Achtung:** Code-Kommentare referenzieren inkonsistent „Faktor 2.0" und „50 %", die Konstante ist aktuell 400 %. ### Provider-Steuerung (RBAC) @@ -112,7 +105,9 @@ Referenz **Gateway-Audit** zu Prepaid-Beispielen: Trial **5 CHF**, Standard **10 | **PAST_DUE** | Zahlung fehlgeschlagen, Stripe-Retry-Phase | | **EXPIRED** | Beendet (terminal) | -**AI-Gate (Produktregel aus Mandanten-Konzept):** Für die Freigabe von AI-Calls gelten **`ACTIVE`** und **`TRIALING`**; bei **`PAST_DUE`**, **`EXPIRED`** und fehlender Subscription sind **AI-Calls blockiert** (auch wenn die Stripe-State-Maschine `PAST_DUE` als „Grace“ modelliert). +**AI-Gate (Code-Stand 2026-04-05):** Im Code ist `PAST_DUE` Teil der operativen Statusmenge (`OPERATIVE_STATUSES = ACTIVE, TRIALING, PAST_DUE`). `_checkSubscription` in `mainServiceBilling.py` gibt fuer alle drei Status `None` (erlaubt) zurueck. AI-Calls werden bei `PAST_DUE` **nicht blockiert**. Nur `EXPIRED`, `PENDING`, `SCHEDULED` und fehlende Subscription blockieren AI. + +> Konzept vs. Code: Eine strengere Produktregel (PAST_DUE blockiert AI) war konzeptionell vorgesehen, ist aber aktuell nicht implementiert. ### Erlaubte Statusübergänge (Kernmenge) @@ -176,7 +171,7 @@ Typische Trigger: Stripe-Webhooks (`checkout.session.completed`, `invoice.paymen 1. **Zwei Ebenen:** LLM-Verbrauch (Prepaid/Transaktionen) und Plattform-Subscription (Pläne, Stripe, Caps) — gemeinsames Gate vor AI-Calls. 2. **Mandant** ist die primäre Verrechnungseinheit für Usage; Transaktionen tragen den Mandanten-Kontext über das **BillingAccount**, nicht über Chat-Stammdaten. -3. **Pro Mandant** höchstens eine operative Subscription **`ACTIVE`/`TRIALING`** für KI-Freigabe; **`PAST_DUE`** und **`EXPIRED`** blockieren AI gemäß Mandanten-Konzept. +3. **Pro Mandant** hoechstens eine operative Subscription; `ACTIVE`, `TRIALING` und `PAST_DUE` gelten als **operativ** (AI erlaubt). Nur `EXPIRED`, `PENDING`, `SCHEDULED` und fehlende Subscription blockieren AI. 4. **Kündigung:** Nutzung bis **Ende der bezahlten Periode** (`recurring = false`, Stripe `cancel_at_period_end`); kein separater Status `CANCELLED`. 5. **Status-Änderungen** nur über definierte Transitionen mit **`subscriptionId`** — kein implizites Scannen nach „aktueller“ Zeile allein über `mandateId` bei Writes. 6. **Provider-Zugriff** über RBAC `resource.aicore.*`, konsistent mit Billing-Erfassung pro `aicoreProvider`. diff --git a/b-reference/platform/database-architecture.md b/b-reference/platform/database-architecture.md new file mode 100644 index 0000000..5c0daca --- /dev/null +++ b/b-reference/platform/database-architecture.md @@ -0,0 +1,271 @@ + + + + +# Datenbank-Architektur + +## Ueberblick + +PowerOn verwendet **PostgreSQL** mit einer klaren Trennung: jedes Modul (App, Chat, Billing, Knowledge, Features) hat eine eigene physische Datenbank (`poweron_*`). Datenbank-Zugriffe laufen ueber **Interfaces** (`interfaceDb*.py`), die auf dem zentralen **DatabaseConnector** (`connectorDbPostgre.py`) aufbauen. Datenbanken und Tabellen werden **automatisch** beim ersten Zugriff erzeugt -- kein manuelles Schema-Management noetig. + +--- + +## Architektur-Stack + +``` +Route / Service + | + v +Interface (interfaceDb*.py) + |-- XxxObjects Klasse + |-- getInterface() Factory (cached) + |-- setUserContext() -> RBAC-Anbindung + | + v +DatabaseConnector (connectorDbPostgre.py) + |-- Auto-Create Database + |-- Auto-Create/Extend Tables (aus Pydantic-Modellen) + |-- CRUD-Operationen (getRecordset, recordCreate, recordUpdate, recordDelete) + | + v +PostgreSQL (+ pgvector Extension) +``` + +--- + +## Datenbanken + +### Inventar + +| Datenbank | Modul / Zweck | Interface | +|-----------|---------------|-----------| +| `poweron_app` | Kernapplikation: UAM, Mandanten, Rollen, RBAC-Regeln, Navigation, Config | `interfaceDbApp.py` | +| `poweron_chat` | Chat-Verlaeufe, Messages, Attachments | `interfaceDbChat.py` | +| `poweron_management` | System-Management, Logs | `interfaceDbManagement.py` | +| `poweron_knowledge` | RAG Knowledge-Store (mit pgvector) | `interfaceDbKnowledge.py` | +| `poweron_billing` | Billing Accounts, Transactions, Usage | `interfaceDbBilling.py` | +| `poweron_billing` | Subscriptions (gleiche DB wie Billing) | `interfaceDbSubscription.py` | +| `poweron_workspace` | AI Workspace Feature-Daten | Feature-Interface | +| `poweron_automation` | Automation v1 Workflows, Runs | Feature-Interface | +| `poweron_automation2` | Automation v2 (Graph-Editor) Workflows, Runs | Feature-Interface | +| `poweron_chatbot` | Chatbot Feature-Daten | Feature-Interface | +| `poweron_trustee` | Trustee Feature-Daten | Feature-Interface | +| `poweron_commcoach` | CommCoach Feature-Daten | Feature-Interface | +| `poweron_neutralization` | Neutralisierungs-Daten | Feature-Interface | +| `poweron_realestate` | RealEstate Feature-Daten | Feature-Interface | +| `poweron_teamsbot` | Teams-Bot Feature-Daten | Feature-Interface | +| `poweron_test` | Test-Datenbank | Nur in Tests | + +### Namenskonvention + +Feature-Datenbanken folgen dem Pattern `poweron_{featureCode.lower()}`. Der Datenbankname wird hart-codiert in der `_initializeDatabase()` Methode des jeweiligen Interfaces. + +### Konfiguration + +Verbindungsparameter kommen aus `APP_CONFIG` (config.ini / Environment): + +| Key | Beschreibung | +|-----|-------------| +| `DB_HOST` | PostgreSQL Host | +| `DB_PORT` | PostgreSQL Port | +| `DB_USER` | Datenbank-Benutzer | +| `DB_PASSWORD_SECRET` | Passwort (ggf. verschluesselt) | +| `DB_DATABASE` | Default-DB-Name (Standard: `poweron_app`) | + +--- + +## Interface-Pattern + +Jedes Interface folgt einem einheitlichen Aufbau: + +### XxxObjects Klasse + +```python +class AppObjects: + db: DatabaseConnector # Connector zur eigenen DB + rbac: RbacClass # RBAC-Integration + currentUser: dict # Aktueller Benutzer + mandateId: str # Aktueller Mandant + featureInstanceId: str # (nur bei Feature-Interfaces) +``` + +### _initializeDatabase() + +Setzt den Datenbanknamen und erstellt den Connector: + +```python +def _initializeDatabase(self): + dbDatabase = "poweron_app" # hart-codiert pro Interface + self.db = DatabaseConnector( + host=APP_CONFIG.get("DB_HOST"), + database=dbDatabase, + user=APP_CONFIG.get("DB_USER"), + password=APP_CONFIG.get("DB_PASSWORD_SECRET"), + port=APP_CONFIG.get("DB_PORT") + ) +``` + +### setUserContext() + +Bindet Benutzer und RBAC an die Instanz: + +```python +def setUserContext(self, currentUser, mandateId): + self.currentUser = currentUser + self.userId = currentUser["id"] + self.mandateId = mandateId + self.rbac = RbacClass(self.db, dbApp=dbApp) + self.db.updateContext(self.userId) +``` + +**RBAC-Besonderheit:** `poweron_app` ist Sonderfall (`dbApp=self.db`), weil RBAC-Regeln selbst in `poweron_app` liegen. Alle anderen Interfaces nutzen `getRootDbAppConnector()` um Rules aus der App-DB zu lesen: + +```python +dbApp = getRootDbAppConnector() +self.rbac = RbacClass(self.db, dbApp=dbApp) +``` + +### getInterface() Factory + +Cached pro Kontext: + +| Interface | Cache-Key | Ergebnis | +|-----------|-----------|----------| +| App | `{mandateId}_{userId}` | `AppObjects` | +| Chat | `{mandateId}_{featureInstanceId}_{userId}` | `ChatObjects` | +| Billing | `{userId}_{mandateId}` | `BillingObjects` | +| Knowledge | `"default"` (Singleton) | `KnowledgeObjects` | + +--- + +## DatabaseConnector + +### Auto-Initialisierung + +Beim Erstellen eines `DatabaseConnector` (`initDbSystem()`): + +``` +1. _create_database_if_not_exists() + -> Verbindet sich zu DB "postgres" + -> Prueft pg_database auf Existenz + -> CREATE DATABASE "{name}" falls fehlend + +2. _create_tables() + -> Erstellt nur die _system Registry-Tabelle + -> Applikations-Tabellen werden lazy durch Interfaces erzeugt + +3. _connect() + -> psycopg2-Verbindung zur Ziel-DB + +4. _initializeSystemTable() + -> _ensureTableExists(SystemTable) +``` + +### Lazy Table Creation + +Tabellen werden **bei erstem Zugriff** automatisch aus Pydantic-Modellen erzeugt (`_ensureTableExists(model_class)`): + +- **Tabellenname** = Python-Klassenname des Pydantic-Modells (z.B. `Role`, `AccessRule`, `UserMandate`) +- **Spalten** werden aus Modellfeldern abgeleitet +- **JSONB** fuer komplexe Typen (dict, list, Pydantic-Submodelle) +- **vector** fuer pgvector-Felder (Knowledge-Store) +- **Additive Migration**: Wenn die Tabelle existiert, werden fehlende Spalten automatisch hinzugefuegt. Bestehende Spalten werden **nicht** geaendert oder entfernt. + +### Connector-Caching + +```python +_get_cached_connector(host, database, port) +``` + +Wiederverwendet einen `DatabaseConnector` pro Datenbank-Identitaet (`host:database:port`). Genutzt von App, Management, Knowledge. Chat und Billing instanziieren direkt (kein Cache). + +### userId Context + +`userId` wird ueber eine `contextvar` am Connector gesetzt (`updateContext(userId)`). Damit werden System-Felder (`_createdBy`, `_updatedBy`) automatisch befuellt. + +--- + +## RBAC-Integration in DB-Abfragen + +Die RBAC-Schicht greift direkt in Datenbank-Abfragen ein: + +### Kern-Funktionen in interfaceRbac.py + +| Funktion | Zweck | +|----------|-------| +| `getRecordsetWithRBAC()` | Listet Datensaetze mit RBAC-Filter in SQL WHERE | +| `buildRbacWhereClause()` | Uebersetzt Access Levels in SQL-Bedingungen | +| `buildDataObjectKey()` | Baut Keys wie `data.uam.UserInDB` fuer Rule-Lookup | +| `TABLE_NAMESPACE` | Mapping von Modellnamen zu logischen Namespaces | + +### Namespace-Mapping + +`TABLE_NAMESPACE` ordnet Pydantic-Modellnamen logischen Bereichen zu: + +| Namespace | Modelle (Beispiele) | +|-----------|-------------------| +| `uam` | UserInDB, UserMandate, Role, AccessRule | +| `chat` | ChatHistory, ChatMessage | +| `files` | FileRecord, FileFolder | +| `feature.trustee` | TrusteePosition, TrusteeDocument | +| `feature.workspace` | Workspace-spezifische Modelle | + +### SQL-Filter nach Access Level + +| Level | SQL WHERE | +|-------|-----------| +| `a` (ALL) | Kein Filter | +| `m` (MANDATE) | `mandateId = :mandateId` | +| `o` (OWN) | `_createdBy = :userId` | +| `n` (NONE) | `1=0` (kein Ergebnis) | + +--- + +## System-Felder + +Jedes `PowerOnModel` erbt automatisch System-Felder: + +| Feld | Beschreibung | Schutz | +|------|-------------|--------| +| `id` | UUID, auto-generiert | Immutable | +| `_createdBy` | userId des Erstellers | Immutable | +| `_createdAt` | Zeitstempel Erstellung | Immutable | +| `_updatedBy` | userId der letzten Aenderung | Auto-gesetzt | +| `_updatedAt` | Zeitstempel letzte Aenderung | Auto-gesetzt | + +Felder mit fuehrendem `_` sind fuer Anwendungs-CUD geschuetzt -- der Connector erzwingt das unabhaengig von Access Rules. + +--- + +## Feature-DB-Registrierung + +Features registrieren sich **nicht** in einer zentralen DB-Liste. Stattdessen: + +1. `registry.py` → `registerAllFeaturesInCatalog()` laedt Feature-Main-Module +2. Jedes Feature-Main definiert seinen `FEATURE_CODE` und `dbDatabase` +3. Der Datenbank-Name wird in `_initializeDatabase()` des Feature-Interfaces gesetzt +4. Der `DatabaseConnector` erzeugt die DB automatisch beim ersten Zugriff + +### Admin-Sicht + +`routeSecurityAdmin.py` bietet eine API, die alle `poweron_%`-Datenbanken aus PostgreSQL auflistet -- fuer Systemadministration und Diagnostik. + +--- + +## Schluessel-Dateien + +| Thema | Pfad | +|-------|------| +| PostgreSQL Connector | `gateway/modules/connectors/connectorDbPostgre.py` | +| App-DB Interface | `gateway/modules/interfaces/interfaceDbApp.py` | +| Chat-DB Interface | `gateway/modules/interfaces/interfaceDbChat.py` | +| Management-DB Interface | `gateway/modules/interfaces/interfaceDbManagement.py` | +| Knowledge-DB Interface | `gateway/modules/interfaces/interfaceDbKnowledge.py` | +| Billing-DB Interface | `gateway/modules/interfaces/interfaceDbBilling.py` | +| Subscription Interface | `gateway/modules/interfaces/interfaceDbSubscription.py` | +| RBAC in DB-Schicht | `gateway/modules/interfaces/interfaceRbac.py` | +| Feature-Interface (Template) | `gateway/modules/interfaces/interfaceFeatures.py` | +| Bootstrap / DB-Seed | `gateway/modules/interfaces/interfaceBootstrap.py` | +| DB-Migration Script | `gateway/scripts/script_db_export_migration.py` | +| Admin DB-Listing | `gateway/modules/routes/routeSecurityAdmin.py` | +| Datenmodelle (Pydantic) | `gateway/modules/datamodels/` | diff --git a/b-reference/platform/platform-diagrams.md b/b-reference/platform/platform-diagrams.md index 24ebc22..0c9c928 100644 --- a/b-reference/platform/platform-diagrams.md +++ b/b-reference/platform/platform-diagrams.md @@ -168,7 +168,7 @@ This diagram shows the complete microservices structure, organized into Core Ser graph TB subgraph "Core Services" CHAT[self.services.chat
Chat & Conversation] - WF_SVC[self.services.workflow
Workflow State] + WF_SVC[self.services.workflow
Runtime Workflow State
not a registered service] UTILS[self.services.utils
Utilities] end @@ -185,6 +185,14 @@ graph TB TICKET[self.services.ticket
Jira, ClickUp, etc.] end + subgraph "Additional Services not shown" + AGENT[self.services.agent] + KNOWLEDGE[self.services.knowledge] + MESSAGING[self.services.messaging] + BILLING[self.services.billing] + SUBSCRIPTION[self.services.subscription] + end + subgraph "Security" SEC_SVC[self.services.security
Auth, Authorization
Token Management] end @@ -415,7 +423,7 @@ graph TB subgraph "Integration Points" WF_DIR[gateway/modules/workflows/methods/] - SVC_DIR[gateway/modules/services/] + SVC_DIR[gateway/modules/serviceCenter/services/] CONN_DIR[gateway/modules/connectors/] ROUTE_DIR[gateway/modules/routes/] FEAT_DIR[gateway/modules/features/] @@ -481,4 +489,4 @@ graph TB --- -*Last updated: 2025* +*Last updated: 2026-04-05 (audit: service paths, missing services, date corrected)* diff --git a/b-reference/platform/rbac.md b/b-reference/platform/rbac.md index 7a50e0d..cbe2c15 100644 --- a/b-reference/platform/rbac.md +++ b/b-reference/platform/rbac.md @@ -4,66 +4,334 @@ # RBAC-System -## Überblick +## Ueberblick -Das Role-Based Access Control bündelt Berechtigungen in **Access Rules** pro Rollenlabel und **Kontext** (DATA, UI, RESOURCE). Auswertung erfolgt zentral über `interfaceRbac.py`; Zielbild laut Architekturdokumentation: möglichst **filternd in der Datenbank** statt voller Tabellen-Scans in Python. Nutzer können **mehrere Rollenlabels** gleichzeitig tragen; die effektive Berechtigung entsteht aus **Öffnungslogik (Union)** über alle Rollen. +Das Role-Based Access Control baut auf vier Stufen auf: **System**, **Mandant**, **Feature** und **Feature-Instanz**. Berechtigungen werden in **Access Rules** pro Rollenlabel und Kontext (DATA, UI, RESOURCE) gebunden. Auswertung erfolgt zentral ueber `interfaceRbac.py`; Zielbild: moeglichst **filternd in SQL** statt vollstaendiger Tabellen-Scans in Python. Nutzer koennen mehrere Rollenlabels gleichzeitig tragen; die effektive Berechtigung entsteht aus **Oeffnungslogik (Union)** ueber alle Rollen. -## Zwei getrennte Rollensysteme +--- -| | **Mandantenrollen** | **Feature-Instanz-Rollen** | -|--|---------------------|----------------------------| -| **Labels (Beispiele)** | `admin`, `user`, `viewer` | `workspace-admin`, `workspace-user`, `workspace-viewer`, feature-spezifisch | -| **Scope** | `mandateId` | `mandateId` + `featureCode` + `featureInstanceId` | -| **Zuordnung** | `UserMandateRole` | `FeatureAccessRole` | +## 4-Stufen-Hierarchie -**Invariante:** Mandantenrollen und Feature-Instanz-Rollen **nicht vermischen** — kein Zuweisen eines Mandanten-Labels zu `FeatureAccessRole` oder umgekehrt. +``` +System (PowerOn Platform) +| ++-- System Template Rollen (isSystemRole=true, mandateId=null, featureCode=null) +| Labels: "admin", "user", "viewer" +| -> kopiert bei Mandant-Erstellung via copySystemRolesToMandate() +| ++-- Feature Template Rollen (isSystemRole=false, mandateId=null, featureCode=X) +| Labels: "workspace-admin", "workspace-user", "workspace-viewer", +| "graphicalEditor-admin", "graphicalEditor-user", ... +| -> kopiert bei Feature-Instanz-Erstellung via _copyTemplateRoles() +| ++-- Mandant (Tenant) + +-- Mandate-Rollen (Instanzen der System Templates) + | mandateId=X, featureInstanceId=null, featureCode=null + | "admin", "user", "viewer" + | Zuordnung: UserMandateRole <- UserMandate <- User + | + +-- Feature-Instanz A (z.B. "AI Workspace Produktion") + | +-- Feature-Instanz-Rollen (Instanzen der Feature Templates) + | | mandateId=X, featureInstanceId=Y, featureCode="workspace" + | | "workspace-admin", "workspace-user", "workspace-viewer" + | | Zuordnung: FeatureAccessRole <- FeatureAccess <- User + | +-- Daten (Workflows, Files, Chats, ...) + | + +-- Feature-Instanz B (z.B. "Graphical Editor Dev") + +-- Feature-Instanz-Rollen + +-- Daten (Workflows, Runs, Tasks, ...) +``` -Zusätzlich (Systemebene, laut RBAC-Dokument): globale Rollen wie **SysAdmin** / **SystemUser** für mandantenübergreifende Sonderfälle; Mandantenrollen **Admin** / **User** / **Viewer** für Zugriff innerhalb eines Mandats. +### Sonderfaelle -## Access Rules (DATA / UI / RESOURCE) +- **SysAdmin**: Eine globale Rolle auf Root-Mandant-Ebene. Wird in `_initSysAdminRole()` beim Bootstrap angelegt -- keine Template-Kopie. +- **SystemUser / EventUser**: Technische System-Accounts, nicht an echte Benutzer gebunden. -### Kontexte +--- -- **DATA:** Tabellen und Felder; CRUD über `read`, `create`, `update`, `delete` mit Access Levels `a` (all), `m` (my), `g` (group / Mandat), `n` (none); zusätzlich `view`. -- **UI:** Sichtbarkeit/Aktivierung von Oberflächenelementen; relevant ist **`view`** (boolean). Item-Format: kaskadierender String (z. B. `playground.voice.settings`). -- **RESOURCE:** Systemressourcen (KI-Modelle, Aktionen, …); ebenfalls primär **`view`**. Item-Format: kaskadierend (z. B. `ai.model.anthropic`). +## Zwei getrennte Template-Rollen-Systeme -### Spezifität (pro Rolle) +Templates sind **Blaupausen**. Sie definieren welche Rollen und Berechtigungen ein neuer Mandant oder eine neue Feature-Instanz erhaelt. Die beiden Template-Systeme sind **vollstaendig getrennt** -- kein Kreuz-Zuweisen. -Innerhalb **einer** Rolle gewinnt die **spezifischste** passende Regel (längster passender Präfix/exakter `item`) gegenüber generischen Regeln (`item = null`). +### System Template Rollen (fuer Mandanten) -### Mehrere Rollen +| Feld | Wert | +|------|------| +| `isSystemRole` | `true` | +| `mandateId` | `null` | +| `featureInstanceId` | `null` | +| `featureCode` | `null` | +| `roleLabel` | `"admin"`, `"user"`, `"viewer"` | +| Definiert in | `interfaceBootstrap.py` → `initRoles()` (Zeilen ~473-540) | +| Kopier-Mechanismus | `copySystemRolesToMandate()` in `interfaceBootstrap.py` | +| Ausgeloest bei | Mandant-Erstellung (`createMandate()`, `_provisionMandateForUser()`) | +| Bootstrap-Sync | `_ensureAllMandatesHaveSystemRoles()` beim Startup | -Über Rollen hinweg: **Union** — wenn eine Rolle nach interner Auflösung `view: true` liefert, gilt das Element als sichtbar/erlaubt (OR über Rollen). Für **DATA** werden die **freizügigsten** Stufen über Rollen kombiniert. +### Feature Template Rollen (fuer Feature-Instanzen) -### DATA: Öffnungsrechte (CUD vs. Read) +| Feld | Wert | +|------|------| +| `isSystemRole` | `false` | +| `mandateId` | `null` | +| `featureInstanceId` | `null` | +| `featureCode` | z.B. `"workspace"`, `"automation2"`, `"trustee"` | +| `roleLabel` | z.B. `"workspace-admin"`, `"workspace-user"` | +| Definiert in | `TEMPLATE_ROLES` in jedem Feature-Main (z.B. `mainWorkspace.py` Zeilen ~83-135) | +| Sync in DB | `_syncTemplateRolesToDb()` bei `registerFeature()` | +| Kopier-Mechanismus | `_copyTemplateRoles()` in `interfaceFeatures.py` (Zeilen ~209-304) | +| Ausgeloest bei | Feature-Instanz-Erstellung (`createFeatureInstance(copyTemplateRoles=True)`) | -- Ohne Leserecht (`read = n`) keine CUD. -- Mit `read = m` sind CUD höchstens `m` oder `n`. -- Mit `read = g` sind CUD höchstens `g`, `m` oder `n`. -- Mit `read = a` sind alle Stufen für CUD zulässig. +### TEMPLATE_ROLES Format (Beispiel Workspace) -### Systemfelder (DATA) +```python +TEMPLATE_ROLES = [ + { + "roleLabel": "workspace-admin", + "description": {"en": "Workspace Administrator", "de": "Workspace-Administrator"}, + "accessRules": [ + {"context": "UI", "item": "ui.feature.workspace", "view": True}, + {"context": "RESOURCE", "item": "resource.feature.workspace", "view": True}, + {"context": "DATA", "item": None, "read": "a", "create": "a", "update": "a", "delete": "a", "view": True} + ] + }, + { + "roleLabel": "workspace-user", + "description": {"en": "Workspace User", "de": "Workspace-Benutzer"}, + "accessRules": [ + {"context": "UI", "item": "ui.feature.workspace", "view": True}, + {"context": "RESOURCE", "item": "resource.feature.workspace", "view": True}, + {"context": "DATA", "item": None, "read": "m", "create": "m", "update": "m", "delete": "m", "view": True} + ] + } +] +``` -Felder `id` und Namen mit führendem `_` sind für Anwendungs-CUD geschützt; Connector erzwingt das unabhängig von Access Rules. +### Kopier-Invariante -### Frontend-Optionen für Rollen +Templates werden **nur bei Erstellung** kopiert. Spaetere Aenderungen an Templates werden **nicht** automatisch an bestehende Mandanten oder Feature-Instanzen propagiert. Fuer nachtraeglichen Sync existiert `syncRolesFromTemplate()` in `interfaceFeatures.py` -- aber dies muss explizit aufgerufen werden. -`frontend_options` an Feldern kann statische Listen oder String-Referenzen (z. B. `"user.role"`) nutzen; dynamische Optionen über `/api/options/{optionsName}`. Typ-Hilfen: `gateway/modules/shared/frontendTypes.py`. +--- -## Schlüssel-Dateien +## Datenmodell -| Rolle | Pfad | +### Role + +```python +class Role(PowerOnModel): + id: str # UUID, auto-generiert + mandateId: Optional[str] # null = Global/Template + featureInstanceId: Optional[str] # null = nicht Instanz-spezifisch + featureCode: Optional[str] # z.B. "workspace", "graphicalEditor" + roleLabel: str # z.B. "workspace-admin" + description: TextMultilingual # {"en": "...", "de": "..."} + isSystemRole: bool # true nur fuer System Template Rollen +``` + +**Immutable nach Erstellung:** `mandateId`, `featureInstanceId`, `featureCode` (erzwungen in `datamodelRbac.py`). + +### AccessRule + +```python +class AccessRule(PowerOnModel): + id: str + roleId: str # FK -> Role + context: AccessRuleContext # DATA | UI | RESOURCE + item: Optional[str] # null = generic, oder spezifischer Key + view: bool + read: Optional[AccessLevel] # nur bei DATA relevant + create: Optional[AccessLevel] + update: Optional[AccessLevel] + delete: Optional[AccessLevel] +``` + +### AccessLevel + +```python +class AccessLevel(str, Enum): + NONE = "n" # Kein Zugriff + OWN = "o" # Nur eigene Datensaetze (userId-Match) + MANDATE = "m" # Alle im gleichen Mandanten + ALL = "a" # Alle (mandantenuebergreifend, nur SysAdmin) +``` + +### Membership-Modelle + +```python +class UserMandate(PowerOnModel): + userId: str + mandateId: str + enabled: bool + +class UserMandateRole(PowerOnModel): + userMandateId: str # FK -> UserMandate + roleId: str # FK -> Role (Mandate-Rolle) + +class FeatureAccess(PowerOnModel): + userId: str + featureInstanceId: str + enabled: bool + +class FeatureAccessRole(PowerOnModel): + featureAccessId: str # FK -> FeatureAccess + roleId: str # FK -> Role (Feature-Instanz-Rolle) +``` + +### ER-Diagramm (Vereinfacht) + +``` +User + | + +-- UserMandate (1:N) + | | + | +-- UserMandateRole (1:N) ---> Role (mandateId=X) + | | + | +-- AccessRule (1:N) + | + +-- FeatureAccess (1:N) + | + +-- FeatureAccessRole (1:N) ---> Role (featureInstanceId=Y) + | + +-- AccessRule (1:N) +``` + +--- + +## Access-Rule-Kontexte + +### DATA + +Steuert CRUD-Berechtigungen auf Datenbank-Ebene. + +- **Item-Format**: Auto-generiert aus Tabellenname + Namespace (z.B. `data.uam.UserInDB`, `data.feature.trustee.TrusteePosition`) +- **Prueft**: `read`, `create`, `update`, `delete` als Access Levels +- **`view`**: Boolean, steuert Grundsichtbarkeit +- **Mapping**: `TABLE_NAMESPACE` in `interfaceRbac.py` ordnet Modellnamen logischen Namespaces zu +- **`buildDataObjectKey()`**: Baut Keys wie `data.{namespace}.{ModelName}` oder `data.feature.{featureCode}.{ModelName}` + +### UI + +Steuert Sichtbarkeit und Aktivierung von Oberflaechenelementen. + +- **Item-Format**: Kaskadierende Strings (z.B. `playground.voice.settings`, `ui.feature.workspace.editor`) +- **Prueft**: Nur `view` (Boolean) +- **Verwendung**: Navigation, Seitenleiste, Buttons, Tabs + +### RESOURCE + +Steuert Zugriff auf Systemressourcen. + +- **Item-Format**: Kaskadierende Strings (z.B. `ai.model.anthropic`, `resource.feature.workspace.execute`) +- **Prueft**: Primaer `view` (Boolean) +- **Verwendung**: KI-Modelle, Aktionen, Features + +--- + +## Resolution-Algorithmus + +### Priority-System + +| Rollen-Typ | Scope | Priority | +|------------|-------|----------| +| **Global/Template** | `mandateId=null, featureInstanceId=null` | 1 (niedrigste) | +| **Mandate-Rolle** | `mandateId=X, featureInstanceId=null` | 2 | +| **Feature-Instanz-Rolle** | `mandateId=X, featureInstanceId=Y` | 3 (hoechste) | + +### Resolution-Ablauf + +1. **Rollen laden**: Mandate-Rollen via `UserMandate` → `UserMandateRole`; Feature-Rollen via `FeatureAccess` → `FeatureAccessRole` (`getRulesForUserBulk()` in `rbac.py`) +2. **AccessRules laden** fuer alle gefundenen Rollen +3. **Gruppierung** nach Priority-Stufe +4. **Hoechste Prioritaet gewinnt** bei DATA-Permissions +5. **View wird OR-verknuepft** ueber alle Rollen der hoechsten Prioritaetsstufe +6. **Item-Spezifitaet** innerhalb einer Stufe: exact > prefix > generic (laengster passender Praefix gewinnt) + +### DATA: Oeffnungsrechte (CUD vs. Read) + +| Read-Level | Maximales CUD | +|------------|---------------| +| `n` (NONE) | Kein CUD moeglich | +| `o` (OWN) | CUD hoechstens `o` oder `n` | +| `m` (MANDATE) | CUD hoechstens `m`, `o` oder `n` | +| `a` (ALL) | Alle Stufen fuer CUD zulaessig | + +### Mehrere Rollen (Union-Logik) + +Ueber Rollen hinweg gilt **Union** (OR): wenn eine Rolle nach interner Aufloesung `view: true` liefert, gilt das Element als sichtbar/erlaubt. Fuer DATA werden die **freizuegigsten** Stufen ueber Rollen kombiniert. + +### SQL-Integration + +`buildRbacWhereClause()` in `interfaceRbac.py` uebersetzt die aufgeloesten Access Levels in SQL-WHERE-Bedingungen: +- `a`: Kein Filter +- `m`: `WHERE mandateId = :mandateId` +- `o`: `WHERE _createdBy = :userId` +- `n`: `WHERE 1=0` (kein Zugriff) + +RBAC-gefilterte Abfragen laufen ueber `getRecordsetWithRBAC()`. + +--- + +## Bootstrap-Ablauf + +Beim Gateway-Start (`initBootstrap()` in `interfaceBootstrap.py`): + +``` +1. initRootMandate() -- Root-Mandant sicherstellen +2. _deduplicateRoles() -- Duplikate bereinigen, isSystemRole-Flags fixen +3. initRoles() -- System Template Rollen (admin/user/viewer) anlegen +4. initRbacRules() -- Default AccessRules auf Template-Rollen +5. _ensureAllMandatesHaveSystemRoles() + -- copySystemRolesToMandate() fuer jeden Mandanten +6. _initSysAdminRole() -- SysAdmin-Rolle auf Root-Mandant +7. _ensureUiContextRules() -- UI-Kontext-Regeln pruefen +8. Admin/Event-User sichern +9. assignInitialUserMemberships() + -- UserMandate + UserMandateRole fuer Admin/SysAdmin +``` + +Danach bei Feature-Registrierung (`registerAllFeaturesInCatalog()` → `registerFeature()`): + +``` +10. _syncTemplateRolesToDb() -- Feature Template Rollen upserten + -- AccessRules fuer Templates synchronisieren +``` + +Bei Laufzeit: + +``` +- createMandate() -- copySystemRolesToMandate() fuer neuen Mandanten +- createFeatureInstance() -- _copyTemplateRoles() fuer neue Feature-Instanz +``` + +--- + +## Systemfelder (DATA) + +Felder `id` und Namen mit fuehrendem `_` (z.B. `_createdBy`, `_createdAt`) sind fuer Anwendungs-CUD geschuetzt. Der Connector erzwingt das unabhaengig von Access Rules. + +## Frontend-Optionen fuer Rollen + +`frontend_options` an Feldern kann statische Listen oder String-Referenzen (z.B. `"user.role"`) nutzen. Dynamische Optionen ueber `/api/options/{optionsName}`. Typ-Hilfen: `gateway/modules/shared/frontendTypes.py`. + +--- + +## Schluessel-Dateien + +| Thema | Pfad | |-------|------| -| RBAC-Auswertung / DB-Schicht | `gateway/modules/interfaces/interfaceRbac.py` | -| AccessRule- und Permission-Modelle | `gateway/modules/datamodels/` (u. a. RBAC-bezogene Modelle) | -| Nutzer / Rollenlabels | User-Modell mit `roleLabels` (Multiselect, Union) | -| Konzept & Feldsemantik | `wiki/z-archive/appdoc/doc_security_role_based_access.md` | +| RBAC-Auswertung, SQL-Integration | `gateway/modules/interfaces/interfaceRbac.py` | +| AccessRule/Permission-Modelle | `gateway/modules/datamodels/datamodelRbac.py` | +| Membership-Modelle | `gateway/modules/datamodels/datamodelMembership.py` | +| Bootstrap (System Templates, Copy) | `gateway/modules/interfaces/interfaceBootstrap.py` | +| Feature-Template-Copy | `gateway/modules/interfaces/interfaceFeatures.py` | +| Feature-Registrierung | `gateway/modules/system/registry.py` | +| RBAC-Rule-Resolution | `gateway/modules/security/rbac.py` | +| TEMPLATE_ROLES (Beispiel) | `gateway/modules/features/workspace/mainWorkspace.py` | ## Regeln / Invarianten -- Mandanten- vs. Feature-Rollen **strikt trennen** (keine Kreuz-Zuweisung der Label-Typen). -- **Spezifisch vor generisch** innerhalb einer Rolle; **Union** über Rollen. -- UI/RESOURCE: Effektiv sichtbar nur, wenn nach Regelauflösung **`view: true`** (kein `hasAccess: false` in Navigationskonzept — nicht berechtigte UI-Einträge werden gar nicht geliefert). -- DATA: Validierung der Regeln so, dass CUD nicht über dem jeweiligen Read-Level liegen. -- Performance-Ziel: DATA-Zugriffe mit RBAC-Filter in SQL, nicht erst vollständige Tabellen in Python filtern. +- Mandanten-Rollen und Feature-Instanz-Rollen **strikt trennen** (keine Kreuz-Zuweisung der Label-Typen) +- Templates werden nur bei Erstellung kopiert -- keine automatische Propagierung +- **Spezifisch vor generisch** innerhalb einer Rolle; **Union** ueber Rollen +- UI/RESOURCE: Sichtbar nur wenn `view: true`; nicht berechtigte UI-Eintraege werden gar nicht geliefert +- DATA: CUD darf nicht ueber dem jeweiligen Read-Level liegen +- Performance-Ziel: DATA-Zugriffe mit RBAC-Filter in SQL diff --git a/c-work/1-plan/2026-03-web-image-search.md b/c-work/1-plan/2026-03-web-image-search.md index 240fd65..9969a9d 100644 --- a/c-work/1-plan/2026-03-web-image-search.md +++ b/c-work/1-plan/2026-03-web-image-search.md @@ -1,361 +1,83 @@ -# Refactoring Concept: Add WEB_SEARCH_MEDIA Operation Type + + + -## Executive Summary +# Web Image Search -- WEB_SEARCH_MEDIA Operation Type -**Refactoring Goal**: Add image search capability by introducing `WEB_SEARCH_MEDIA` operation type and Google Custom Search connector. +## Beschreibung und Kontext -**Current State**: -- ✅ `WEB_SEARCH_DATA` already exists (replaces former `WEB_SEARCH`) -- ✅ Tavily connector handles `WEB_SEARCH_DATA` for web page/content search -- ✅ `AiCallPromptWebSearchData` model exists +PowerOn unterstützt Web-Suche über den Tavily-Connector (`WEB_SEARCH_DATA`). Es fehlt eine dedizierte Bildersuche. Ziel ist ein neuer OperationType `WEB_SEARCH_MEDIA` mit Google Custom Search Connector, damit der AI Agent Bilder suchen und als ActionDocuments zurückgeben kann. -**Target State**: -- Add `WEB_SEARCH_MEDIA` operation type for image/media search -- Create Google Custom Search connector for `WEB_SEARCH_MEDIA` -- Create `AiCallPromptWebSearchMedia` model -- Add `ai.searchImages` action using `WEB_SEARCH_MEDIA` +**Business-Treiber:** Mandanten benötigen Bildmaterial (z.B. für Immobilien-Exposés, Marketing-Content) direkt im AI Workspace. -**Estimated Complexity**: Medium (1-2 days development) +**Risiko bei Nicht-Umsetzung:** Manuelle Bildersuche ausserhalb der Plattform, kein automatisierter Image-Workflow. -**Integration Impact**: Low - additive changes, no breaking changes +## Fokus und kritische Details ---- +- Google Custom Search API erfordert API Key + Search Engine ID mit aktivierter Image Search +- Model-Selection muss `WEB_SEARCH_MEDIA` korrekt zum Google-Connector routen +- Rückgabeformat: JSON-Array mit Image-URLs, konsistent mit Tavily-Format +- Rate Limiting der Google API (100 queries/Tag im Free Tier) -## 1. Current Architecture +## Ziel und Nicht-Ziele -### 1.1 Operation Types +- **Ziel:** `WEB_SEARCH_MEDIA` OperationType in `OperationTypeEnum` +- **Ziel:** Google Custom Search Connector (`aicorePluginGoogle.py`) +- **Ziel:** `AiCallPromptWebSearchMedia` Pydantic Model +- **Ziel:** `ai.searchImages` Action in `methodAi` +- **Explizit NICHT:** Video-Suche, Tavily-Erweiterung, SharePoint-Integration -**Current Operation Types** (`gateway/modules/datamodels/datamodelAi.py`): -```python -class OperationTypeEnum(str, Enum): - # ... existing operations ... - - # Web Operations - WEB_SEARCH_DATA = "webSearchData" # Web page search (Tavily) ✅ EXISTS - WEB_CRAWL = "webCrawl" # Web crawl for a given URL -``` +## Betroffene Module -**Target Operation Types**: -```python -class OperationTypeEnum(str, Enum): - # ... existing operations ... - - # Web Operations - WEB_SEARCH_DATA = "webSearchData" # Web page search (Tavily) ✅ EXISTS - WEB_SEARCH_MEDIA = "webSearchMedia" # Image/media search (Google) ⬅️ NEW - WEB_CRAWL = "webCrawl" # Web crawl for a given URL -``` +- Gateway: `modules/datamodels/datamodelAi.py`, `modules/aicore/aicorePluginGoogle.py` (NEU), `modules/services/serviceAi/mainServiceAi.py`, `modules/workflows/methods/methodAi/` +- Frontend: keine (Agent nutzt Action intern) +- DB-Migration: nein +- Andere: Google Cloud Console Setup (API Key) -### 1.2 Model Capabilities +## Entscheidungen -**Tavily Connector** (`gateway/modules/aicore/aicorePluginTavily.py`): -- ✅ Registered for `WEB_SEARCH_DATA` (rating: 9) -- ✅ Registered for `WEB_CRAWL` (rating: 10) -- ❌ Does NOT support image search - designed for web/text content only +| Datum | Entscheidung | Begründung | +|-------|-------------|------------| +| 2026-03-01 | Google Custom Search statt Tavily | Tavily unterstützt keine Bildersuche | +| 2026-03-01 | Eigener Connector statt Tavily-Erweiterung | Saubere Trennung, unabhängige Skalierung | -**Model Selection**: -- Dynamic model selection routes based on `OperationTypeEnum` in `AiCallOptions` -- Models register capabilities via `operationTypes` with ratings (1-10) -- System automatically selects best model for each operation type +## Umsetzungs-Checkliste ---- +- [ ] `WEB_SEARCH_MEDIA` in `OperationTypeEnum` hinzufügen +- [ ] `AiCallPromptWebSearchMedia` Model erstellen +- [ ] `aicorePluginGoogle.py` Connector implementieren +- [ ] Connector in Discovery registrieren +- [ ] `mainServiceAi.py` für `WEB_SEARCH_MEDIA` erweitern +- [ ] `searchImages` Action erstellen +- [ ] Action in `methodAi.py` registrieren +- [ ] RBAC / Permissions: nicht betroffen (nutzt bestehende AI-Berechtigung) +- [ ] Neutralisierung betroffen? nein +- [ ] Navigation / Routing: nicht betroffen +- [ ] Billing-Impact? prüfen (API-Kosten pro Query) -## 2. Refactoring Plan +## Akzeptanzkriterien -### 2.1 Add WEB_SEARCH_MEDIA Operation Type +| # | Kriterium (Given-When-Then) | Prio | +|---|---------------------------|------| +| 1 | Given ein AI Agent mit `searchImages` Tool, When der User "Suche Bilder von Zürich" eingibt, Then gibt der Agent eine Liste von Bild-URLs zurück | must | +| 2 | Given kein Google API Key konfiguriert, When `WEB_SEARCH_MEDIA` aufgerufen wird, Then wird ein klarer Fehler zurückgegeben | must | +| 3 | Given `maxResults=5` und `imageType=photo`, When die Suche ausgeführt wird, Then werden max. 5 Fotos zurückgegeben | should | -**File**: `gateway/modules/datamodels/datamodelAi.py` +## Testplan -**Changes**: -```python -class OperationTypeEnum(str, Enum): - # ... existing operations ... - - # Web Operations - WEB_SEARCH_DATA = "webSearchData" # Web page search (Tavily) - WEB_SEARCH_MEDIA = "webSearchMedia" # Image/media search (Google) ⬅️ ADD - WEB_CRAWL = "webCrawl" # Web crawl for a given URL -``` +| ID | AC | Art | Automatisiert | Repo-Pfad | Status | +|----|----|-----|--------------|-----------|--------| +| T1 | 1 | integration | ja | gateway/tests/test_google_connector.py | pending | +| T2 | 2 | unit | ja | gateway/tests/test_google_connector.py | pending | +| T3 | 3 | api | ja | gateway/tests/test_search_images_action.py | pending | -### 2.2 Create AiCallPromptWebSearchMedia Model +## Links -**File**: `gateway/modules/datamodels/datamodelAi.py` +- Google Custom Search API: https://developers.google.com/custom-search/v1/overview +- Bestehender Tavily Connector: `gateway/modules/aicore/aicorePluginTavily.py` -**Add after `AiCallPromptWebSearchData`**: -```python -class AiCallPromptWebSearchMedia(BaseModel): - """Structured prompt format for WEB_SEARCH_MEDIA operation - returns list of image URLs.""" - - instruction: str = Field(description="Search instruction/query for finding relevant images") - maxResults: Optional[int] = Field(default=10, description="Maximum number of images to return (default: 10)") - imageType: Optional[str] = Field(default=None, description="Image type filter: 'photo', 'clipart', 'lineart', 'animated'") - size: Optional[str] = Field(default=None, description="Image size filter: 'small', 'medium', 'large', 'xlarge'") - color: Optional[str] = Field(default=None, description="Color filter: 'color', 'grayscale', 'transparent'") - country: Optional[str] = Field(default=None, description="Two-digit country code (lowercase, e.g., ch, us, de, fr)") - language: Optional[str] = Field(default=None, description="Language code (lowercase, e.g., de, en, fr)") -``` +## Abschluss -### 2.3 Create Google Custom Search Connector - -**New File**: `gateway/modules/aicore/aicorePluginGoogle.py` - -**Structure** (similar to `aicorePluginTavily.py`): -```python -class AiGoogle(BaseConnectorAi): - """Google Custom Search connector for image search.""" - - def getModels(self) -> List[AiModel]: - """Get Google Custom Search model.""" - return [ - AiModel( - name="google-custom-search", - displayName="Google Custom Search", - connectorType="google", - apiUrl="https://www.googleapis.com/customsearch/v1", - # ... model configuration ... - operationTypes=createOperationTypeRatings( - (OperationTypeEnum.WEB_SEARCH_MEDIA, 9) - ), - functionCall=self._routeWebOperation, - # ... - ) - ] - - async def _routeWebOperation(self, modelCall: AiModelCall) -> "AiModelResponse": - """Route web operation based on operation type.""" - operationType = modelCall.options.operationType - - if operationType == OperationTypeEnum.WEB_SEARCH_MEDIA: - return await self.webSearchMedia(modelCall) - else: - return AiModelResponse( - content="", - success=False, - error=f"Unsupported operation type: {operationType}" - ) - - async def webSearchMedia(self, modelCall: AiModelCall) -> "AiModelResponse": - """WEB_SEARCH_MEDIA operation - returns list of image URLs using Google Custom Search.""" - # Parse AiCallPromptWebSearchMedia from messages - # Call Google Custom Search API with searchType=image - # Return JSON array of image URLs (same format as Tavily for consistency) -``` - -**Key Implementation Points**: -- Use Google Custom Search API with `searchType=image` -- Return image URLs in JSON array format (consistent with Tavily) -- Support filters: imageType, size, color, country, language -- Handle API errors and rate limiting - -### 2.4 Update Service AI Handler - -**File**: `gateway/modules/services/serviceAi/mainServiceAi.py` - -**Update `_handleWebOperations()` method**: -```python -async def _handleWebOperations( - self, - prompt: str, - options: AiCallOptions, - opType: OperationTypeEnum, - aiOperationId: str -) -> AiResponse: - """Handle WEB_SEARCH_DATA, WEB_SEARCH_MEDIA, and WEB_CRAWL operation types.""" - # Existing logic handles WEB_SEARCH_DATA and WEB_CRAWL - # Add support for WEB_SEARCH_MEDIA - if opType == OperationTypeEnum.WEB_SEARCH_DATA or opType == OperationTypeEnum.WEB_SEARCH_MEDIA or opType == OperationTypeEnum.WEB_CRAWL: - # ... existing implementation ... -``` - -### 2.5 Add searchImages Action - -**New File**: `gateway/modules/workflows/methods/methodAi/actions/searchImages.py` - -**Implementation**: -```python -async def searchImages(self, parameters: Dict[str, Any]) -> ActionResult: - """Search for images on the web using a prompt and return them as documents.""" - prompt = parameters.get("prompt") - if not prompt: - return ActionResult.isFailure(error="Search prompt is required") - - maxResults = parameters.get("maxResults", 5) - imageType = parameters.get("imageType") - size = parameters.get("size") - color = parameters.get("color") - - # Build AiCallPromptWebSearchMedia - searchPromptModel = AiCallPromptWebSearchMedia( - instruction=prompt, - maxResults=maxResults, - imageType=imageType, - size=size, - color=color - ) - - # Call AI with WEB_SEARCH_MEDIA operation - searchOptions = AiCallOptions( - operationType=OperationTypeEnum.WEB_SEARCH_MEDIA, - resultFormat="json" - ) - - # System will automatically route to Google connector - searchResponse = await self.services.ai.callAiContent( - prompt=searchPromptModel.model_dump_json(exclude_none=True, indent=2), - options=searchOptions, - outputFormat="json" - ) - - # Parse response to extract image URLs - # Download images in parallel - # Create ActionDocument for each image - # Return ActionResult with list of documents -``` - -**Update**: `gateway/modules/workflows/methods/methodAi/methodAi.py` - -**Add action definition**: -```python -"searchImages": WorkflowActionDefinition( - actionId="ai.searchImages", - description="Search for images on the web using a prompt and return them as documents", - dynamicMode=True, - parameters={ - "prompt": WorkflowActionParameter(...), - "maxResults": WorkflowActionParameter(...), - "imageType": WorkflowActionParameter(...), - "size": WorkflowActionParameter(...) - }, - execute=searchImages.__get__(self, self.__class__) -) -``` - ---- - -## 3. Implementation Steps - -### Phase 1: Add WEB_SEARCH_MEDIA Operation Type (30 min) - -1. ✅ Add `WEB_SEARCH_MEDIA = "webSearchMedia"` to `OperationTypeEnum` -2. ✅ Create `AiCallPromptWebSearchMedia` model -3. ✅ Update imports/exports - -### Phase 2: Create Google Connector (4-6 hours) - -1. ✅ Create `aicorePluginGoogle.py` file -2. ✅ Implement `AiGoogle` connector class -3. ✅ Implement `webSearchMedia()` method -4. ✅ Register connector in connector discovery -5. ✅ Test Google API integration -6. ✅ Handle errors and rate limiting - -### Phase 3: Update Service Handlers (1-2 hours) - -1. ✅ Update `serviceAi/mainServiceAi.py` to handle `WEB_SEARCH_MEDIA` -2. ✅ Ensure routing works correctly -3. ✅ Test model selection routes to Google connector - -### Phase 4: Add searchImages Action (3-4 hours) - -1. ✅ Create `actions/searchImages.py` -2. ✅ Implement image search logic -3. ✅ Implement parallel image download -4. ✅ Add action definition to `methodAi.py` -5. ✅ Add to `actions/__init__.py` -6. ✅ Write unit tests - -### Phase 5: Testing & Integration (2-3 hours) - -1. ✅ Test Google connector with real API -2. ✅ Test searchImages action end-to-end -3. ✅ Verify dynamic model selection -4. ✅ Test error handling -5. ✅ Integration testing - -**Total Estimated Time**: 1-2 days - ---- - -## 4. Configuration Requirements - -### 4.1 Environment Variables - -**Required**: -- `GOOGLE_SEARCH_API_KEY` - Google Custom Search API key -- `GOOGLE_SEARCH_ENGINE_ID` - Custom Search Engine ID - - Note: Must enable "Image Search" in Google Custom Search Engine settings - -### 4.2 Google Custom Search Setup - -1. Create Google Custom Search Engine at https://programmablesearchengine.google.com/ -2. Enable "Image Search" in settings -3. Get API key from Google Cloud Console -4. Configure environment variables - ---- - -## 5. Files to Create/Modify - -### New Files -- `gateway/modules/aicore/aicorePluginGoogle.py` - Google connector -- `gateway/modules/workflows/methods/methodAi/actions/searchImages.py` - Image search action - -### Modified Files -- `gateway/modules/datamodels/datamodelAi.py` - Add `WEB_SEARCH_MEDIA` and `AiCallPromptWebSearchMedia` -- `gateway/modules/services/serviceAi/mainServiceAi.py` - Handle `WEB_SEARCH_MEDIA` -- `gateway/modules/workflows/methods/methodAi/methodAi.py` - Add `searchImages` action -- `gateway/modules/workflows/methods/methodAi/actions/__init__.py` - Export `searchImages` -- Connector discovery module (if exists) - Register Google connector - ---- - -## 6. Testing Requirements - -### Unit Tests -- Google connector `webSearchMedia()` method -- `searchImages` action with various parameters -- Error handling (API errors, rate limits, invalid responses) -- Image download and validation - -### Integration Tests -- End-to-end image search workflow -- Dynamic model selection routes to Google connector -- Multiple image downloads in parallel -- Verify ActionDocuments are created correctly - ---- - -## 7. Risks & Mitigation - -| Risk | Impact | Probability | Mitigation | -|------|--------|-------------|------------| -| Google API setup complexity | Medium | Medium | Provide clear setup instructions, validate API keys at startup | -| Dynamic model selection routing | Medium | Low | Thoroughly test operation type routing | -| API rate limiting (Google) | Low | Medium | Implement retry logic with exponential backoff | -| Missing connector registration | Medium | Low | Ensure connector is registered in discovery system | - ---- - -## 8. Dependencies - -- **Google Custom Search API** - REQUIRED -- **Google API Client Library** - May need `google-api-python-client` package -- **HTTP Client** - For image downloads (existing) -- **Base64 Encoding** - Python standard library (no dependency) - ---- - -## 9. Success Criteria - -✅ `WEB_SEARCH_MEDIA` operation type exists -✅ Google connector is registered and functional -✅ `searchImages` action works end-to-end -✅ Dynamic model selection routes `WEB_SEARCH_MEDIA` to Google connector -✅ Images are downloaded and returned as ActionDocuments -✅ All tests pass - ---- - -**Document Version**: 2.0 -**Last Updated**: 2026-01-01 -**Status**: Refactoring Concept - Ready for Implementation +- [ ] b-reference/gateway/ai-agent.md aktualisiert (neuer OperationType) +- [ ] TOPICS.md aktualisiert (falls neues Thema) +- [ ] Dieses Dokument → z-archive/ verschoben diff --git a/c-work/1-plan/2026-04-automation-unification.md b/c-work/1-plan/2026-04-automation-unification.md new file mode 100644 index 0000000..2cd9c95 --- /dev/null +++ b/c-work/1-plan/2026-04-automation-unification.md @@ -0,0 +1,1067 @@ + + + + +# Automation Unification -- Konsolidierung v1/v2/Workspace + +## Beschreibung und Kontext + +PowerOn hat drei teilweise ueberlappende Automatisierungssysteme: Automation v1 (Template+Cron), Automation2 (n8n-Style Graph-Editor), und AI Workspace (Chat-Agent mit Tools). Alle nutzen die gleiche Action Library (`workflows/methods/` + `ActionExecutor`), aber unterschiedliche Persistenz, Scheduler und Execution Engines. + +**Business-Treiber:** Inkonsistenzen zwischen den Systemen (zwei DB-Schemas, zwei Scheduler-Patterns, zwei Engines) erhoehen den Wartungsaufwand und erschweren Feature-Paritaet. + +**Risiko bei Nicht-Umsetzung:** Wachsende Divergenz, doppelter Aufwand bei neuen Aktionen, inkonsistentes Verhalten fuer Endbenutzer. + +**Leitprinzipien:** +- Keine Migration von Altdaten -- Automation v1 ist nicht produktiv, wird nicht migriert +- Automation v1 bleibt bestehen, bis es durch das neue Feature abgeloest ist +- Klare Datenmodelle mit definierten State Machines als stabile, erweiterbare Grundlage +- Klare Trennung von Mandant/Feature/Feature-Instanz und zugehoeriger RBAC-Logik +- Skalierbare AI-Tool-Architektur mit Toolboxes und Sub-Agents fuer 50-100+ Tools + +--- + +## Ist-Analyse + +### Drei bestehende Systeme + +| System | Modell | Staerke | Schwaeche | +|--------|--------|---------|-----------| +| **Automation v1** | Template + Cron + Placeholders | Stabiles, bewaehrtes Scheduling mit Callback-basiertem Sync, inkrementeller Job-Registrierung, Execution-Logs | Kein visuelles Editing, kein Branching, starres Template-Modell | +| **Automation2** | Graph-Editor (n8n-style) + Run/Task-Modell | Visuelles Modellieren, Branching, Loops, Human-in-the-loop, Pause/Resume, diverse Node-Typen | Kein AI-Chat im Editor, kein UDB-Zugang, Scheduler-Sync weniger robust als v1 | +| **AI Workspace** | Chat-driven AI Agent mit Tools | Natural-language Interaktion, 40+ Core Tools + Action Tools, Streaming, RAG | Kein persistiertes Workflow-Modell, kein Scheduling | + +### Wer nutzt die Method/Action Library? + +Die `workflows/methods/` Library (methodAi, methodOutlook, methodSharepoint, etc.) ist die de facto gemeinsame Basis. Alle Systeme nutzen `ActionExecutor.executeAction()`: + +| Consumer | Pfad zur Action Library | +|----------|------------------------| +| **Workspace AI Agent** | `ActionToolAdapter` registriert `dynamicMode=True` Actions als Agent-Tools -> LLM waehlt Tool -> `ActionExecutor` | +| **Automation2 Graph Engine** | `ActionNodeExecutor` liest `_method`/`_action` aus Node-Definition -> `ActionExecutor` | +| **Automation v1** | `WorkflowProcessor` -> `modeAutomation` -> `ActionExecutor` | +| **Workspace Dynamic Mode** | `WorkflowProcessor` -> `modeDynamic` -> AI plant Tasks -> `ActionExecutor` | + +### Wer nutzt die AI Tools? + +| Tool-Typ | Registrierung | Nutzung | +|----------|--------------|---------| +| **Core Tools** (40: readFile, webSearch, sendMail, etc.) | `_registerCoreTools()` | Workspace Agent, CommCoach Agent | +| **Action Tools** (dynamicMode Actions) | `ActionToolAdapter.registerAll()` | Workspace Agent (toolSet-uebergreifend) | +| **Graph Node Types** (ai.prompt, email.checkEmail, etc.) | `STATIC_NODE_TYPES` in `nodeDefinitions/` | Automation2 `executeGraph` | + +### Identifizierte Inkonsistenzen + +| # | Inkonsistenz | +|---|-------------| +| **I-1** | Zwei Datenmodelle: `AutomationDefinition` (v1) vs `Automation2Workflow` (v2) -- kein gemeinsames Workflow-Konzept | +| **I-2** | Zwei Datenbanken: `poweron_automation` vs `poweron_automation2` | +| **I-3** | Zwei Scheduler: v1 (inkrementell, callback `automation.changed`) vs v2 (full wipe + re-register, callback `automation2.workflow.changed`) | +| **I-4** | Zwei Execution Engines: `WorkflowManager` + `WorkflowProcessor` vs `executionEngine.executeGraph` | +| **I-5** | Kein AI-Chat im Graphical Editor | +| **I-6** | `WorkspaceInput` ist nicht als wiederverwendbare Komponente extrahiert | +| **I-7** | UDB nur in Workspace und CommCoach, nicht im Graphical Editor | +| **I-8** | AI Tools skalieren nicht: 50+ Tools ohne Gruppierungslogik, alle werden dem LLM gleichzeitig exponiert | + +--- + +## Ziel-Architektur: Vier Saeulen + +``` ++----------------------------------------------------------------------+ +| POWERON UNIFIED PLATFORM | +| | +| +--------------+ +-----------------------+ +------------------+ | +| | AI Service | | Graphical Editor | | Automation | | +| | (Saeule 1) | | (Saeule 2) | | Scheduler | | +| | | | | | (Saeule 3) | | +| | - AI Gateway | | - Visual Flow Builder | | | | +| | - Agent Loop | | - Node Palette | | - Cron Engine | | +| | - Toolboxes | | - AI Chat Sidebar | | - Event Trigger | | +| | - Sub-Agents | | - UDB Integration | | - Webhook Entry | | +| | - Streaming | | - Tracing/Debug Log | | - Run History | | +| | - Failover | | - Test Runner | | - Monitoring | | +| | - Neutraliz. | | - Version Management | | - Notifications | | +| +------+--------+ +-----------+-------------+ +--------+---------+ | +| | | | | +| +------v------------------------v---------------------------v----------+ | +| | SAEULE 4: UNIFIED ACTION LIBRARY (Toolboxes) | | +| | | | +| | Toolbox "ai" | Toolbox "email" | Toolbox "files" | | +| | ai.process | outlook.readEmails | context.extract | | +| | ai.webResearch | outlook.searchEmails | context.neutralize | | +| | ai.summarize | outlook.draftEmail | file.create | | +| | ai.translate | outlook.sendDraft | context.transform | | +| | | | | | +| | Toolbox "sharepoint"| Toolbox "clickup" | Toolbox "jira" | | +| | sharepoint.findDoc | clickup.searchTasks | jira.connect | | +| | sharepoint.readDocs | clickup.createTask | jira.exportTickets | | +| | sharepoint.upload | clickup.updateTask | jira.importTickets | | +| | | | | | +| | Toolbox "workflow" | Toolbox "trustee" | [future toolboxes] | | +| | readWorkflowGraph | trustee.extract | slack.sendMessage | | +| | addNode, removeNode | trustee.process | teams.postChannel | | +| | connectNodes | trustee.syncAcctng | google.readDrive | | +| +----------------------------------------------------------------------+ | ++------------------------------------------------------------------------+ +``` + +### Saeule 1: AI Service (besteht, zu erweitern) + +**Status:** Kern bereit (`serviceAi` + `serviceAgent` + `aicore`). +**Erweiterung:** Toolbox-Architektur fuer 50-100+ Tools (siehe Abschnitt Toolboxes). + +### Saeule 2: Graphical Editor (Feature "graphicalEditor") + +**Status:** Basis vorhanden (Automation2FlowEditor), zu erweitern. + +| # | Funktionalitaet | Status | +|---|-----------------|--------| +| 1 | **Visual Flow Builder** -- Canvas, Nodes, Connections, Branching, Loops | besteht | +| 2 | **Node Palette** -- abgeleitet aus Toolbox-Registry, jede Action kann als Node exponiert werden | besteht | +| 3 | **AI Chat Sidebar** -- Agent mit Toolbox `"workflow"` fuer Graph-Manipulation | neu | +| 4 | **UDB Integration** -- UnifiedDataBar im Editor | neu | +| 5 | **Tracing/Debug Log** -- RunStepLog mit Input-Snapshot, Duration, Error pro Node | zu erweitern | +| 6 | **Test Runner** -- Visual Highlighting, Step-by-Step, AI-assisted Debugging | zu erweitern | +| 7 | **Version Management** -- Draft/Published Lifecycle | neu | + +### Saeule 3: Automation Scheduler (zu konsolidieren) + +**Von v1 uebernehmen:** +- Inkrementeller Sync (nur geaenderte Jobs registrieren/entfernen, nicht full wipe) +- Persistierter Job-Handle (`eventId`) auf dem Workflow fuer Debugging +- `automation.changed` Callback-Pattern fuer reaktive Synchronisation +- Handler laedt Workflow neu und prueft `active` vor Execution +- Execution-Logs als Audit Trail (capped auf 50 Eintraege) +- Creator-User Tracking via `sysCreatedBy` fuer Execution-Kontext + +**Von v2 beibehalten:** +- `IntervalTrigger` fuer einfache Wiederholungen +- Main-Loop-Bridging (`call_soon_threadsafe`) fuer Thread-Safety +- Delayed Startup Sync (5s) fuer DB-Readiness +- Striktes Cron-Parsing (5/6 Felder) + +### Saeule 4: Unified Action Library mit Toolboxes + +**Status:** Infrastruktur besteht (`workflows/methods/` + `methodDiscovery` + `ActionExecutor`). Zu erweitern um Toolbox-Konzept (siehe eigener Abschnitt). + +--- + +## Fokus und kritische Details + +- Scheduler-Robustheit: v1 hat bewaehrtes inkrementelles Pattern, v2 macht Full-Wipe-Re-Register +- Tool-Skalierung: 40+ Core-Tools + dynamische Actions -- flache Tool-Listen skalieren nicht +- Human-in-the-Loop: Pause/Resume nur in v2 implementiert +- Keine Migration von v1-Altdaten (nicht produktiv) + +--- + +## Ziel und Nicht-Ziele + +- **Ziel:** Einheitliche Plattform mit v1-Scheduler-Robustheit und v2-Graph-Faehigkeiten +- **Ziel:** Toolbox-Architektur fuer skalierbare Tool-Verwaltung (requestToolbox, availableToolboxes) +- **Ziel:** Einheitliches Workflow/Version/Run Datenmodell (Draft -> Published, Runs, Tracing) +- **Explizit NICHT:** Migration von v1-Daten; Abloesung des AI Workspace Chat-Modells + +--- + +## Mandant/Feature/RBAC-Architektur + +### Hierarchie + +``` +System (PowerOn Platform) +| ++-- System Template Rollen (isSystemRole=true, mandateId=null, featureCode=null) +| "admin", "user", "viewer" +| -> kopiert bei Mandant-Erstellung via copySystemRolesToMandate() +| ++-- Feature Template Rollen (isSystemRole=false, mandateId=null, featureCode=X) +| "workspace-admin", "workspace-user", "workspace-viewer" +| "graphicalEditor-admin", "graphicalEditor-user", ... +| -> kopiert bei Feature-Instanz-Erstellung via _copyTemplateRoles() +| ++-- Mandant (Tenant) + +-- Mandate-Rollen (kopiert von System Templates) + | mandateId=X, featureInstanceId=null, featureCode=null + | "admin", "user", "viewer" + | -> UserMandateRole <- UserMandate <- User + | + +-- Feature-Instanz A (z.B. "AI Workspace Produktion") + | +-- Feature-Instanz-Rollen (kopiert von Feature Templates) + | | mandateId=X, featureInstanceId=Y, featureCode="workspace" + | | "workspace-admin", "workspace-user", "workspace-viewer" + | | -> FeatureAccessRole <- FeatureAccess <- User + | +-- Daten (Workflows, Files, Chats, ...) + | + +-- Feature-Instanz B (z.B. "Graphical Editor Dev") + +-- Feature-Instanz-Rollen + +-- Daten (Workflows, Runs, Tasks, ...) +``` + +### Zwei getrennte Template-Rollen-Systeme + +**System Template Rollen (fuer Mandanten)** + +| Feld | Wert | +|------|------| +| `isSystemRole` | `true` | +| `mandateId` | `null` | +| `featureInstanceId` | `null` | +| `featureCode` | `null` | +| `roleLabel` | `"admin"`, `"user"`, `"viewer"` | +| Kopier-Mechanismus | `copySystemRolesToMandate()` in `interfaceBootstrap.py` | +| Ausgeloest bei | Mandant-Erstellung | + +**Feature Template Rollen (fuer Feature-Instanzen)** + +| Feld | Wert | +|------|------| +| `isSystemRole` | `false` | +| `mandateId` | `null` | +| `featureInstanceId` | `null` | +| `featureCode` | z.B. `"workspace"`, `"automation2"` | +| `roleLabel` | z.B. `"workspace-admin"`, `"workspace-user"` | +| Kopier-Mechanismus | `_copyTemplateRoles()` in `interfaceFeatures.py` | +| Ausgeloest bei | Feature-Instanz-Erstellung | + +**Wichtig:** Templates werden **nur bei Erstellung** kopiert. Spaetere Aenderungen werden **nicht** automatisch propagiert. + +### RBAC-Resolution (Priority-System) + +| Rollen-Typ | Scope | RBAC Priority | +|------------|-------|---------------| +| **Global/Template** | `mandateId=null, featureInstanceId=null` | 1 (niedrigste) | +| **Mandate-Rolle** | `mandateId=X, featureInstanceId=null` | 2 | +| **Instanz-Rolle** | `mandateId=X, featureInstanceId=Y` | 3 (hoechste) | + +**Resolution:** +1. Lade Mandate-Rollen (via `UserMandate` -> `UserMandateRole`) +2. Lade Feature-Instanz-Rollen (via `FeatureAccess` -> `FeatureAccessRole`) +3. Lade `AccessRules` fuer alle gefundenen Rollen +4. **Hoechste Prioritaet gewinnt** bei DATA-Permissions +5. **View wird OR-verknuepft** ueber alle Rollen der hoechsten Prioritaetsstufe +6. **Item-Spezifitaet** innerhalb einer Stufe: exact > prefix > generic + +### RBAC-Datenmodell + +```python +class Role(PowerOnModel): + mandateId: Optional[str] # null = Global/Template + featureInstanceId: Optional[str] # null = nicht Instanz-spezifisch + featureCode: Optional[str] # z.B. "workspace", "graphicalEditor" + roleLabel: str # z.B. "workspace-admin" + isSystemRole: bool + +class AccessRule(PowerOnModel): + roleId: str # FK -> Role + context: AccessRuleContext # DATA | UI | RESOURCE + item: Optional[str] # null=generic, oder spezifisch + view: bool + read: Optional[AccessLevel] # "n" | "o" | "m" | "a" + create: Optional[AccessLevel] + update: Optional[AccessLevel] + delete: Optional[AccessLevel] + +class AccessLevel(str, Enum): + NONE = "n" + OWN = "o" + MANDATE = "m" + ALL = "a" +``` + +### AccessRule-Kontexte + +| Kontext | Item-Beispiel | Prueft | +|---------|---------------|--------| +| `UI` | `ui.feature.graphicalEditor.editor` | Seitenleiste, Navigation, Buttons | +| `RESOURCE` | `resource.feature.graphicalEditor.execute` | Ausfuehrungsberechtigungen | +| `DATA` | Auto-generiert aus Tabellenname + featureCode | CRUD auf Datenbank-Ebene | + +--- + +## AI-Tool-Architektur: Toolboxes und Sub-Agents + +### Problem: Tool-Explosion + +Aktuell werden ~50 Tools dem LLM in einem einzigen Prompt exponiert. Bei 100+ Tools: +- LLM-Context wird ueberladen -> schlechtere Tool-Auswahl +- Irrelevante Tools fuer den aktuellen Kontext -> Confusion und Halluzinationen +- Keine Feature-isolierte Tool-Bereitstellung + +### Konzept: Toolboxes + +Eine **Toolbox** ist eine thematisch gebuendelte Gruppe von Tools mit einem klaren Scope: + +``` +Toolbox-Registry +| ++-- Toolbox "core" (immer verfuegbar) +| readFile, listFiles, searchInFileContent, writeFile, deleteFile, +| renameFile, copyFile, listFolders, createFolder, deleteFolder, +| webSearch, readUrl, detectLanguage, translateText +| ++-- Toolbox "ai" (AI-Operationen) +| summarizeContent, describeImage, generateImage, textToSpeech, +| speechToText, renderDocument, createChart, executeCode, neutralizeData +| ++-- Toolbox "datasources" (Externe Verbindungen) +| listConnections, browseDataSource, searchDataSource, +| downloadFromDataSource, uploadToExternal, browseContainer, +| readContentObjects, extractContainerItem +| ++-- Toolbox "email" (Outlook-Integration, requiresConnection="outlook") +| sendMail, outlook.readEmails, outlook.searchEmails, outlook.draftEmail +| ++-- Toolbox "sharepoint" (requiresConnection="sharepoint") +| sharepoint.findDoc, sharepoint.read, sharepoint.upload, ... +| ++-- Toolbox "clickup" (requiresConnection="clickup") +| clickup.searchTasks, clickup.createTask, ... +| ++-- Toolbox "jira" (requiresConnection="jira") +| jira.connect, jira.exportTickets, ... +| ++-- Toolbox "workflow" (Graph-Manipulation fuer Editor-Chat) +| readWorkflowGraph, addNode, removeNode, connectNodes, +| setNodeParameter, listAvailableNodeTypes, validateGraph, +| listWorkflowHistory, readWorkflowMessages +| ++-- Toolbox "trustee" (featureCode="trustee") +| trustee.extract, trustee.process, trustee.syncAccounting +| ++-- Toolbox "chatbot" (featureCode="chatbot") + chatbot.queryDatabase +``` + +### Toolbox-Bereitstellung: Initiales Set + dynamische Eskalation + +**Kern-Idee:** Der Agent startet mit einem kompakten Tool-Set. Er kennt den **Katalog aller verfuegbaren Toolboxes**. Wenn er Spezial-Tools braucht, kann er eine Toolbox **anfordern** -- und bekommt sie in der naechsten Runde. + +``` +Runde 1: + Tools: core (readFile, webSearch, ...) + requestToolbox + System-Prompt: "Verfuegbare Toolboxes: email, sharepoint, clickup, ..." + + User: "Lies meine letzten Emails und fasse sie zusammen" + LLM: -> requestToolbox("email") [braucht Email-Tools] + +Runde 2: + Tools: core + email (sendMail, outlook_readEmails, ...) + requestToolbox + LLM: -> outlook_readEmails(connectionRef, folder="Inbox", limit=10) + +Runde 3: + LLM: -> (Text-Antwort mit Zusammenfassung) -> COMPLETED +``` + +**`requestToolbox` Meta-Tool:** + +```python +{ + "name": "requestToolbox", + "description": "Request additional specialized tools for the current task.", + "parameters": { + "toolboxId": { + "type": "string", + "enum": [...] # Dynamisch: nur verfuegbare Toolboxes + }, + "reason": {"type": "string"} + } +} +``` + +### Toolbox-Datenmodell + +```python +class ToolboxDefinition(BaseModel): + id: str # z.B. "core", "email", "sharepoint" + label: Dict[str, str] # Multilingual: {"en": "Email", "de": "E-Mail"} + description: str + featureCode: Optional[str] # null = Feature-unabhaengig + tools: List[str] # Tool-Namen in dieser Toolbox + isDefault: bool = False # Immer aktiv (z.B. "core") + requiresConnection: Optional[str] # "outlook" -> auto-aktiviert bei Connection + +class ToolboxRegistry: + _toolboxes: Dict[str, ToolboxDefinition] + _toolRegistry: ToolRegistry + + def registerToolbox(self, toolbox: ToolboxDefinition): ... + def getActiveToolboxes(self, featureCode, userConnections, explicitToolboxes): ... + def getToolsForToolboxes(self, activeToolboxIds) -> List[ToolDefinition]: ... + +class AgentConfig(BaseModel): + maxRounds: int = 25 + maxCostCHF: Optional[float] = None + initialToolboxes: List[str] = ["core"] + availableToolboxes: List[str] = [] + temperature: Optional[float] = None +``` + +### Toolbox-Zuordnung pro Feature + +| Feature | `initialToolboxes` | `availableToolboxes` (anforderbar) | +|---------|--------------------|------------------------------------| +| **Workspace** | `["core"]` | `["ai", "datasources", "email", "sharepoint", "clickup", "jira", "workflow"]` -- gefiltert nach User-Connections | +| **Graphical Editor Chat** | `["core", "workflow"]` | `["ai"]` | +| **CommCoach** | `["core"]` | `[]` (keine Eskalation erlaubt) | +| **Chatbot** | `["core"]` | `[]` | + +**Automatische Toolbox-Verfuegbarkeit:** Welche Toolboxes in `availableToolboxes` erscheinen, haengt von den aktiven User-Connections ab. Keine Outlook-Verbindung -> `"email"` erscheint nicht. + +### Sub-Agents (Feature Data Agents) + +``` +User Prompt im Workspace + | + v +Main Agent (Toolboxes: core, ai, datasources, ...) + | + +-- Tool: queryFeatureInstance("trustee", instanceId, question) + | | + | v + | Sub-Agent (Feature Data Agent) + | Eigene ToolRegistry: browseTable, queryTable + | Eigenes System-Prompt mit Schema-Wissen + | Limitiert: maxRounds=5, maxCost=0.10 CHF + | | + | v + | Antwort zurueck an Main Agent + | + +-- Tool: readFile(fileId) -- Core Tool, direkt + +-- Tool: outlook_readEmails(connectionRef) -- Action Tool, direkt +``` + +**Faustregel:** Toolbox wenn die Tools generisch sind. Sub-Agent wenn Feature-spezifisches Kontext-/Schema-Wissen noetig ist. + +### State Machine: AI Agent Run (mit Toolbox-Eskalation) + +``` + +----------+ + | IDLE | + +----+-----+ + | runAgent(prompt, initialToolboxes, availableToolboxes) + v + +----------+ + +---->| THINKING |<-------------------------------+ + | +----+-----+ | + | | | + | +-- LLM Response (Text only) ----------+---> COMPLETED + | | | + | +-- LLM Response (ToolCalls) | + | | | + | v | + | +--------------------+ | + | | EXECUTING_TOOLS | | + | +----+---------------+ | + | | | + | +-- requestToolbox(id) --+ | + | | v | + | | +---------------------+ | + | | | TOOLBOX_ESCALATION | | + | | | Toolbox aktiviert | | + | | | Tools erweitert | | + | | +----------+----------+ | + | | | | + | +-- Sub-Agent call --+ | | + | | v | | + | | +-------------+| | + | | |SUB_AGENT_CALL|| | + | | +------+------+| | + | | | | | + | +-- Regular tools-+-------+-------------+ + | (results -> next round) + | + | maxRounds / maxCost reached + v + +----------+ + | COMPLETED | (oder FAILED / CANCELLED) + +----------+ +``` + +--- + +## Unified Workflow Datenmodell (Ziel) + +### Entitaeten + +```python +class WorkflowStatus(str, Enum): + DRAFT = "draft" + PUBLISHED = "published" + ARCHIVED = "archived" + +class Workflow(PowerOnModel): + id: str + mandateId: str + featureInstanceId: str + label: str + description: Optional[str] + tags: List[str] = [] + isTemplate: bool = False + templateSourceId: Optional[str] # geklont von diesem Template + currentVersionId: Optional[str] # aktive/published Version + active: bool = True # Scheduler-enabled + eventId: Optional[str] # APScheduler Job-ID (v1-Pattern) + +class WorkflowVersion(PowerOnModel): + id: str + workflowId: str # FK -> Workflow + versionNumber: int # auto-increment + status: WorkflowStatus # draft | published | archived + graph: Dict[str, Any] # { nodes: [...], connections: [...] } + invocations: List[Dict[str, Any]] # Entry-Points (manual, schedule, webhook, form) + publishedAt: Optional[float] + publishedBy: Optional[str] +``` + +### State Machine: WorkflowVersion.status + +``` + +-------+ + create --->| DRAFT |<--- edit (graph mutation via API oder AI Tools) + +---+---+ + | publish + v + +-----------+ + unpublish->| PUBLISHED | + (->DRAFT) +-----+-----+ + | archive + v + +-----------+ + | ARCHIVED |--> re-publish (-> PUBLISHED) + +-----------+ + +Invariante: Pro Workflow maximal 1 Version mit status=PUBLISHED. + Scheduler nutzt immer die PUBLISHED Version. +``` + +### Run-Modell + +```python +class RunStatus(str, Enum): + PENDING = "pending" + RUNNING = "running" + PAUSED = "paused" + COMPLETED = "completed" + FAILED = "failed" + CANCELLED = "cancelled" + +class WorkflowRun(PowerOnModel): + id: str + workflowId: str # FK -> Workflow + versionId: str # FK -> WorkflowVersion + status: RunStatus + trigger: Dict[str, Any] # { type: "manual"|"schedule"|"webhook"|..., metadata } + startedAt: float + completedAt: Optional[float] + nodeOutputs: Dict[str, Any] # Outputs pro Node-ID + currentNodeId: Optional[str] # Paused bei diesem Node + resumeContext: Dict[str, Any] # Kontext fuer Resume + error: Optional[str] + costTokens: Optional[int] # Aggregierte Token-Kosten + costCredits: Optional[float] # Aggregierte Credit-Kosten +``` + +### State Machine: WorkflowRun.status + +``` + +---------+ + executeGraph-->| RUNNING | + +----+----+ + | + +----------------+----------------+ + | | | + v v v ++--------+ +----------+ +--------+ +| PAUSED | |COMPLETED | | FAILED | ++---+----+ +----------+ +--------+ + | + | resume(taskResult | emailReceived) + v ++---------+ +| RUNNING |---> COMPLETED | FAILED | PAUSED | CANCELLED ++---------+ + +Pause-Gruende: + - input.* Node -> HumanTask erstellt + - email.checkEmail -> EmailWait (Background Poller) +Cancel: + - Manuell durch User + - Timeout bei HumanTask (expiresAt) +``` + +### RunStepLog und HumanTask + +```python +class StepStatus(str, Enum): + RUNNING = "running" + COMPLETED = "completed" + FAILED = "failed" + SKIPPED = "skipped" + +class RunStepLog(PowerOnModel): + id: str + runId: str # FK -> WorkflowRun + nodeId: str # Node-ID im Graph + nodeType: str # z.B. "ai.prompt", "email.checkEmail" + status: StepStatus + inputSnapshot: Dict[str, Any] # Parameters + Upstream-Daten bei Execution + output: Optional[Dict[str, Any]] + error: Optional[str] + startedAt: float + completedAt: Optional[float] + durationMs: Optional[int] + tokensUsed: Optional[int] + retryCount: int = 0 + +class TaskStatus(str, Enum): + PENDING = "pending" + COMPLETED = "completed" + CANCELLED = "cancelled" + EXPIRED = "expired" + +class HumanTask(PowerOnModel): + id: str + runId: str # FK -> WorkflowRun + workflowId: str # FK -> Workflow (Convenience-FK) + nodeId: str + nodeType: str # input.approval, input.form, etc. + config: Dict[str, Any] # Node-Parameter (Formular-Felder, Approval-Text) + assigneeId: str + status: TaskStatus + result: Optional[Dict[str, Any]] + expiresAt: Optional[float] +``` + +### ER-Diagramm + +``` ++------------------+ +----------------------+ +| Workflow | 1---N | WorkflowVersion | +|------------------| |----------------------| +| id | | id | +| mandateId | | workflowId (FK) | +| featureInstanceId| | versionNumber | +| label | | status | +| description | | graph {} | +| tags [] | | invocations [] | +| isTemplate | | publishedAt | +| currentVersionId +---1-->| publishedBy | +| active | +----------+-----------+ +| eventId | | 1:N ++------------------+ +----------v-----------+ + | WorkflowRun | + |----------------------| + | id | + | workflowId (FK) | + | versionId (FK) | + | status | + | trigger {} | + | nodeOutputs {} | + | currentNodeId | + | resumeContext {} | + | costTokens | + | costCredits | + +----------+-----------+ + 1:N | | 1:N + +-------------+ +-----------+ + v v + +------------------+ +------------------+ + | RunStepLog | | HumanTask | + |------------------| |------------------| + | runId (FK) | | runId (FK) | + | nodeId | | workflowId (FK) | + | nodeType | | nodeId | + | status | | config {} | + | inputSnapshot {} | | assigneeId | + | output {} | | status | + | durationMs | | result {} | + | tokensUsed | | expiresAt | + | retryCount | +------------------+ + +------------------+ +``` + +--- + +## Graph-Struktur und Node-Definitionen + +### Graph-Schema (innerhalb WorkflowVersion.graph) + +```json +{ + "nodes": [ + { + "id": "node-uuid", + "type": "trigger.schedule", + "position": { "x": 100, "y": 200 }, + "parameters": { "cronExpression": "0 8 * * 1-5" } + }, + { + "id": "node-uuid-2", + "type": "ai.prompt", + "position": { "x": 300, "y": 200 }, + "parameters": { "prompt": "Analysiere die Emails..." } + } + ], + "connections": [ + { "source": "node-uuid", "target": "node-uuid-2", "sourceOutput": 0, "targetInput": 0 } + ] +} +``` + +### Node-Type zu Method/Action Mapping + +| Node Type | `_method` | `_action` | `_paramMap` | +|-----------|-----------|-----------|-------------| +| `trigger.manual` | -- | -- | -- (TriggerExecutor) | +| `trigger.schedule` | -- | -- | -- (TriggerExecutor) | +| `trigger.form` | -- | -- | -- (TriggerExecutor) | +| `flow.ifElse` | -- | -- | -- (FlowExecutor) | +| `flow.switch` | -- | -- | -- (FlowExecutor) | +| `flow.loop` | -- | -- | -- (FlowExecutor) | +| `input.*` | -- | -- | -- (InputExecutor -> HumanTask) | +| `ai.prompt` | `ai` | `process` | `prompt->aiPrompt` | +| `ai.webResearch` | `ai` | `webResearch` | `query->prompt` | +| `ai.summarizeDocument` | `ai` | `summarizeDocument` | -- | +| `ai.translateDocument` | `ai` | `translateDocument` | `targetLanguage->targetLanguage` | +| `ai.generateDocument` | `ai` | `generateDocument` | `prompt->prompt` | +| `email.checkEmail` | `outlook` | `readEmails` | `connectionId->connectionReference` | +| `email.searchEmail` | `outlook` | `searchEmails` | `connectionId->connectionReference` | +| `email.draftEmail` | `outlook` | `composeAndDraft...` | `connectionId->connectionReference` | +| `sharepoint.*` | `sharepoint` | entsprechend | `connectionId->connectionReference` | +| `clickup.*` | `clickup` | entsprechend | `connectionId->connectionReference` | +| `file.create` | `file` | `create` | `template->template` | + +### Execution-Routing + +``` +executeGraph(graph, services, ...) + | + +-- parseGraph() -> nodes, connections, nodeIds + +-- validateGraph() -> Konsistenzpruefung + +-- topoSort(nodes, connectionMap) -> geordnete Node-Liste + | + +-- Pro Node: + | + +-- nodeType.startsWith("trigger.") -> TriggerExecutor + +-- nodeType.startsWith("flow.") -> FlowExecutor + | ifElse: evaluiert Bedingung, setzt active path + | switch: evaluiert Match, setzt active path + | loop: iteriert mit _loopState + +-- nodeType.startsWith("input.") -> InputExecutor + | -> erstellt HumanTask, setzt Run auf PAUSED + +-- alles andere -> ActionNodeExecutor + +-- _getNodeDefinition(nodeType) -> _method, _action, _paramMap + +-- Parameter-Mapping (Node-Params -> Action-Params) + +-- Upstream-Daten mergen (documents von vorherigen Nodes) + +-- ActionExecutor.executeAction(method, action, params) +``` + +--- + +## Konsolidierter Scheduler (Ziel) + +```python +class WorkflowScheduler: + def start(self, eventUser): + eventManager.start() + self._syncScheduledWorkflows(eventUser) + self._registerDelayedSync(eventUser, delaySeconds=5) + callbackRegistry.register("workflow.changed", + lambda _: self._syncScheduledWorkflows(eventUser)) + + def _syncScheduledWorkflows(self, eventUser): + """Inkrementeller Sync (v1-Pattern).""" + workflows = self._getAllSchedulableWorkflows() + for wf in workflows: + jobId = f"workflow.{wf.id}" + if wf.active and wf.currentVersionId: + version = self._getPublishedVersion(wf.currentVersionId) + cronKwargs = self._extractSchedule(version) + if cronKwargs: + handler = self._createHandler(wf, version, eventUser) + eventManager.registerCron( + jobId=jobId, func=handler, + cronKwargs=cronKwargs, + replaceExisting=True # v1-Pattern: atomic replace + ) + if wf.eventId != jobId: + self._updateEventId(wf.id, jobId) + else: + self._removeJob(jobId) + if wf.eventId: + self._updateEventId(wf.id, None) + + def _createHandler(self, wf, version, eventUser): + """Handler: Reload + Active-Check + Execute (v1-Pattern).""" + async def _handler(): + current = self._getWorkflow(wf.id) + if not current or not current.active: + return + services = self._buildServices(current, eventUser) + result = await executeGraph(graph=version.graph, services=services, ...) + self._appendExecutionLog(current.id, result) # capped audit trail + return self._wrapAsync(_handler) # Thread-Bridge (v2-Pattern) +``` + +--- + +## Use Cases + +### UC-1: Workflow im Workspace erstellen/bearbeiten + +User beschreibt im Workspace-Chat einen gewuenschten Workflow -> Agent nutzt Toolbox `"workflow"` -> generiert Graph -> speichert als Draft -> User oeffnet im Graphical Editor. + +Voraussetzung: Die `"workflow"` Toolbox muss im Workspace aktivierbar sein. Die Tools validieren jeden Graph-Mutationsschritt: +- `addNode(type, parameters)` -> prueft: existiert der Node-Typ? Sind die Parameter valide? +- `connectNodes(source, target)` -> prueft: sind die I/O-Typen kompatibel? Keine Zyklen? +- `validateGraph()` -> vollstaendige Validierung inkl. Trigger-Pruefung + +### UC-2: AI Chat im Graphical Editor + +Sidebar mit Chat. Agent nutzt Toolbox `"workflow"` mit System-Prompt, der den aktuellen Graph kennt. Er kann Nodes hinzufuegen, entfernen, umkonfigurieren und den Graph erklaeren. Gleicher `serviceAgent.runAgent()` Pfad wie Workspace, aber mit `toolboxes: ["core", "workflow"]` und Graph als Kontext im System-Prompt. + +### UC-3: Workflow testen mit Tracing Log + +User klickt "Test Run" -> Graph wird ausgefuehrt -> jeder Node wird visuell hervorgehoben (Status-Farben) -> Tracing Log zeigt pro Node: Input, Output, Dauer, Fehler. Bei Fehlern: User bespricht den Fehler im AI Chat -> Agent liest RunStepLog und schlaegt Korrekturen vor. + +**Technisch:** `RunStepLog` pro Node mit Timestamps, Input-Snapshot, Duration, Error-Stack. SSE/WebSocket fuer Live-Updates. Run-Modes: "Full Run" und "Step-by-Step" (Pause nach jedem Node). + +### UC-4: Workflow automatisieren (Scheduler) + +User konfiguriert Schedule im Editor (via trigger.schedule Node oder Invocation) -> konsolidierter Scheduler registriert Cron-Job -> Run-History wird persistiert -> bei Fehlern Notifications. + +--- + +## Backend Code-Struktur (Ziel) + +``` +gateway/modules/ +| ++-- features/ +| +-- graphicalEditor/ <-- NEUES FEATURE (ersetzt automation2) +| | +-- mainGraphicalEditor.py +| | | FEATURE_CODE = "graphicalEditor" +| | | UI_OBJECTS = ["ui.feature.graphicalEditor.editor", +| | | "ui.feature.graphicalEditor.workflows", +| | | "ui.feature.graphicalEditor.tasks"] +| | | RESOURCE_OBJECTS = ["resource.feature.graphicalEditor.execute", +| | | "resource.feature.graphicalEditor.schedule"] +| | | TEMPLATE_ROLES = ["graphicalEditor-admin", +| | | "graphicalEditor-user", +| | | "graphicalEditor-viewer"] +| | +-- routeFeatureGraphicalEditor.py (prefix: /api/workflows) +| | +-- interfaceFeatureGraphicalEditor.py (DB: poweron_workflows) +| | +-- datamodelFeatureGraphicalEditor.py +| | +-- nodeDefinitions/ +| | triggers.py, flow.py, input.py, ai.py, email.py, sharepoint.py, ... +| | +| +-- workspace/ <-- BLEIBT (AI Chat mit UDB) +| +-- automation/ <-- BLEIBT bis Abloesung +| +-- commcoach/ <-- BLEIBT +| +-- chatbot/ <-- BLEIBT +| +-- trustee/ <-- BLEIBT +| ++-- serviceCenter/services/ +| +-- serviceAgent/ +| | +-- mainServiceAgent.py <-- Core Tools + Toolbox-Tagging +| | +-- agentLoop.py <-- Toolbox-Filtering statt toolSet +| | +-- toolRegistry.py <-- BLEIBT +| | +-- toolboxRegistry.py <-- NEU: Toolbox-Verwaltung +| | +-- actionToolAdapter.py <-- BLEIBT (+ Toolbox-Zuordnung) +| | +-- featureDataAgent.py <-- BLEIBT (Sub-Agent Pattern) +| | +-- datamodelAgent.py <-- toolboxes: List[str] statt toolSet +| ++-- workflows/ +| +-- methods/ <-- BLEIBT (Unified Action Library) +| +-- processing/ <-- BLEIBT (ActionExecutor, methodDiscovery) +| +-- automation2/ <-- Graph Engine BLEIBT +| +-- scheduler/ <-- NEU (konsolidierter Scheduler) +| | +-- mainScheduler.py +| +-- automation/ <-- BLEIBT bis Abloesung +| +-- workflowManager.py <-- BLEIBT (fuer Workspace Dynamic Mode) +``` + +--- + +## Frontend Code-Struktur (Ziel) + +``` +frontend_nyla/src/ +| ++-- components/ <-- SHARED COMPONENTS +| +-- ChatBar/ <-- NEU (extrahiert aus WorkspaceInput) +| | ChatBar.tsx Props: onSend, onStop, onVoice, +| | fileAttachments, showProviderSelector, ... +| +-- ChatStream/ <-- NEU (extrahiert aus workspace/ChatStream) +| | ChatStream.tsx SSE-driven message stream, wiederverwendbar +| +-- UnifiedDataBar/ <-- BLEIBT +| +-- FlowEditor/ <-- RENAMED von Automation2FlowEditor +| +-- editor/ +| FlowEditor.tsx + AI Chat Panel +| FlowCanvas.tsx + Visual Run Tracing +| EditorChatPanel.tsx <-- NEU +| ++-- pages/views/ +| +-- workspace/ +| | WorkspacePage.tsx Nutzt ChatBar, ChatStream, UDB +| +-- graphicalEditor/ <-- RENAMED von automation2 +| GraphicalEditorPage.tsx FlowEditor + UDB + ChatPanel +| WorkflowsListPage.tsx +| TasksPage.tsx +| ++-- api/ + workflowApi.ts <-- Konsolidiert (ersetzt automation + automation2 API) +``` + +--- + +## Empfehlungen und offene Punkte + +### Starke Konzepte (umsetzen) + +| Konzept | Bewertung | +|---------|-----------| +| **Toolbox-Architektur** | Skaliert von 50 auf 100+ Tools ohne LLM-Ueberlastung. Klare Isolierung. | +| **Sub-Agents pro Feature** | Bewaehrtes Pattern (Trustee), erweiterbar. Haelt Main Agent schlank. | +| **Validierungs-Layer fuer Workflow-Modellierung** | Graph-Mutation nur ueber validierte Tools. | +| **v1-Scheduling-Patterns in v2 uebernehmen** | Inkrementeller Sync ist robuster als Full-Wipe. | +| **State Machines als Grundlage** | Klare Zustaende, klare Uebergaenge. Erweiterbar. | + +### Was User brauchen + +| User Need | Feature | Prioritaet | +|-----------|---------|-----------| +| **Schneller Einstieg** | Workflow-Templates fuer haeufige Use Cases | Hoch | +| **Vertrauen** | Tracing Log + Test Runner | Hoch | +| **Effizienz** | AI-gestuetzte Workflow-Erstellung per Chat | Hoch | +| **Fehlertoleranz** | Retry-Policies, Pause/Resume, klare Fehlermeldungen | Hoch | +| **Kontext** | UDB im Editor -- Zugriff auf Files/Sources bei Konfiguration | Hoch | +| **Zusammenarbeit** | Workflows teilen im Mandant, Rollen-basierter Zugriff | Mittel | +| **Monitoring** | Dashboard: laufende Automationen, Fehlerrate, Kosten | Mittel | +| **Flexibilitaet** | Custom Script Nodes (Python-Sandbox), AI Decision Nodes | Mittel | +| **Mobile** | Notifications + Approval-Tasks per Mobile | Phase 2 | + +### Offene Architektur-Fragen + +| Frage | Empfehlung | +|-------|------------| +| **Toolbox-Selektion: statisch oder dynamisch?** | Hybrid: Basis pro Feature statisch, Connection-basierte dynamisch. | +| **Sub-Agent Tiefe: verschachtelt?** | Maximal 1 Level tief. Kein Sub-Sub-Agent. | +| **Graph-Execution und WorkflowManager zusammenfuehren?** | Nein, getrennt. `executeGraph` fuer Editor, `WorkflowManager` fuer Workspace Dynamic Mode. | +| **Feature-Code: `automation2` -> `graphicalEditor`?** | Ja, Rename in Phase 1. | + +--- + +## Phasen-Plan + +### Phase 1: Foundation +- Unified Workflow Datenmodell (Workflow + WorkflowVersion + WorkflowRun + RunStepLog + HumanTask) +- Toolbox-Registry implementieren (Abloesung `_CORE_ONLY_TOOLS`) +- ChatBar-Komponente extrahieren +- Feature Rename: `automation2` -> `graphicalEditor` +- Scheduler konsolidieren (v1 Patterns in v2 Engine) + +### Phase 2: Editor Enhancement +- AI Chat Sidebar im Editor (Toolbox `"workflow"` + Graph-Manipulation-Tools) +- UDB Integration im Editor +- WorkflowVersion Lifecycle (Draft/Published) +- Enhanced RunStepLog + Visual Tracing + +### Phase 3: Productization +- Workflow Templates / Marketplace +- Monitoring Dashboard +- Retry Policies pro Node +- Notifications bei Scheduler-Fehlern +- Automation v1 Feature entfernen (wird nicht mehr benoetigt) + +### Phase 4: Advanced +- AI Decision Node (`ai.decide`) +- Custom Script Node (Python Sandbox) +- Dynamische Toolbox-Aktivierung basierend auf Connections +- Sub-Agent Pattern fuer weitere Features + +--- + +## Betroffene Module + +- Gateway: `features/automation/`, `features/automation2/`, `workflows/`, `serviceCenter/services/serviceAgent/` +- Frontend: `components/Automation2FlowEditor/`, `pages/views/automation2/` +- DB-Migration: ja (neues Workflow/Version/Run Schema) +- Andere: Scheduler (APScheduler/eventManager), RBAC Template-Rollen + +## Entscheidungen + +| Datum | Entscheidung | Begruendung | +|-------|-------------|------------| +| 2026-04-05 | v1-Scheduler-Pattern in v2 uebernehmen | Robuster (inkrementell, Job-Handle, Reload+active-Check) | +| 2026-04-05 | Toolbox-Konzept statt flache Tool-Listen | Skalierbarkeit bei 50-100+ Tools | +| 2026-04-05 | Keine v1-Datenmigration | v1 nicht produktiv | +| 2026-04-05 | Feature-Code `automation2` -> `graphicalEditor` | Klarer Name fuer User und Entwickler | +| 2026-04-05 | Graph-Execution und WorkflowManager getrennt lassen | Verschiedene Orchestrierungsmodelle | +| 2026-04-05 | Sub-Agent maximal 1 Level tief | Komplexitaet kontrollierbar | + +## Umsetzungs-Checkliste + +- [ ] Vereinheitlichtes Workflow/WorkflowVersion/WorkflowRun Schema +- [ ] v1-Scheduler-Pattern in v2 Engine +- [ ] Toolbox Registry + requestToolbox Meta-Tool +- [ ] Feature-Code Rename `automation2` -> `graphicalEditor` +- [ ] Frontend: ChatBar + ChatStream extrahieren +- [ ] Frontend: UDB + Chat im Graph-Editor +- [ ] RBAC Template-Rollen konsolidieren +- [ ] RunStepLog + Visual Tracing +- [ ] Neutralisierung: keine Aenderung (nutzt gleiche Services) +- [ ] Billing-Impact: pruefen (Automation-Runs zaehlen?) + +## Akzeptanzkriterien + +| # | Kriterium (Given-When-Then) | Prio | +|---|---------------------------|------| +| 1 | Given ein Automation2-Workflow mit Schedule, When der Scheduler neu startet, Then werden nur geaenderte Jobs aktualisiert (inkrementell wie v1) | must | +| 2 | Given ein Agent mit 50+ Tools, When der Agent startet, Then sind nur core-Toolboxes geladen und Spezial-Toolboxes werden on-demand nachgeladen | should | +| 3 | Given ein Workflow-Run, When ein Human-Task-Node erreicht wird, Then pausiert der Run und wartet auf User-Input | must | +| 4 | Given ein Workflow-Graph im Editor, When der User im AI-Chat "Fuege einen Email-Check hinzu" tippt, Then fuegt der Agent einen email.checkEmail Node ein und verbindet ihn korrekt | should | +| 5 | Given ein Workflow mit Published Version, When der User den Graph editiert, Then wird ein neuer Draft erstellt und die Published Version bleibt aktiv | must | + +## Testplan + +| ID | AC | Art | Automatisiert | Repo-Pfad | Status | +|----|----|-----|--------------|-----------|--------| +| T1 | 1 | integration | ja | gateway/tests/test_automation2_scheduler.py | pending | +| T2 | 2 | integration | ja | gateway/tests/test_agent_toolbox.py | pending | +| T3 | 3 | api | ja | gateway/tests/test_workflow_pause_resume.py | pending | +| T4 | 4 | integration | ja | gateway/tests/test_editor_chat_graph.py | pending | +| T5 | 5 | unit | ja | gateway/tests/test_workflow_versioning.py | pending | + +## Glossar + +| Begriff | Definition | +|---------|-----------| +| **Toolbox** | Thematisch gebuendelte Gruppe von AI-Tools, kontextabhaengig aktiviert | +| **Sub-Agent** | Spezialisierter Mini-Agent mit eigener Tool-Registry, aufgerufen vom Main Agent | +| **Workflow** | Persistiertes Automatisierungsmodell mit Graph-Struktur | +| **WorkflowVersion** | Immutable Snapshot eines Workflow-Graphen (draft, published, archived) | +| **WorkflowRun** | Einzelne Ausfuehrung einer WorkflowVersion | +| **RunStepLog** | Detaillierter Log pro Node-Execution innerhalb eines Runs | +| **HumanTask** | Aufgabe fuer menschliche Eingabe, erstellt bei Pause eines Runs | +| **Node** | Ausfuehrungsschritt im Graph (Trigger, Action, Flow-Control, Input/Human) | +| **Method** | Integrations-Kategorie (z.B. methodOutlook) mit mehreren Actions | +| **Action** | Spezifische Operation innerhalb einer Method (z.B. outlook.readEmails) | +| **Invocation** | Entry-Point fuer einen Workflow (Manual, Schedule, Webhook, Form, Event) | +| **UDB** | Unified Data Bar -- Multi-Tab-Panel fuer Chats, Files und Sources | +| **ChatBar** | Wiederverwendbare Prompt-Input-Komponente | +| **Feature Container** | Einheitliches Verzeichnis-Pattern fuer ein Feature | + +## Links + +- Detail-Spec (Business): z-archive/b-reference/automation-business-spec.md +- Detail-Spec (Datenmodell): z-archive/b-reference/automation-data-model.md +- Referenz (Ist-Zusammenfassung): b-reference/gateway/automation.md + +## Abschluss + +- [ ] b-reference/gateway/automation.md aktualisiert +- [ ] TOPICS.md aktualisiert +- [ ] Dieses Dokument -> z-archive/ verschoben diff --git a/d-guides/coding-conventions.md b/d-guides/coding-conventions.md index 2782287..585d2f2 100644 --- a/d-guides/coding-conventions.md +++ b/d-guides/coding-conventions.md @@ -5,16 +5,61 @@ ## Naming -- Alle internen Funktionen beginnen mit `_` Prefix -- camelCase für Variablen und Funktionsnamen (kein snake_case) +- Alle internen Funktionen beginnen mit `_` Prefix (nicht exportierbar) +- **camelCase** fuer Variablen und Funktionsnamen (kein snake_case) +- **PascalCase** fuer Klassen und Pydantic-Models +- Dateien: `camelCase` fuer Module (z.B. `mainServiceAi.py`, `routeBilling.py`) -## Frontend-Regeln - -- Keine Browser-Dialoge (alert/confirm/prompt) -- stattdessen `useConfirm()` und `usePrompt()` Hooks -- CSS Modules für Komponenten-Styles - -## Backend-Regeln +## Frontend (React/TypeScript) +- Keine Browser-Dialoge (`alert`, `confirm`, `prompt`) -- stattdessen `useConfirm()` / `usePrompt()` Hooks +- CSS Modules fuer Styling +- Hooks-Pattern fuer State und API-Zugriffe (`useApiRequest`, `useBilling`, etc.) - Fehler propagieren -- keine stillen Fallbacks bei kritischen Pfaden -- Pydantic-Models als einzige Quelle für UI-Feld-Definitionen -- `PowerOnModel` als Basis mit `sysCreatedAt`, `sysCreatedBy`, `sysModifiedAt`, `sysModifiedBy` + +## Backend (FastAPI/Python) + +- **Pydantic-Models** als einzige Quelle fuer UI-Feld-Definitionen +- **`PowerOnModel`** als Basis mit System-Audit-Feldern (`sysCreatedAt`, `sysCreatedBy`, `sysModifiedAt`, `sysModifiedBy`) +- Fehler propagieren -- Exceptions explizit werfen, nicht schlucken +- Config ueber `APP_CONFIG` (aus `modules/shared/configuration.py`) + +## Projektstruktur Gateway + +``` +gateway/ + app.py # FastAPI-App, Middleware, Startup + config.ini # Statische Konfiguration + modules/ + auth/ # JWT, OAuth, CSRF, Authentication + datamodels/ # Pydantic-Models (zentrale Quelle) + features/ # Feature-Module (workspace, automation, ...) + / + main.py # FEATURE_CODE, Registrierung + routeFeature.py # HTTP-Endpunkte + interfaceFeature.py # DB-Interface + datamodelFeature.py # Feature-spezifische Models + interfaces/ # DB-Interfaces (CRUD, Queries) + routes/ # Core-Routes (billing, admin, GDPR, ...) + security/ # RBAC-Engine + serviceCenter/ + core/ # serviceSecurity, serviceUtils, serviceStreaming + services/ # serviceAi, serviceChat, serviceAgent, ... + registry.py # Service-Registrierung und Dependencies + shared/ # configuration.py, Utilities + system/ # registry.py (Feature-Discovery) + workflows/ + methods/ # Unified Action Library + processing/ # WorkflowProcessor, Modes + automation/ # v1 Runtime + automation2/ # v2 Engine + connectors/ # Externe Systeme (DB, SharePoint, Jira, ...) + aicore/ # AI-Provider-Plugins, Model-Selector +``` + +## Anti-Patterns + +- Keine impliziten Type-Conversions in API-Responses +- Keine DB-Queries in Route-Handlern (immer ueber Interfaces) +- Kein direkter `os.environ`-Zugriff -- immer `APP_CONFIG` +- Keine hartkodierten Secrets -- verschluesselt in Env-Dateien diff --git a/d-guides/deployment/Instanzenübersicht.drawio b/d-guides/deployment/instanzenuebersicht.drawio similarity index 100% rename from d-guides/deployment/Instanzenübersicht.drawio rename to d-guides/deployment/instanzenuebersicht.drawio diff --git a/d-guides/deployment/Instanzenübersicht.svg b/d-guides/deployment/instanzenuebersicht.svg similarity index 100% rename from d-guides/deployment/Instanzenübersicht.svg rename to d-guides/deployment/instanzenuebersicht.svg diff --git a/d-guides/deployment/poweron_sec.kdbx b/d-guides/deployment/poweron-sec.kdbx similarity index 100% rename from d-guides/deployment/poweron_sec.kdbx rename to d-guides/deployment/poweron-sec.kdbx diff --git a/d-guides/dev-setup.md b/d-guides/dev-setup.md index 139a1c3..2397294 100644 --- a/d-guides/dev-setup.md +++ b/d-guides/dev-setup.md @@ -1,25 +1,85 @@ - + # Dev-Setup ## Voraussetzungen -- Python (Anaconda/Miniconda) -- Node.js -- PostgreSQL mit pgvector +- **Python 3.11** (CI-Standard; Minimum 3.10) +- **Node.js** (aktuelles LTS empfohlen) +- **PostgreSQL** mit **pgvector**-Extension +- **Conda** oder **venv** fuer Python-Umgebung ## Gateway starten ```bash -conda activate cd gateway/ +# Python-Umgebung aktivieren (Conda oder venv) +conda activate +# oder: python -m venv .venv && .\.venv\Scripts\Activate.ps1 + +pip install -r requirements.txt + +# Config vorbereiten: +# 1. config.ini muss im gateway/ Verzeichnis liegen (statische Settings) +# 2. .env Datei aus env_dev.env kopieren/symlinken: +# copy env_dev.env .env +# 3. APP_KEY_SYSVAR in config.ini zeigt auf Master-Key (OS-Variable oder Dateipfad) + uvicorn app:app --host 0.0.0.0 --port 8000 ``` +### Konfigurationsdateien + +| Datei | Zweck | +|-------|-------| +| `gateway/config.ini` | Statische Settings (Auth-Defaults, Upload-Limits, Operator-Text) | +| `gateway/.env` | Runtime-Env (DB, API-Keys, Secrets) -- wird aus `env_dev.env` kopiert | +| `gateway/env_dev.env` | Dev-Umgebung Template (verschluesselte Secrets mit `DEV_ENC:` Prefix) | +| `gateway/env_int.env` | Integration-Umgebung | +| `gateway/env_prod.env` | Produktion | + +### Wichtige Env-Variablen (Auszug) + +| Variable | Beschreibung | +|----------|-------------| +| `DB_HOST`, `DB_USER`, `DB_PASSWORD_SECRET` | PostgreSQL-Verbindung | +| `APP_KEY_SYSVAR` | Master-Key fuer Secrets-Entschluesselung | +| `APP_API_URL` | API-URL (bestimmt Cookie-Secure-Flag) | +| `APP_ALLOWED_ORIGINS` | CORS Origins (inkl. `http://localhost:5176`) | +| `Service_GOOGLE_AUTH_*` | Google OAuth Auth-App | +| `Service_GOOGLE_DATA_*` | Google OAuth Data-App | + ## Frontend Nyla starten ```bash cd frontend_nyla/ -npx vite --port 8080 --mode dev +npm install +npm run dev +# oder: npx vite --port 5176 --mode dev ``` + +Frontend laeuft auf **http://localhost:5176** und erwartet Gateway auf **http://localhost:8000**. + +### Frontend-Konfiguration + +| Datei | Zweck | +|-------|-------| +| `frontend_nyla/config/.env.dev` | Dev-Mode Env (VITE_API_BASE_URL etc.) | +| `frontend_nyla/config/.env.int` | Integration | +| `frontend_nyla/config/.env.prod` | Produktion | + +## Secrets-Verwaltung + +Env-Dateien enthalten verschluesselte Werte (`DEV_ENC:`, `INT_ENC:`, `PROD_ENC:`). +Zur Entschluesselung wird der Master-Key ueber `APP_KEY_SYSVAR` geladen. + +Details: -> `d-guides/encrypt-env-secrets.md` + +## Weiterführende Guides + +- Secrets verschluesseln: `d-guides/encrypt-env-secrets.md` +- Google OAuth einrichten: `d-guides/google-oauth-setup.md` +- Cookie/JWT-Migration: `d-guides/security-migration-guide.md` +- Coding-Konventionen: `d-guides/coding-conventions.md` +- Testing: `d-guides/testing-strategy.md` diff --git a/d-guides/encrypt-env-secrets.md b/d-guides/encrypt-env-secrets.md index 5be3eef..ee5ccb2 100644 --- a/d-guides/encrypt-env-secrets.md +++ b/d-guides/encrypt-env-secrets.md @@ -1,6 +1,6 @@ ### Admin-Leitfaden: *_SECRET-Werte in Env-Dateien verschlüsseln -Diese Anleitung beschreibt, wie das PowerOn-Gateway-Tool `tool_security_encrypt_all_env_files.py` alle unverschlüsselten `*_SECRET`-Variablen in `env_dev.env`, `env_int.env` und `env_prod.env` verschlüsselt. +Diese Anleitung beschreibt, wie das PowerOn-Gateway-Tool `scripts/script_security_encrypt_all_env_files.py` alle unverschlüsselten `*_SECRET`-Variablen in `env_dev.env`, `env_int.env` und `env_prod.env` verschlüsselt. --- @@ -17,7 +17,7 @@ Das Tool kann in jedem Repository eingesetzt werden, sofern die untenstehenden V - Die zu verarbeitenden Env-Dateien: `env_dev.env`, `env_int.env`, `env_prod.env` (oder eine Teilmenge via `--files`). - **Rechte**: Schreibrechte auf die Env-Dateien, Leserechte auf die Masterkey-Quelle (siehe Punkt 2). -Hinweis: Das Tool importiert `encrypt_value` aus `modules/shared/configuration.py`. Stellen Sie sicher, dass dieser Pfad importierbar ist (Start im Gateway-Verzeichnis oder `PYTHONPATH` setzen). +Hinweis: Das Tool importiert `encryptValue` aus `modules/shared/configuration.py`. Stellen Sie sicher, dass dieser Pfad importierbar ist (Start im Gateway-Verzeichnis oder `PYTHONPATH` setzen). Das Script fuegt `gateway` automatisch zu `sys.path` hinzu. --- @@ -25,12 +25,12 @@ Hinweis: Das Tool importiert `encrypt_value` aus `modules/shared/configuration.p 1. `requirements.txt` im Projekt (Minimalbeispiel): ``` - cryptography>=42.0.0 + cryptography>=41.0.0 ``` Falls Sie das Tool im bestehenden Gateway verwenden, nutzen Sie stattdessen die dortige Datei `gateway/requirements.txt`. 2. Notwendige Gateway-Komponenten (sofern außerhalb von `gateway` verwendet): - - `modules/shared/configuration.py` (liefert `APP_CONFIG`, `_get_master_key`, `encrypt_value`, `decrypt_value`) + - `modules/shared/configuration.py` (liefert `APP_CONFIG`, `_getMasterKey`, `encryptValue`, `decryptValue`) - Optional, aber unterstützt: `modules/shared/auditLogger.py` (Audit-Events; Ausfall ist toleriert) - `config.ini` im gleichen Baum wie `modules`, mit mindestens: - `APP_ENV_TYPE = dev|int|prod` (kann pro Env-Datei zusätzlich gesetzt werden) @@ -80,7 +80,7 @@ Tool: `gateway/tool_security_encrypt_all_env_files.py` - Liest je Datei `APP_ENV_TYPE`. - Findet alle Schlüssel, deren Name auf `_SECRET` endet, und deren Werte noch kein `*_ENC:`-Präfix haben. - Unterstützt einzeilige und mehrzeilige JSON-Werte (Klammer-Auf/Zu wird erkannt). - - Verschlüsselt die Werte mit dem passenden Umgebungs-Key und ersetzt die Originalwerte in der Datei durch `ENV_ENC:`. + - Verschlüsselt die Werte mit dem passenden Umgebungs-Key und ersetzt die Originalwerte in der Datei durch den umgebungsspezifischen Prefix, z.B. `DEV_ENC:`, `INT_ENC:`, `PROD_ENC:` (abhaengig von `APP_ENV_TYPE`). - Legt vor Änderungen eine Backup-Datei mit Zeitstempel neben der Originaldatei an, z. B. `env_prod.env.20251015_101530.backup`. - Trockenlauf (nur anzeigen, nichts schreiben): diff --git a/d-guides/google-oauth-setup.md b/d-guides/google-oauth-setup.md index 85d4500..6f30057 100644 --- a/d-guides/google-oauth-setup.md +++ b/d-guides/google-oauth-setup.md @@ -15,11 +15,10 @@ This guide explains how to set up Google OAuth 2.0 authentication for the PowerO 4. Enter a project name (e.g., "PowerOn OAuth") 5. Click "Create" -## Step 2: Enable Google+ API +## Step 2: Configure OAuth Consent Screen -1. In your new project, go to "APIs & Services" > "Library" -2. Search for "Google+ API" or "Google Identity" -3. Click on "Google+ API" and click "Enable" +1. In your new project, go to "APIs & Services" > "OAuth consent screen" +2. Configure the consent screen (Google+ API is deprecated, use Google Identity Services / OIDC) ## Step 3: Create OAuth 2.0 Credentials @@ -35,9 +34,10 @@ This guide explains how to set up Google OAuth 2.0 authentication for the PowerO 4. Back to creating OAuth client ID: - Application type: "Web application" - Name: "PowerOn Web Client" - - Authorized redirect URIs: Add your redirect URI - - For development: `http://localhost:8000/api/google/auth/callback` - - For production: `https://yourdomain.com/api/google/auth/callback` + - Authorized redirect URIs: Add **two** redirect URIs per environment: + - Login callback: `http://localhost:8000/api/google/auth/login/callback` + - Data connect callback: `http://localhost:8000/api/google/auth/connect/callback` + - For production: replace `localhost:8000` with your domain (HTTPS) 5. Click "Create" 6. **Important**: Copy the Client ID and Client Secret - you'll need these for the next step @@ -48,12 +48,19 @@ This guide explains how to set up Google OAuth 2.0 authentication for the PowerO 2. Replace the placeholder values with your actual Google OAuth credentials: ```env -# Google OAuth Configuration -Service_GOOGLE_CLIENT_ID = your-actual-client-id-from-google-console -Service_GOOGLE_CLIENT_SECRET = your-actual-client-secret-from-google-console -Service_GOOGLE_REDIRECT_URI = http://localhost:8000/api/google/auth/callback +# Google OAuth -- Auth App (Login) +Service_GOOGLE_AUTH_CLIENT_ID = your-auth-client-id +Service_GOOGLE_AUTH_CLIENT_SECRET = your-auth-client-secret +Service_GOOGLE_AUTH_REDIRECT_URI = http://localhost:8000/api/google/auth/login/callback + +# Google OAuth -- Data App (SharePoint/Drive Connection) +Service_GOOGLE_DATA_CLIENT_ID = your-data-client-id +Service_GOOGLE_DATA_CLIENT_SECRET = your-data-client-secret +Service_GOOGLE_DATA_REDIRECT_URI = http://localhost:8000/api/google/auth/connect/callback ``` +> **Hinweis:** Das Gateway unterscheidet zwei OAuth-Apps: eine fuer Login (Auth) und eine fuer Daten-Connections (Data). Details: `routeSecurityGoogle.py`. + 3. Save the file 4. Restart your PowerOn gateway server diff --git a/d-guides/testing-strategy.md b/d-guides/testing-strategy.md index db2b3ae..bc74902 100644 --- a/d-guides/testing-strategy.md +++ b/d-guides/testing-strategy.md @@ -1,26 +1,105 @@ - + # Testing-Strategie ## Testpyramide -- **Unit Tests:** pytest (Gateway), vitest (Frontend) -- **Integration Tests:** API-Tests gegen laufenden Gateway -- **E2E Tests:** (zu definieren) +``` + / E2E \ <- Manuell / geplant + / Functional \ <- Standalone-Scripts (AI, real calls) + / Integration \ <- pytest (DB, Workflows, RBAC) + / Unit \ <- pytest (Models, Utils, Logic) +``` -## Akzeptanzkriterien-Format +## Gateway: `gateway/tests/` -Given-When-Then Sätze in Arbeits-Dokumenten (`c-work/_TEMPLATE.md`) +### Test-Kategorien -## Testplan-Tabelle +| Kategorie | Pfad | Beschreibung | Ausfuehrung | +|-----------|------|-------------|-------------| +| **Unit** | `tests/unit/` | Datamodels, RBAC-Logic, Utils, Workflows | `pytest tests/unit/` | +| **Integration** | `tests/integration/` | DB-abhaengig (RBAC, Workflow-Execution) | `pytest tests/integration/` | +| **Validation** | `tests/validation/` | Architektur-Validierung | `pytest tests/validation/` | +| **Functional** | `tests/functional/` | AI-Modell-Tests, KPI, JSON-Verarbeitung | `python tests/functional/