|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import re |
|
|
from typing import Dict, Tuple, Optional |
|
|
from sentence_transformers import SentenceTransformer |
|
|
from langdetect import detect, detect_langs |
|
|
from langdetect.lang_detect_exception import LangDetectException |
|
|
from config.settings import settings |
|
|
|
|
|
class MultilingualManager: |
|
|
def __init__(self): |
|
|
self.vietnamese_model = None |
|
|
self.multilingual_model = None |
|
|
self.current_language = 'vi' |
|
|
self._initialize_models() |
|
|
|
|
|
def _initialize_models(self): |
|
|
"""Khởi tạo các mô hình đa ngôn ngữ""" |
|
|
try: |
|
|
print("🔄 Đang tải mô hình embedding tiếng Việt...") |
|
|
self.vietnamese_model = SentenceTransformer(settings.VIETNAMESE_EMBEDDING_MODEL) |
|
|
print("✅ Đã tải mô hình embedding tiếng Việt") |
|
|
except Exception as e: |
|
|
print(f"❌ Lỗi tải mô hình embedding tiếng Việt: {e}") |
|
|
|
|
|
self.vietnamese_model = None |
|
|
|
|
|
try: |
|
|
print("🔄 Đang tải mô hình embedding đa ngôn ngữ...") |
|
|
self.multilingual_model = SentenceTransformer( |
|
|
settings.MULTILINGUAL_EMBEDDING_MODEL, |
|
|
trust_remote_code=True |
|
|
) |
|
|
print("✅ Đã tải mô hình embedding đa ngôn ngữ") |
|
|
except Exception as e: |
|
|
print(f"❌ Lỗi tải mô hình embedding đa ngôn ngữ: {e}") |
|
|
|
|
|
try: |
|
|
self.multilingual_model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2') |
|
|
print("✅ Đã tải mô hình fallback đa ngôn ngữ") |
|
|
except Exception as fallback_error: |
|
|
print(f"❌ Lỗi tải mô hình fallback: {fallback_error}") |
|
|
self.multilingual_model = None |
|
|
|
|
|
def detect_language(self, text: str, fallback_method: bool = True) -> str: |
|
|
""" |
|
|
Phát hiện ngôn ngữ với độ chính xác cao sử dụng langdetect |
|
|
""" |
|
|
if not text or len(text.strip()) == 0: |
|
|
return 'vi' |
|
|
|
|
|
|
|
|
text = self._clean_text_for_detection(text) |
|
|
|
|
|
if len(text.strip()) < 3: |
|
|
return 'vi' |
|
|
|
|
|
try: |
|
|
|
|
|
languages = detect_langs(text) |
|
|
|
|
|
|
|
|
best_lang = str(languages[0]).split(':')[0] |
|
|
|
|
|
|
|
|
lang_map = { |
|
|
'vi': 'vi', 'en': 'en', 'fr': 'fr', 'es': 'es', |
|
|
'de': 'de', 'ja': 'ja', 'ko': 'ko', 'zh-cn': 'zh', 'zh-tw': 'zh', |
|
|
'it': 'en', 'pt': 'en', 'ru': 'en', 'ar': 'en' |
|
|
} |
|
|
|
|
|
detected_lang = lang_map.get(best_lang, 'en') |
|
|
|
|
|
|
|
|
confidence = float(str(languages[0]).split(':')[1]) |
|
|
if confidence < 0.6 and fallback_method: |
|
|
|
|
|
return self._fallback_language_detection(text) |
|
|
|
|
|
print(f"🔍 Phát hiện ngôn ngữ: {detected_lang} (độ tin cậy: {confidence:.2f})") |
|
|
return detected_lang |
|
|
|
|
|
except LangDetectException as e: |
|
|
print(f"⚠️ LangDetect lỗi, sử dụng fallback: {e}") |
|
|
return self._fallback_language_detection(text) if fallback_method else 'vi' |
|
|
except Exception as e: |
|
|
print(f"⚠️ Lỗi phát hiện ngôn ngữ: {e}") |
|
|
return self._fallback_language_detection(text) if fallback_method else 'vi' |
|
|
|
|
|
def _fallback_language_detection(self, text: str) -> str: |
|
|
""" |
|
|
Fallback method sử dụng các quy tắc heuristic |
|
|
""" |
|
|
text_lower = text.lower() |
|
|
|
|
|
|
|
|
vietnamese_indicators = [ |
|
|
'của', 'và', 'là', 'có', 'được', 'trong', 'cho', 'với', 'như', 'tôi', |
|
|
'bạn', 'ông', 'bà', 'anh', 'chị', 'em', 'này', 'kia', 'đó', 'đây', |
|
|
'không', 'có', 'phải', 'rất', 'nhất', 'các', 'những', 'một', 'hai', 'ba' |
|
|
] |
|
|
|
|
|
english_indicators = [ |
|
|
'the', 'and', 'is', 'are', 'for', 'with', 'this', 'that', 'you', 'they', |
|
|
'what', 'where', 'when', 'why', 'how', 'which', 'who', 'their', 'have', 'has', |
|
|
'from', 'your', 'will', 'would', 'could', 'should', 'about', 'into', 'through' |
|
|
] |
|
|
|
|
|
|
|
|
vi_count = sum(1 for word in vietnamese_indicators if word in text_lower) |
|
|
en_count = sum(1 for word in english_indicators if word in text_lower) |
|
|
|
|
|
|
|
|
vietnamese_chars = set('àáâãèéêìíòóôõùúýăđĩũơưạảấầẩẫậắằẳẵặẹẻẽếềểễệỉịọỏốồổỗộớờởỡợụủứừửữựỳỵỷỹ') |
|
|
vi_char_count = sum(1 for char in text if char in vietnamese_chars) |
|
|
|
|
|
|
|
|
if vi_count > en_count or vi_char_count > 2: |
|
|
return 'vi' |
|
|
elif en_count > vi_count: |
|
|
return 'en' |
|
|
else: |
|
|
|
|
|
if any(char in text for char in 'あいうえおぁ-んァ-ン'): |
|
|
return 'ja' |
|
|
elif any(char in text for char in '你好'): |
|
|
return 'zh' |
|
|
elif any(char in text for char in '안녕'): |
|
|
return 'ko' |
|
|
elif any(char in text for char in 'àâæçèéêëîïôœùûüÿ'): |
|
|
return 'fr' |
|
|
elif any(char in text for char in 'áéíóúñü'): |
|
|
return 'es' |
|
|
elif any(char in text for char in 'äöüß'): |
|
|
return 'de' |
|
|
else: |
|
|
return 'en' |
|
|
|
|
|
def _clean_text_for_detection(self, text: str) -> str: |
|
|
"""Làm sạch văn bản để phát hiện ngôn ngữ chính xác hơn""" |
|
|
|
|
|
text = re.sub(r'http\S+', '', text) |
|
|
text = re.sub(r'[0-9]+', '', text) |
|
|
text = re.sub(r'[^\w\s]', ' ', text) |
|
|
text = re.sub(r'\s+', ' ', text) |
|
|
return text.strip() |
|
|
|
|
|
def detect_language_with_confidence(self, text: str) -> Tuple[str, float]: |
|
|
""" |
|
|
Phát hiện ngôn ngữ với điểm tin cậy |
|
|
""" |
|
|
if not text or len(text.strip()) < 3: |
|
|
return 'vi', 0.0 |
|
|
|
|
|
try: |
|
|
languages = detect_langs(text) |
|
|
best_lang = str(languages[0]) |
|
|
lang_code, confidence = best_lang.split(':') |
|
|
|
|
|
lang_map = { |
|
|
'vi': 'vi', 'en': 'en', 'fr': 'fr', 'es': 'es', |
|
|
'de': 'de', 'ja': 'ja', 'ko': 'ko', 'zh-cn': 'zh', 'zh-tw': 'zh' |
|
|
} |
|
|
|
|
|
detected_lang = lang_map.get(lang_code, 'en') |
|
|
confidence_score = float(confidence) |
|
|
|
|
|
return detected_lang, confidence_score |
|
|
|
|
|
except LangDetectException as e: |
|
|
print(f"⚠️ Lỗi phát hiện ngôn ngữ với confidence: {e}") |
|
|
return 'vi', 0.5 |
|
|
except Exception as e: |
|
|
print(f"⚠️ Lỗi phát hiện ngôn ngữ với confidence: {e}") |
|
|
return 'vi', 0.5 |
|
|
|
|
|
def detect_language_simple(self, text: str) -> str: |
|
|
""" |
|
|
Phát hiện ngôn ngữ đơn giản (nhanh hơn, ít chính xác hơn) |
|
|
""" |
|
|
if not text or len(text.strip()) < 3: |
|
|
return 'vi' |
|
|
|
|
|
try: |
|
|
|
|
|
lang_code = detect(text) |
|
|
|
|
|
lang_map = { |
|
|
'vi': 'vi', 'en': 'en', 'fr': 'fr', 'es': 'es', |
|
|
'de': 'de', 'ja': 'ja', 'ko': 'ko', 'zh-cn': 'zh', 'zh-tw': 'zh' |
|
|
} |
|
|
|
|
|
return lang_map.get(lang_code, 'en') |
|
|
|
|
|
except LangDetectException: |
|
|
return self._fallback_language_detection(text) |
|
|
except Exception: |
|
|
return 'vi' |
|
|
|
|
|
def get_embedding_model(self, language: str = None) -> Optional[SentenceTransformer]: |
|
|
"""Lấy mô hình embedding dựa trên ngôn ngữ đã phát hiện""" |
|
|
if language and language in settings.SUPPORTED_LANGUAGES: |
|
|
lang = language |
|
|
else: |
|
|
lang = self.current_language |
|
|
|
|
|
if lang == 'vi' and self.vietnamese_model is not None: |
|
|
return self.vietnamese_model |
|
|
else: |
|
|
return self.multilingual_model |
|
|
|
|
|
def get_llm_model(self, language: str = None) -> str: |
|
|
"""Lấy tên mô hình LLM dựa trên ngôn ngữ đã phát hiện""" |
|
|
if language and language in settings.SUPPORTED_LANGUAGES: |
|
|
lang = language |
|
|
else: |
|
|
lang = self.current_language |
|
|
|
|
|
if lang == 'vi': |
|
|
return settings.VIETNAMESE_LLM_MODEL |
|
|
else: |
|
|
return settings.MULTILINGUAL_LLM_MODEL |
|
|
|
|
|
def get_language_info(self, language: str = None) -> Dict: |
|
|
"""Lấy thông tin ngôn ngữ bao gồm mã và tên đầy đủ""" |
|
|
if language and language in settings.SUPPORTED_LANGUAGES: |
|
|
lang = language |
|
|
else: |
|
|
lang = self.current_language |
|
|
|
|
|
language_names = { |
|
|
'vi': 'Tiếng Việt', |
|
|
'en': 'English', |
|
|
'fr': 'Français', |
|
|
'es': 'Español', |
|
|
'de': 'Deutsch', |
|
|
'ja': '日本語', |
|
|
'ko': '한국어', |
|
|
'zh': '中文' |
|
|
} |
|
|
|
|
|
return { |
|
|
'code': lang, |
|
|
'name': language_names.get(lang, 'Unknown'), |
|
|
'embedding_model': settings.VIETNAMESE_EMBEDDING_MODEL if lang == 'vi' else settings.MULTILINGUAL_EMBEDDING_MODEL, |
|
|
'llm_model': settings.VIETNAMESE_LLM_MODEL if lang == 'vi' else settings.MULTILINGUAL_LLM_MODEL, |
|
|
'embedding_status': 'active' if (self.vietnamese_model if lang == 'vi' else self.multilingual_model) else 'inactive' |
|
|
} |
|
|
|
|
|
def get_supported_languages(self) -> Dict[str, str]: |
|
|
"""Lấy danh sách ngôn ngữ được hỗ trợ""" |
|
|
return { |
|
|
'vi': 'Tiếng Việt', |
|
|
'en': 'English', |
|
|
'fr': 'Français', |
|
|
'es': 'Español', |
|
|
'de': 'Deutsch', |
|
|
'ja': '日本語', |
|
|
'ko': '한국어', |
|
|
'zh': '中文' |
|
|
} |
|
|
|
|
|
def is_language_supported(self, language: str) -> bool: |
|
|
"""Kiểm tra xem ngôn ngữ có được hỗ trợ không""" |
|
|
return language in self.get_supported_languages() |