Spaces:
Sleeping
Sleeping
| # ============================================================ | |
| # hf_space/app.py β AI Microservice cho Hugging Face Space | |
| # ChαΊ‘y emotion analysis + embedding, Δược gα»i tα»« main app | |
| # ============================================================ | |
| import os | |
| import numpy as np | |
| from fastapi import FastAPI, HTTPException | |
| from pydantic import BaseModel | |
| app = FastAPI(title="MindSpace AI Service") | |
| # ββ Load models once at startup ββββββββββββββββββββββββββββββ | |
| print("β³ Loading emotion model...") | |
| from transformers import pipeline as hf_pipeline | |
| emotion_pipe = hf_pipeline( | |
| "text-classification", | |
| model="j-hartmann/emotion-english-distilroberta-base", | |
| top_k=None, | |
| device=-1, # CPU | |
| ) | |
| print("β Emotion model loaded") | |
| print("β³ Loading embedding model...") | |
| from sentence_transformers import SentenceTransformer | |
| embed_model = SentenceTransformer("all-MiniLM-L6-v2") | |
| print("β Embedding model loaded") | |
| PLUTCHIK = ["anger", "disgust", "fear", "joy", "sadness", "surprise", "trust", "anticipation"] | |
| MODEL_EMOTIONS = ["anger", "disgust", "fear", "joy", "sadness", "surprise", "neutral"] | |
| # ββ Request / Response models ββββββββββββββββββββββββββββββββ | |
| class EmotionRequest(BaseModel): | |
| text: str | |
| recent_history: list[str] | None = None | |
| class EmotionResponse(BaseModel): | |
| scores: dict[str, float] | |
| dominant_emotion: str | |
| raw_text: str | |
| method: str | |
| class EmbedRequest(BaseModel): | |
| texts: list[str] | |
| class EmbedResponse(BaseModel): | |
| embeddings: list[list[float]] | |
| dim: int | |
| # ββ Endpoints ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def health(): | |
| return {"status": "ok"} | |
| def analyze_emotion(req: EmotionRequest): | |
| text = req.text.strip() | |
| if not text: | |
| raise HTTPException(status_code=400, detail="Empty text") | |
| # C1: Expand nαΊΏu text quΓ‘ ngαΊ―n (< 4 tα»«) | |
| method = "direct" | |
| if len(text.split()) < 4 and req.recent_history: | |
| context = " ".join(req.recent_history[-2:]) | |
| text = f"{context} {text}" | |
| method = "expanded" | |
| # Run emotion model | |
| results = emotion_pipe(text[:512])[0] | |
| # Map scores | |
| raw_scores = {r["label"].lower(): round(r["score"], 4) for r in results} | |
| # Build Plutchik 8 scores | |
| scores = {e: 0.0 for e in PLUTCHIK} | |
| for emotion in MODEL_EMOTIONS: | |
| if emotion in raw_scores and emotion in scores: | |
| scores[emotion] = raw_scores[emotion] | |
| # C3: Combine vα»i history nαΊΏu cΓ³ | |
| if req.recent_history and method == "direct": | |
| try: | |
| hist_text = " ".join(req.recent_history[-3:]) | |
| hist_results = emotion_pipe(hist_text[:512])[0] | |
| hist_scores = {r["label"].lower(): r["score"] for r in hist_results} | |
| alpha = 0.7 # Ζ―u tiΓͺn current input | |
| for e in PLUTCHIK: | |
| if e in hist_scores: | |
| scores[e] = round(alpha * scores[e] + (1 - alpha) * hist_scores[e], 4) | |
| method = "combined" | |
| except Exception: | |
| pass | |
| # Normalize | |
| total = sum(scores.values()) | |
| if total > 0: | |
| scores = {e: round(v / total, 4) for e, v in scores.items()} | |
| dominant = max(PLUTCHIK, key=lambda e: scores[e]) | |
| return EmotionResponse( | |
| scores=scores, | |
| dominant_emotion=dominant, | |
| raw_text=req.text, | |
| method=method, | |
| ) | |
| def embed_texts(req: EmbedRequest): | |
| if not req.texts: | |
| raise HTTPException(status_code=400, detail="Empty texts") | |
| embeddings = embed_model.encode(req.texts, normalize_embeddings=True) | |
| return EmbedResponse( | |
| embeddings=embeddings.tolist(), | |
| dim=embeddings.shape[1], | |
| ) | |
| def embed_single(req: dict): | |
| text = req.get("text", "") | |
| if not text: | |
| raise HTTPException(status_code=400, detail="Empty text") | |
| vec = embed_model.encode([text], normalize_embeddings=True)[0] | |
| return {"embedding": vec.tolist(), "dim": len(vec)} | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run("app:app", host="0.0.0.0", port=7860) | |