wired infomaniac to ai adapters and tools
This commit is contained in:
parent
02a781122f
commit
8a70c6ea9a
3 changed files with 233 additions and 0 deletions
|
|
@ -20,6 +20,7 @@ Lade immer zuerst diese Datei. Dann gezielt die passende(n) Referenz-Datei(en).
|
|||
| Komponentenübersicht | b-reference/product.md | Repo-übergreifende Fragen, Tech-Stack |
|
||||
| Gateway-Architektur | b-reference/gateway/architecture.md | Backend-Module, Services, Interfaces |
|
||||
| AI Agent & Tools | b-reference/gateway/ai-agent.md | Agent-Verhalten, Tool-Registrierung, RAG |
|
||||
| Agent-Tool File Bridge | b-reference/gateway/agent-file-bridge.md | Wie Agent-Tool-Outputs (download / writeFile / renderDocument / generateImage / createChart) als ChatDocument im Workflow landen, `docItem:<id>`-Pattern, runAgent Workflow-Propagation |
|
||||
| Workflow-Engine | b-reference/gateway/workflow.md | Methoden, Aktionen, WorkflowManager |
|
||||
| Automation | b-reference/gateway/automation.md | Graphical Editor, Scheduler, System-Automatisierung (`/automations`, `/api/system/workflow-runs/*`) |
|
||||
| Billing & Subscriptions | b-reference/gateway/billing.md | Abrechnung, Prepaid, State Machine |
|
||||
|
|
|
|||
220
b-reference/gateway/agent-file-bridge.md
Normal file
220
b-reference/gateway/agent-file-bridge.md
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
<!-- status: canonical -->
|
||||
<!-- lastReviewed: 2026-04-29 -->
|
||||
<!-- verifiedAgainst: gateway/modules/serviceCenter/services/serviceAgent/coreTools/_helpers.py (_attachFileAsChatDocument helper); _dataSourceTools.py / _workspaceTools.py / _mediaTools.py (5 file-producing agent tools); mainServiceAgent.py runAgent workflow propagation; mainServiceChat.py (storeMessageWithDocuments + getChatDocumentsFromDocumentList); workflowProcessor.persistTaskResult (canonical pattern); methodTrustee.extractFromFiles (peer pattern); datamodelChat.ChatDocument; datamodelDocref.coerceDocumentReferenceList -->
|
||||
|
||||
# Agent-Tool File Bridge
|
||||
|
||||
## Worum geht es
|
||||
|
||||
Ein Agent-Tool kann eine Datei produzieren (Download, generieren, schreiben).
|
||||
Daraus muss zwingend dreierlei werden, sonst sind die nachgelagerten
|
||||
AI-Tools (`ai_process`, `ai_summarizeDocument`, `context_extractContent`,
|
||||
`context_neutralizeData`) blind:
|
||||
|
||||
1. ein **`FileItem`** in der Management-DB (Inhalt + Metadaten),
|
||||
2. ein **`ChatDocument`**, das im aktuellen Workflow auf das `FileItem`
|
||||
verweist und unter `workflow.messages[*].documents[*]` landet,
|
||||
3. eine **`ChatMessage`**, die das `ChatDocument` traegt (sonst hat es
|
||||
keinen `messageId` und ist nicht referenzierbar).
|
||||
|
||||
`getChatDocumentsFromDocumentList` (in `mainServiceChat.py`) loest
|
||||
`docItem:<id>`-Referenzen ausschliesslich gegen `ChatDocument.id`-Werte
|
||||
in `workflow.messages[*].documents[*]` auf. **Wenn der Agent nur ein
|
||||
`FileItem` erzeugt, aber kein `ChatDocument`, ist der File-Output fuer
|
||||
jedes documentList-konsumierende Tool unsichtbar.**
|
||||
|
||||
## Symptom (vor dem Fix)
|
||||
|
||||
Klassischer Trace nach `downloadFromDataSource` -> `ai_summarizeDocument`:
|
||||
|
||||
```
|
||||
INFO ai_summarizeDocument called with documentList=[...]
|
||||
WARN getChatDocumentsFromDocumentList: No workflow available (self._workflow is not set)
|
||||
INFO Building structure prompt with 0 valid ContentParts
|
||||
```
|
||||
|
||||
Erstes WARN entsteht, wenn `runAgent` das Workflow nicht in die
|
||||
Service-Contexts propagiert. Zweites Symptom (0 ContentParts) bleibt
|
||||
auch danach, solange Agent-Tools nur ein `FileItem` produzieren ohne
|
||||
`ChatDocument`.
|
||||
|
||||
## Architektur-Pattern
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Tool[Agent Tool\ndownloadFromDataSource\nwriteFile create\nrenderDocument\ngenerateImage\ncreateChart] -->|saveUploadedFile<br/>saveGeneratedFile| FI[FileItem]
|
||||
FI -->|_attachFileAsChatDocument| Bridge((Bridge Helper))
|
||||
Bridge -->|storeMessageWithDocuments| CM[ChatMessage]
|
||||
CM --> CD[ChatDocument]
|
||||
CD -.fileId.-> FI
|
||||
CD -.id used as.-> Ref{{docItem:<chatDocId>}}
|
||||
Ref -->|documentList parameter| AITools[ai_process<br/>ai_summarizeDocument<br/>context_extractContent<br/>context_neutralizeData]
|
||||
AITools -->|getChatDocumentsFromDocumentList| CD
|
||||
```
|
||||
|
||||
## Komponenten
|
||||
|
||||
### `_attachFileAsChatDocument` (Single Source of Truth)
|
||||
|
||||
Datei: `gateway/modules/serviceCenter/services/serviceAgent/coreTools/_helpers.py`
|
||||
|
||||
```python
|
||||
def _attachFileAsChatDocument(
|
||||
services: Any,
|
||||
fileItem: Any,
|
||||
*,
|
||||
label: str = "agent_tool_output",
|
||||
userMessage: str = "",
|
||||
role: str = "assistant",
|
||||
) -> Optional[str]:
|
||||
"""Bind a persisted FileItem to the active workflow as a ChatDocument.
|
||||
|
||||
Returns the new ChatDocument.id (or None if no active workflow).
|
||||
"""
|
||||
```
|
||||
|
||||
Liest `chatService._workflow` (= `chatService._context.workflow`),
|
||||
baut ein `ChatDocument`-Dict mit `roundNumber`/`taskNumber`/`actionNumber`
|
||||
aus dem Workflow, packt es in eine `ChatMessage` (Rolle `assistant`,
|
||||
Status `step`, eindeutiges `documentsLabel`) und persistiert via
|
||||
`chatService.storeMessageWithDocuments`. Wirft nie -- liefert im
|
||||
Fehlerfall `None` und loggt Warning.
|
||||
|
||||
### `_formatToolFileResult` (Unified ToolResult Text)
|
||||
|
||||
Renderkonvention: jedes file-erzeugende Tool gibt im Result-Text
|
||||
**immer beide IDs** raus:
|
||||
|
||||
```
|
||||
Downloaded 'platform-overview.html' (87654 bytes)
|
||||
documentList ref: docItem:abcd-1234-...
|
||||
file id: c7bf161b-8113-4b53-...
|
||||
```
|
||||
|
||||
* `docItem:<chatDocId>` -> hineinkopieren in `documentList` von AI-Tools.
|
||||
* `file id: <fileId>` -> fuer `readFile`, `searchInFileContent`,
|
||||
`writeFile mode=append`, Bild-Embeds (``).
|
||||
|
||||
Wenn kein aktiver Workflow gebunden ist, faellt die `documentList ref`-Zeile
|
||||
weg -- die Datei ist trotzdem ueber `file id` direkt lesbar. Der
|
||||
documentList-Pfad braucht Workflow-Kontext sowieso.
|
||||
|
||||
### Workflow-Propagation in `runAgent`
|
||||
|
||||
Datei: `gateway/modules/serviceCenter/services/serviceAgent/mainServiceAgent.py`
|
||||
|
||||
`runAgent(workflowId=...)` setzt das Workflow-Objekt jetzt in
|
||||
`self.services.workflow` UND in `_context.workflow` aller Services
|
||||
(`chat`, `ai`, `extraction`, `sharepoint`, `clickup`, `utils`, `billing`,
|
||||
`generation`). Spiegel von `workflowManager._propagateWorkflowToContext`.
|
||||
Ohne diesen Schritt ist `chatService._workflow` `None` und der Bridge-Helper
|
||||
greift nie.
|
||||
|
||||
## Aufrufstellen
|
||||
|
||||
| Tool | Datei | Aufruf |
|
||||
|------|-------|--------|
|
||||
| `downloadFromDataSource` | `_dataSourceTools.py` | nach `saveUploadedFile`, `label=datasource:<service>` |
|
||||
| `writeFile mode=create` | `_workspaceTools.py` | nach `saveUploadedFile`, `label=writeFile:<name>` |
|
||||
| `renderDocument` | `_mediaTools.py` | im Multi-Doc-Loop pro generiertem File, `label=renderDocument:<name>` |
|
||||
| `generateImage` | `_mediaTools.py` | im Multi-Image-Loop pro PNG, `label=generateImage:<name>` |
|
||||
| `createChart` | `_mediaTools.py` | nach Chart-PNG-Save, `label=createChart:<name>` |
|
||||
|
||||
Alle anderen file-konsumierenden Tools (`readFile`, `replaceInFile`,
|
||||
`searchInFileContent`, `listFiles`, `getFileInfo`, ...) brauchen keinen
|
||||
Bridge-Aufruf -- sie veraendern nur Inhalt oder lesen, ohne neuen Workflow-State zu erzeugen.
|
||||
|
||||
## Vergleich mit anderen Bridges
|
||||
|
||||
| Pfad | Helper | Wo |
|
||||
|------|--------|-----|
|
||||
| Workflow-Action mit `ActionResult.documents` | `persistTaskResult` (intern) | `workflowProcessor.py:608-707` |
|
||||
| `methodTrustee.extractFromFiles` (FileItem-IDs aus Sub-Trustee) | inline Pattern | `methodTrustee/actions/extractFromFiles.py:507-545` |
|
||||
| Agent-Tool-Output (NEU) | `_attachFileAsChatDocument` | `coreTools/_helpers.py` |
|
||||
|
||||
Alle drei produzieren am Ende dieselbe Struktur:
|
||||
`ChatMessage` -> `ChatDocument` -> `fileId`. Der Agent-Pfad war bisher
|
||||
die einzige Luecke.
|
||||
|
||||
## Tolerante `documentList`-Parsing-Schicht
|
||||
|
||||
Komplementaer dazu: `coerceDocumentReferenceList` in
|
||||
`gateway/modules/datamodels/datamodelDocref.py` parst das, was der LLM
|
||||
am anderen Ende reinschickt -- typischerweise
|
||||
|
||||
* `["docItem:<id>", "docItem:<id2>"]` (kanonisch),
|
||||
* `[{"id": "<id>", "name": "..."}]` (LLM-Listen-Style),
|
||||
* `{"documents": [{"id": "<id>"}, ...]}` (LLM-Dict-Wrapper),
|
||||
* `"docItem:<id>"` (Single-String).
|
||||
|
||||
Alle vier Shapes werden in `DocumentReferenceList` mit
|
||||
`DocumentItemReference(documentId=<id>)` umgewandelt; daraus baut
|
||||
`getChatDocumentsFromDocumentList` die `docItem:<id>`-Suchschluessel
|
||||
und matched gegen `workflow.messages[*].documents[*].id` -- also exakt
|
||||
gegen die `ChatDocument.id`s, die der Bridge-Helper persistiert.
|
||||
|
||||
Die Kette ist damit:
|
||||
|
||||
```
|
||||
agent tool
|
||||
-> _attachFileAsChatDocument (writes ChatDocument.id = X)
|
||||
-> _formatToolFileResult (returns "docItem:X" to LLM)
|
||||
-> LLM puts "docItem:X" in documentList (or any tolerant variant)
|
||||
-> coerceDocumentReferenceList (normalises to docItem:X)
|
||||
-> getChatDocumentsFromDocumentList (matches workflow.messages[*].documents[*].id == X)
|
||||
-> ai_summarizeDocument receives ContentParts
|
||||
```
|
||||
|
||||
## Failure Modes
|
||||
|
||||
| Symptom | Ursache | Wo schauen |
|
||||
|---------|---------|------------|
|
||||
| `getChatDocumentsFromDocumentList: No workflow available` | `chatService._context.workflow` ist `None` | `mainServiceAgent.runAgent` -- propagiert es das Workflow? |
|
||||
| "Building structure prompt with 0 valid ContentParts" trotz erfolgreichem Download | Bridge nicht aufgerufen oder Workflow None | Pruefen ob `_attachFileAsChatDocument` `None` returnt |
|
||||
| `Invalid documentList type: <class 'dict'>` | Tolerant-Coercer wurde umgangen | Action benutzt nicht `coerceDocumentReferenceList` |
|
||||
| `'dict' object has no attribute 'fileName'` aus `listFiles` | `getAllFiles` returnt dicts, Code greift mit `.attribute` | `mainServiceChat.listFiles` muss `.get(...)` benutzen |
|
||||
| `File with ID ... not found` direkt nach `Duplicate detected for user ...` | "Ghost duplicate" -- `checkForDuplicateFile` returned ein RBAC-unsichtbares File. Wurzel: `interfaceDbComponent` ohne `featureInstanceId` initialisiert, Duplicate-Suche faellt auf mandate-Scope zurueck. | `interfaceDbManagement.checkForDuplicateFile` macht jetzt einen `getFile`-Cross-Check; wenn `getFile None`, gilt als kein Duplicate -> Caller erstellt frische per-Scope-Kopie. Fix in 2026-04-29. |
|
||||
|
||||
## Ghost-Duplicate-Fix (2026-04-29)
|
||||
|
||||
Symptom des Tages: `downloadFromDataSource` returnte zuverlaessig
|
||||
`File with ID 21b0b995-... not found`, **direkt nach** einer
|
||||
`Duplicate detected for user ...` INFO-Zeile aus `saveUploadedFile`.
|
||||
|
||||
Mechanik:
|
||||
|
||||
* `interfaceDbComponent` wird ueber `serviceHub` ohne `featureInstanceId`
|
||||
konstruiert -- nur `mandateId` wird durchgereicht.
|
||||
* `checkForDuplicateFile` baute bisher den `recordFilter` mit
|
||||
`if self.featureInstanceId: ... elif self.mandateId: ...` und benutzte
|
||||
`db.getRecordset` (kein RBAC-Filter). Foglich matchte er ein File aus
|
||||
einer **anderen** featureInstance, solange Hash + Name + sysCreatedBy
|
||||
+ Mandate uebereinstimmten.
|
||||
* `getFile` benutzt aber `getRecordsetWithRBAC` und filtert das
|
||||
fremd-scope File raus -> `None`.
|
||||
* Caller (`saveUploadedFile.updateFile(...)`) crashed mit
|
||||
`File with ID ... not found`.
|
||||
|
||||
Sauberer Fix in `checkForDuplicateFile`: nach dem Recordset-Treffer
|
||||
zwingend ein `self.getFile(fileId)`-Cross-Check. Wenn RBAC blockiert,
|
||||
returnen wir `None` -> der Caller erstellt eine frische per-Scope-Kopie.
|
||||
Damit entfaellt das Geister-Duplicate komplett, ohne Workaround in
|
||||
`updateFile` und ohne `interfaceDbComponent` umzuverdrahten. Folge:
|
||||
identische Files koennen pro Mandate **mehrfach** existieren (eines
|
||||
pro featureInstance) -- das ist gewollt, weil sie pro Scope eigene
|
||||
Folder-/Tag-/Neutralize-Metadaten brauchen.
|
||||
|
||||
## Auch wichtig
|
||||
|
||||
* Der RBAC-Check in `interfaceDbChat.createMessage`
|
||||
(`checkRbacPermission(ChatWorkflow, "update", workflowId)`) gilt
|
||||
weiter -- ohne Update-Permission auf den Workflow kein ChatDocument-Bind.
|
||||
Gewollt.
|
||||
* `chatService.storeMessageWithDocuments` synchronisiert auch das
|
||||
in-Memory-Workflow-Objekt (haengt die neue ChatMessage an
|
||||
`workflow.messages` an), so dass der gleiche Run sofort darauf
|
||||
zugreifen kann.
|
||||
* Das `documentsLabel` (`label`-Argument im Helper) wird vom
|
||||
alternativen `docList:<label>`-Resolver benutzt, der mehrere Files
|
||||
ueber ein gemeinsames Label sammelt -- nicht primaerer Pfad, aber
|
||||
vorhanden.
|
||||
|
|
@ -14,6 +14,18 @@ Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler.
|
|||
|
||||
## 2026-04-29
|
||||
|
||||
- 2026-04-29 | fix | gateway | **`checkForDuplicateFile` macht jetzt einen RBAC-Cross-Check und liefert keine "Geister-Duplicate" mehr aus.** Ursache des `File with ID ... not found`-Fehlers in `downloadFromDataSource` direkt nach `Duplicate detected for user ...`: `interfaceDbComponent` wird ueber `serviceHub` ohne `featureInstanceId` initialisiert, `checkForDuplicateFile` faellt deshalb auf den `mandateId`-Filter zurueck und benutzte `db.getRecordset` (kein RBAC) -- damit konnte er ein File aus einer FREMDEN featureInstance returnen. Der nachfolgende `updateFile`-Aufruf prallte dann am RBAC-gefilterten `getFile` ab und crashte den ganzen Tool-Call. Sauberer Fix an der Wurzel: nach dem Recordset-Treffer zwingend `self.getFile(fileId)` als RBAC-Cross-Check; wenn `None`, gilt als kein Duplicate fuer den aktuellen Scope und der Caller (`saveUploadedFile`/`createFile`) erstellt eine frische per-Scope-Kopie. Damit entfaellt jeglicher Workaround in `updateFile` (die in der Vorgaenger-Session gebaute Try/Except-Schleife wurde gestern bereits ausgebaut, weil sie in einer Sackgasse stand). Folge: identische Files koennen pro Mandate mehrfach existieren -- eines pro featureInstance -- was gewollt ist, da sie pro Scope eigene Folder-/Tag-/Neutralize-Metadaten brauchen. Symptom-Doku in `wiki/b-reference/gateway/agent-file-bridge.md` (Section "Ghost-Duplicate-Fix"). (c-work: Begleitfix zum Agent-File-Bridge-Build vom selben Tag)
|
||||
|
||||
- 2026-04-29 | fix | gateway | **Agent-Tools binden jetzt jede produzierte Datei als ChatDocument an den aktiven Workflow -- damit funktioniert der `documentList`-Resolver fuer Agent-Outputs.** Bisher erzeugten `downloadFromDataSource`, `writeFile mode=create`, `renderDocument`, `generateImage` und `createChart` zwar eine `FileItem` (via `saveUploadedFile`/`saveGeneratedFile`), liessen den Workflow-Document-Graph aber unberuehrt -- statt eines `ChatDocument` floss nur ein `sideEvent fileCreated` an die UI. Folge: `getChatDocumentsFromDocumentList` (und damit `ai_summarizeDocument` / `ai_process` / `context_extractContent` / `context_neutralizeData`) konnte die FileItem-IDs nicht aufloesen, weil der Resolver ausschliesslich `workflow.messages[*].documents[*].id` matcht. Symptom war "Building structure prompt with 0 valid ContentParts" und entsprechend leere Summaries direkt nach einem Agent-Download. Loesung: zentraler Helper `_attachFileAsChatDocument(services, fileItem, label, userMessage)` in `coreTools/_helpers.py`, der intern eine ChatMessage (Rolle `assistant`, Status `step`, generierter `documentsLabel`) inklusive einem ChatDocument (mit `roundNumber`/`taskNumber`/`actionNumber` aus dem aktiven Workflow) via `chatService.storeMessageWithDocuments` persistiert -- exakt das Pattern, das `workflowProcessor.persistTaskResult` und `methodTrustee.extractFromFiles` schon laenger benutzen. Helper wird jetzt aus allen fuenf File-erzeugenden Agent-Tools aufgerufen; der ToolResult-Text traegt einheitlich beide IDs (`documentList ref: docItem:<chatDocId>` fuer AI-Tools, `file id: <fileId>` fuer `readFile`/Embeds). Tool-Descriptions + `conversationManager.buildSystemPrompt` ergaenzt um die explizite Anweisung "use docItem:<id> in documentList, NOT the file id, NOT a `{"documents":[...]}` wrapper". Zusatzlich: `mainServiceAgent.runAgent` propagiert das Workflow-Object jetzt in alle Service-Contexts (`chat._context.workflow` etc.) -- bisher tat das nur `workflowManager._propagateWorkflowToContext`, weshalb der Agent ueber die Workspace-Route ohne aktiven Workflow-Context lief und `chatService._workflow` `None` war. Folge: der Helper konnte ohne diesen Propagations-Fix nie greifen. Die in der vorhergehenden Session eingebaute RBAC-tolerante `updateFile`-Try/Except-Schleife in `_downloadFromDataSource` ist jetzt obsolet und wieder ausgebaut -- mit korrektem ChatDocument-Bind kommt der `featureInstanceId` ueber den Workflow-Pfad und die Duplicate-File-RBAC-Probleme verschwinden an der Wurzel. Pattern-Doc neu unter `wiki/b-reference/gateway/agent-file-bridge.md`. (c-work: kein dedizierter Eintrag, kritischer Fix on top der documentList-Coercer-Aenderung)
|
||||
|
||||
- 2026-04-29 | fix | gateway | **`ai_process` / `context_extractContent` / `context_neutralizeData` akzeptieren jetzt LLM-typische Dict-Wrapper-Formate fuer `documentList`.** Der Agent serialisiert `documentList` regelmaessig als `{"documents":[{"id":"<uuid>","name":"<file>"}]}` (Folge der `type=DocumentList`-Schema-Definition, das LLMs als generic Object lesen). Bisher fielen alle drei Actions in den `else: Invalid documentList type: <class 'dict'>`-Branch und liefen mit leerer Document-Liste weiter -- der Folder-Summarize-Workflow produzierte dann eine Zusammenfassung "ueber 0 Dokumente". Loesung: zentraler Helper `coerceDocumentReferenceList(value)` in `datamodelDocref.py` der ALLE praktisch vorkommenden Shapes parst -- `None`, `str`, `DocumentReferenceList`, `list[str]`, `list[dict mit id/documentId/label]`, `dict mit "documents"/"references"/"items"/"files"-Wrapper`, und `dict mit id/documentId` (single item). Alle vier Aufrufstellen (process.py + extractContent.py + neutralizeData.py x2) auf den Helper umgezogen; die Inline-`ActionDocument`-Sonderbehandlung in `process.py` bleibt davor. Helper wirft NIE -- gibt im worst case eine leere Liste mit WARN-Log zurueck, der Caller entscheidet ob das fatal ist. (c-work: kein dedizierter Eintrag, Begleitfix zum Infomaniak-Test)
|
||||
|
||||
- 2026-04-29 | fix | gateway | **`listFiles`-Tool: dict-vs-attribute-Mismatch in `mainServiceChat.listFiles` behoben.** `interfaceDbComponent.getAllFiles()` returnt seit dem Pydantic-Refactor `List[dict]` (jeder Eintrag ist ein `FileItem.model_dump()` mit Label-Spalten), aber `mainServiceChat.listFiles()` griff weiter mit `fileItem.fileName` / `fileItem.id` zu (Pydantic-Style) -- daher `'dict' object has no attribute 'fileName'` und `'dict' object has no attribute 'id'` aus dem `listFiles`-Agent-Tool. Komplette Methode auf `.get(...)` umgestellt; Type-Annotation in `getAllFiles` (`Union[List[FileItem], PaginatedResult]`) ist immer noch irrefuehrend -- das ist ein orthogonaler Tech-Debt-Punkt, kein Blocker. (c-work: kein dedizierter Eintrag)
|
||||
|
||||
- 2026-04-29 | fix | gateway | **kDrive: Single-File-DataSources funktionieren jetzt sauber (Browse + Download).** Wenn der User eine einzelne Datei als DataSource anhaengt (z.B. `path=/2980592/12`, wo `12` die kDrive-File-ID einer `.html`-Datei ist), brachten browseDataSource und downloadFromDataSource bisher Folgefehler: (1) Browse rief blind `/2/drive/{driveId}/files/{fileId}/files` auf -> kDrive antwortet `400 destination_not_a_directory` -> Adapter loggte das als Empty-Folder -> Agent dachte "leerer Ordner" und konstruierte einen Pseudo-Pfad `/2980592/12/platform-overview.html`. (2) `download(/2980592/12/platform-overview.html)` nahm dann `segments[-1] = "platform-overview.html"` als File-ID -> `/2/drive/2980592/files/platform-overview.html` -> `404 method_not_found`. Saubere Loesung: (a) Neuer Helper `_lastNumericSegment(segments)` zieht aus einem Pfad immer die letzte rein-numerische ID -- kDrive-IDs sind grundsaetzlich Integer; haengt der Agent einen Filename dran, wird dieser ignoriert. (b) `KdriveAdapter.browse` holt vor dem Children-Listing die Item-Metadata (`/files/{id}`); bei `type=file` wird ein Single-Entry mit Name/Size/MimeType/Modified zurueckgegeben statt children aufzurufen. (c) `KdriveAdapter.download` benutzt denselben Helper + denselben Metadata-Fetch (jetzt extrahiert in `_fetchItemMeta`). Folge: Single-File-Quellen sind jetzt browse-/downloadbar; Folder-Quellen verhalten sich unveraendert. (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
|
||||
|
||||
- 2026-04-29 | fix | gateway | **downloadFromDataSource: Metadata-Updates auf Duplicate-Files werfen den Tool-Call nicht mehr aus dem Tritt.** `saveUploadedFile` returnt bei Hash+Name-Match das EXISTIERENDE FileItem -- das kann aber in einem anderen `featureInstanceId`/Folder-Scope leben als die aktuelle Anfrage. Die nachfolgenden `updateFile`-Aufrufe (`featureInstanceId`/`neutralize`/`folderId`) prallten dann am RBAC-gefilterten `getFile` ab und warfen `File with ID ... not found` -- obwohl der Download korrekt war (File ist in der DB, addressierbar via `fileItem.id`). Fix: die drei Metadata-Updates laufen jetzt in einer Schleife mit individuellem Try/Except und WARN-Log; der Tool-Call meldet weiter Success mit der File-ID. (c-work: kein dedizierter Eintrag, Folge des kDrive-Fixes oben)
|
||||
|
||||
- 2026-04-29 | fix | gateway | **Agent-Tools: `kdriveFolder`/`calendarFolder`/`contactFolder` werden jetzt korrekt auf die Connector-Services `kdrive`/`calendar`/`contact` gemappt.** Der `SourcesTab.tsx` schreibt diese FE-seitigen Source-Type-Literals beim Anhaengen einer Datenquelle ans `DataSource`-Record; `_dataSourceTools._resolveDataSource` (und parallel `routeFeatureWorkspace._SOURCE_TYPE_TO_SERVICE`) hatten aber nur Eintraege fuer SharePoint/OneDrive/Drive/Outlook/Gmail/FTP/ClickUp -- daher kamen `Service 'kdriveFolder' not available. Options: ['kdrive', 'calendar', 'contact']` aus `browseDataSource`/`searchDataSource`/`downloadFromDataSource` sobald der Agent eine Infomaniak/Calendar/Contact-Quelle anfasste. Beide Maps um die drei Eintraege erweitert; `DataSource.sourceType`-Field-Description ergaenzt; Inline-Kommentar im `_dataSourceTools.py` warnt vor Drift gegenueber FE und `_SERVICE_MAP`. Bestehende `DataSource`-Records funktionieren ohne Migration (das `sourceType`-Literal bleibt gleich). (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
|
||||
|
||||
- 2026-04-29 | fix | gateway | **kDrive-Download folgt jetzt dem 302-Redirect.** Der Endpoint `/2/drive/{driveId}/files/{fileId}/download` antwortet bandwidth-bedingt mit `302 Found -> presigned CDN URL` (Standard fuer File-Bytes); unser `_infomaniakDownload`-Helper hatte aber `allow_redirects=False` (kopiert vom `_infomaniakGet`-Helper, wo das wichtig ist um nicht versehentlich auf OAuth-Login-Pages zu landen). Folge: jeder kDrive-Download lieferte `None` -> `Tool result: downloadFromDataSource FAILED -> Download returned empty` im Agent-Log. Fix: nur fuer Downloads `allow_redirects=True`, mit Doc-String der erklaert warum (gleiches Pattern bei Calendar/Contacts-Export-Endpoints, gleicher Host = Authorization-Header bleibt erhalten). Listing/Metadata-Calls bleiben weiter `allow_redirects=False`, damit nicht-PAT-faehige Routes als `302` sichtbar bleiben statt eine HTML-Login-Page zu liefern. (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
|
||||
|
|
|
|||
Loading…
Reference in a new issue