feat(library-desk): integrate entity linking into main app

Main App:
- Mount /static directory for serving Wiki.js integration scripts
- Register entity_linking router
- Refactor API key verification to dependencies module

Dependencies:
- Add service factory functions for all services
- Add get_wiki_service() for wiki operations
- Add get_graph_service() for entity operations
- Add get_ingestion_service() for auto entity linking
- Improve health check for SearXNG
This commit is contained in:
2025-12-10 01:18:40 +01:00
parent d80f063186
commit 7354a66a3d
2 changed files with 124 additions and 41 deletions
+103 -5
View File
@@ -73,12 +73,13 @@ def get_wikijs_client() -> WikiJSClient:
Get Wiki.js client singleton.
Returns:
Initialized Wiki.js GraphQL client
Initialized Wiki.js GraphQL client with username/password auth
"""
settings = get_settings()
client = WikiJSClient(
base_url=settings.wikijs_url,
api_key=settings.wikijs_api_key
username=settings.wikijs_username,
password=settings.wikijs_password
)
logger.debug("Created Wiki.js client instance")
return client
@@ -255,9 +256,8 @@ async def check_service_health() -> dict:
# SearXNG
try:
searxng = get_searxng_client()
# Try a simple search
await searxng.search_general("test", limit=1)
health["searxng"] = True
# Just check if service is up (no actual search)
health["searxng"] = await searxng.health_check()
except Exception as e:
logger.error(f"SearXNG health check failed: {e}")
health["searxng"] = False
@@ -274,6 +274,72 @@ async def check_service_health() -> dict:
return health
# Service factory functions
@lru_cache
def get_vector_service() -> "VectorService":
"""Get VectorService singleton."""
from src.services.vector_service import VectorService
return VectorService(
qdrant_client=get_qdrant_client(),
wikijs_client=get_wikijs_client(),
ollama_client=get_ollama_client()
)
@lru_cache
def get_graph_service() -> "GraphService":
"""Get GraphService singleton."""
from src.services.graph_service import GraphService
return GraphService(
neo4j_client=get_neo4j_client(),
wikijs_client=get_wikijs_client()
)
@lru_cache
def get_wiki_service() -> "WikiService":
"""Get WikiService singleton."""
from src.services.wiki_service import WikiService
return WikiService(wiki_client=get_wikijs_client())
@lru_cache
def get_consolidation_service() -> "ConsolidationService":
"""Get ConsolidationService singleton."""
from src.services.consolidation_service import ConsolidationService
return ConsolidationService(
neo4j=get_neo4j_client(),
ollama=get_ollama_client(),
wiki=get_wikijs_client(),
settings=get_settings(),
ingestion_service=get_ingestion_service()
)
@lru_cache
def get_ingestion_service() -> "IngestionService":
"""Get IngestionService singleton."""
from src.services.ingestion_service import IngestionService
return IngestionService(
vector_service=get_vector_service(),
graph_service=get_graph_service(),
wiki_client=get_wikijs_client()
)
@lru_cache
def get_hybrid_rag_service() -> "HybridRAGService":
"""Get HybridRAGService singleton."""
from src.services.hybrid_rag_service import HybridRAGService
return HybridRAGService(
vector_service=get_vector_service(),
graph_service=get_graph_service(),
searxng_client=get_searxng_client(),
ollama_client=get_ollama_client(),
settings=get_settings()
)
# Utility: Get default user from settings or multi_tenancy
def get_default_user() -> str:
"""
@@ -284,3 +350,35 @@ def get_default_user() -> str:
"""
from src.core.multi_tenancy import DEFAULT_USER
return DEFAULT_USER
# Authentication
from fastapi import Security, HTTPException
from fastapi.security import HTTPBearer
security = HTTPBearer()
async def verify_api_key(
credentials: Annotated[HTTPBearer, Security(security)],
settings: SettingsDep
) -> str:
"""
Verify API key from Bearer token.
Args:
credentials: HTTP Bearer credentials
settings: Application settings
Returns:
API key if valid
Raises:
HTTPException: If API key is invalid
"""
if credentials.credentials != settings.library_api_key:
raise HTTPException(
status_code=403,
detail="Invalid API key"
)
return credentials.credentials
+21 -36
View File
@@ -8,14 +8,16 @@ Following best practices:
- OpenAPI documentation
"""
from fastapi import FastAPI, HTTPException, Depends, Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi import FastAPI, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from typing import Dict, Any
import logging
from pathlib import Path
from src.config import Settings, get_settings
from src.core.dependencies import verify_api_key
# Configure logging
logging.basicConfig(
@@ -24,9 +26,6 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
# Security
security = HTTPBearer()
# Initialize FastAPI app
app = FastAPI(
title="Library Desk API",
@@ -45,22 +44,23 @@ app.add_middleware(
allow_headers=["*"],
)
# Register routers
from src.routers import wiki, tools, graph, vector, hybrid_rag, consolidation, ingestion, entity_linking
# Dependencies
async def verify_api_key(
credentials: HTTPAuthorizationCredentials = Security(security),
settings: Settings = Depends(get_settings)
) -> str:
"""
Verify API key from Bearer token.
Following best practice: use dependencies for validation.
"""
if credentials.credentials != settings.library_api_key:
raise HTTPException(
status_code=403,
detail="Invalid API key"
)
return credentials.credentials
app.include_router(wiki.router)
app.include_router(tools.router)
app.include_router(graph.router)
app.include_router(vector.router)
app.include_router(hybrid_rag.router)
app.include_router(consolidation.router)
app.include_router(ingestion.router)
app.include_router(entity_linking.router)
# Mount static files directory for Wiki.js integration scripts
static_dir = Path(__file__).parent.parent / "static"
if static_dir.exists():
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
logger.info(f"Mounted static files from {static_dir}")
# Response Models
@@ -264,22 +264,7 @@ async def get_repo_status(
# Query endpoints (stubs for future implementation)
@app.post("/query/hybrid", tags=["Query"])
async def hybrid_query(
query: Dict[str, Any],
api_key: str = Depends(verify_api_key)
) -> Dict[str, Any]:
"""
HybridRAG query endpoint.
Combines Neo4j (structure) + Qdrant (semantics) + SearXNG (web).
TODO: Implement HybridRAG logic
"""
return {
"message": "HybridRAG not yet implemented",
"query": query
}
# NOTE: /query/hybrid is now implemented in routers/hybrid_rag.py
@app.post("/query/semantic", tags=["Query"])
async def semantic_query(