Files
siemens_ragas/webmain.py

138 lines
4.7 KiB
Python
Raw Permalink Normal View History

"""CLI entry point that launches the evaluation console web server.
Run alongside the existing main.py CLI; both share the same rag_eval library
and the same runs/ artifacts. Example:
python webmain.py
python webmain.py --host 0.0.0.0 --port 8800
python webmain.py --host 0.0.0.0 --port 8800 --log-level debug
"""
from __future__ import annotations
import argparse
import logging
import logging.config
from datetime import datetime
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent
def _build_log_config(log_file: Path, level: str) -> dict:
"""Build a uvicorn-compatible logging config dict.
Writes to both stderr (console) and a rotating daily log file.
All webapp.* and rag_eval.* loggers inherit from root so every
logger.info() call in the API routes is captured.
"""
level_upper = level.upper()
return {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"detailed": {
"format": "%(asctime)s %(levelname)-8s %(name)s %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
"console": {
"format": "%(asctime)s %(levelname)-8s %(name)-30s %(message)s",
"datefmt": "%H:%M:%S",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
"formatter": "console",
"level": level_upper,
},
"file": {
"class": "logging.handlers.RotatingFileHandler",
"filename": str(log_file),
"maxBytes": 50 * 1024 * 1024, # 50 MB per file
"backupCount": 7, # keep 7 rotated files
"encoding": "utf-8",
"formatter": "detailed",
"level": "DEBUG", # file always captures everything
},
},
"loggers": {
# Our application loggers — detailed level
"webapp": {"handlers": ["console", "file"], "level": level_upper, "propagate": False},
"rag_eval": {"handlers": ["console", "file"], "level": level_upper, "propagate": False},
# uvicorn access log — captured to file, shown on console
"uvicorn.access": {"handlers": ["console", "file"], "level": "INFO", "propagate": False},
"uvicorn.error": {"handlers": ["console", "file"], "level": "INFO", "propagate": False},
"uvicorn": {"handlers": ["console", "file"], "level": "INFO", "propagate": False},
},
"root": {
"handlers": ["console", "file"],
"level": "WARNING", # suppress noisy third-party libs at WARNING
},
}
def parse_args() -> argparse.Namespace:
"""Parse host/port/reload options for the console server."""
parser = argparse.ArgumentParser(description="Launch the RAGAS evaluation console.")
parser.add_argument("--host", default="127.0.0.1", help="Bind host (default 127.0.0.1).")
parser.add_argument("--port", type=int, default=8800, help="Bind port (default 8800).")
parser.add_argument(
"--reload",
action="store_true",
help="Enable auto-reload for local development.",
)
parser.add_argument(
"--log-level",
default="info",
choices=["debug", "info", "warning", "error"],
help="Console log level (default: info). File always captures DEBUG.",
)
parser.add_argument(
"--log-file",
default=None,
help="Log file path (default: logs/server_YYYY-MM-DD.log).",
)
return parser.parse_args()
def main() -> None:
"""Start uvicorn with the configured application and logging."""
import uvicorn
args = parse_args()
# Resolve log file path
logs_dir = REPO_ROOT / "logs"
logs_dir.mkdir(parents=True, exist_ok=True)
if args.log_file:
log_file = Path(args.log_file)
else:
date_str = datetime.now().strftime("%Y-%m-%d")
log_file = logs_dir / f"server_{date_str}.log"
log_config = _build_log_config(log_file, args.log_level)
# Apply config before uvicorn starts so our loggers are ready immediately
logging.config.dictConfig(log_config)
logger = logging.getLogger("webapp.server")
logger.info(
"Starting RAGAS Console host=%s port=%d log_level=%s log_file=%s",
args.host, args.port, args.log_level, log_file,
)
uvicorn.run(
"webapp.server:app",
host=args.host,
port=args.port,
reload=args.reload,
log_config=log_config, # hand our config to uvicorn so it uses same handlers
)
if __name__ == "__main__":
main()