131 lines
11 KiB
Markdown
131 lines
11 KiB
Markdown
<!-- status: done -->
|
||
<!-- started: 2026-04-08 -->
|
||
<!-- implemented: 2026-04-11 -->
|
||
<!-- component: gateway -->
|
||
|
||
# Gateway: INT-Stabilität — Analyse (Log) und Fix-Plan
|
||
|
||
## Beschreibung und Kontext
|
||
|
||
Beim Testen der Applikation auf der **Integrations-Instanz (INT)** mit **mehreren parallelen Nutzern** wurden Symptome im Gateway-Log sichtbar (u. a. `gateway_log_2026-04-08T11-14-17-514Z.log`). Ziel dieses Dokuments ist eine **codebezogene Ursachenanalyse** und ein **Umsetzungs- und Testplan** — ohne die Themen mit Architektur-Storys zu vermischen.
|
||
|
||
**Business-Treiber:** Zuverlässiger Multi-User-Betrieb (Upload/Index, Workspace-Agent, Outlook-Tools, Knowledge/Neutralisierung) und weniger „sporadische" Fehler, die Support-Zeit kosten.
|
||
|
||
**Risiko bei Nicht-Umsetzung:** Weiterhin nicht reproduzierbar wirkende Permission-Fehler nach erfolgreicher Verarbeitung, fehlgeschlagene OpenAI-Streaming-Calls wegen Tool-Schema, nutzloser Outlook-Tool-Flow bei UUID-Referenzen, Crashes/Fail-Safe-Sprünge bei Bild-only-Indexierung.
|
||
|
||
**Abhängigkeiten:** Änderungen betreffen primär `gateway`; keine zwingende Frontend-Änderung, ausser Tool-Beschreibungen/UX explizit angepasst werden.
|
||
|
||
## Fokus und kritische Details
|
||
|
||
### Thema A — Management-Interface: globaler Singleton
|
||
|
||
- **Fragil:** `interfaceDbManagement.getInterface()` liefert eine **gemeinsame** `ComponentObjects`-Instanz (`"default"`); jeder Aufruf setzt `setUserContext` auf demselben Objekt.
|
||
- **Edge Case:** Async-Pfade (z. B. `_autoIndexFile` mit `await knowledgeService.indexFile`) — zwischen Start und `updateFile` kann ein anderer Request den Kontext überschreiben → `PermissionError` auf `FileItem.update` trotz erfolgreicher Indexierung.
|
||
- **Code:** `gateway/modules/interfaces/interfaceDbManagement.py`, `gateway/modules/routes/routeDataFiles.py` (`_autoIndexFile`).
|
||
- **Fix:** `_autoIndexFile` holt `mgmtInterface` nach dem letzten `await` frisch. Singleton bleibt bestehen — vollständige Ablösung ist separates Architektur-Thema.
|
||
|
||
### Thema B — Agent-Tool-JSON-Schema: Arrays ohne `items`
|
||
|
||
- **Fragil:** `ActionToolAdapter` mappt `List[str]` auf JSON-Schema `type: array` **ohne** `items`.
|
||
- **Edge Case:** OpenAI lehnt Tools mit HTTP 400 ab (*array schema missing items*); Failover zu anderen Providern maskiert das Problem.
|
||
- **Code:** `gateway/modules/serviceCenter/services/serviceAgent/actionToolAdapter.py`; betroffene Aktion u. a. `ai.process` (`methodAi.py`, Parameter `documentList`).
|
||
- **Fix:** Neues `_ARRAY_ITEMS_MAPPING` ordnet Python-Array-Typen dem korrekten `items`-Schema zu. `_convertParameterSchema` setzt bei `type: "array"` automatisch `items` (Fallback: `{"type": "string"}`).
|
||
|
||
### Thema C — Outlook: Referenzformat vs. Lookup
|
||
|
||
- **Fragil:** `listConnections` zeigt u. a. `id: <uuid>`; `getUserConnectionFromConnectionReference` parst nur `connection:{authority}:{username}`.
|
||
- **Edge Case:** Modell übergibt UUID an Outlook-Tools → „Connection not found".
|
||
- **Code:** `coreTools/_connectionTools.py`, `mainServiceChat.getUserConnectionFromConnectionReference`, `methodOutlook/helpers/connection.py`.
|
||
- **Fix:** `listConnections` gibt jetzt `connection:{authority}:{username}` aus statt UUIDs. `getUserConnectionFromConnectionReference` hat UUID-Fallback: wenn Format nicht `connection:…`, wird nach Connection-ID gesucht.
|
||
|
||
### Thema D — Knowledge: `_neutralSvc` bei nur Bildern
|
||
|
||
- **Bug:** `_neutralSvc` wird nur im Zweig mit Textobjekten gesetzt; Bild-Neutralisierung nutzt `_neutralSvc` danach — bei **keinen** Textobjekten → `UnboundLocalError` (im Log als fail-safe sichtbar).
|
||
- **Code:** `gateway/modules/serviceCenter/services/serviceKnowledge/mainServiceKnowledge.py` (`indexFile`).
|
||
- **Fix:** `_neutralSvc` wird vor Text- und Bild-Block initialisiert (`None`), sobald `_shouldNeutralize` wahr ist. Bild-Block prüft `_neutralSvc` in der Bedingung.
|
||
|
||
### Thema E — Workspace: Mandats-Kontext aus zwei Quellen
|
||
|
||
- **Fragil:** `_getChatInterface` nutzt `context.mandateId`; `ServiceCenterContext` für den Agent nutzt Mandat aus `_validateInstanceAccess` (Instanz).
|
||
- **Edge Case:** Unterschiedliche `interfaceDbChat`-Cache-Keys für „denselben" Workspace → seltene Inkonsistenzen (Workflow sichtbar / nicht sichtbar).
|
||
- **Code:** `gateway/modules/features/workspace/routeFeatureWorkspace.py`.
|
||
- **Fix:** `_getChatInterface` akzeptiert optionalen `mandateId`-Parameter. **Alle 34** `_validateInstanceAccess`-Aufrufe entpacken den Return-Wert. Alle `_getChatInterface`- und `ServiceCenterContext`-Aufrufe nutzen die validierte Instanz-`mandateId`.
|
||
|
||
### Thema F — Chat: `createMessage` / `getWorkflow` → None
|
||
|
||
- **Fragil:** `getWorkflow` liefert `None` bei leerem RBAC-Recordset **und** bei Exceptions beim Validieren des `ChatWorkflow`-Modells; `createMessage` meldet pauschal „No access to workflow".
|
||
- **Edge Case:** Agent-Lauf endet „complete", Persistenz der Assistant-Message schlägt fehl — Ursache ohne besseres Logging schwer trennbar.
|
||
- **Code:** `gateway/modules/interfaces/interfaceDbChat.py`.
|
||
- **Fix:** `getWorkflow` unterscheidet RBAC-leer (`debug`: "RBAC filter or not found") vs. Validierungsfehler (`error`: "data validation failed"). `createMessage` loggt `warning` mit Verweis auf Ursache.
|
||
|
||
## Ziel und Nicht-Ziele
|
||
|
||
- **Ziel:** Die oben genannten **konkreten** Defekte/Risiken beheben oder absichern; INT- und Last-Szenarien reproduzierbar testen; Logging dort verbessern, wo die Ursache heute verschleiert wird.
|
||
- **Explizit NICHT:** Grossrefactor des gesamten Service-Centers; neue Features; umfassende Wiki-b-reference Pflege (erst nach Abschluss laut README-Lebenszyklus).
|
||
|
||
## Betroffene Module
|
||
|
||
- **Gateway:** `interfaceDbManagement`, `routeDataFiles`, `actionToolAdapter`, `mainServiceChat`, `mainServiceKnowledge`, `routeFeatureWorkspace`, `interfaceDbChat`, Agent-Core-Tools (`_connectionTools`), optional `connection.py` (Outlook).
|
||
- **Frontend:** nein (ausser spätere Texte/Hinweise für Connection-Referenz — optional).
|
||
- **DB-Migration:** nein (für diese Fixes nicht erwartet).
|
||
- **Andere Komponenten:** nein.
|
||
|
||
## Entscheidungen
|
||
|
||
| Datum | Entscheidung | Begründung |
|
||
|-------|-------------|------------|
|
||
| 2026-04-08 | Plan-Dokument angelegt | INT-Log + Code-Review; Umsetzung folgt in `2-build`. |
|
||
| 2026-04-11 | Themen A–F umgesetzt (pragmatische Fixes) | Kein Grossrefactor Singleton; stattdessen Re-Acquire nach `await` + konsistente `mandateId`-Übergabe. |
|
||
|
||
## Umsetzungs-Checkliste (Fix-Reihenfolge empfohlen)
|
||
|
||
- [x] **D** `_neutralSvc` / Bildpfad in `mainServiceKnowledge.indexFile` absichern — `_neutralSvc` wird vor Text- und Bild-Block initialisiert; Bild-Block prüft `_neutralSvc` in der Bedingung. Datei: `serviceKnowledge/mainServiceKnowledge.py`.
|
||
- [x] **B** JSON-Schema für `List[str]` / `List[int]` in `actionToolAdapter` — neues `_ARRAY_ITEMS_MAPPING`; `_convertParameterSchema` setzt bei `type: "array"` automatisch `items`. Datei: `serviceAgent/actionToolAdapter.py`.
|
||
- [x] **C** Connection-Lookup: `listConnections` gibt `connection:{authority}:{username}` aus statt UUIDs; `getUserConnectionFromConnectionReference` hat UUID-Fallback. Dateien: `coreTools/_connectionTools.py`, `mainServiceChat.py`.
|
||
- [x] **A** Management-Interface: pragmatischer Fix — `_autoIndexFile` holt `mgmtInterface` nach dem letzten `await` frisch. Singleton bleibt; vollständige Ablösung ist separates Architektur-Thema. Datei: `routes/routeDataFiles.py`.
|
||
- [x] **E** Workspace `mandateId`: `_getChatInterface` akzeptiert optionalen `mandateId`-Parameter; alle 34 `_validateInstanceAccess`-Aufrufe entpacken den Return-Wert; alle `_getChatInterface`- und `ServiceCenterContext`-Aufrufe nutzen die validierte Instanz-`mandateId`. Datei: `features/workspace/routeFeatureWorkspace.py`.
|
||
- [x] **F** `getWorkflow` / `createMessage` Logging: `getWorkflow` unterscheidet RBAC-leer (`debug`) vs. Validierungsfehler (`error`); `createMessage` loggt `warning` mit Verweis auf Ursache. Datei: `interfaces/interfaceDbChat.py`.
|
||
|
||
Weitere Checkliste aus Template:
|
||
|
||
- [x] API-Endpunkte — keine Signaturänderung; `_getChatInterface` hat neuen optionalen Parameter (intern)
|
||
- [x] DB-Schema / Migration — nein (nicht nötig)
|
||
- [x] Frontend-Komponenten — nein
|
||
- [x] RBAC / Permissions — keine Änderung; Fix E stellt sicher, dass Cache-Keys konsistent sind
|
||
- [x] Neutralisierung betroffen? — ja (Thema D, behoben)
|
||
- [x] Navigation / Routing — nein
|
||
- [x] Billing-Impact? — nein
|
||
|
||
## Akzeptanzkriterien
|
||
|
||
| # | Kriterium (Given-When-Then) | Prio | Status |
|
||
|---|---------------------------|------|--------|
|
||
| AC1 | Given eine Datei mit nur Bild-`contentObjects` und aktiver Neutralisierung, When `indexFile` läuft, Then kein `UnboundLocalError` und definiertes Verhalten (OK/skip mit Log). | must | **done** |
|
||
| AC2 | Given generiertes Tool-Schema für `ai_process`, When es an die OpenAI-kompatible API geht, Then `documentList` ist ein `array` **mit** `items` (kein 400 wegen fehlender `items`). | must | **done** |
|
||
| AC3 | Given `listConnections` liefert eine Verbindungs-Referenz, When ein Outlook-Tool dieselbe Referenz nutzt, Then Verbindung wird aufgelöst (Format `connection:…` oder UUID-Fallback). | must | **done** |
|
||
| AC4 | Given zwei parallele Uploads/Auto-Index-Läufe unterschiedlicher User, When Indexierung endet, Then `updateFile(..., active)` schlägt nicht wegen **fremdem** User-Kontext fehl. | must | **done** (Re-Acquire) |
|
||
| AC5 | Given Workspace-Stream mit gültiger Instanz, When Chat-Interface und Agent-Chat-Service erzeugt werden, Then identische effektive `mandateId`/`featureInstanceId`-Kombination für den Chat-Cache. | should | **done** |
|
||
| AC6 | Given `getWorkflow` scheitert an Datenvalidierung, When `createMessage` läuft, Then Log enthält **ursächliche** Exception, nicht nur „No access". | should | **done** |
|
||
|
||
## Testplan
|
||
|
||
| ID | AC | Art | Automatisiert | Repo-Pfad / Methode | Status |
|
||
|----|----|-----|--------------|---------------------|--------|
|
||
| T1 | AC1 | unit | ja | `gateway/tests/...` — `indexFile` mit Mock neutralization, nur image `contentObjects` | pending |
|
||
| T2 | AC2 | unit | ja | Test: `_buildToolDefinition` / Schema für `ai_process` → assert `properties.documentList.items` | pending |
|
||
| T3 | AC3 | unit/integration | ja/teils | `getUserConnectionFromConnectionReference(uuid)` vs. `connection:msft:user@…` | pending |
|
||
| T4 | AC4 | integration | empfohlen | Zwei parallele Requests gegen INT oder lokaler Stress-Skript + assert File-Status | pending |
|
||
| T5 | AC5 | integration | optional | Request ohne Header-Mandat, mit Instanz-ID — ein Log-Punkt für effektive Chat-Keys | pending |
|
||
| T6 | AC6 | manuell/log | teils | Nach Fix: künstlich kaputtes Workflow-Datum in DB oder temporärer Log-Level | pending |
|
||
|
||
## Links
|
||
|
||
- PR: (nach Umsetzung)
|
||
- Issue: (falls extern getrackt)
|
||
- Ausgangs-Log (lokal): `local/debug/gateway_log_2026-04-08T11-14-17-514Z.log`
|
||
|
||
## Abschluss
|
||
|
||
- [x] Alle Themen A–F umgesetzt (2026-04-11)
|
||
- [ ] b-reference/ aktualisiert — z. B. `b-reference/gateway/architecture.md` nur wenn Architektur-Entscheid (Singleton) dauerhaft geändert wird
|
||
- [ ] TOPICS.md aktualisiert (falls neues Thema)
|