File size: 4,295 Bytes
0785301
7402e0f
a6d62e8
2432a11
 
7402e0f
a6d62e8
7402e0f
a6d62e8
dec42d4
 
2432a11
 
 
 
dec42d4
 
2432a11
7402e0f
dec42d4
2432a11
 
 
 
a6d62e8
7402e0f
0785301
dec42d4
7402e0f
dec42d4
7402e0f
dec42d4
a6d62e8
7402e0f
 
a6d62e8
 
7402e0f
0785301
 
7402e0f
 
 
 
0785301
 
 
a6d62e8
 
 
 
 
 
 
 
 
0785301
 
 
2432a11
 
 
0785301
2432a11
 
 
7402e0f
 
2432a11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a6d62e8
 
 
2432a11
 
 
a6d62e8
 
 
 
 
2432a11
 
 
 
 
 
 
 
 
 
 
 
7402e0f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import asyncio
import os
import logging
import random
import time
import re

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("[summary_service] google.genai module not found; summary generation disabled")

try:
    from google.api_core.exceptions import GoogleAPIError
except Exception:
    GoogleAPIError = Exception

gemini_client = None

if not genai:
    logging.warning("[summary_service] google.genai not available, summary generation will be disabled")
elif not GEMINI_API_KEY:
    logging.warning("[summary_service] GEMINI_API_KEY is not set, summary generation will be disabled")
else:
    try:
        gemini_client = genai.Client(api_key=GEMINI_API_KEY)
        logging.info(f"[summary_service] Initialized google.genai client with model={GEMINI_MODEL}")
    except Exception as e:
        logging.exception(f"[summary_service] Failed to init google.genai client: {e}")
        gemini_client = None

async def generate_summary(text: str) -> str:
    if not gemini_client:
        return ""
    
    if not text:
        return ""

    prompt = f"""
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.
Yêu cầu:
1. Viết khoảng 3-5 câu, tổng hợp đầy đủ chủ đề và các ý chính.
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ố.
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.
4. Trả về VĂN BẢN THUẦN (plain text), không bọc trong ``` hoặc JSON.
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 "").strip()
            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))
                        # add jitter
                        delay = delay + random.uniform(0, 0.5 * delay)
                        logging.warning(f"[summary_service] model overloaded (attempt {attempt}/{MAX_RETRIES}), retrying after {delay:.2f}s")
                        time.sleep(delay)
                        continue
                # non-retryable or out of retries
                logging.exception(f"[summary_service] generate_summary call failed on attempt {attempt}: {e}")
                break

        # propagate last exception to outer handler
        if last_exc:
            raise last_exc
        return ""

    try:
        result = await loop.run_in_executor(None, call)
        result = result.replace("```", "").strip()
        if result:
            return result
    except GoogleAPIError as e:
        logging.error(f"[summary_service] Gemini API error: {e}")
    except Exception as e:
        logging.exception(f"[summary_service] generate_summary failed: {e}")

    # fallback: return a very small extracted summary (first 1-2 sentences) or empty
    try:
        sentences = re.split(r"(?<=[.!?])\s+", text.strip())
        if not sentences:
            return ""
        fallback = " ".join(sentences[:2]).strip()
        # keep fallback reasonably short
        if len(fallback) > 400:
            fallback = fallback[:400].rsplit(" ", 1)[0] + "..."
        logging.info("[summary_service] Returning fallback summary after errors")
        return fallback
    except Exception:
        return ""