diff --git a/frontend/src/app/api/queries/useGetNudgesQuery.ts b/frontend/src/app/api/queries/useGetNudgesQuery.ts index 2e313e0c..cf6450db 100644 --- a/frontend/src/app/api/queries/useGetNudgesQuery.ts +++ b/frontend/src/app/api/queries/useGetNudgesQuery.ts @@ -6,8 +6,7 @@ import { type Nudge = string; -const DEFAULT_NUDGES = [ -]; +const DEFAULT_NUDGES: Nudge[] = []; export const useGetNudgesQuery = ( chatId?: string | null, diff --git a/frontend/src/app/connectors/page.tsx b/frontend/src/app/connectors/page.tsx index ad70ec90..06aa0265 100644 --- a/frontend/src/app/connectors/page.tsx +++ b/frontend/src/app/connectors/page.tsx @@ -92,6 +92,7 @@ export default function ConnectorsPage() { selectedFiles={selectedFiles} isAuthenticated={false} // This would come from auth context in real usage accessToken={undefined} // This would come from connected account + isIngesting={isSyncing} /> diff --git a/frontend/src/app/knowledge/chunks/page.tsx b/frontend/src/app/knowledge/chunks/page.tsx index 080120cc..6da3dc5e 100644 --- a/frontend/src/app/knowledge/chunks/page.tsx +++ b/frontend/src/app/knowledge/chunks/page.tsx @@ -83,13 +83,13 @@ function ChunksPageContent() { }, [data, filename]); // Set selected state for all checkboxes when selectAll changes - useEffect(() => { - if (selectAll) { - setSelectedChunks(new Set(chunks.map((_, index) => index))); - } else { - setSelectedChunks(new Set()); - } - }, [selectAll, setSelectedChunks, chunks]); + // useEffect(() => { + // if (selectAll) { + // setSelectedChunks(new Set(chunks.map((_, index) => index))); + // } else { + // setSelectedChunks(new Set()); + // } + // }, [selectAll, setSelectedChunks, chunks]); const handleBack = useCallback(() => { router.push("/knowledge"); diff --git a/frontend/src/app/settings/page.tsx b/frontend/src/app/settings/page.tsx index 6fe74c4c..148da3bd 100644 --- a/frontend/src/app/settings/page.tsx +++ b/frontend/src/app/settings/page.tsx @@ -85,6 +85,7 @@ interface Connector { connectionId?: string; access_token?: string; selectedFiles?: GoogleDriveFile[] | OneDriveFile[]; + available?: boolean; } interface SyncResult { diff --git a/frontend/src/components/cloud-connectors-dialog.tsx b/frontend/src/components/cloud-connectors-dialog.tsx index d38cf44f..077582bf 100644 --- a/frontend/src/components/cloud-connectors-dialog.tsx +++ b/frontend/src/components/cloud-connectors-dialog.tsx @@ -283,6 +283,7 @@ export function CloudConnectorsDialog({ accessToken={connectorAccessTokens[connector.type]} onPickerStateChange={() => {}} clientId={connector.clientId} + isIngesting={false} /> ); diff --git a/frontend/src/contexts/task-context.tsx b/frontend/src/contexts/task-context.tsx index 12ad3c24..9b3d9908 100644 --- a/frontend/src/contexts/task-context.tsx +++ b/frontend/src/contexts/task-context.tsx @@ -19,6 +19,7 @@ import { import { useAuth } from "@/contexts/auth-context"; // Task interface is now imported from useGetTasksQuery +export type { Task }; export interface TaskFile { filename: string; diff --git a/src/api/docling.py b/src/api/docling.py new file mode 100644 index 00000000..66b7777e --- /dev/null +++ b/src/api/docling.py @@ -0,0 +1,104 @@ +"""Docling service proxy endpoints.""" + +import socket +import struct +from pathlib import Path + +import httpx +from starlette.requests import Request +from starlette.responses import JSONResponse + +from utils.container_utils import ( + detect_container_environment, + get_container_host, + guess_host_ip_for_containers, +) +from utils.logging_config import get_logger + +logger = get_logger(__name__) + + +def _get_gateway_ip_from_route() -> str | None: + """Return the default gateway IP visible from the current network namespace.""" + try: + with Path("/proc/net/route").open() as route_table: + next(route_table) # Skip header + for line in route_table: + fields = line.strip().split() + min_fields = 3 # interface, destination, gateway + if len(fields) >= min_fields and fields[1] == "00000000": + gateway_hex = fields[2] + gw_int = int(gateway_hex, 16) + gateway_ip = socket.inet_ntoa(struct.pack(" str: + """Determine the host address used for docling health checks.""" + container_type = detect_container_environment() + if container_type: + container_host = get_container_host() + if container_host: + logger.info("Using container-aware host '%s'", container_host) + return container_host + + gateway_ip = _get_gateway_ip_from_route() + if gateway_ip: + logger.info("Detected host gateway IP: %s", gateway_ip) + return gateway_ip + + # Either we're not inside a container or gateway detection failed. + fallback_ip = guess_host_ip_for_containers(logger=logger) + if container_type: + logger.info("Falling back to container bridge host %s", fallback_ip) + else: + logger.info("Running outside a container; using host %s", fallback_ip) + return fallback_ip + + +# Detect the host IP once at startup +HOST_IP = determine_docling_host() +DOCLING_SERVICE_URL = f"http://{HOST_IP}:5001" + + +async def health(request: Request) -> JSONResponse: + """ + Proxy health check to docling-serve. + This allows the frontend to check docling status via same-origin request. + """ + try: + async with httpx.AsyncClient() as client: + response = await client.get( + f"{DOCLING_SERVICE_URL}/health", + timeout=2.0 + ) + + if response.status_code == 200: + return JSONResponse({ + "status": "healthy", + "host": HOST_IP + }) + else: + return JSONResponse({ + "status": "unhealthy", + "message": f"Health check failed with status: {response.status_code}", + "host": HOST_IP + }, status_code=503) + + except httpx.TimeoutException: + return JSONResponse({ + "status": "unhealthy", + "message": "Connection timeout", + "host": HOST_IP + }, status_code=503) + except Exception as e: + logger.error("Docling health check failed", error=str(e)) + return JSONResponse({ + "status": "unhealthy", + "message": str(e), + "host": HOST_IP + }, status_code=503)