bichnhan2701 commited on
Commit
0785301
·
1 Parent(s): 936d423

Upload note services

Browse files
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ NOTE_SERVICE_SETUP.md
2
+ .myvenv
3
+ __pycache__/
4
+ *.pyc
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+ WORKDIR /app
3
+
4
+ # install dependencies
5
+ COPY requirements.txt ./
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ EXPOSE 8080
11
+ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]
app/api/notes.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, BackgroundTasks, HTTPException
2
+ from pydantic import BaseModel
3
+ from typing import Optional, List
4
+ from app.services.storage import create_note, get_note
5
+ from app.jobs.enrichment_job import run_enrichment
6
+
7
+ router = APIRouter(prefix="/notes")
8
+
9
+ class CreateNoteRequest(BaseModel):
10
+ note_id: str
11
+ raw_text: str
12
+ normalized_text: Optional[str] = None
13
+ keywords: List[str] = []
14
+ chunks: list = []
15
+ duration: Optional[float] = None
16
+ sample_rate: Optional[int] = None
17
+ asr_model: Optional[str] = None
18
+ normalization_model: Optional[str] = None
19
+ generate: List[str] = []
20
+
21
+ @router.post("")
22
+ async def create_note(req: CreateNoteRequest, bg: BackgroundTasks):
23
+ create_note(req.note_id, req.dict())
24
+
25
+ if req.generate:
26
+ bg.add_task(run_enrichment, req.note_id, req.generate)
27
+
28
+ return {"note_id": req.note_id, "status": "stored"}
29
+
30
+ @router.get("/{note_id}")
31
+ def fetch_note(note_id: str):
32
+ note = get_note(note_id)
33
+ if not note:
34
+ raise HTTPException(404, "Note not found")
35
+ return note
app/config.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import os
2
+
3
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "")
4
+ FIREBASE_SERVICE_ACCOUNT = os.getenv("FIREBASE_SERVICE_ACCOUNT", "")
app/jobs/enrichment_job.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from app.services.storage import get_note, update_note
2
+ from app.services.summary_service import generate_summary
3
+ from app.services.mindmap_service import generate_mindmap
4
+
5
+ async def run_enrichment(note_id: str, tasks: list):
6
+ note = get_note(note_id)
7
+ if not note:
8
+ return
9
+
10
+ text = note.get("normalized_text") or note["raw_text"]
11
+
12
+ update_note(note_id, status="processing")
13
+ updates = {}
14
+
15
+ if "summary" in tasks:
16
+ updates["summary"] = await generate_summary(text)
17
+
18
+ if "mindmap" in tasks:
19
+ updates["mindmap"] = await generate_mindmap(text)
20
+
21
+ update_note(note_id, data=updates, status="ready")
app/main.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from app.api.notes import router as notes_router
3
+
4
+ app = FastAPI(title="Note Services API")
5
+ app.include_router(notes_router)
6
+
7
+ @app.get("/health")
8
+ def health():
9
+ return {"status": "ok"}
app/services/firebase.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import firebase_admin
2
+ from firebase_admin import credentials, firestore
3
+ import json
4
+ from app.config import FIREBASE_SERVICE_ACCOUNT
5
+
6
+ if not firebase_admin._apps:
7
+ if not FIREBASE_SERVICE_ACCOUNT:
8
+ raise RuntimeError("Missing FIREBASE_SERVICE_ACCOUNT")
9
+
10
+ cred = credentials.Certificate(json.loads(FIREBASE_SERVICE_ACCOUNT))
11
+ firebase_admin.initialize_app(cred)
12
+
13
+ db = firestore.client()
app/services/mindmap_service.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio, json
2
+ from app.config.settings import GEMINI_API_KEY
3
+ import google.generativeai as genai
4
+
5
+ if GEMINI_API_KEY:
6
+ genai.configure(api_key=GEMINI_API_KEY)
7
+ _model = genai.GenerativeModel("gemini-pro")
8
+ else:
9
+ _model = None
10
+
11
+
12
+ async def generate_mindmap(text: str) -> dict:
13
+ if not _model:
14
+ return {}
15
+
16
+ prompt = f"""
17
+ Bạn là chuyên gia tạo Sơ đồ tư duy. Hãy phân tích văn bản sau và tạo cấu trúc JSON Mindmap.
18
+ Yêu cầu:
19
+ 1. Xác định Ý chính làm Root.
20
+ 2. Phân tách ý phụ thành nhánh con (tối đa 3 cấp).
21
+ 3. Nhãn (label) ngắn gọn (< 7 từ).
22
+ 4. Màu sắc (colorHex): Root="#6200EE", Con="#F59E2B", "#2ECF9A", "#2F9BFF".
23
+
24
+ Cấu trúc JSON bắt buộc (Chỉ trả về JSON):
25
+ {{
26
+ "root": {{
27
+ "label": "Chủ đề",
28
+ "colorHex": "#6200EE",
29
+ "children": [
30
+ {{
31
+ "label": "Ý 1",
32
+ "colorHex": "#F59E2B",
33
+ "children": []
34
+ }}
35
+ ]
36
+ }}
37
+ }}
38
+
39
+ Văn bản:
40
+ {text}
41
+ """
42
+
43
+ loop = asyncio.get_event_loop()
44
+
45
+ def call():
46
+ r = _model.generate_content(prompt)
47
+ return r.text
48
+
49
+ raw = await loop.run_in_executor(None, call)
50
+
51
+ start = raw.find("{")
52
+ end = raw.rfind("}")
53
+ if start != -1 and end != -1:
54
+ return json.loads(raw[start:end+1])
55
+
56
+ return {}
app/services/storage.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ from app.services.firebase import db
3
+
4
+ COLLECTION = "notes"
5
+
6
+ def create_note(note_id: str, payload: dict):
7
+ now = datetime.utcnow()
8
+ payload.update({
9
+ "status": "created",
10
+ "created_at": now,
11
+ "updated_at": now
12
+ })
13
+ db.collection(COLLECTION).document(note_id).set(payload)
14
+
15
+ def update_note(note_id: str, data: dict = None, status: str = None):
16
+ updates = {"updated_at": datetime.utcnow()}
17
+ if data:
18
+ updates.update(data)
19
+ if status:
20
+ updates["status"] = status
21
+
22
+ db.collection(COLLECTION).document(note_id).update(updates)
23
+
24
+ def get_note(note_id: str):
25
+ doc = db.collection(COLLECTION).document(note_id).get()
26
+ return doc.to_dict() if doc.exists else None
app/services/summary_service.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ from app.config.settings import GEMINI_API_KEY
3
+ import google.generativeai as genai
4
+
5
+ if GEMINI_API_KEY:
6
+ genai.configure(api_key=GEMINI_API_KEY)
7
+ _model = genai.GenerativeModel("gemini-pro")
8
+ else:
9
+ _model = None
10
+
11
+
12
+ async def generate_summary(text: str) -> str:
13
+ if not _model:
14
+ return ""
15
+
16
+ prompt = f"""
17
+ Bạn là chuyên gia tóm tắt. Hãy tóm tắt văn bản sau thành **một đoạn văn duy nhất**.
18
+ Yêu cầu:
19
+ 1. Viết khoảng 3-5 câu, tổng hợp đầy đủ chủ đề và các ý chính.
20
+ 2. Viết liền mạch, KHÔNG xuống dòng, KHÔNG dùng gạch đầu dòng hay đánh số.
21
+ 3. Chỉ dựa trên thông tin được cung cấp, tuyệt đối KHÔNG tự thêm thông tin bên ngoài.
22
+ 4. Trả về văn bản thuần (plain text).
23
+
24
+ Văn bản:
25
+ \"\"\"{text}\"\"\"
26
+ """
27
+
28
+ loop = asyncio.get_event_loop()
29
+
30
+ def call():
31
+ r = _model.generate_content(prompt)
32
+ return r.text.strip()
33
+
34
+ result = await loop.run_in_executor(None, call)
35
+ return result.replace("```", "").strip()
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ google-generativeai
4
+ firebase-admin