mindnet/app/routers/ingest.py

125 lines
4.3 KiB
Python
Raw Normal View History

2025-12-10 22:05:35 +01:00
"""
2025-12-15 15:40:39 +01:00
FILE: app/routers/ingest.py
2025-12-16 14:11:17 +01:00
DESCRIPTION: Endpunkte für WP-11. Nimmt Markdown entgegen.
2025-12-23 14:33:51 +01:00
Refactored für WP-14: Nutzt BackgroundTasks für non-blocking Save.
Update WP-20: Unterstützung für Hybrid-Cloud-Analyse Feedback.
VERSION: 0.8.0 (WP-20 Hybrid Ready)
2025-12-15 15:40:39 +01:00
STATUS: Active
DEPENDENCIES: app.core.ingestion, app.services.discovery, fastapi, pydantic
2025-12-10 22:05:35 +01:00
"""
2025-12-15 15:40:39 +01:00
2025-12-10 22:05:35 +01:00
import os
import time
2025-12-11 08:18:41 +01:00
import logging
2025-12-16 14:11:17 +01:00
import asyncio
from fastapi import APIRouter, HTTPException, BackgroundTasks
2025-12-10 22:05:35 +01:00
from pydantic import BaseModel
2025-12-11 12:04:08 +01:00
from typing import Optional, Dict, Any
2025-12-10 22:05:35 +01:00
from app.core.ingestion import IngestionService
2025-12-11 13:50:40 +01:00
from app.services.discovery import DiscoveryService
2025-12-10 22:05:35 +01:00
2025-12-11 10:46:37 +01:00
logger = logging.getLogger(__name__)
2025-12-10 22:05:35 +01:00
router = APIRouter()
2025-12-16 14:11:17 +01:00
# Services Init
2025-12-11 13:50:40 +01:00
discovery_service = DiscoveryService()
2025-12-11 12:04:08 +01:00
2025-12-10 22:05:35 +01:00
class AnalyzeRequest(BaseModel):
text: str
type: str = "concept"
class SaveRequest(BaseModel):
markdown_content: str
2025-12-11 08:18:41 +01:00
filename: Optional[str] = None
2025-12-11 10:46:37 +01:00
folder: str = "00_Inbox"
2025-12-10 22:05:35 +01:00
class SaveResponse(BaseModel):
status: str
file_path: str
note_id: str
2025-12-16 14:11:17 +01:00
message: str # Neu für UX Feedback
stats: Dict[str, Any] # Kann leer sein bei async processing
# --- Background Task Wrapper ---
async def run_ingestion_task(markdown_content: str, filename: str, vault_root: str, folder: str):
"""
Führt die Ingestion im Hintergrund aus, damit der Request nicht blockiert.
2025-12-23 14:33:51 +01:00
Integrierter WP-20 Hybrid-Modus über den IngestionService.
2025-12-16 14:11:17 +01:00
"""
logger.info(f"🔄 Background Task started: Ingesting {filename}...")
try:
ingest_service = IngestionService()
result = await ingest_service.create_from_text(
markdown_content=markdown_content,
filename=filename,
vault_root=vault_root,
folder=folder
)
# Hier könnte man später Notification-Services (Websockets) triggern
if result.get("status") == "error":
logger.error(f"❌ Background Ingestion Error for {filename}: {result.get('error')}")
else:
logger.info(f"✅ Background Task finished: {filename} ({result.get('chunks_count')} Chunks)")
except Exception as e:
logger.error(f"❌ Critical Background Task Failure: {e}", exc_info=True)
2025-12-10 22:05:35 +01:00
@router.post("/analyze")
async def analyze_draft(req: AnalyzeRequest):
"""
2025-12-11 13:50:40 +01:00
WP-11 Intelligence: Liefert Link-Vorschläge via DiscoveryService.
2025-12-10 22:05:35 +01:00
"""
try:
2025-12-11 13:50:40 +01:00
result = await discovery_service.analyze_draft(req.text, req.type)
return result
2025-12-10 22:05:35 +01:00
except Exception as e:
2025-12-11 08:18:41 +01:00
logger.error(f"Analyze failed: {e}", exc_info=True)
2025-12-11 12:04:08 +01:00
return {"suggestions": [], "error": str(e)}
2025-12-10 22:05:35 +01:00
@router.post("/save", response_model=SaveResponse)
2025-12-16 14:11:17 +01:00
async def save_note(req: SaveRequest, background_tasks: BackgroundTasks):
2025-12-11 12:04:08 +01:00
"""
2025-12-16 14:11:17 +01:00
WP-14 Fix: Startet Ingestion im Hintergrund (Fire & Forget).
2025-12-23 14:33:51 +01:00
Verhindert Timeouts bei aktiver Smart-Edge-Allocation (WP-15) und Cloud-Hybrid-Modus (WP-20).
2025-12-11 12:04:08 +01:00
"""
2025-12-11 08:18:41 +01:00
try:
vault_root = os.getenv("MINDNET_VAULT_ROOT", "./vault")
abs_vault_root = os.path.abspath(vault_root)
if not os.path.exists(abs_vault_root):
2025-12-23 14:33:51 +01:00
try:
os.makedirs(abs_vault_root, exist_ok=True)
except Exception as e:
logger.warning(f"Could not create vault root: {e}")
2025-12-10 22:05:35 +01:00
2025-12-11 13:50:40 +01:00
final_filename = req.filename or f"draft_{int(time.time())}.md"
2025-12-11 12:04:08 +01:00
2025-12-16 14:11:17 +01:00
# Wir geben sofort eine ID zurück (optimistisch),
# auch wenn die echte ID erst nach dem Parsing feststeht.
# Für UI-Feedback nutzen wir den Filename.
# Task in die Queue schieben
background_tasks.add_task(
run_ingestion_task,
2025-12-11 13:50:40 +01:00
markdown_content=req.markdown_content,
filename=final_filename,
vault_root=abs_vault_root,
folder=req.folder
)
2025-12-11 08:18:41 +01:00
return SaveResponse(
2025-12-16 14:11:17 +01:00
status="queued",
file_path=os.path.join(req.folder, final_filename),
note_id="pending",
2025-12-23 14:33:51 +01:00
message="Speicherung & Hybrid-KI-Analyse (WP-20) im Hintergrund gestartet.",
2025-12-11 13:50:40 +01:00
stats={
2025-12-16 14:11:17 +01:00
"chunks": -1, # Indikator für Async
"edges": -1
2025-12-11 13:50:40 +01:00
}
2025-12-11 08:18:41 +01:00
)
2025-12-16 14:11:17 +01:00
2025-12-11 08:18:41 +01:00
except Exception as e:
2025-12-16 14:11:17 +01:00
logger.error(f"Save dispatch failed: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Save dispatch failed: {str(e)}")