wiki/b-reference/platform-core/architecture.md

20 KiB

Gateway -- Architektur

Überblick

Das Gateway ist das Python-Backend der PowerOn-Plattform (FastAPI, PostgreSQL). Es kapselt Mandanten- und Feature-Logik hinter einer REST-API und folgt einer klaren Schichtenfolge: Connectors sprechen externe Systeme und Datenbanken an, Interfaces normalisieren Zugriffe und DTOs, Services bündeln fachliche Operationen darauf, und das ServiceCenter löst diese Services pro Request mit einheitlichem Kontext auf. Routen in modules/routes/ sind der HTTP-Einstieg; Security-Middleware und gemeinsame Utilities liegen in modules/security/ bzw. modules/shared/. Feature-spezifische Domänen leben in modules/features/ als weitgehend autonome Module.

Modulstruktur

Unter platform-core/modules/ (Kontext-Audit):

Modul Zweck
aicore/ Model-Registry, Model-Selector, Provider-Plugins (Anthropic, OpenAI, Mistral, Perplexity, Tavily, PrivateLLM)
auth/ Authentifizierung, CSRF, Token-Refresh-Middleware, JWT
connectors/ DB-Connector (PostgreSQL), Provider-Subpakete (Microsoft, Google, ClickUp, FTP, Infomaniak), Ticket/Messaging/Geo-Konnektoren. Pro Provider registrieren ServiceAdapter (OutlookAdapter, OneDriveAdapter, SharepointAdapter, TeamsAdapter, CalendarAdapter, ContactsAdapter, GmailAdapter, DriveAdapter, KdriveAdapter, …) die UDB-Services. Adapter-Registry pro Connector ist _SERVICE_MAP
datamodels/ Pydantic-Datenmodelle (u. a. Ai, Billing, Chat, Content, Files, Knowledge, Rbac, Subscription, UiLanguage, Workflow)
features/ Feature-Module (autonome Domänen): workspace, graphicalEditor, commcoach, neutralization, realEstate, trustee, teamsbot
interfaces/ DB-Interfaces (App, Billing, Chat, Knowledge, Management, Subscription), AI-Objects, RBAC, Features, Messaging
migration/ Daten-Migrationen
routes/ REST-API-Routen (u. a. Admin, Billing, DataFiles, DataSources, i18n, Security, Store, System)
security/ RBAC (rbac.py, rbacCatalog.py), Root-Access
serviceCenter/ Zentrale Service-Orchestrierung (Registry, Resolver, Kontext, Haupt-Services)
serviceHub/ Service-Registry und Dependency Injection (u. a. PublicService-Wrapper)
shared/ Gemeinsame Utilities (attributeUtils, i18nRegistry, Konfiguration, Logging, …)
system/ System-Konfiguration, Feature-Discovery (registry.py: loadFeatureMainModules, loadFeatureRouters)
workflows/ Workflow-Engine mit Methoden, Aktionen, Execution Engine und konsolidiertem Scheduler

ServiceCenter (Kern-Orchestrierung)

Das ServiceCenter ist die zentrale Schicht, die Kern- und importierbare Services verbindet. Pro Request/Session wird ein ServiceCenterContext (serviceCenter/context.py) mitgeführt und propagiert u. a.:

  • user: User (gesamtes User-Objekt), mandateId, featureInstanceId
  • workflow_id, workflow, feature_code
  • requireNeutralization (optional) — Neutralisierungsflag auf Chat-Ebene

Die öffentliche API umfasst u. a. getService(key, context) zur Auflösung einer Service-Instanz; registrierte Services sind in registry.py als CORE_SERVICES und IMPORTABLE_SERVICES hinterlegt, mit feature-spezifischer Auflösung und optionaler Vorabladung (preWarm).

Services (serviceCenter/services/)

Service Hauptmodul (Auszug) Zweck
serviceAgent mainServiceAgent.py AI-Agent mit vielen Tools (u. a. Dateien, Suche, Container, Web, Mail)
serviceAi mainServiceAi.py AI-Call-Gateway: Neutralisierung, Billing-Preflight, Provider-Auswahl, Streaming, Extraktion, Generierung
serviceKnowledge mainServiceKnowledge.py Knowledge Store: Indexierung, semantische Suche, RAG-Kontext (buildAgentContext)
serviceBilling mainServiceBilling.py Billing-Checks, Transaktionen
serviceChat mainServiceChat.py Chat-Persistenz
serviceExtraction mainServiceExtraction.py Dokument-Extraktion (PDF, DOCX, XLSX, …)
serviceGeneration mainServiceGeneration.py Dokument-Generierung
serviceMessaging mainServiceMessaging.py E-Mail, Notifications
serviceSubscription mainServiceSubscription.py Subscription-Verwaltung
serviceWeb mainServiceWeb.py Web-Scraping

Hinweis (Agent): Agent-Tools liefern Rohdaten; Neutralisierung vor dem Versand an ein Sprachmodell erfolgt zentral im serviceAi (mainServiceAi.py), nicht in den Tools selbst.

Einordnung (Framework)

Daten- und Steuerfluss: Client oder Workflow → Service Center → Service → Interface → Connector → externes System. Das Service Center bündelt dabei Erkennbarkeit (Registry), Konfiguration und querschnittliche Aspekte wie Logging, RBAC-Objekt-Registrierung (registerServiceObjects) und konsistente Service-Lebenszyklen; serviceHub ergänzt dies mit PublicService, sodass nur bewusst exponierte Methoden nach außen sichtbar sind.

Interfaces (DB-Schicht)

Die genannten Module kapseln die Datenbankzugriffe bzw. die zugehörigen Fachverträge (Audit-Liste):

Interface Verantwortlich für
interfaceDbApp.py User, Mandate, FeatureAccess, UserConnections, Preferences
interfaceDbBilling.py BillingAccount, BillingTransaction, Subscriptions
interfaceDbChat.py ChatWorkflow, ChatMessage, ChatDocument
interfaceDbKnowledge.py FileContentIndex, ContentChunk, RoundMemory (RAG/Knowledge Store)
interfaceDbManagement.py FileItem, Folder, Prompt, DataSource (mandantenweite Stammdaten), UiLanguageSet-Seeding
interfaceDbSubscription.py Subscription-Verwaltung
interfaceAiObjects.py AI-Call-Abstraktion (Text, Embedding, Vision, Streaming)
interfaceFeatures.py Feature-Instanz-Lifecycle, Template-Rollen-Kopie
interfaceRbac.py RBAC-Regelauswertung

Weitere Interface-Dateien im Ordner (z. B. Voice, Tickets, Messaging, Bootstrap) erfüllen dieselbe Normalisierungsrolle gegenüber Connectors bzw. externen APIs; die Tabelle oben entspricht der expliziten DB-/Kern-Zuordnung aus dem Kontext-Audit.

Schlüssel-Dateien

Datei / Pfad Rolle
platform-core/app.py FastAPI-Anwendung (generischer Entry-Point), Routen, Middleware, EventManager, Scheduler-MainLoop
platform-core/modules/serviceCenter/__init__.py Service Center: getService, preWarm, RBAC-Registrierung für Service-Objekte
platform-core/modules/serviceCenter/context.py ServiceCenterContext pro Request
platform-core/modules/serviceCenter/registry.py Service-Registry (CORE / IMPORTABLE)
platform-core/modules/serviceCenter/resolver.py Auflösung von Service-Instanzen inkl. Cache
platform-core/modules/serviceHub/__init__.py Hub / DI, PublicService-Wrapper für kontrollierte Oberflächen
platform-core/modules/routes/*.py HTTP-Endpunkte, Aufruf in Richtung Service Center / Features (inkl. routeI18n.py für DB-backed i18n mit AI-Übersetzung)
platform-core/modules/interfaces/*.py Stabile Verträge und DB-Zugriffe (keine direkte Vendor-Logik in Services)
platform-core/modules/connectors/*.py Vendor-spezifische Adapter (Auth, Transport, Mapping)
platform-core/modules/security/* RBAC-Auswertung, RBAC-Katalog, Root-Access
platform-core/modules/auth/* CSRF, Token-Refresh-Middleware, JWT, OAuth
platform-core/modules/shared/* Querschnitt: Konfiguration, Audit-Logging, Events, Utilities
platform-core/modules/system/registry.py Feature-Discovery, Router-Laden, Katalog-Registrierung beim App-Start
platform-core/modules/workflows/workflowManager.py Zentrale Workflow-Steuerung (Workspace/Chat Workflows)
platform-core/modules/workflows/automation2/executionEngine.py Graph-Execution-Engine: topoSort, Transit-Routing, _normalizeToSchema nach Execute, flow.merge-Wait, Resume-Schema-Validierung
platform-core/modules/workflows/scheduler/mainScheduler.py Konsolidierter Workflow-Scheduler
platform-core/modules/interfaces/interfaceBootstrap.py System-Bootstrap (Templates, Billing, Stripe)
platform-core/modules/interfaces/interfaceVoiceObjects.py Fassade Google STT/TTS, Billing-Callback Streaming
platform-core/modules/connectors/connectorVoiceGoogle.py Google Speech v1 + Translation + TTS-Client
platform-core/modules/routes/routeVoiceGoogle.py /voice-google/* inkl. STT-Streaming-WebSocket

Google Voice (STT / TTS)

Siehe Kanon-Seite voice-google.md (Batch- vs Streaming-Pfad, WS-Protokoll open/end_of_single_utterance, Parameter model / lightweight / audioFormat, Zuordnung CommCoach / Teamsbot / Workspace / Agent-Tools).

i18n (Mehrsprachigkeit)

Das Gateway nutzt ein DB-basiertes Sprachsystem (UiLanguageSet). Alle UI-sichtbaren Texte (HTTPException-Details, Model-Labels, API-Messages) werden ueber t() getaggt und per AI uebersetzt.

Komponente Datei Zweck
i18nRegistry.py modules/shared/ t(), resolveText(), @i18nModel Decorator, _REGISTRY, _CACHE, _setLanguage, Boot-Sync
attributeUtils.py modules/shared/ getModelLabels() / getModelLabel() lesen aus i18nRegistry.MODEL_LABELS, nutzen resolveText()
routeI18n.py modules/routes/ Admin-API: Sprachset-CRUD, AI-Uebersetzung, xx-Sync, TextMultilingual-Batch-Uebersetzung
app.py platform-core/ Boot-Hooks (_syncRegistryToDb, _loadCache), Request-Middleware (_setLanguage)

Architektur-Regeln

  1. t() NUR mit String-Literalen: t("Speichern") ist korrekt, t(variable) ist verboten. Jeder i18n-Key muss als Literal im Code stehen, damit er beim Import registriert wird.
  2. Backend uebersetzt, Frontend rendert: Das Backend liefert uebersetzte Strings via API. Das Frontend ruft t() nur fuer eigene statische UI-Texte auf, nie fuer Werte die vom Backend kommen.
  3. Fallback [key]: Wenn eine Uebersetzung fehlt, gibt t() [key] zurueck (z.B. [Speichern]), damit fehlende Uebersetzungen im UI sofort sichtbar sind.
  4. Keine lokalen Resolve-Helfer: Alle Label-Aufloesung laeuft ueber resolveText() (zentral in i18nRegistry.py). Keine _resolveLabel(), _resolveTextMultilingual(), _storeLabelText() etc. in Route-Dateien.

Zentrale Funktionen

t(key: str) -> str — Tag + Translate:

  • Bei Import: registriert Key in _REGISTRY
  • Bei Runtime: liefert _CACHE[lang][key], Fallback [key]
  • Sprache kommt aus _CURRENT_LANGUAGE ContextVar (gesetzt durch Middleware)

resolveText(value: Any, lang: Optional[str] = None) -> str — Universelle Label-Aufloesung:

  • strt(value) (behandelt als i18n-Key)
  • dict → Nutzersprache aus Kontext (oder lang wenn gesetzt), dann xx, dann erster nicht-leerer Wert (keine eckigen Klammern — das ist Inhalt, kein fehlender UI-Key)
  • TextMultilingualmodel_dump() dann dict-Logik
  • None""

Boot-Reihenfolge

  1. Module laden → @i18nModel Decorators + t()-Aufrufe registrieren Keys in _REGISTRY
  2. _syncRegistryToDb():
    • Scannt Route-Dateien nach routeApiMsg("…") (api.* Keys)
    • _registerNavLabels(): Navigation-Labels aus NAVIGATION_SECTIONS (nav.* Keys)
    • _registerFeatureUiLabels(): FEATURE_LABEL und UI_OBJECTS Labels (nav.* Keys)
    • _registerRbacLabels(): DATA_OBJECTS, RESOURCE_OBJECTS, TEMPLATE_ROLES, QUICK_ACTIONS Labels (rbac.data, rbac.resource, rbac.role, rbac.quickaction Keys)
    • Merged Gateway-Keys ins xx-Basisset (UI-Keys bleiben erhalten)
  3. _loadCache(): Laedt alle UiLanguageSets in den In-Memory-Cache (_CACHE)

Request-Flow

Middleware setzt _setLanguage(lang) aus Accept-Language Header → t("Key") liefert Uebersetzung aus _CACHE[lang] (O(1) Dict-Lookup, kein DB-Call). resolveText() nutzt denselben Mechanismus.

TextMultilingual

Felder vom Typ TextMultilingual speichern Benutzertexte mehrsprachig. xx ist das Pflichtfeld (Quelltext/Deutsch), weitere Sprachen werden dynamisch als Extra-Felder gespeichert. Bei neuer Sprache werden alle TextMultilingual-Felder per Batch-Job automatisch uebersetzt (routeI18n._translateTextMultilingualFields). On-Demand: POST /api/i18n/translate-field.

Context-Namensraeume

ui (Frontend), api.<routeModuleName> (HTTPExceptions), table.<ClassName> (Model-Labels), table.<ClassName>.<fieldName> (Feld-Labels), nav (Navigation/Feature-Labels), rbac.data / rbac.resource / rbac.role / rbac.quickaction (RBAC-Katalog)

Entries-Identitaet: Ein Entry wird durch (key, context) eindeutig identifiziert — derselbe Text kann mit verschiedenen Contexts existieren.

BackgroundJob-Progress-Messages

Hintergrundjobs laufen ausserhalb des Request-Kontexts und haben deshalb keinen _CURRENT_LANGUAGE-Wert. Walker schreiben deshalb einen strukturierten i18n-Payload in die DB und der Route-Handler uebersetzt server-side beim Read -- der Frontend ruft t() nie auf Backend-supplied Werten auf (Regel #2).

1. Walker schreibt strukturiert (platform-core/modules/serviceCenter/.../subConnectorSync*.py):

progressCb(
    0,
    messageKey="{n} Dateien verarbeitet, {indexed} indexiert",
    messageParams={"n": processed, "indexed": result.indexed},
)

messageKey ist ein String-Literal (Pflicht, damit es scanbar bleibt). JobProgressCallback speichert {key, params} in BackgroundJob.progressMessageData (JSONB) und einen deutschen Best-Effort-Fallback in progressMessage (fuer Logs/Audit/Legacy).

2. Key-Registrierung -- progressCb(..., messageKey="…") durchlaeuft NICHT t(), deshalb braucht jeder Key ein string-literales t("…") in der Feature-Registrierungsdatei (siehe serviceKnowledge/_progressMessages.py, features/trustee/mainTrustee.py). KEINE Schleifen ueber Listen mit t(variable) -- jede Zeile muss t("LITERAL") sein, sonst nimmt der Boot-Scan den Key nicht in UiLanguageSet auf.

3. Route uebersetzt server-side (routeJobs._serialiseJob, routeRagInventory):

from modules.shared.i18nRegistry import resolveJobMessage

out["progressMessage"] = (
    resolveJobMessage(j.get("progressMessageData"))
    or j.get("progressMessage", "")
)

resolveJobMessage(messageData) ruft intern t(key) mit der Request-Sprache und substituiert die Params. Das ist analog zu resolveText(value) -- es ist der einzige zulaessige t(variable)-Pfad, weil er innerhalb der i18n-Infrastruktur liegt.

4. Frontend rendert 1:1 -- progressMessage ist bereits uebersetzt, die Render-Stellen lesen das Feld direkt:

<span>{conn.runningJobs[0].progressMessage || t('Synchronisierung läuft...')}</span>

Das t('Synchronisierung läuft...') ist ein lokales UI-Fallback (string-literal, ok), das progressMessage aus dem Backend geht nicht durch t().

Feature: Trustee -- Daten-Tabellen-Endpunkte

Alle 13 Trustee-Tabellen sind ueber paginierte, RBAC-gefilterte GET-Endpunkte abrufbar. Die sechs CRUD-Modelle (TrusteeOrganisation, TrusteeRole, TrusteeAccess, TrusteeContract, TrusteeDocument, TrusteePosition) haben weiterhin die etablierten REST-Routen; sieben weitere (zuvor nur als JSON-Export oder Aggregat-Endpunkt verfuegbar) wurden ergaenzt:

Endpunkt Modell Zweck
GET /api/trustee/{instanceId}/data/accounts TrusteeDataAccount Synchronisierter Kontenplan (read-only)
GET /api/trustee/{instanceId}/data/journal-entries TrusteeDataJournalEntry Synchronisierte Buchungskoepfe (read-only)
GET /api/trustee/{instanceId}/data/journal-lines TrusteeDataJournalLine Synchronisierte Buchungszeilen (read-only)
GET /api/trustee/{instanceId}/data/contacts TrusteeDataContact Synchronisierte Kontakte (read-only)
GET /api/trustee/{instanceId}/data/account-balances TrusteeDataAccountBalance Synchronisierte Saldenliste (read-only)
GET /api/trustee/{instanceId}/accounting/configs TrusteeAccountingConfig Connector-Konfiguration (read-only; Secrets maskiert)
GET /api/trustee/{instanceId}/accounting/syncs TrusteeAccountingSync Audit-Eintraege der Sync-Laeufe (read-only)

Alle sieben Endpunkte teilen sich den Helper _paginatedReadEndpoint (routeFeatureTrustee.py), der das Pattern aus get_documents / get_positions reproduziert: Unified Filter API (mode=filterValues|ids), _validateInstanceAccess fuer Instanz-Gating und getRecordsetPaginatedWithRBAC mit featureCode + data.feature.trustee.<Model> fuer datenbasierten RBAC. FK-Felder werden automatisch via enrichRowsWithFkLabels aufgeloest (siehe FK Label Resolution).

UI-seitig sind diese Endpunkte unter /mandates/{m}/trustee/{i}/data-tables[?tab=<key>] (View TrusteeDataTablesView) zugaenglich; UI-Sichtbarkeit der Seite haengt am Permission-Eintrag ui.feature.trustee.data-tables (Template-Rollen trustee-viewer, trustee-user, trustee-accountant, Admin via Wildcard).

Typed Action Architecture (4-Schichten)

Seit Phase 1-5 (April 2026) folgt jede Workflow-Aktion einem strikten 4-Schichten-Modell. Quellen: wiki/c-work/3-validate/2026-04-typed-action-architecture.md, Tests in platform-core/tests/unit/workflows/, platform-core/tests/integration/trustee/.

Schicht Modul / Verantwortung Type-Anker
1. Editor (Frontend) ui-nyla/src/components/FlowEditor/nodes/shared/ -- RequiredAttributePicker, DataPicker (strict-mode + Object-Drill-Down mit *-Wildcard), paramValidation.ts Action-Signaturen aus dem Catalog (/api/automation2/catalog)
2. Action / Method platform-core/modules/workflows/methods/method<Feature>/actions/*.py (extractFromFiles, processDocuments, syncToAccounting, ...) Typisierte Pydantic-Eingabe + ActionResult-Output, FeatureInstanceRef als Discriminator-Envelope
3. Adapter platform-core/modules/features/graphicalEditor/nodeDefinitions/registerNodeWithMethod -- leitet Node-Definitions aus Action-Signaturen ab, liefert Snapshot fuer den Editor (adapter_validator) Snapshot-Test test_staticNodesHaveNoDriftAgainstLiveMethods (_KNOWN_ADAPTER_DRIFTS = frozenset())
4. Runtime platform-core/modules/workflows/automation2/executionEngine.py -- executeGraph mit materializeFeatureInstanceRefs + materializeConnectionRefs, _unwrapTypedRef fuer Action-Calls Migrations-Helper in automation2/featureInstanceRefMigration.py + DB-CLI scripts/script_migrate_feature_instance_refs.py

Konsequenzen:

  • Single source of truth ist die Action-Signatur. Editor, Adapter, Runtime und AI-Agent (actionToolAdapter) lesen alle dieselbe Catalog-Sicht.
  • Pick-not-push auf Schicht 4: Runtime resolviert connectionReference und unwrappt typisierte Envelopes (FeatureInstanceRef, DataRef) automatisch -- Legacy-Aktionen bleiben funktionsfaehig.
  • Save-with-errors ist explizit erlaubt (AC-9-Patch im CanvasHeader); Run wird mit executeBlockedReason blockiert, der Save-Toast meldet Pflicht-Fehler-Anzahl.
  • DB-Hygiene ist optional via script_migrate_feature_instance_refs.py (--dry-run standard) -- ohne Skript laufen Workflows korrekt, der Editor zeigt aber bis zum naechsten Save noch das Legacy-Format.

Regeln / Invarianten

  • Schichten: Connectors sind anbieterspezifisch und ersetzbar; Services hängen von Interfaces ab, nicht direkt von Connectors. Geschäftslogik und Guardrails liegen in den Services.
  • Datenbank: Persistenz und die genannten Domänen-Reads/Writes laufen über die Interface-Schicht, nicht ad hoc aus Routen heraus.
  • Service Center: Aufrufe laufen über die zentrale Auflösung (getService + Kontext); Workflows und Routen konsumieren dieselbe Landschaft.
  • Sicherheit & Governance: RBAC und Geheimnisse werden zentral geführt; Service-Aufrufe und Workflow-Schritte sollen nachvollziehbar sein (Audit); Kontingente und Limits pro Mandant/Service sind vorgesehen.
  • HTTP-Einstieg: Globale Middleware (CSRF, Token-Refresh unter modules/auth/); RBAC-Auswertung und Katalog unter modules/security/.
  • Code-Konventionen (Projekt): Interne Hilfsfunktionen mit Präfix _; Bezeichner in camelCase für Variablen und Funktionen.