Spaces:
Runtime error
Runtime error
| """ | |
| translator.py — NLLB-200 offline translation engine | |
| Wraps Meta's NLLB-200-distilled-600M for any-to-any translation. | |
| """ | |
| import time | |
| import traceback | |
| from typing import Dict, Any, Optional | |
| from config import NLLB_MODEL_NAME, NLLB_LANGS, LANGUAGE_NAMES, NLLB_TO_ISO | |
| try: | |
| import spaces | |
| def dummy_gpu_func(): | |
| pass | |
| except ImportError: | |
| pass | |
| class Translator: | |
| """Offline translation using Meta NLLB-200.""" | |
| def __init__(self): | |
| self.model = None | |
| self.tokenizer = None | |
| self.ready = False | |
| self.model_name = NLLB_MODEL_NAME | |
| def load(self): | |
| """Load the NLLB model and tokenizer.""" | |
| try: | |
| from transformers import AutoTokenizer, AutoModelForSeq2SeqLM | |
| print(f"[Translator] Loading NLLB model: {self.model_name}...") | |
| self.tokenizer = AutoTokenizer.from_pretrained( | |
| self.model_name, | |
| local_files_only=True | |
| ) | |
| print("[Translator] Tokenizer loaded.") | |
| self.model = AutoModelForSeq2SeqLM.from_pretrained( | |
| self.model_name, | |
| local_files_only=True | |
| ) | |
| print("[Translator] Model loaded.") | |
| self.ready = True | |
| print("[Translator] NLLB ready for translation!") | |
| except Exception as e: | |
| print(f"[Translator] ERROR loading NLLB: {e}") | |
| traceback.print_exc() | |
| # Try downloading if not available locally | |
| try: | |
| print("[Translator] Trying to download model...") | |
| from transformers import AutoTokenizer, AutoModelForSeq2SeqLM | |
| self.tokenizer = AutoTokenizer.from_pretrained(self.model_name) | |
| self.model = AutoModelForSeq2SeqLM.from_pretrained(self.model_name) | |
| self.ready = True | |
| print("[Translator] NLLB downloaded and loaded!") | |
| except Exception as e2: | |
| print(f"[Translator] Download also failed: {e2}") | |
| traceback.print_exc() | |
| self.ready = False | |
| def translate(self, text: str, source_lang: str, target_lang: str, | |
| max_length: int = 512) -> Dict[str, Any]: | |
| """ | |
| Translate text from source language to target language. | |
| Args: | |
| text: Text to translate | |
| source_lang: ISO language code (e.g., 'en', 'fr', 'ta') | |
| target_lang: ISO language code | |
| max_length: Maximum output token length | |
| Returns: | |
| dict with translated text, timing info, etc. | |
| """ | |
| if not self.ready or self.model is None or self.tokenizer is None: | |
| return { | |
| "translated": text, | |
| "error": "NLLB model not loaded", | |
| "time_ms": 0 | |
| } | |
| # Get NLLB codes | |
| source_nllb = NLLB_LANGS.get(source_lang) | |
| target_nllb = NLLB_LANGS.get(target_lang) | |
| if not source_nllb: | |
| return { | |
| "translated": text, | |
| "error": f"Unsupported source language: {source_lang}", | |
| "time_ms": 0 | |
| } | |
| if not target_nllb: | |
| return { | |
| "translated": text, | |
| "error": f"Unsupported target language: {target_lang}", | |
| "time_ms": 0 | |
| } | |
| # Same language — no translation needed | |
| if source_lang == target_lang: | |
| return { | |
| "translated": text, | |
| "source_lang": source_lang, | |
| "target_lang": target_lang, | |
| "time_ms": 0, | |
| "error": None | |
| } | |
| try: | |
| start = time.time() | |
| # Set source language | |
| self.tokenizer.src_lang = source_nllb | |
| # Tokenize | |
| inputs = self.tokenizer(text, return_tensors="pt", truncation=True, | |
| max_length=max_length) | |
| # Get target language token ID | |
| target_token_id = self.tokenizer.convert_tokens_to_ids(target_nllb) | |
| # Generate translation | |
| translated_tokens = self.model.generate( | |
| **inputs, | |
| forced_bos_token_id=target_token_id, | |
| max_length=max_length | |
| ) | |
| # Decode | |
| translated_text = self.tokenizer.batch_decode( | |
| translated_tokens, | |
| skip_special_tokens=True | |
| )[0] | |
| elapsed = round((time.time() - start) * 1000) # ms | |
| return { | |
| "translated": translated_text, | |
| "source_lang": source_lang, | |
| "target_lang": target_lang, | |
| "source_nllb": source_nllb, | |
| "target_nllb": target_nllb, | |
| "time_ms": elapsed, | |
| "error": None | |
| } | |
| except Exception as e: | |
| print(f"[Translator] Translation error: {e}") | |
| traceback.print_exc() | |
| return { | |
| "translated": text, | |
| "error": str(e), | |
| "time_ms": 0 | |
| } | |
| def get_supported_languages(self) -> Dict[str, str]: | |
| """Return all supported languages as {code: name}.""" | |
| return dict(sorted(LANGUAGE_NAMES.items(), key=lambda x: x[1])) | |
| def get_language_count(self) -> int: | |
| """Return the number of supported languages.""" | |
| return len(NLLB_LANGS) | |
| def is_language_supported(self, lang_code: str) -> bool: | |
| """Check if a language is supported.""" | |
| return lang_code in NLLB_LANGS | |