Spaces:
Sleeping
Sleeping
File size: 3,048 Bytes
9373226 5797a99 9373226 5797a99 9373226 5797a99 9373226 5797a99 9373226 5797a99 9373226 5797a99 9373226 5797a99 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 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
@app.get("/")
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",
}
)
@app.get("/health")
def health():
return {"ok": True}
@app.get("/api/meta")
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),
}
@app.post("/api/adapt")
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}") |