Spaces:
Sleeping
Sleeping
| from __future__ import annotations | |
| from pathlib import Path | |
| from typing import Any, Dict, Literal | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.responses import FileResponse, JSONResponse | |
| from fastapi.staticfiles import StaticFiles | |
| from pydantic import BaseModel, Field | |
| from services.config import settings | |
| from services.dataset import SubstitutionDatabase | |
| from services.recipe_service import RecipeAdapterService | |
| from services.semantic import WordVectorFallback | |
| BASE_DIR = Path(__file__).resolve().parent | |
| ROOT_INDEX = BASE_DIR / "index.html" | |
| STATIC_DIR = BASE_DIR / "static" | |
| STATIC_INDEX = STATIC_DIR / "index.html" | |
| app = FastAPI(title="BiteWise API", version="2.0.0") | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| if STATIC_DIR.exists(): | |
| app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static") | |
| class AdaptRequest(BaseModel): | |
| recipe_text: str = Field(min_length=5) | |
| diet: Literal["vegan", "keto", "both"] = "vegan" | |
| _db = None | |
| _semantic = None | |
| _service = None | |
| def get_service() -> RecipeAdapterService: | |
| global _db, _semantic, _service | |
| if _service is not None: | |
| return _service | |
| _db = SubstitutionDatabase(settings.dataset_path) | |
| _semantic = WordVectorFallback( | |
| model_name=settings.semantic_model_name, | |
| model_path=settings.semantic_model_path, | |
| enable_download=settings.enable_semantic_download, | |
| ) | |
| _service = RecipeAdapterService(db=_db, semantic=_semantic) | |
| return _service | |
| def _get_index_file() -> Path | None: | |
| if ROOT_INDEX.exists(): | |
| return ROOT_INDEX | |
| if STATIC_INDEX.exists(): | |
| return STATIC_INDEX | |
| return None | |
| def root(): | |
| index_file = _get_index_file() | |
| if index_file is not None: | |
| return FileResponse(str(index_file)) | |
| return JSONResponse( | |
| { | |
| "name": "BiteWise API", | |
| "status": "running", | |
| "hint": "Put index.html in the repo root or in static/index.html", | |
| } | |
| ) | |
| def health(): | |
| return {"ok": True} | |
| def meta(): | |
| service = get_service() | |
| return { | |
| "ner_model": settings.ner_model_name, | |
| "qa_model": settings.qa_model_name, | |
| "semantic_model": settings.semantic_model_name, | |
| "semantic_available": service.semantic.available, | |
| "semantic_mode": service.semantic._kind, | |
| "dataset_path": str(settings.dataset_path), | |
| } | |
| def adapt(req: AdaptRequest) -> Dict[str, Any]: | |
| try: | |
| service = get_service() | |
| return service.adapt(req.recipe_text, req.diet) | |
| except FileNotFoundError as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| except ValueError as e: | |
| raise HTTPException(status_code=400, detail=str(e)) | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"Unexpected error: {e}") |