File size: 4,084 Bytes
bdc8662
 
 
 
 
 
 
 
 
 
 
 
832430f
bdc8662
 
 
 
 
 
 
b71b0e6
 
 
bdc8662
 
 
 
 
b71b0e6
 
 
bdc8662
 
 
 
 
 
 
 
 
 
 
 
 
 
832430f
bdc8662
 
832430f
 
bdc8662
 
 
 
 
 
 
 
 
 
 
 
832430f
 
 
bdc8662
 
832430f
bdc8662
 
 
 
 
 
 
 
 
 
 
 
 
 
832430f
bdc8662
 
832430f
 
bdc8662
 
 
 
 
 
 
 
 
 
 
 
 
832430f
 
 
bdc8662
 
832430f
bdc8662
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
"""
Translation layer — translates between the user's language and English.

The main LLM always receives and produces English.
This module handles conversion in both directions so the UI shows the user's language.

Formatting rules:
    Text inside (parentheses) and *asterisks* is roleplay markup — it must be
    preserved as-is so the parser can still identify actions and thoughts.
"""

import logging
import time
from huggingface_hub import InferenceClient

logger = logging.getLogger(__name__)

_TO_EN = (
    "You are a professional translator. "
    "Translate the following text to English. "
    "Translate ALL content, including text inside parentheses ( ) and asterisks *. "
    "Keep the parentheses ( ) and asterisks * markers exactly in place — do not remove or add them. "
    "Return ONLY the translated text, no explanations or extra text."
)

_FROM_EN = (
    "You are a professional translator. "
    "Translate the following text to {language}. "
    "Translate ALL content, including text inside parentheses ( ) and asterisks *. "
    "Keep the parentheses ( ) and asterisks * markers exactly in place — do not remove or add them. "
    "Return ONLY the translated text, no explanations or extra text."
)


def translate_to_english(
    text:            str,
    source_language: str,
    client:          InferenceClient,
    model:           str,
) -> str:
    """
    Translate `text` from `source_language` to English.
    Returns the original text unchanged if already English or on any failure.
    """
    if not text.strip() or source_language.strip().lower() == "english":
        logger.debug("[Translator] to_english skipped (already English or empty).")
        return text

    logger.info(f"[Translator] ▶ {source_language}→EN | model='{model}' | {len(text)} chars")
    start = time.time()
    try:
        response = client.chat_completion(
            messages=[
                {"role": "system", "content": _TO_EN},
                {"role": "user",   "content": text},
            ],
            model=model,
            max_tokens=1024,
            stream=False,
            temperature=0.1,
        )
        result = response.choices[0].message.content.strip()
        elapsed = time.time() - start
        logger.info(f"[Translator] ✓ {source_language}→EN | {elapsed:.1f}s | {len(result)} chars")
        logger.debug(f"[Translator] {source_language}→EN: {text[:80]!r} → {result[:80]!r}")
        return result
    except Exception as e:
        logger.error(f"[Translator] to_english FAILED | model='{model}' | error: {e}", exc_info=True)
        return text


def translate_from_english(
    text:            str,
    target_language: str,
    client:          InferenceClient,
    model:           str,
) -> str:
    """
    Translate `text` from English to `target_language`.
    Returns the original text unchanged if target is English or on any failure.
    """
    if not text.strip() or target_language.strip().lower() == "english":
        logger.debug("[Translator] from_english skipped (target is English or empty).")
        return text

    logger.info(f"[Translator] ▶ EN→{target_language} | model='{model}' | {len(text)} chars")
    start = time.time()
    try:
        system = _FROM_EN.format(language=target_language)
        response = client.chat_completion(
            messages=[
                {"role": "system", "content": system},
                {"role": "user",   "content": text},
            ],
            model=model,
            max_tokens=1024,
            stream=False,
            temperature=0.1,
        )
        result = response.choices[0].message.content.strip()
        elapsed = time.time() - start
        logger.info(f"[Translator] ✓ EN→{target_language} | {elapsed:.1f}s | {len(result)} chars")
        logger.debug(f"[Translator] EN→{target_language}: {text[:80]!r} → {result[:80]!r}")
        return result
    except Exception as e:
        logger.error(f"[Translator] from_english FAILED | model='{model}' | error: {e}", exc_info=True)
        return text