WP11 #8
|
|
@ -1,25 +1,21 @@
|
|||
"""
|
||||
app/routers/ingest.py
|
||||
API-Endpunkte für WP-11 (Discovery & Persistence).
|
||||
Fixed Async/Await Integration with Discovery Service.
|
||||
app/routers/ingest.py - DEBUG VERSION
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, Dict, Any
|
||||
from typing import Optional, List, Dict, Any
|
||||
|
||||
from app.core.ingestion import IngestionService
|
||||
# WICHTIG: Wir nutzen wieder den spezialisierten DiscoveryService
|
||||
from app.services.discovery import DiscoveryService
|
||||
from app.core.retriever import Retriever
|
||||
from app.models.dto import QueryRequest
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# --- DTOs ---
|
||||
|
||||
class AnalyzeRequest(BaseModel):
|
||||
text: str
|
||||
type: str = "concept"
|
||||
|
|
@ -35,20 +31,61 @@ class SaveResponse(BaseModel):
|
|||
note_id: str
|
||||
stats: Dict[str, Any]
|
||||
|
||||
# --- Services ---
|
||||
discovery_service = DiscoveryService()
|
||||
|
||||
# --- Endpoints ---
|
||||
|
||||
@router.post("/analyze")
|
||||
async def analyze_draft(req: AnalyzeRequest):
|
||||
"""
|
||||
WP-11 Intelligence: Liefert Link-Vorschläge (Exact + Semantic).
|
||||
WP-11 Intelligence: Liefert Link-Vorschläge.
|
||||
DEBUG MODE: Threshold gesenkt, Logging erhöht.
|
||||
"""
|
||||
try:
|
||||
# Wir delegieren an den Service, der Exact Matching, Config und Semantik beherrscht
|
||||
result = await discovery_service.analyze_draft(req.text, req.type)
|
||||
return result
|
||||
retriever = Retriever()
|
||||
suggestions = []
|
||||
|
||||
query_text = req.text[:400]
|
||||
logger.info(f"ANALYZING TEXT: '{query_text}' (Type: {req.type})")
|
||||
|
||||
if not query_text.strip():
|
||||
return {"suggestions": []}
|
||||
|
||||
# Wir suchen
|
||||
hits_result = await retriever.search(QueryRequest(query=query_text, top_k=5, mode="hybrid"))
|
||||
|
||||
logger.info(f"RETRIEVER FOUND: {len(hits_result.results)} raw hits")
|
||||
|
||||
seen_titles = set()
|
||||
for hit in hits_result.results:
|
||||
# Titel holen
|
||||
title = hit.payload.get("title") or hit.payload.get("note_id") or hit.node_id
|
||||
|
||||
# Logging für jeden Treffer
|
||||
logger.info(f" -> CHECK HIT: {title} | Score: {hit.total_score:.4f}")
|
||||
|
||||
if not title or title in seen_titles:
|
||||
continue
|
||||
seen_titles.add(title)
|
||||
|
||||
# Edge Logic
|
||||
edge_kind = "related_to"
|
||||
if req.type == "project": edge_kind = "depends_on"
|
||||
if req.type == "decision": edge_kind = "references"
|
||||
|
||||
# --- ÄNDERUNG: THRESHOLD GESENKT ---
|
||||
# War vorher 0.65. Jetzt 0.3 für Tests.
|
||||
if hit.total_score > 0.3:
|
||||
suggestions.append({
|
||||
"target_title": title,
|
||||
"target_id": hit.node_id,
|
||||
"suggested_markdown": f"[[rel:{edge_kind} {title}]]",
|
||||
"reason": f"Semantisch ähnlich ({hit.total_score:.2f})",
|
||||
"type": "semantic"
|
||||
})
|
||||
else:
|
||||
logger.info(f" -> SKIPPED (Score too low)")
|
||||
|
||||
logger.info(f"RETURNING {len(suggestions)} SUGGESTIONS")
|
||||
return {"suggestions": suggestions}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Analyze failed: {e}", exc_info=True)
|
||||
|
|
@ -56,36 +93,24 @@ async def analyze_draft(req: AnalyzeRequest):
|
|||
|
||||
@router.post("/save", response_model=SaveResponse)
|
||||
async def save_note(req: SaveRequest):
|
||||
"""
|
||||
WP-11 Persistence: Speichert Markdown physisch und indiziert es sofort.
|
||||
"""
|
||||
"""WP-11 Persistence"""
|
||||
try:
|
||||
# 1. Pfad-Setup
|
||||
vault_root = os.getenv("MINDNET_VAULT_ROOT", "./vault")
|
||||
abs_vault_root = os.path.abspath(vault_root)
|
||||
|
||||
if not os.path.exists(abs_vault_root):
|
||||
try:
|
||||
os.makedirs(abs_vault_root, exist_ok=True)
|
||||
except Exception:
|
||||
raise HTTPException(status_code=500, detail=f"Vault root missing and cannot create: {abs_vault_root}")
|
||||
|
||||
# 2. Filename
|
||||
final_filename = req.filename
|
||||
if not final_filename:
|
||||
final_filename = f"draft_{int(time.time())}.md"
|
||||
|
||||
# 3. Ingestion Service (Async)
|
||||
ingest_service = IngestionService()
|
||||
logger.info(f"Saving {final_filename}")
|
||||
|
||||
logger.info(f"Saving {final_filename} to {req.folder}")
|
||||
|
||||
# Async Call zum Ingestion Service
|
||||
result = await ingest_service.create_from_text(
|
||||
result = await ingest_service.save_and_index(
|
||||
markdown_content=req.markdown_content,
|
||||
filename=final_filename,
|
||||
vault_root=abs_vault_root,
|
||||
folder=req.folder
|
||||
filename=final_filename
|
||||
)
|
||||
|
||||
if result.get("status") == "error":
|
||||
|
|
@ -93,12 +118,9 @@ async def save_note(req: SaveRequest):
|
|||
|
||||
return SaveResponse(
|
||||
status="success",
|
||||
file_path=result.get("path", "unknown"),
|
||||
file_path=result.get("file_path", "unknown"),
|
||||
note_id=result.get("note_id", "unknown"),
|
||||
stats={
|
||||
"chunks": result.get("chunks_count", 0),
|
||||
"edges": result.get("edges_count", 0)
|
||||
}
|
||||
stats=result.get("stats", {})
|
||||
)
|
||||
|
||||
except HTTPException as he:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user