From e4ba4d83283eb223622baa182bc9eddd4a91941b Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Wed, 4 Feb 2026 15:35:23 +0100
Subject: [PATCH] billing concept
---
concepts/Billing-Konzept.md | 191 +++++++++++++++++++++++-------------
1 file changed, 121 insertions(+), 70 deletions(-)
diff --git a/concepts/Billing-Konzept.md b/concepts/Billing-Konzept.md
index 741e0c2..29067f4 100644
--- a/concepts/Billing-Konzept.md
+++ b/concepts/Billing-Konzept.md
@@ -26,8 +26,9 @@ Mandant (Kostenstelle)
```
### 3. LLM-Provider-Steuerung
-- Benutzer können pro Workflow definieren, welche **AICore-Provider** erlaubt sind
-- Zuordnung auf Level **AICore-Connector**: `anthropic`, `openai`, `perplexity`, `tavily`, `internal`
+- Benutzer können pro Workflow definieren, welche **AICore-Provider** verwendet werden
+- Provider-Liste ist **dynamisch** basierend auf registrierten AICore-Plugins (Plug&Play)
+- Zuordnung auf Level **AICore-Connector** (z.B. `anthropic`, `openai`, `perplexity`, `tavily`, `internal`, weitere zukünftig)
- Ermöglicht Kostenkontrolle und Compliance-Anforderungen
### 4. Transparente Kostenerfassung
@@ -194,15 +195,25 @@ class UsageStatistics(BaseModel):
costByFeature: Dict[str, float] # {'playground': 15.00, 'automation': 5.80}
```
-### Erweiterung bestehender Modelle
+### Hinweis: Chat-Daten bleiben User-owned
+
+**Wichtig:** Die Chat-Modelle (`ChatWorkflow`, `ChatStat`, `ChatMessage`, etc.) speichern **keine** `mandateId`. Sie sind "User-owned" und die Mandanten-Zuordnung erfolgt über RBAC-Filterung zur Laufzeit.
+
+**Billing-Zuordnung:** Der Mandanten-Kontext wird **in der BillingTransaction** gespeichert, nicht im ChatWorkflow:
+- Bei jedem AI-Call wird der `mandateId` aus dem Request-Context in der Transaktion gespeichert
+- Die Verknüpfung Workflow → Mandant erfolgt über `BillingTransaction.workflowId`
+- Statistiken werden aus den BillingTransactions aggregiert, nicht aus ChatStat
```python
-# Erweiterung: ChatWorkflow (datamodelChat.py)
-class ChatWorkflow(BaseModel):
- # ... bestehende Felder ...
-
- # NEU: Mandanten-Kontext für Billing
- mandateId: Optional[str] # FK → Mandate
+# KEINE Änderung an ChatWorkflow erforderlich!
+# ChatWorkflow bleibt "User-owned, no mandate context"
+
+# Stattdessen: BillingTransaction speichert den Mandanten-Kontext
+class BillingTransaction(BaseModel):
+ # ...
+ workflowId: Optional[str] # Referenz zum Workflow
+ featureInstanceId: Optional[str] # Feature-Instanz (z.B. chatplayground)
+ # mandateId kommt über BillingAccount
```
**Hinweis:** Die Provider-Steuerung erfolgt NICHT über Datenbank-Attribute, sondern über das RBAC-System (siehe Abschnitt "LLM-Provider als RBAC Resources").
@@ -245,23 +256,35 @@ class ChatWorkflow(BaseModel):
Die Steuerung, welche LLM-Provider ein Benutzer verwenden darf, erfolgt über das RBAC-System:
-**Registrierte Resources:**
+**Resource-Namenskonvention:**
+```
+resource.aicore.{connectorType}
+```
+
+**Dynamische Registrierung:**
+- Jedes AICore-Plugin registriert sich automatisch als RBAC-Resource
+- Die Liste der verfügbaren Provider wird aus `ModelRegistry.getConnectors()` ermittelt
+- Neue Plugins werden automatisch erkannt (Plug&Play)
+
+**Beispiel aktuell registrierter Provider:**
- `resource.aicore.anthropic`
- `resource.aicore.openai`
- `resource.aicore.perplexity`
- `resource.aicore.tavily`
- `resource.aicore.internal`
+- *(weitere zukünftig via Plug&Play)*
**Bootstrap-Berechtigungen (Mandate ROOT):**
-- **SysAdmin:** Alle Provider (USE)
-- **Admin:** Alle Provider (USE)
-- **User:** Alle Provider (USE)
+- **SysAdmin:** Alle registrierten Provider (USE)
+- **Admin:** Alle registrierten Provider (USE)
+- **User:** Alle registrierten Provider (USE)
**Vorteile des RBAC-Ansatzes:**
- Zentrale Verwaltung über bestehende Rollen-Struktur
- Granulare Steuerung pro Mandant möglich
- Keine zusätzlichen Datenbank-Attribute erforderlich
- Konsistent mit bestehendem Berechtigungssystem
+- Automatische Erweiterung bei neuen AICore-Plugins
---
@@ -463,9 +486,13 @@ def _setupRootMandate():
def _setupAicoreRbacPermissions(mandateId: str):
- """Erstellt RBAC-Berechtigungen für LLM-Provider"""
+ """Erstellt RBAC-Berechtigungen für LLM-Provider (dynamisch)"""
+
+ # Provider dynamisch aus ModelRegistry laden (Plug&Play)
+ from modules.aicore.aicoreModelRegistry import modelRegistry
+ connectors = modelRegistry.discoverConnectors()
+ providers = [c.getConnectorType() for c in connectors]
- providers = ['anthropic', 'openai', 'perplexity', 'tavily', 'internal']
roles = ['sysadmin', 'admin', 'user']
for role in roles:
@@ -696,9 +723,10 @@ Static Block: BILLING
**Integration in:** `/workflows/playground`
**Funktionen:**
-- Dropdown/Checkboxen für erlaubte Provider
+- Provider-Liste wird **dynamisch** aus registrierten AICore-Plugins geladen
- Zeigt nur Provider an, für die der User RBAC-Berechtigung hat
- Auswahl gilt für den nächsten AI-Call
+- Preise werden aus den Plugin-Definitionen gelesen
```
┌─────────────────────────────────────────────────────────┐
@@ -729,7 +757,7 @@ Das Automation-Editor Modal wird in drei Tabs aufgeteilt:
**Tab-Struktur:**
1. **General** - Name, Beschreibung, Schedule, Status
2. **Modellierung** - Template, Placeholders, Preview
-3. **LLM Modelle** - Provider-Auswahl basierend auf RBAC-Berechtigungen
+3. **LLM Modelle** - Provider-Auswahl (dynamisch aus AICore-Plugins, gefiltert nach RBAC)
```
┌─────────────────────────────────────────────────────────┐
@@ -739,7 +767,7 @@ Das Automation-Editor Modal wird in drei Tabs aufgeteilt:
│ ───────────────────────────────────────────────────── │
│ TAB: LLM Modelle │
│ │
-│ Verfügbare Provider (basierend auf Ihren Berechtigungen):
+│ Verfügbare Provider (dynamisch, RBAC-gefiltert):
│ ○ Anthropic (Claude) CHF 0.020/1k tokens │
│ ● OpenAI (GPT-4) CHF 0.015/1k tokens │
│ ○ Perplexity (Web) CHF 0.008/1k tokens │
@@ -822,36 +850,36 @@ anbei erhalten Sie den monatlichen Kostenreport für Ihren Mandanten
ZUSAMMENFASSUNG
───────────────────────────────────────────────────────
-Gesamtkosten: $ 156.80 USD
+Gesamtkosten: CHF 156.80
Anzahl AI-Operationen: 423
-Durchschnitt pro Operation: $ 0.37 USD
+Durchschnitt pro Operation: CHF 0.37
AUFSCHLÜSSELUNG NACH PROVIDER
───────────────────────────────────────────────────────
-Anthropic (Claude) $ 112.50 USD (72%)
-OpenAI (GPT-4) $ 38.30 USD (24%)
-Internal $ 6.00 USD (4%)
+Anthropic (Claude) CHF 112.50 (72%)
+OpenAI (GPT-4) CHF 38.30 (24%)
+Internal CHF 6.00 (4%)
AUFSCHLÜSSELUNG NACH FEATURE
───────────────────────────────────────────────────────
-Chat Playground $ 95.00 USD (61%)
-Automation $ 45.80 USD (29%)
-Chatbot (Feature) $ 16.00 USD (10%)
+Chat Playground CHF 95.00 (61%)
+Automation CHF 45.80 (29%)
+Chatbot (Feature) CHF 16.00 (10%)
TOP 5 NUTZER (nach Verbrauch)
───────────────────────────────────────────────────────
-1. Max Mustermann $ 48.50 USD
-2. Anna Schmidt $ 35.20 USD
-3. Peter Weber $ 28.90 USD
-4. Lisa Müller $ 22.10 USD
-5. Thomas Meier $ 12.30 USD
+1. Max Mustermann CHF 48.50
+2. Anna Schmidt CHF 35.20
+3. Peter Weber CHF 28.90
+4. Lisa Müller CHF 22.10
+5. Thomas Meier CHF 12.30
KONTOSTAND
───────────────────────────────────────────────────────
-Vorheriger Stand: $ 456.05 USD
-Verbrauch: -$ 156.80 USD
+Vorheriger Stand: CHF 456.05
+Verbrauch: -CHF 156.80
───────────────────────────────────────────────────────
-Aktueller Stand: $ 299.25 USD
+Aktueller Stand: CHF 299.25
Bei Fragen stehen wir Ihnen gerne zur Verfügung.
@@ -871,8 +899,8 @@ Betreff: ⚠️ PowerOn - Niedriges Guthaben für SOHA Treuhand AG
Ihr Guthaben für den Mandanten "SOHA Treuhand AG" hat die
Warnschwelle unterschritten.
-Aktuelles Guthaben: $ 45.00 USD
-Warnschwelle: $ 50.00 USD (10%)
+Aktuelles Guthaben: CHF 45.00
+Warnschwelle: CHF 50.00 (10%)
Bitte laden Sie Ihr Guthaben auf, um Unterbrechungen zu vermeiden.
@@ -888,29 +916,29 @@ PowerOn Platform
### Phase 1: Datenmodell & Grundstruktur
-1. **Datenbank-Schema erstellen**
+1. **Neue Billing-Datenbank erstellen** (`poweron_billing`)
- `BillingAccount`, `BillingTransaction`, `BillingSettings`, `UsageStatistics` Tabellen
- Migration für bestehende Mandanten (Default: `UNLIMITED`)
-2. **Erweiterung bestehender Modelle**
- - `ChatWorkflow.mandateId` hinzufügen
- - `ChatWorkflow.allowedProviders` hinzufügen
- - `AutomationDefinition.allowedProviders` hinzufügen
+2. **Keine Änderung an Chat-Modellen**
+ - Chat-Daten bleiben "User-owned" (kein mandateId in ChatWorkflow)
+ - Mandanten-Kontext wird in BillingTransaction gespeichert
3. **BillingService implementieren**
- Balance-Prüfung
- - Transaktions-Erfassung
- - Provider-Validierung
+ - Transaktions-Erfassung mit mandateId aus Request-Context
+ - Provider-Validierung über RBAC
### Phase 2: Integration in Workflows
-1. **ChatWorkflow erweitern**
- - `mandateId` bei Start speichern
- - Provider-Filter bei Model-Selection
+1. **Billing-Integration in WorkflowProcessor**
+ - `mandateId` und `featureInstanceId` aus Request-Context verwenden
+ - BillingTransaction bei jedem AI-Call erstellen (mit mandateId)
+ - Provider-Filter über RBAC bei Model-Selection
2. **Billing-Checks einbauen**
- - Vor jedem AI-Call: Balance prüfen
- - Nach jedem AI-Call: Kosten erfassen
+ - Vor jedem AI-Call: Balance prüfen (BillingAccount des Mandanten)
+ - Nach jedem AI-Call: Kosten als BillingTransaction erfassen
3. **Error Handling**
- `InsufficientBalanceException`
@@ -947,7 +975,8 @@ PowerOn Platform
1. **Root-Mandant Setup**
- `PREPAY_USER` Modell
- - 10 USD Startguthaben für neue User
+ - 10 CHF Startguthaben für neue User
+ - RBAC-Berechtigungen für alle LLM-Provider
2. **Bestehende User migrieren**
- Accounts erstellen
@@ -956,6 +985,7 @@ PowerOn Platform
3. **Bestehende Mandanten migrieren**
- Default-Settings erstellen
- `UNLIMITED` oder `PREPAY_MANDATE`
+ - RBAC-Berechtigungen für LLM-Provider erstellen
### Phase 6: Email-Reports
@@ -973,9 +1003,9 @@ PowerOn Platform
---
-## Offene Fragen und Entscheidungen
+## Entscheidungen
-### Beantwortet
+### Architektur-Entscheidungen
1. **Soll Chat Playground über Mandanten laufen?**
→ **Ja**, dies ist für konsistente Verrechnung notwendig. `ChatWorkflow.mandateId` wird aus dem Request-Context übernommen.
@@ -984,29 +1014,47 @@ PowerOn Platform
→ `data.chat.*` und `data.files.*` sind aktuell `USER_OWNED`. Für Billing muss `ChatWorkflow` den Mandanten-Kontext speichern.
3. **Läuft Automation bereits über Mandanten?**
- → **Ja**, `AutomationDefinition.mandateId` existiert bereits. Erweiterung nur um `allowedProviders`.
+ → **Ja**, `AutomationDefinition.mandateId` existiert bereits.
-### Noch zu klären
+4. **Provider-Steuerung**
+ → Über RBAC-System als Resources (`resource.aicore.*`), NICHT als Datenbank-Attribute.
+
+### Geschäftsentscheidungen
1. **Payment-Integration**
- - Wie laden Kunden Guthaben auf? (Stripe, Rechnung, manuell)
- - Self-Service oder nur Admin?
+ → Stripe-Anbindung wird vorbereitet (zukünftig)
+ → Aktuell: SysAdmin-Seite für manuelle Guthaben-Aufladung
2. **Preisgestaltung**
- - Aufschlag auf Provider-Kosten? (z.B. +20%)
- - Fixkosten pro Mandant/User?
+ → **50% Aufschlag** auf Provider-Kosten, direkt in AICore-Modellen definiert
+ → Preise werden in CHF kalkuliert und gespeichert
3. **Währung**
- - Nur USD intern, Anzeige in CHF/EUR?
- - Wechselkurs-Handling?
+ → **Alles in CHF**, keine Währungsumrechnung im System
+ → Provider-Kosten werden intern auf CHF umgerechnet und mit 50% Aufschlag versehen
4. **Datenhaltung**
- - Wie lange Transaktions-Historie aufbewahren?
- - Archivierung alter Daten?
+ → **10 Jahre** Aufbewahrungspflicht für Transaktionen
+ → Automatische Archivierung/Löschung nach 10 Jahren
5. **GDPR/Datenschutz**
- - User-Löschung: Was passiert mit Billing-Daten?
- - Anonymisierung nach X Jahren?
+ → Transaktionsdaten nach 10 Jahren löschen
+ → Gelöschte User: Nur ID-Referenz in Transaktionen (bereits anonym, da keine personenbezogenen Daten)
+ → Daten im System belassen für Mandanten-Abrechnungen
+
+### Preistabelle (50% Aufschlag) - Beispiele
+
+Jedes AICore-Plugin definiert seine eigenen Preise via `calculatePriceCHF()`. Die Preise werden dynamisch aus den Plugins geladen.
+
+| Provider | Provider-Kosten (ca.) | Interner Preis (CHF) |
+|------------|----------------------|----------------------|
+| Anthropic | $0.015/1k tokens | CHF 0.020/1k tokens |
+| OpenAI | $0.010/1k tokens | CHF 0.015/1k tokens |
+| Perplexity | $0.005/1k tokens | CHF 0.008/1k tokens |
+| Tavily | $0.003/1k tokens | CHF 0.005/1k tokens |
+| Internal | $0.001/1k tokens | CHF 0.002/1k tokens |
+
+**Hinweis:** Preise werden direkt in den AICore-Plugin-Modellen definiert (`calculatePriceCHF`) und beinhalten bereits den 50% Aufschlag.
---
@@ -1016,11 +1064,13 @@ PowerOn Platform
|---------|--------------|
| **AICore** | Plugin-System für LLM-Provider Integration |
| **Billing Account** | Konto für Guthaben/Saldo (pro Mandant oder User) |
+| **Billing Address** | Rechnungsadresse, erforderlich für CREDIT_POSTPAY |
| **Connector** | AICore-Plugin für einen spezifischen Provider |
| **Kostenstelle** | Mandant, dem Kosten zugeordnet werden |
| **Mandate** | Mandant/Tenant in der Multi-Tenant-Architektur |
| **Provider** | LLM-Anbieter (anthropic, openai, etc.) |
-| **priceCHF** | Berechneter Preis einer AI-Operation in USD |
+| **priceCHF** | Berechneter Preis einer AI-Operation in CHF (inkl. 50% Aufschlag) |
+| **RBAC Resource** | Berechtigungs-Objekt für Provider-Zugriff (z.B. `resource.aicore.anthropic`) |
---
@@ -1031,18 +1081,19 @@ PowerOn Platform
```
gateway/modules/
├── aicore/
-│ ├── aicoreModelSelector.py # Provider-Filter hinzufügen
+│ ├── aicoreModelSelector.py # Provider-Filter via RBAC
+│ ├── aicorePluginAnthropic.py # Preise auf CHF mit 50% Aufschlag
+│ ├── aicorePluginOpenai.py # Preise auf CHF mit 50% Aufschlag
│ └── aicoreBase.py # connectorType für Billing
├── datamodels/
-│ ├── datamodelChat.py # ChatWorkflow erweitern
+│ ├── datamodelChat.py # ChatWorkflow.mandateId hinzufügen
│ └── datamodelUam.py # Mandate-Referenz
-├── features/automation/
-│ └── datamodelFeatureAutomation.py # allowedProviders hinzufügen
├── interfaces/
-│ └── interfaceBootstrap.py # Root-Mandant Billing Setup
+│ ├── interfaceBootstrap.py # Root-Mandant Billing Setup + RBAC
+│ └── interfaceRbac.py # Resource-Berechtigungen für Provider
├── workflows/
│ ├── automation/mainWorkflow.py # Billing-Integration
-│ └── processing/workflowProcessor.py # Balance-Checks
+│ └── processing/workflowProcessor.py # Balance-Checks + RBAC Provider-Check
└── routes/
└── routeChat.py # mandateId im Workflow speichern
```