bichnhan2701's picture
Update note services logic
7402e0f
import asyncio
import re
import logging
from random import random
from time import time
import google.genai as genai
import json
from app.config import GEMINI_API_KEY, GEMINI_MODEL
try:
import google.genai as genai
try:
from google.genai import errors as genai_errors
except Exception:
genai_errors = None
except Exception:
genai = None
genai_errors = None
logging.warning("[mindmap_service] google.genai module not found; mindmap generation disabled")
try:
from google.api_core.exceptions import GoogleAPIError
except Exception:
GoogleAPIError = Exception
gemini_client = None
if not genai:
logging.warning("[mindmap_service] google.genai not available, mindmap generation will be disabled")
elif not GEMINI_API_KEY:
logging.warning("[mindmap_service] GEMINI_API_KEY is not set, mindmap generation will be disabled")
else:
try:
gemini_client = genai.Client(api_key=GEMINI_API_KEY)
logging.info(f"[mindmap_service] Initialized google.genai client with model={GEMINI_MODEL}")
except Exception as e:
logging.exception(f"[mindmap_service] Failed to init google.genai client: {e}")
gemini_client = None
async def generate_mindmap(text: str) -> dict:
if not text:
return {}
prompt = f"""
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.
Yêu cầu:
1. Xác định Ý chính làm Root.
2. Phân tách ý phụ thành nhánh con (tối đa 3 cấp).
3. Nhãn (label) ngắn gọn (< 7 từ).
4. Màu sắc (colorHex):
- Root: "#6200EE"
- Các nhánh con: sử dụng một trong các màu: "#F59E2B", "#2ECF9A", "#2F9BFF"
5. CHỈ TRẢ VỀ JSON, không giải thích thêm.
Cấu trúc JSON bắt buộc:
{{
"root": {{
"label": "Chủ đề",
"colorHex": "#6200EE",
"children": [
{{
"label": "Ý 1",
"colorHex": "#F59E2B",
"children": []
}}
]
}}
}}
Văn bản:
\"\"\"{text}\"\"\"
"""
loop = asyncio.get_event_loop()
MAX_RETRIES = 3
BASE_DELAY = 1.0
def call():
last_exc = None
for attempt in range(1, MAX_RETRIES + 1):
try:
resp = gemini_client.models.generate_content(
model=GEMINI_MODEL,
contents=prompt,
)
return resp.text or ""
except Exception as e:
last_exc = e
is_server_error = False
try:
if genai_errors and isinstance(e, genai_errors.ServerError):
is_server_error = True
except Exception:
is_server_error = False
msg = str(e)
if "503" in msg or "UNAVAILABLE" in msg or is_server_error:
if attempt < MAX_RETRIES:
delay = BASE_DELAY * (2 ** (attempt - 1))
delay = delay + random.uniform(0, 0.5 * delay)
logging.warning(f"[mindmap_service] model overloaded (attempt {attempt}/{MAX_RETRIES}), retrying after {delay:.2f}s")
time.sleep(delay)
continue
logging.exception(f"[mindmap_service] generate_mindmap call failed on attempt {attempt}: {e}")
break
if last_exc:
raise last_exc
return ""
try:
raw = await loop.run_in_executor(None, call)
start = raw.find("{")
end = raw.rfind("}")
if start != -1 and end != -1:
try:
return json.loads(raw[start:end + 1])
except Exception as e:
logging.warning(f"[mindmap_service] Failed to parse mindmap JSON: {e}")
else:
logging.warning("[mindmap_service] Mindmap response has no JSON block")
except GoogleAPIError as e:
logging.error(f"[mindmap_service] Gemini API error: {e}")
except Exception as e:
logging.exception(f"[mindmap_service] generate_mindmap failed: {e}")
# fallback: build a minimal mindmap from the text (best-effort)
try:
# use first sentence as root label (shortened)
sentences = re.split(r"(?<=[.!?])\s+", text.strip())
root_label = sentences[0] if sentences else "Topic"
# shorten to ~8 words
root_label = " ".join(root_label.split()[:8]).strip()
children = []
# take up to 3 next sentences as child labels
for s in sentences[1:4]:
label = " ".join(s.split()[:6]).strip()
if label:
children.append({
"label": label,
"colorHex": "#F59E2B",
"children": []
})
fallback = {
"root": {
"label": root_label or "Topic",
"colorHex": "#6200EE",
"children": children,
}
}
logging.info("[mindmap_service] Returning fallback mindmap after errors")
return fallback
except Exception:
return {}