Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions backend/rag_solution/data_ingestion/docling_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""

# Standard library imports
import asyncio
import logging
import os
import uuid
Expand Down Expand Up @@ -119,8 +120,10 @@ async def process(self, file_path: str, document_id: str) -> AsyncIterator[Docum
if self.converter is None:
raise ImportError("Docling DocumentConverter not available")

# Convert document using Docling
result = self.converter.convert(file_path)
# Convert document using Docling (run in thread pool to avoid blocking event loop)
# Docling's AI models are CPU-intensive and can block the async event loop
logger.debug("Running Docling conversion in thread pool for: %s", file_path)
result = await asyncio.to_thread(self.converter.convert, file_path)

# Extract metadata
metadata = self._extract_docling_metadata(result.document, file_path)
Expand Down Expand Up @@ -393,10 +396,16 @@ def _get_page_number(self, item: Any) -> int | None:
# Try new API first (page_no), fallback to old API (page)
page_no = getattr(item.prov[0], "page_no", None)
if page_no is not None:
return int(page_no)
try:
return int(page_no)
except (ValueError, TypeError):
logger.warning("Invalid page_no value: %s", page_no)
page = getattr(item.prov[0], "page", None)
if page is not None:
return int(page)
try:
return int(page)
except (ValueError, TypeError):
logger.warning("Invalid page value: %s", page)
return None

def _table_to_text(self, table_data: dict) -> str:
Expand Down
42 changes: 42 additions & 0 deletions backend/rag_solution/router/health_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,45 @@ def health_check(
raise HTTPException(status_code=503, detail=f"System unhealthy. Components: {', '.join(unhealthy_components)}")

return {"status": "healthy", "components": components}


@router.get(
"/health/ready",
summary="Readiness probe",
description="Lightweight readiness check for Kubernetes readiness probe",
response_model=dict,
responses={
200: {"description": "Application is ready to serve traffic"},
503: {"description": "Application is not ready"},
},
)
def readiness_check(db: Annotated[Session, Depends(get_db)]) -> dict[str, Any]:
"""
Perform a lightweight readiness check for Kubernetes readiness probe.

This endpoint is optimized for fast response times and checks only
critical dependencies required to serve traffic (database connection).
Unlike /health, it doesn't check external services like vector DB or LLM providers.

Args:
db: The database session.

Returns:
dict: Readiness status

Raises:
HTTPException: If the application is not ready to serve traffic
"""
# Check only critical database connection
datastore_status = check_datastore(db)

if datastore_status["status"] == "unhealthy":
raise HTTPException(
status_code=503,
detail=f"Application not ready: {datastore_status['message']}"
)

return {
"status": "ready",
"message": "Application is ready to serve traffic"
}
Loading
Loading