Image_Inversion / translator.py
IdlecloudX's picture
Update translator.py
0599754 verified
raw
history blame
6.7 kB
import hashlib, hmac, json, os, random, time
from datetime import datetime
from typing import List, Sequence, Optional
import requests
import threading
APP_TENCENT_SECRET_ID = os.environ.get("TENCENT_SECRET_ID")
APP_TENCENT_SECRET_KEY = os.environ.get("TENCENT_SECRET_KEY")
APP_BAIDU_CREDENTIALS = json.loads(os.environ.get("BAIDU_CREDENTIALS_JSON", "[]"))
APP_ACCESS_KEY = os.environ.get("TRANSLATOR_ACCESS_KEY")
TENCENT_TRANSLATE_URL = os.environ.get("TENCENT_TRANSLATE_URL", "https://tmt.tencentcloudapi.com")
BAIDU_TRANSLATE_URL = os.environ.get("BAIDU_TRANSLATE_URL", "https://fanyi-api.baidu.com/api/trans/vip/translate")
_user_keys = threading.local()
def set_user_provided_keys(tencent_id, tencent_key, baidu_json_str):
_user_keys.tencent_id = tencent_id if tencent_id else None
_user_keys.tencent_key = tencent_key if tencent_key else None
try:
_user_keys.baidu_credentials = json.loads(baidu_json_str) if baidu_json_str else []
except json.JSONDecodeError:
_user_keys.baidu_credentials = []
def clear_user_provided_keys():
if hasattr(_user_keys, 'tencent_id'): del _user_keys.tencent_id
if hasattr(_user_keys, 'tencent_key'): del _user_keys.tencent_key
if hasattr(_user_keys, 'baidu_credentials'): del _user_keys.baidu_credentials
def _get_tencent_credentials():
user_id = getattr(_user_keys, 'tencent_id', None)
user_key = getattr(_user_keys, 'tencent_key', None)
if user_id and user_key:
# print("DEBUG: Using user-provided Tencent keys.")
return user_id, user_key
# print("DEBUG: Using app-level Tencent keys.")
return APP_TENCENT_SECRET_ID, APP_TENCENT_SECRET_KEY
def _get_baidu_credentials():
user_creds = getattr(_user_keys, 'baidu_credentials', [])
if user_creds:
# print("DEBUG: Using user-provided Baidu credentials.")
return user_creds
# print("DEBUG: Using app-level Baidu credentials.")
return APP_BAIDU_CREDENTIALS
_baidu_idx = 0
def _next_baidu_cred():
global _baidu_idx
creds_list = _get_baidu_credentials()
if not creds_list:
return None
cred = creds_list[_baidu_idx]
_baidu_idx = (_baidu_idx + 1) % len(creds_list)
return cred
def _sign(key: bytes, msg: str) -> bytes:
return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()
def _tc3_signature(secret_key: str, date: str, service: str, string_to_sign: str) -> str:
secret_date = _sign(("TC3" + secret_key).encode(), date)
secret_service = _sign(secret_date, service)
secret_signing = _sign(secret_service, "tc3_request")
return hmac.new(secret_signing, string_to_sign.encode("utf-8"), hashlib.sha256).hexdigest()
def _translate_with_tencent(texts: Sequence[str], src="auto", tgt="zh") -> Optional[List[str]]:
secret_id, secret_key = _get_tencent_credentials()
if not (secret_id and secret_key):
return None
service, host, action, version, region = "tmt", "tmt.tencentcloudapi.com", "TextTranslate", "2018-03-21", "ap-beijing"
ts = int(time.time())
date = datetime.utcfromtimestamp(ts).strftime("%Y-%m-%d")
algorithm = "TC3-HMAC-SHA256"
payload = {"SourceText": "\n".join(texts), "Source": src, "Target": tgt, "ProjectId": 0}
payload_str = json.dumps(payload, ensure_ascii=False)
hashed_request_payload = hashlib.sha256(payload_str.encode()).hexdigest()
canonical_headers = f"content-type:application/json; charset=utf-8\nhost:{host}\nx-tc-action:{action.lower()}\n"
signed_headers = "content-type;host;x-tc-action"
canonical_request = f"POST\n/\n\n{canonical_headers}\n{signed_headers}\n{hashed_request_payload}"
credential_scope = f"{date}/{service}/tc3_request"
string_to_sign = f"{algorithm}\n{ts}\n{credential_scope}\n{hashlib.sha256(canonical_request.encode()).hexdigest()}"
signature = _tc3_signature(secret_key, date, service, string_to_sign)
authorization = f"{algorithm} Credential={secret_id}/{credential_scope}, SignedHeaders={signed_headers}, Signature={signature}"
headers = {
"Authorization": authorization, "Content-Type": "application/json; charset=utf-8",
"Host": host, "X-TC-Action": action, "X-TC-Timestamp": str(ts),
"X-TC-Version": version, "X-TC-Region": region,
}
try:
resp = requests.post(TENCENT_TRANSLATE_URL, headers=headers, data=payload_str, timeout=8)
resp.raise_for_status()
data = resp.json()
if "Error" in data.get("Response", {}):
print(f"[translator] Tencent API returned error: {data['Response']['Error']['Message']}")
return None
return data["Response"]["TargetText"].split("\n")
except Exception as e:
print(f"[translator] Tencent API request failed β†’ {e}")
return None
def _translate_with_baidu(texts: Sequence[str], src="auto", tgt="zh") -> Optional[List[str]]:
creds = _next_baidu_cred()
if not creds:
return None
app_id, secret_key = creds["app_id"], creds["secret_key"]
salt = random.randint(32768, 65536)
query = "\n".join(texts)
sign = hashlib.md5((app_id + query + str(salt) + secret_key).encode()).hexdigest()
params = {"q": query, "from": src, "to": tgt, "appid": app_id, "salt": salt, "sign": sign}
try:
resp = requests.get(BAIDU_TRANSLATE_URL, params=params, timeout=8)
resp.raise_for_status()
data = resp.json()
if "error_code" in data:
print(f"[translator] Baidu API returned error: {data['error_msg']} (code: {data['error_code']})")
return None
return [item["dst"] for item in data["trans_result"]]
except Exception as e:
print(f"[translator] Baidu API request failed β†’ {e}")
return None
def translate_texts(texts: Sequence[str],
src_lang: str = "auto",
tgt_lang: str = "zh",
access_key: str = None) -> List[str]:
if not texts:
return []
has_user_tencent = getattr(_user_keys, 'tencent_id', None) and getattr(_user_keys, 'tencent_key', None)
has_user_baidu = bool(getattr(_user_keys, 'baidu_credentials', []))
can_use_app_keys = False
if not (has_user_tencent or has_user_baidu):
if not APP_ACCESS_KEY:
can_use_app_keys = True
elif access_key and access_key == APP_ACCESS_KEY:
can_use_app_keys = True
if has_user_tencent or has_user_baidu or can_use_app_keys:
out = _translate_with_tencent(texts, src_lang, tgt_lang)
if out is None:
out = _translate_with_baidu(texts, src_lang, tgt_lang)
if out is not None:
return out
return list(texts)