Spaces:
Sleeping
Sleeping
Commit
·
557b80a
1
Parent(s):
1e5dfc2
fix error 503
Browse files- app/api/transcribe.py +0 -1
- app/services/nlp_postprocess.py +42 -6
app/api/transcribe.py
CHANGED
|
@@ -10,7 +10,6 @@ import time
|
|
| 10 |
from app.core.audio_utils import save_upload_file, get_audio_info, ensure_wav_16k_mono, make_temp_path, download_file_from_url
|
| 11 |
from app.core.asr_engine import load_model, transcribe_file, transcribe_file_chunks
|
| 12 |
from app.config import settings
|
| 13 |
-
from app.services.text_normalizer import normalize_text
|
| 14 |
from app.services.nlp_postprocess import normalize_and_extract
|
| 15 |
# Summary and mindmap generation moved to Note Service; do not import here
|
| 16 |
from app.services.note_client import NoteServiceClient
|
|
|
|
| 10 |
from app.core.audio_utils import save_upload_file, get_audio_info, ensure_wav_16k_mono, make_temp_path, download_file_from_url
|
| 11 |
from app.core.asr_engine import load_model, transcribe_file, transcribe_file_chunks
|
| 12 |
from app.config import settings
|
|
|
|
| 13 |
from app.services.nlp_postprocess import normalize_and_extract
|
| 14 |
# Summary and mindmap generation moved to Note Service; do not import here
|
| 15 |
from app.services.note_client import NoteServiceClient
|
app/services/nlp_postprocess.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
import asyncio
|
| 2 |
import json
|
| 3 |
import logging
|
|
|
|
|
|
|
| 4 |
|
| 5 |
from app.infra.redis_client import redis_client
|
| 6 |
from app.utils.hashing import sha256
|
|
@@ -17,6 +19,9 @@ except Exception:
|
|
| 17 |
pass
|
| 18 |
|
| 19 |
CACHE_TTL = 60 * 60 * 24 * 3 # 3 days
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
# Tạo client Gemini nếu có API key
|
| 22 |
_gemini_client = None
|
|
@@ -89,8 +94,43 @@ Cấu trúc JSON bắt buộc (chỉ trả JSON, không giải thích thêm):
|
|
| 89 |
# resp.text là chuỗi model trả (có thể chứa code block)
|
| 90 |
return resp.text
|
| 91 |
|
| 92 |
-
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
if text:
|
| 95 |
# clean JSON
|
| 96 |
start = text.find("{")
|
|
@@ -106,10 +146,6 @@ Cấu trúc JSON bắt buộc (chỉ trả JSON, không giải thích thêm):
|
|
| 106 |
logging.warning(f"[nlp_postprocess] Failed to parse Gemini JSON, fallback to raw_text: {e}")
|
| 107 |
else:
|
| 108 |
logging.warning("[nlp_postprocess] Gemini response has no JSON block, fallback to raw_text")
|
| 109 |
-
except GoogleAPIError as e:
|
| 110 |
-
logging.error(f"[nlp_postprocess] Gemini API error: {e}")
|
| 111 |
-
except Exception as e:
|
| 112 |
-
logging.exception(f"[nlp_postprocess] Gemini call failed, fallback to raw_text: {e}")
|
| 113 |
|
| 114 |
# 4) Try write back to Redis (best effort)
|
| 115 |
try:
|
|
|
|
| 1 |
import asyncio
|
| 2 |
import json
|
| 3 |
import logging
|
| 4 |
+
import random
|
| 5 |
+
import time
|
| 6 |
|
| 7 |
from app.infra.redis_client import redis_client
|
| 8 |
from app.utils.hashing import sha256
|
|
|
|
| 19 |
pass
|
| 20 |
|
| 21 |
CACHE_TTL = 60 * 60 * 24 * 3 # 3 days
|
| 22 |
+
# Retry settings for transient model errors (503 / UNAVAILABLE)
|
| 23 |
+
RETRY_MAX_ATTEMPTS = 3
|
| 24 |
+
RETRY_BASE_BACKOFF = 1.0
|
| 25 |
|
| 26 |
# Tạo client Gemini nếu có API key
|
| 27 |
_gemini_client = None
|
|
|
|
| 94 |
# resp.text là chuỗi model trả (có thể chứa code block)
|
| 95 |
return resp.text
|
| 96 |
|
| 97 |
+
# Try with a small exponential backoff for transient server errors
|
| 98 |
+
text = None
|
| 99 |
+
attempt = 0
|
| 100 |
+
while attempt < RETRY_MAX_ATTEMPTS:
|
| 101 |
+
attempt += 1
|
| 102 |
+
try:
|
| 103 |
+
text = await loop.run_in_executor(None, call)
|
| 104 |
+
break
|
| 105 |
+
except Exception as e:
|
| 106 |
+
# Try to detect transient server-side/genai errors (503 / UNAVAILABLE)
|
| 107 |
+
is_transient = False
|
| 108 |
+
try:
|
| 109 |
+
# try to import genai-specific ServerError if available
|
| 110 |
+
from google.genai import errors as _genai_errors # type: ignore
|
| 111 |
+
ServerError = getattr(_genai_errors, "ServerError", None)
|
| 112 |
+
except Exception:
|
| 113 |
+
ServerError = None
|
| 114 |
+
|
| 115 |
+
if ServerError is not None and isinstance(e, ServerError):
|
| 116 |
+
is_transient = True
|
| 117 |
+
else:
|
| 118 |
+
msg = str(e)
|
| 119 |
+
if "503" in msg or "UNAVAILABLE" in msg.upper() or "model is overloaded" in msg.lower():
|
| 120 |
+
is_transient = True
|
| 121 |
+
|
| 122 |
+
if is_transient and attempt < RETRY_MAX_ATTEMPTS:
|
| 123 |
+
backoff = RETRY_BASE_BACKOFF * (2 ** (attempt - 1)) + random.uniform(0, 0.5)
|
| 124 |
+
logging.warning(f"[nlp_postprocess] Gemini transient error (attempt {attempt}): {e}; retrying in {backoff:.1f}s")
|
| 125 |
+
# use asyncio.sleep to not block event loop
|
| 126 |
+
await asyncio.sleep(backoff)
|
| 127 |
+
continue
|
| 128 |
+
else:
|
| 129 |
+
logging.exception(f"[nlp_postprocess] Gemini call failed, fallback to raw_text: {e}")
|
| 130 |
+
text = None
|
| 131 |
+
break
|
| 132 |
+
|
| 133 |
+
if text:
|
| 134 |
if text:
|
| 135 |
# clean JSON
|
| 136 |
start = text.find("{")
|
|
|
|
| 146 |
logging.warning(f"[nlp_postprocess] Failed to parse Gemini JSON, fallback to raw_text: {e}")
|
| 147 |
else:
|
| 148 |
logging.warning("[nlp_postprocess] Gemini response has no JSON block, fallback to raw_text")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
# 4) Try write back to Redis (best effort)
|
| 151 |
try:
|