"""Minimal FastAPI app for Harbor RL environment. This is a lean entry point that only includes task evaluation endpoints — no database, auth, CSRF, or background workers. Run with: uvicorn lexenvs.harbor_app:app --host 0.0.0.0 --port 8000 """ from __future__ import annotations import logging import os from collections.abc import AsyncGenerator import svcs from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from lexenvs.config.settings import get_settings from lexenvs.routers import tasks from lexenvs.services.claim_extractor import ClaimExtractor from lexenvs.services.objective_evaluator_service import ObjectiveEvaluatorService from lexenvs.services.rubric_evaluator_service import ( RubricEvaluatorService, create_rubric_evaluator_service, ) from lexenvs.services.task_loader_service import ( TaskLoaderService, create_task_loader_service, ) from lexenvs.utils import load_card_database logger = logging.getLogger(__name__) def create_harbor_app() -> FastAPI: """Factory to create the Harbor FastAPI application.""" @svcs.fastapi.lifespan async def lifespan(app: FastAPI, registry: svcs.Registry) -> AsyncGenerator[None, None]: """Register task-related services.""" registry.register_factory(TaskLoaderService, create_task_loader_service) registry.register_factory(RubricEvaluatorService, create_rubric_evaluator_service) # Register objective evaluator if card database exists card_db = load_card_database() if card_db is not None: settings = get_settings() claim_extractor = None if settings.openrouter_api_key: claim_extractor = ClaimExtractor( api_key=settings.openrouter_api_key, model=settings.claim_extractor_model, base_url=settings.claim_extractor_base_url, ) logger.info( "Factual fidelity: LLM claim extraction enabled via %s", settings.claim_extractor_base_url, ) else: logger.info("Factual fidelity: no OPENROUTER_API_KEY, LLM extraction disabled") registry.register_value( ObjectiveEvaluatorService, ObjectiveEvaluatorService(card_db, claim_extractor=claim_extractor), ) yield harbor_app = FastAPI( title="LexEnvs Harbor Environment", description="Harbor-compatible RL environment for credit card optimization tasks", version="1.0.0", docs_url="/docs", lifespan=lifespan, ) allowed_origins = os.environ.get("LEX_ENVS_CORS_ORIGINS", "*").split(",") harbor_app.add_middleware( CORSMiddleware, allow_origins=allowed_origins, allow_methods=["*"], allow_headers=["*"], ) harbor_app.include_router(tasks.router, prefix="/api") @harbor_app.get("/") async def root() -> dict[str, str]: """Root endpoint — confirms the server is running.""" return { "service": "LexEnvs Harbor Environment", "docs": "/docs", "health": "/health/live", "tasks": "/api/tasks", } @harbor_app.get("/health/live") async def liveness() -> dict[str, str]: """Lightweight liveness probe.""" return {"status": "ok"} return harbor_app app = create_harbor_app()