Asuranosuke commited on
Commit
c665e73
Β·
verified Β·
1 Parent(s): 9b066da

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +37 -0
  2. app.py +140 -0
  3. requirements.txt +38 -0
Dockerfile ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ ENV PYTHONDONTWRITEBYTECODE=1
4
+ ENV PYTHONUNBUFFERED=1
5
+
6
+ # HuggingFace cache dir
7
+ ENV HF_HOME=/app/.cache
8
+ ENV TRANSFORMERS_CACHE=/app/.cache
9
+
10
+ RUN apt-get update && apt-get install -y \
11
+ build-essential \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
+ WORKDIR /app
15
+
16
+ COPY requirements.txt .
17
+ RUN pip install --upgrade pip && \
18
+ pip install --no-cache-dir -r requirements.txt
19
+
20
+ # Pre-download models at build time (trΓ‘nh timeout lΓΊc runtime)
21
+ RUN python -c "\
22
+ from transformers import pipeline; \
23
+ pipeline('text-classification', model='j-hartmann/emotion-english-distilroberta-base', top_k=None, device=-1); \
24
+ print('emotion model cached')"
25
+
26
+ RUN python -c "\
27
+ from sentence_transformers import SentenceTransformer; \
28
+ SentenceTransformer('all-MiniLM-L6-v2'); \
29
+ print('embedding model cached')"
30
+
31
+ # Chỉ copy app.py β€” khΓ΄ng copy cαΊ£ project
32
+ COPY app.py .
33
+
34
+ # HF Spaces bαΊ―t buα»™c port 7860
35
+ EXPOSE 7860
36
+
37
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
app.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # hf_space/app.py β€” AI Microservice cho Hugging Face Space
3
+ # ChαΊ‘y emotion analysis + embedding, được gọi tα»« main app
4
+ # ============================================================
5
+
6
+ import os
7
+
8
+ import numpy as np
9
+ from fastapi import FastAPI, HTTPException
10
+ from pydantic import BaseModel
11
+
12
+ app = FastAPI(title="MindSpace AI Service")
13
+
14
+ # ── Load models once at startup ──────────────────────────────
15
+ print("⏳ Loading emotion model...")
16
+ from transformers import pipeline as hf_pipeline
17
+
18
+ emotion_pipe = hf_pipeline(
19
+ "text-classification",
20
+ model="j-hartmann/emotion-english-distilroberta-base",
21
+ top_k=None,
22
+ device=-1, # CPU
23
+ )
24
+ print("βœ… Emotion model loaded")
25
+
26
+ print("⏳ Loading embedding model...")
27
+ from sentence_transformers import SentenceTransformer
28
+
29
+ embed_model = SentenceTransformer("all-MiniLM-L6-v2")
30
+ print("βœ… Embedding model loaded")
31
+
32
+ PLUTCHIK = ["anger", "disgust", "fear", "joy", "sadness", "surprise", "trust", "anticipation"]
33
+ MODEL_EMOTIONS = ["anger", "disgust", "fear", "joy", "sadness", "surprise", "neutral"]
34
+
35
+
36
+ # ── Request / Response models ────────────────────────────────
37
+
38
+ class EmotionRequest(BaseModel):
39
+ text: str
40
+ recent_history: list[str] | None = None
41
+
42
+ class EmotionResponse(BaseModel):
43
+ scores: dict[str, float]
44
+ dominant_emotion: str
45
+ raw_text: str
46
+ method: str
47
+
48
+ class EmbedRequest(BaseModel):
49
+ texts: list[str]
50
+
51
+ class EmbedResponse(BaseModel):
52
+ embeddings: list[list[float]]
53
+ dim: int
54
+
55
+
56
+ # ── Endpoints ────────────────────────────────────────────────
57
+
58
+ @app.get("/health")
59
+ def health():
60
+ return {"status": "ok"}
61
+
62
+
63
+ @app.post("/emotion", response_model=EmotionResponse)
64
+ def analyze_emotion(req: EmotionRequest):
65
+ text = req.text.strip()
66
+ if not text:
67
+ raise HTTPException(status_code=400, detail="Empty text")
68
+
69
+ # C1: Expand nαΊΏu text quΓ‘ ngαΊ―n (< 4 tα»«)
70
+ method = "direct"
71
+ if len(text.split()) < 4 and req.recent_history:
72
+ context = " ".join(req.recent_history[-2:])
73
+ text = f"{context} {text}"
74
+ method = "expanded"
75
+
76
+ # Run emotion model
77
+ results = emotion_pipe(text[:512])[0]
78
+
79
+ # Map scores
80
+ raw_scores = {r["label"].lower(): round(r["score"], 4) for r in results}
81
+
82
+ # Build Plutchik 8 scores
83
+ scores = {e: 0.0 for e in PLUTCHIK}
84
+ for emotion in MODEL_EMOTIONS:
85
+ if emotion in raw_scores and emotion in scores:
86
+ scores[emotion] = raw_scores[emotion]
87
+
88
+ # C3: Combine vα»›i history nαΊΏu cΓ³
89
+ if req.recent_history and method == "direct":
90
+ try:
91
+ hist_text = " ".join(req.recent_history[-3:])
92
+ hist_results = emotion_pipe(hist_text[:512])[0]
93
+ hist_scores = {r["label"].lower(): r["score"] for r in hist_results}
94
+ alpha = 0.7 # Ζ―u tiΓͺn current input
95
+ for e in PLUTCHIK:
96
+ if e in hist_scores:
97
+ scores[e] = round(alpha * scores[e] + (1 - alpha) * hist_scores[e], 4)
98
+ method = "combined"
99
+ except Exception:
100
+ pass
101
+
102
+ # Normalize
103
+ total = sum(scores.values())
104
+ if total > 0:
105
+ scores = {e: round(v / total, 4) for e, v in scores.items()}
106
+
107
+ dominant = max(PLUTCHIK, key=lambda e: scores[e])
108
+
109
+ return EmotionResponse(
110
+ scores=scores,
111
+ dominant_emotion=dominant,
112
+ raw_text=req.text,
113
+ method=method,
114
+ )
115
+
116
+
117
+ @app.post("/embed", response_model=EmbedResponse)
118
+ def embed_texts(req: EmbedRequest):
119
+ if not req.texts:
120
+ raise HTTPException(status_code=400, detail="Empty texts")
121
+
122
+ embeddings = embed_model.encode(req.texts, normalize_embeddings=True)
123
+ return EmbedResponse(
124
+ embeddings=embeddings.tolist(),
125
+ dim=embeddings.shape[1],
126
+ )
127
+
128
+
129
+ @app.post("/embed/single")
130
+ def embed_single(req: dict):
131
+ text = req.get("text", "")
132
+ if not text:
133
+ raise HTTPException(status_code=400, detail="Empty text")
134
+ vec = embed_model.encode([text], normalize_embeddings=True)[0]
135
+ return {"embedding": vec.tolist(), "dim": len(vec)}
136
+
137
+
138
+ if __name__ == "__main__":
139
+ import uvicorn
140
+ uvicorn.run("app:app", host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # requirements.txt β€” MindSpace main app
3
+ # AI chαΊ‘y trΓͺn HF Space
4
+ # ============================================================
5
+
6
+ # Web framework
7
+ fastapi>=0.110.0
8
+ uvicorn>=0.29.0
9
+
10
+ # Auth
11
+ python-jose[cryptography]>=3.3.0
12
+
13
+ # LLM
14
+ openai>=1.0.0
15
+
16
+ # Vector search (FAISS vαΊ«n cαΊ§n numpy nhΖ°ng khΓ΄ng cαΊ§n torch)
17
+ faiss-cpu>=1.7.4
18
+ qdrant-client>=1.8.0
19
+
20
+ # Database
21
+ sqlalchemy>=2.0.0
22
+ psycopg2-binary>=2.9.9
23
+
24
+ # PDF
25
+ pypdf>=4.0.0
26
+
27
+ # Utils
28
+ python-dotenv>=1.0.0
29
+ pydantic>=2.0.0
30
+ httpx>=0.27.0
31
+ requests>=2.31.0
32
+ numpy>=1.26.0
33
+
34
+
35
+ # AI / ML β€” phαΊ§n main app KHΓ”NG cΓ³
36
+ torch==2.2.2
37
+ transformers>=4.39.0
38
+ sentence-transformers>=2.7.0