feat(xai-service): implement xAI Files & Collections service for document synchronization
This commit is contained in:
@@ -13,6 +13,8 @@ from typing import Dict, Any
|
||||
from motia import FlowContext
|
||||
from services.espocrm import EspoCRMAPI
|
||||
from services.document_sync_utils import DocumentSync
|
||||
from services.xai_service import XAIService
|
||||
import hashlib
|
||||
import json
|
||||
import redis
|
||||
import os
|
||||
@@ -65,10 +67,7 @@ async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]):
|
||||
# APIs initialisieren
|
||||
espocrm = EspoCRMAPI()
|
||||
sync_utils = DocumentSync(espocrm, redis_client, ctx)
|
||||
|
||||
# TODO: xAI Service wird in nächstem Schritt hinzugefügt
|
||||
# from services.xai_service import XAIService
|
||||
# xai_service = XAIService(ctx)
|
||||
xai_service = XAIService(ctx)
|
||||
|
||||
try:
|
||||
# 1. ACQUIRE LOCK (verhindert parallele Syncs)
|
||||
@@ -98,10 +97,10 @@ async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]):
|
||||
# 3. BESTIMME SYNC-AKTION BASIEREND AUF ACTION
|
||||
|
||||
if action == 'delete':
|
||||
await handle_delete(entity_id, document, sync_utils, ctx, entity_type)
|
||||
await handle_delete(entity_id, document, sync_utils, xai_service, ctx, entity_type)
|
||||
|
||||
elif action in ['create', 'update']:
|
||||
await handle_create_or_update(entity_id, document, sync_utils, ctx, entity_type)
|
||||
await handle_create_or_update(entity_id, document, sync_utils, xai_service, ctx, entity_type)
|
||||
|
||||
else:
|
||||
ctx.logger.warn(f"⚠️ Unbekannte Action: {action}")
|
||||
@@ -138,7 +137,7 @@ async def handler(event_data: Dict[str, Any], ctx: FlowContext[Any]):
|
||||
ctx.logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
async def handle_create_or_update(entity_id: str, document: Dict[str, Any], sync_utils: DocumentSync, ctx: FlowContext[Any], entity_type: str = 'CDokumente'):
|
||||
async def handle_create_or_update(entity_id: str, document: Dict[str, Any], sync_utils: DocumentSync, xai_service: XAIService, ctx: FlowContext[Any], entity_type: str = 'CDokumente'):
|
||||
"""
|
||||
Behandelt Create/Update von Documents
|
||||
|
||||
@@ -257,62 +256,59 @@ async def handle_create_or_update(entity_id: str, document: Dict[str, Any], sync
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# xAI SYNC DURCHFÜHREN
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
|
||||
ctx.logger.info("")
|
||||
ctx.logger.info("=" * 80)
|
||||
ctx.logger.info("🤖 xAI SYNC STARTEN")
|
||||
ctx.logger.info("=" * 80)
|
||||
|
||||
# TODO: Implementierung mit xai_service.py
|
||||
ctx.logger.warn("⚠️ xAI Service noch nicht implementiert!")
|
||||
ctx.logger.info("")
|
||||
ctx.logger.info("TODO: Folgende Schritte werden implementiert:")
|
||||
ctx.logger.info("1. 📥 Download File von EspoCRM")
|
||||
ctx.logger.info("2. 📤 Upload zu xAI (falls noch kein xaiFileId)")
|
||||
ctx.logger.info("3. 📚 Add zu Collections")
|
||||
ctx.logger.info("4. 💾 Update EspoCRM: xaiFileId + xaiCollections")
|
||||
ctx.logger.info("")
|
||||
|
||||
# PLACEHOLDER Implementation:
|
||||
#
|
||||
# # 1. Download File von EspoCRM
|
||||
# download_info = await sync_utils.get_document_download_info(entity_id)
|
||||
# if not download_info:
|
||||
# raise Exception("Konnte Download-Info nicht ermitteln")
|
||||
#
|
||||
# # 2. Upload zu xAI (falls noch nicht vorhanden)
|
||||
# xai_file_id = document.get('xaiFileId')
|
||||
# if not xai_file_id:
|
||||
# file_content = await download_file_from_espocrm(download_info)
|
||||
# xai_file_id = await xai_service.upload_file(file_content, download_info['filename'])
|
||||
#
|
||||
# # 3. Add zu Collections
|
||||
# for collection_id in collection_ids:
|
||||
# await xai_service.add_file_to_collection(collection_id, xai_file_id)
|
||||
#
|
||||
# # 4. Update EspoCRM
|
||||
# await sync_utils.release_sync_lock(
|
||||
# entity_id,
|
||||
# success=True,
|
||||
# extra_fields={
|
||||
# 'xaiFileId': xai_file_id,
|
||||
# 'xaiCollections': collection_ids
|
||||
# }
|
||||
# )
|
||||
|
||||
# Für jetzt: Success ohne Sync
|
||||
|
||||
# 1. Hole Download-Informationen (falls nicht schon aus Preview-Schritt vorhanden)
|
||||
download_info = await sync_utils.get_document_download_info(entity_id, entity_type)
|
||||
if not download_info:
|
||||
raise Exception("Konnte Download-Info nicht ermitteln – Datei fehlt?")
|
||||
|
||||
ctx.logger.info(f"📥 Datei: {download_info['filename']} ({download_info['size']} bytes, {download_info['mime_type']})")
|
||||
|
||||
# 2. Download Datei von EspoCRM
|
||||
espocrm = sync_utils.espocrm
|
||||
file_content = await espocrm.download_attachment(download_info['attachment_id'])
|
||||
ctx.logger.info(f"✅ Downloaded {len(file_content)} bytes")
|
||||
|
||||
# 3. MD5-Hash berechnen für Change-Detection
|
||||
file_hash = hashlib.md5(file_content).hexdigest()
|
||||
ctx.logger.info(f"🔑 MD5: {file_hash}")
|
||||
|
||||
# 4. Upload zu xAI
|
||||
# Immer neu hochladen wenn needs_sync=True (neues File oder Hash geändert)
|
||||
ctx.logger.info("📤 Uploading to xAI...")
|
||||
xai_file_id = await xai_service.upload_file(
|
||||
file_content,
|
||||
download_info['filename'],
|
||||
download_info['mime_type']
|
||||
)
|
||||
ctx.logger.info(f"✅ xAI file_id: {xai_file_id}")
|
||||
|
||||
# 5. Zu allen Ziel-Collections hinzufügen
|
||||
ctx.logger.info(f"📚 Füge zu {len(collection_ids)} Collection(s) hinzu...")
|
||||
added_collections = await xai_service.add_to_collections(collection_ids, xai_file_id)
|
||||
ctx.logger.info(f"✅ In {len(added_collections)}/{len(collection_ids)} Collections eingetragen")
|
||||
|
||||
# 6. EspoCRM Metadaten aktualisieren und Lock freigeben
|
||||
await sync_utils.update_sync_metadata(
|
||||
entity_id,
|
||||
xai_file_id=xai_file_id,
|
||||
collection_ids=added_collections,
|
||||
file_hash=file_hash,
|
||||
entity_type=entity_type
|
||||
)
|
||||
await sync_utils.release_sync_lock(
|
||||
entity_id,
|
||||
success=True,
|
||||
extra_fields={
|
||||
# TODO: Echte xAI-Daten hier einsetzen
|
||||
# 'xaiFileId': xai_file_id,
|
||||
# 'xaiCollections': collection_ids
|
||||
}
|
||||
entity_type=entity_type
|
||||
)
|
||||
|
||||
|
||||
ctx.logger.info("=" * 80)
|
||||
ctx.logger.info("✅ DOCUMENT SYNC ABGESCHLOSSEN (PLACEHOLDER)")
|
||||
ctx.logger.info("✅ DOCUMENT SYNC ABGESCHLOSSEN")
|
||||
ctx.logger.info("=" * 80)
|
||||
|
||||
except Exception as e:
|
||||
@@ -322,7 +318,7 @@ async def handle_create_or_update(entity_id: str, document: Dict[str, Any], sync
|
||||
await sync_utils.release_sync_lock(entity_id, success=False, error_message=str(e))
|
||||
|
||||
|
||||
async def handle_delete(entity_id: str, document: Dict[str, Any], sync_utils: DocumentSync, ctx: FlowContext[Any], entity_type: str = 'CDokumente'):
|
||||
async def handle_delete(entity_id: str, document: Dict[str, Any], sync_utils: DocumentSync, xai_service: XAIService, ctx: FlowContext[Any], entity_type: str = 'CDokumente'):
|
||||
"""
|
||||
Behandelt Delete von Documents
|
||||
|
||||
@@ -346,25 +342,15 @@ async def handle_delete(entity_id: str, document: Dict[str, Any], sync_utils: Do
|
||||
ctx.logger.info(f" xaiFileId: {xai_file_id}")
|
||||
ctx.logger.info(f" Collections: {xai_collections}")
|
||||
|
||||
# TODO: Implementierung mit xai_service
|
||||
ctx.logger.warn("⚠️ xAI Delete-Operation noch nicht implementiert!")
|
||||
ctx.logger.info("")
|
||||
ctx.logger.info("TODO: Folgende Schritte werden implementiert:")
|
||||
ctx.logger.info("1. 🗑️ Remove File aus allen Collections")
|
||||
ctx.logger.info("2. ⚠️ File NICHT von xAI löschen (kann in anderen Collections sein)")
|
||||
ctx.logger.info("")
|
||||
|
||||
# PLACEHOLDER Implementation:
|
||||
#
|
||||
# for collection_id in xai_collections:
|
||||
# await xai_service.remove_file_from_collection(collection_id, xai_file_id)
|
||||
#
|
||||
# ctx.logger.info(f"✅ File aus {len(xai_collections)} Collection(s) entfernt")
|
||||
|
||||
ctx.logger.info(f"🗑️ Entferne aus {len(xai_collections)} Collection(s)...")
|
||||
await xai_service.remove_from_collections(xai_collections, xai_file_id)
|
||||
ctx.logger.info(f"✅ File aus {len(xai_collections)} Collection(s) entfernt")
|
||||
ctx.logger.info(" (File selbst bleibt in xAI – kann in anderen Collections sein)")
|
||||
|
||||
await sync_utils.release_sync_lock(entity_id, success=True, entity_type=entity_type)
|
||||
|
||||
|
||||
ctx.logger.info("=" * 80)
|
||||
ctx.logger.info("✅ DELETE ABGESCHLOSSEN (PLACEHOLDER)")
|
||||
ctx.logger.info("✅ DELETE ABGESCHLOSSEN")
|
||||
ctx.logger.info("=" * 80)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user