6.9 KiB
6.9 KiB
Document Sync mit xAI Collections - Implementierungs-Status
✅ Implementiert
1. Webhook Endpunkte
- POST
/vmh/webhook/document/create - POST
/vmh/webhook/document/update - POST
/vmh/webhook/document/delete
2. Event Handler (document_sync_event_step.py)
- Queue Topics:
vmh.document.{create|update|delete} - Redis Distributed Locking
- Vollständiges Document Loading von EspoCRM
3. Sync Utilities (document_sync_utils.py)
- ✅ Datei-Status Prüfung: "Neu", "Geändert" → xAI-Sync erforderlich
- ✅ Hash-basierte Change Detection: MD5/SHA Vergleich für Updates
- ✅ Related Entities Discovery: Many-to-Many Attachments durchsuchen
- ✅ Collection Requirements: Automatische Ermittlung welche Collections nötig sind
⏳ In Arbeit
4. Thumbnail-Generierung (generate_thumbnail())
Anforderungen:
- Erste Seite eines PDFs als Vorschaubild
- DOCX/DOC → PDF → Image Konvertierung
- Bild-Dateien: Resize auf Thumbnail-Größe
- Fallback: Generic File-Icons basierend auf MIME-Type
Benötigte Dependencies:
# Python Packages
pip install pdf2image python-docx Pillow docx2pdf
# System Dependencies (Ubuntu/Debian)
apt-get install poppler-utils libreoffice
Implementierungs-Schritte:
- PDF Handling (Priorität 1):
from pdf2image import convert_from_path
from PIL import Image
import io
def generate_pdf_thumbnail(pdf_path: str) -> bytes:
# Konvertiere erste Seite zu Image
images = convert_from_path(pdf_path, first_page=1, last_page=1, dpi=150)
thumbnail = images[0]
# Resize auf Thumbnail-Größe (z.B. 200x280)
thumbnail.thumbnail((200, 280), Image.Resampling.LANCZOS)
# Convert zu bytes
buffer = io.BytesIO()
thumbnail.save(buffer, format='PNG')
return buffer.getvalue()
- DOCX Handling (Priorität 2):
from docx2pdf import convert
import tempfile
import os
def generate_docx_thumbnail(docx_path: str) -> bytes:
# Temporäres PDF erstellen
with tempfile.NamedTemporaryFile(suffix='.pdf', delete=False) as tmp:
pdf_path = tmp.name
# DOCX → PDF Konvertierung (benötigt LibreOffice)
convert(docx_path, pdf_path)
# PDF-Thumbnail generieren
thumbnail = generate_pdf_thumbnail(pdf_path)
# Cleanup
os.remove(pdf_path)
return thumbnail
- Image Handling (Priorität 3):
from PIL import Image
import io
def generate_image_thumbnail(image_path: str) -> bytes:
img = Image.open(image_path)
img.thumbnail((200, 280), Image.Resampling.LANCZOS)
buffer = io.BytesIO()
img.save(buffer, format='PNG')
return buffer.getvalue()
- Thumbnail Upload zu EspoCRM:
# EspoCRM unterstützt Preview-Images via Attachment API
async def upload_thumbnail_to_espocrm(
document_id: str,
thumbnail_bytes: bytes,
espocrm_api
):
# Create Attachment
attachment_data = {
'name': 'preview.png',
'type': 'image/png',
'role': 'Inline Attachment',
'parentType': 'Document',
'parentId': document_id,
'field': 'previewImage' # Custom field?
}
# Upload via EspoCRM Attachment API
# POST /api/v1/Attachment mit multipart/form-data
# TODO: espocrm.py muss upload_attachment() Methode bekommen
Offene Fragen:
- Welches Feld in EspoCRM Document für Preview?
previewImage?thumbnail? - Größe des Thumbnails? (empfohlen: 200x280 oder 300x400)
- Format: PNG oder JPEG?
❌ Noch nicht implementiert
5. xAI Service (xai_service.py)
Anforderungen:
- File Upload zu xAI (basierend auf
test_xai_collections_api.py) - Add File zu Collections
- Remove File von Collections
- File Download von EspoCRM
Referenz-Code vorhanden:
/opt/motia-iii/bitbylaw/test_xai_collections_api.py(630 Zeilen, alle xAI Operations getestet)
Implementierungs-Plan:
class XAIService:
def __init__(self, context=None):
self.management_key = os.getenv('XAI_MANAGEMENT_KEY')
self.api_key = os.getenv('XAI_API_KEY')
self.context = context
async def upload_file(self, file_content: bytes, filename: str) -> str:
"""Upload File zu xAI → returns file_id"""
# Multipart/form-data upload
# POST https://api.x.ai/v1/files
pass
async def add_to_collection(self, collection_id: str, file_id: str):
"""Add File zu Collection"""
# POST https://management-api.x.ai/v1/collections/{collection_id}/documents/{file_id}
pass
async def remove_from_collection(self, collection_id: str, file_id: str):
"""Remove File von Collection"""
# DELETE https://management-api.x.ai/v1/collections/{collection_id}/documents/{file_id}
pass
async def download_from_espocrm(self, attachment_id: str) -> bytes:
"""Download File von EspoCRM Attachment"""
# GET https://crm.bitbylaw.com/api/v1/Attachment/file/{attachment_id}
pass
📋 Integration Checklist
Vollständiger Upload-Flow:
- ✅ Webhook empfangen → Event emittieren
- ✅ Event Handler: Lock acquire
- ✅ Document laden von EspoCRM
- ✅ Entscheidung: Sync nötig? (Datei-Status, Hash-Check, Collections)
- ⏳ Download File von EspoCRM
- ⏳ Hash berechnen (MD5/SHA)
- ⏳ Thumbnail generieren
- ❌ Upload zu xAI (falls neu oder Hash changed)
- ❌ Add zu Collections
- ⏳ Update EspoCRM Metadaten (xaiFileId, xaiCollections, xaiSyncedHash, thumbnail)
- ✅ Lock release
Datei-Stati in EspoCRM:
- "Neu": Komplett neue Datei → xAI Upload + Collection Add
- "Geändert": File-Inhalt geändert → xAI Re-Upload + Collection Update
- "Gesynct": Erfolgreich gesynct, keine Änderungen
- "Fehler": Sync fehlgeschlagen (mit Error-Message)
EspoCRM Custom Fields:
Erforderlich für Document Entity:
dateiStatus(Enum): "Neu", "Geändert", "Gesynct", "Fehler"md5(String): MD5 Hash des Filessha(String): SHA Hash des FilesxaiFileId(String): xAI File IDxaiCollections(Array): JSON Array von Collection IDsxaiSyncedHash(String): Hash beim letzten erfolgreichen SyncxaiSyncStatus(Enum): "syncing", "synced", "failed"xaiSyncError(Text): Fehlermeldung bei Sync-FehlerpreviewImage(Attachment?): Vorschaubild
🚀 Nächste Schritte
Priorität 1: xAI Service
- Code aus
test_xai_collections_api.pyextrahieren - In
services/xai_service.pyübertragen - EspoCRM Download-Funktion implementieren
Priorität 2: Thumbnail-Generator
- Dependencies installieren
- PDF-Thumbnail implementieren
- EspoCRM Upload-Methode erweitern
Priorität 3: Integration testen
- Document in EspoCRM anlegen
- Datei-Status auf "Neu" setzen
- Webhook triggern
- Logs analysieren
📚 Referenzen
- xAI API Tests:
/opt/motia-iii/bitbylaw/test_xai_collections_api.py - EspoCRM API:
services/espocrm.py - Beteiligte Sync (Referenz-Implementierung):
steps/vmh/beteiligte_sync_event_step.py