Spaces:
Running
Running
| import logging | |
| from contextlib import asynccontextmanager | |
| from typing import Optional | |
| from fastapi import FastAPI, HTTPException, Query | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.staticfiles import StaticFiles | |
| from pydantic import BaseModel | |
| from word_vectors import WordVectorAnalyzer | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format="%(asctime)s [%(levelname)s] %(name)s — %(message)s", | |
| ) | |
| logger = logging.getLogger(__name__) | |
| analyzer: Optional[WordVectorAnalyzer] = None | |
| async def lifespan(app: FastAPI): | |
| global analyzer | |
| logger.info("Starting up — loading Word2Vec model...") | |
| try: | |
| analyzer = WordVectorAnalyzer() | |
| logger.info("Model loaded and ready.") | |
| except Exception: | |
| logger.exception("Failed to load model") | |
| yield | |
| logger.info("Shutting down.") | |
| app = FastAPI(title="Word2Vec Galaxy", lifespan=lifespan) | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_methods=["GET", "POST"], | |
| allow_headers=["Content-Type"], | |
| ) | |
| def health(): | |
| return {"status": "ready" if analyzer is not None else "loading", | |
| "model_loaded": analyzer is not None} | |
| def get_similar( | |
| word: str = Query(..., min_length=1, max_length=100), | |
| n: int = Query(20, ge=5, le=50), | |
| ): | |
| if analyzer is None: | |
| raise HTTPException(503, detail="Model is still loading — please wait.") | |
| similar = analyzer.find_similar_words(word, n) | |
| if not similar: | |
| raise HTTPException(404, detail=f"'{word}' not found in vocabulary.") | |
| words, vectors = analyzer.reduce_dimensions(similar) | |
| similarities: list[float] = [] | |
| for w in words: | |
| if w == word: | |
| similarities.append(1.0) | |
| else: | |
| try: | |
| similarities.append(float(analyzer.model.similarity(word, w))) | |
| except Exception: | |
| similarities.append(0.5) | |
| return { | |
| "target": word, | |
| "words": words, | |
| "vectors": vectors.tolist(), | |
| "similarities": similarities, | |
| } | |
| class AnalogyRequest(BaseModel): | |
| word1: str # subtracted (e.g. "man") | |
| word2: str # added (e.g. "woman") | |
| word3: str # base (e.g. "king") | |
| def get_analogy(req: AnalogyRequest): | |
| if analyzer is None: | |
| raise HTTPException(503, detail="Model is still loading — please wait.") | |
| result, error = analyzer.word_analogy(req.word1, req.word2, req.word3) | |
| if error: | |
| raise HTTPException(400, detail=error) | |
| words_in = [req.word1, req.word2, req.word3, result] | |
| valid_words, vectors = analyzer.reduce_dimensions(words_in) | |
| return { | |
| "word1": req.word1, | |
| "word2": req.word2, | |
| "word3": req.word3, | |
| "result": result, | |
| "words": valid_words, | |
| "vectors": vectors.tolist(), | |
| } | |
| app.mount("/", StaticFiles(directory="static", html=True), name="static") | |