Spaces:
Running
Running
| from __future__ import annotations | |
| import os | |
| from typing import Union | |
| import torch | |
| from fastapi import FastAPI, HTTPException | |
| from pydantic import BaseModel | |
| from sentence_transformers import SentenceTransformer | |
| DEFAULT_MODEL_NAME = "keepitreal/vietnamese-sbert" | |
| MODEL_NAME = os.getenv("MODEL_NAME", DEFAULT_MODEL_NAME) | |
| app = FastAPI(title="Free Embedding API") | |
| ERROR_MESSAGE = None | |
| try: | |
| DEVICE = "cuda" if torch.cuda.is_available() else "cpu" | |
| MODEL = SentenceTransformer(MODEL_NAME, device=DEVICE) | |
| except Exception as exc: # pragma: no cover - startup failure is surfaced by the endpoint. | |
| import traceback | |
| ERROR_MESSAGE = f"{exc}\n{traceback.format_exc()}" | |
| print(f"Failed to load embedding model: {ERROR_MESSAGE}") | |
| DEVICE = "unavailable" | |
| MODEL = None | |
| class EmbedRequest(BaseModel): | |
| input: Union[str, list[str]] | |
| class EmbedResponse(BaseModel): | |
| embedding: Union[list[float], list[list[float]]] | |
| model: str | |
| def read_root() -> dict[str, Union[str, None]]: | |
| return { | |
| "status": "ok" if MODEL is not None else "model_unavailable", | |
| "model": MODEL_NAME, | |
| "device": DEVICE, | |
| "error": ERROR_MESSAGE, | |
| } | |
| async def get_embeddings(request: EmbedRequest) -> EmbedResponse: | |
| if MODEL is None: | |
| raise HTTPException(status_code=500, detail="Embedding model was not loaded.") | |
| sentences = request.input | |
| is_single_input = isinstance(sentences, str) | |
| if is_single_input: | |
| sentences = [sentences] | |
| sentences = [sentence for sentence in sentences if sentence.strip()] | |
| if not sentences: | |
| raise HTTPException(status_code=400, detail="Input must not be empty.") | |
| try: | |
| embeddings = MODEL.encode(sentences, normalize_embeddings=True) | |
| embeddings_list = embeddings.tolist() | |
| except Exception as exc: | |
| raise HTTPException(status_code=500, detail=f"Embedding failed: {exc}") from exc | |
| if is_single_input: | |
| embeddings_list = embeddings_list[0] | |
| return EmbedResponse(embedding=embeddings_list, model=MODEL_NAME) | |