147 lines
4.9 KiB
Python
147 lines
4.9 KiB
Python
"""TUI runner functions and initialization."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
from logging import Logger
|
|
from logging.handlers import QueueHandler, RotatingFileHandler
|
|
from pathlib import Path
|
|
from queue import Queue
|
|
from typing import NamedTuple
|
|
|
|
from ....config import configure_prefect, get_settings
|
|
from ....core.models import StorageBackend
|
|
from .storage_manager import StorageManager
|
|
|
|
|
|
class _TuiLoggingContext(NamedTuple):
|
|
"""Container describing configured logging outputs for the TUI."""
|
|
|
|
queue: Queue[logging.LogRecord]
|
|
formatter: logging.Formatter
|
|
log_file: Path | None
|
|
|
|
|
|
_logging_context: _TuiLoggingContext | None = None
|
|
|
|
|
|
def _configure_tui_logging(*, log_level: str) -> _TuiLoggingContext:
|
|
"""Configure logging so that messages do not break the TUI output."""
|
|
|
|
global _logging_context
|
|
if _logging_context is not None:
|
|
return _logging_context
|
|
|
|
resolved_level = getattr(logging, log_level.upper(), logging.INFO)
|
|
log_queue: Queue[logging.LogRecord] = Queue()
|
|
formatter = logging.Formatter(
|
|
fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
)
|
|
|
|
root_logger = logging.getLogger()
|
|
root_logger.setLevel(resolved_level)
|
|
|
|
# Remove existing stream handlers to prevent console flicker inside the TUI
|
|
for handler in list(root_logger.handlers):
|
|
root_logger.removeHandler(handler)
|
|
|
|
queue_handler = QueueHandler(log_queue)
|
|
queue_handler.setLevel(resolved_level)
|
|
root_logger.addHandler(queue_handler)
|
|
|
|
log_file: Path | None = None
|
|
try:
|
|
log_dir = Path.cwd() / "logs"
|
|
log_dir.mkdir(parents=True, exist_ok=True)
|
|
log_file = log_dir / "tui.log"
|
|
file_handler = RotatingFileHandler(
|
|
log_file,
|
|
maxBytes=2_000_000,
|
|
backupCount=5,
|
|
encoding="utf-8",
|
|
)
|
|
file_handler.setLevel(resolved_level)
|
|
file_handler.setFormatter(formatter)
|
|
root_logger.addHandler(file_handler)
|
|
except OSError as exc: # pragma: no cover - filesystem specific
|
|
fallback = logging.getLogger(__name__)
|
|
fallback.warning("Failed to configure file logging for TUI: %s", exc)
|
|
|
|
_logging_context = _TuiLoggingContext(log_queue, formatter, log_file)
|
|
return _logging_context
|
|
|
|
|
|
LOGGER: Logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def run_textual_tui() -> None:
|
|
"""Run the enhanced modern TUI with better error handling and initialization."""
|
|
settings = get_settings()
|
|
configure_prefect(settings)
|
|
|
|
logging_context = _configure_tui_logging(log_level=settings.log_level)
|
|
|
|
LOGGER.info("Initializing collection management TUI")
|
|
LOGGER.info("Scanning available storage backends")
|
|
|
|
# Initialize storage manager
|
|
storage_manager = StorageManager(settings)
|
|
backend_status = await storage_manager.initialize_all_backends()
|
|
|
|
# Report initialization results
|
|
for backend, success in backend_status.items():
|
|
if success:
|
|
LOGGER.info("%s connected successfully", backend.value)
|
|
else:
|
|
LOGGER.warning("%s connection failed", backend.value)
|
|
|
|
available_backends = storage_manager.get_available_backends()
|
|
if not available_backends:
|
|
LOGGER.error("Could not connect to any storage backend")
|
|
LOGGER.info("Please check your configuration and try again")
|
|
LOGGER.info("Supported backends: Weaviate, OpenWebUI, R2R")
|
|
return
|
|
|
|
LOGGER.info(
|
|
"Launching TUI with %d backend(s): %s",
|
|
len(available_backends),
|
|
", ".join(backend.value for backend in available_backends),
|
|
)
|
|
|
|
# Get individual storage instances for backward compatibility
|
|
from ....storage.openwebui import OpenWebUIStorage
|
|
from ....storage.weaviate import WeaviateStorage
|
|
|
|
weaviate_backend = storage_manager.get_backend(StorageBackend.WEAVIATE)
|
|
openwebui_backend = storage_manager.get_backend(StorageBackend.OPEN_WEBUI)
|
|
r2r_backend = storage_manager.get_backend(StorageBackend.R2R)
|
|
|
|
# Type-safe casting to specific storage types
|
|
weaviate = weaviate_backend if isinstance(weaviate_backend, WeaviateStorage) else None
|
|
openwebui = openwebui_backend if isinstance(openwebui_backend, OpenWebUIStorage) else None
|
|
|
|
# Import here to avoid circular import
|
|
from ..app import CollectionManagementApp
|
|
app = CollectionManagementApp(
|
|
storage_manager,
|
|
weaviate,
|
|
openwebui,
|
|
r2r_backend,
|
|
log_queue=logging_context.queue,
|
|
log_formatter=logging_context.formatter,
|
|
log_file=logging_context.log_file,
|
|
)
|
|
try:
|
|
await app.run_async()
|
|
finally:
|
|
LOGGER.info("Shutting down storage connections")
|
|
await storage_manager.close_all()
|
|
LOGGER.info("All storage connections closed gracefully")
|
|
|
|
|
|
def dashboard() -> None:
|
|
"""Launch the modern collection dashboard."""
|
|
asyncio.run(run_textual_tui())
|