jing-ju commited on
Commit
3d9d9e7
·
verified ·
1 Parent(s): 968a4e7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +185 -37
app.py CHANGED
@@ -1,12 +1,24 @@
1
  import os
 
 
 
 
2
  import gradio as gr
3
  import torch
4
- from transformers import AutoTokenizer, AutoModelForCausalLM, CompressedTensorsQuantizationConfig
 
 
 
 
5
 
6
- # Model thử nghiệm (fp8). Nếu CPU free bị quá chậm/OOM, cân nhắc chuyển sang 7B-fp8.
7
- MODEL_NAME = os.getenv("MODEL_NAME", "tencent/Hunyuan-MT-Chimera-7B-fp8")
 
 
 
 
8
 
9
- # Tham số sinh (giữ thấp để CPU Free đỡ nặng)
10
  GEN_KW = dict(
11
  max_new_tokens=256,
12
  top_k=20,
@@ -16,52 +28,188 @@ GEN_KW = dict(
16
  do_sample=True,
17
  )
18
 
19
- # Tải tokenizer
 
 
 
 
 
 
20
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
21
 
22
- # Ghi đè quantization_config để tránh lỗi 'ignore' NoneType
23
  ctq = CompressedTensorsQuantizationConfig(
24
  quantization_method="fp8",
25
- ignore=[] # <-- chìa khoá tránh lỗi TypeError: 'NoneType' object is not iterable
26
  )
27
 
28
- # Tải model. Trên CPU Space, KHÔNG đặt device_map="auto".
29
- # Cảnh báo "torch_dtype deprecated" có thể bỏ qua; để dtype/torch_dtype="auto".
30
  model = AutoModelForCausalLM.from_pretrained(
31
  MODEL_NAME,
32
  trust_remote_code=True,
33
  quantization_config=ctq,
34
  )
 
35
 
36
- def _chat_translate(prompt: str) -> str:
37
- messages = [{"role": "user", "content": prompt}]
38
- inputs = tokenizer.apply_chat_template(
39
- messages, tokenize=True, add_generation_prompt=False, return_tensors="pt"
40
- )
41
- device = getattr(model, "device", torch.device("cpu"))
42
- outputs = model.generate(inputs.to(device), **GEN_KW)
43
- return tokenizer.decode(outputs[0], skip_special_tokens=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
- def zh_to_vi(text: str) -> str:
46
- return _chat_translate(
47
- "Translate the following segment into Vietnamese, without additional explanation.\n\n" + text
48
- )
 
49
 
50
- def vi_to_zh(text: str) -> str:
51
- return _chat_translate(
52
- "Translate the following segment into Chinese, without additional explanation.\n\n" + text
53
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  with gr.Blocks() as demo:
56
- gr.Markdown("## Hunyuan-MT Chimera-7B-fp8 (Trial on CPU) — ZH ⇄ VI\n⚠️ Bản CPU free: chậm/giới hạn độ dài.")
57
- with gr.Tab("ZH VI"):
58
- inp_zh = gr.Textbox(label="Tiếng Trung", lines=6, placeholder="Nhập đoạn tiếng Trung…")
59
- out_vi = gr.Textbox(label="Tiếng Việt", lines=6)
60
- gr.Button("Dịch ZH→VI").click(zh_to_vi, inputs=inp_zh, outputs=out_vi, api_name="zh_vi")
61
- with gr.Tab("VI → ZH"):
62
- inp_vi = gr.Textbox(label="Tiếng Việt", lines=6, placeholder="Nhập đoạn tiếng Việt…")
63
- out_zh = gr.Textbox(label="Tiếng Trung", lines=6)
64
- gr.Button("Dịch VI→ZH").click(vi_to_zh, inputs=inp_vi, outputs=out_zh, api_name="vi_zh")
65
-
66
- # Hạn chế tải để demo cho ít user
67
- demo.queue(concurrency_count=1, max_size=2).launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
+ import math
3
+ import re
4
+ from typing import List, Optional
5
+
6
  import gradio as gr
7
  import torch
8
+ from transformers import (
9
+ AutoTokenizer,
10
+ AutoModelForCausalLM,
11
+ CompressedTensorsQuantizationConfig,
12
+ )
13
 
14
+ # =========================
15
+ # CẤU HÌNH MẶC ĐỊNH
16
+ # =========================
17
+ # Model mặc định: nhẹ hơn và phù hợp hơn cho CPU Free
18
+ DEFAULT_MODEL = "tencent/Hunyuan-MT-7B-fp8"
19
+ MODEL_NAME = os.getenv("MODEL_NAME", DEFAULT_MODEL)
20
 
21
+ # Tham số sinh gợi ý (giữ thấp để tránh quá tải CPU)
22
  GEN_KW = dict(
23
  max_new_tokens=256,
24
  top_k=20,
 
28
  do_sample=True,
29
  )
30
 
31
+ # Giới hạn token đầu vào mỗi lượt để tránh OOM/timeout trên CPU
32
+ # (tổng input ≲ 900–1000 token trên CPU Free cho an toàn)
33
+ MAX_INPUT_TOKENS = int(os.getenv("MAX_INPUT_TOKENS", "800"))
34
+
35
+ # =========================
36
+ # TẢI MODEL & TOKENIZER
37
+ # =========================
38
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
39
 
40
+ # Ghi đè config lượng tử hóa để tránh lỗi "ignore NoneType" trên một số bản fp8
41
  ctq = CompressedTensorsQuantizationConfig(
42
  quantization_method="fp8",
43
+ ignore=[], # chìa khóa tránh TypeError: 'NoneType' object is not iterable
44
  )
45
 
 
 
46
  model = AutoModelForCausalLM.from_pretrained(
47
  MODEL_NAME,
48
  trust_remote_code=True,
49
  quantization_config=ctq,
50
  )
51
+ DEVICE = getattr(model, "device", torch.device("cpu"))
52
 
53
+ # =========================
54
+ # TIỆN ÍCH CHUẨN HÓA NGÔN NGỮ
55
+ # =========================
56
+ # Map tên ngôn ngữ phổ biến -> tên tiếng Anh để nhúng vào prompt (đơn giản hóa)
57
+ LANG_ALIASES = {
58
+ # Vietnamese
59
+ "vi": "Vietnamese", "vie": "Vietnamese",
60
+ "vietnamese": "Vietnamese", "tiếng việt": "Vietnamese",
61
+ # Chinese
62
+ "zh": "Chinese", "chi": "Chinese", "zho": "Chinese",
63
+ "chinese": "Chinese", "tiếng trung": "Chinese", "hán ngữ": "Chinese",
64
+ "mandarin": "Chinese",
65
+ # English
66
+ "en": "English", "eng": "English", "tiếng anh": "English", "english": "English",
67
+ # Japanese
68
+ "ja": "Japanese", "jpn": "Japanese", "tiếng nhật": "Japanese", "japanese": "Japanese",
69
+ # Korean
70
+ "ko": "Korean", "kor": "Korean", "tiếng hàn": "Korean", "korean": "Korean",
71
+ # French
72
+ "fr": "French", "fra": "French", "fre": "French", "tiếng pháp": "French", "french": "French",
73
+ # German
74
+ "de": "German", "deu": "German", "ger": "German", "tiếng đức": "German", "german": "German",
75
+ # Spanish
76
+ "es": "Spanish", "spa": "Spanish", "tiếng tây ban nha": "Spanish", "spanish": "Spanish",
77
+ # Thai
78
+ "th": "Thai", "tha": "Thai", "tiếng thái": "Thai", "thai": "Thai",
79
+ # Indonesian
80
+ "id": "Indonesian", "ind": "Indonesian", "tiếng indonesia": "Indonesian", "indonesian": "Indonesian",
81
+ # Malay
82
+ "ms": "Malay", "msa": "Malay", "tiếng malaysia": "Malay", "malay": "Malay",
83
+ # Portuguese
84
+ "pt": "Portuguese", "por": "Portuguese", "tiếng bồ đào nha": "Portuguese", "portuguese": "Portuguese",
85
+ # Russian
86
+ "ru": "Russian", "rus": "Russian", "tiếng nga": "Russian", "russian": "Russian",
87
+ }
88
 
89
+ def normalize_lang_name(s: Optional[str]) -> Optional[str]:
90
+ if not s:
91
+ return None
92
+ key = s.strip().lower()
93
+ return LANG_ALIASES.get(key, s.strip())
94
 
95
+ # =========================
96
+ # CHIA ĐOẠN THEO TOKEN
97
+ # =========================
98
+ def chunk_text_by_tokens(text: str, max_tokens: int) -> List[str]:
99
+ """
100
+ Chia văn bản thành các đoạn dựa vào số token của tokenizer để tránh vượt ngưỡng input.
101
+ Ưu tiên cắt theo dấu câu. Nếu đoạn vẫn dài, cắt tiếp theo token.
102
+ """
103
+ # Tách theo các dấu câu lớn trước
104
+ rough_parts = re.split(r"(?<=[\.!?。!?])\s+", text.strip())
105
+ chunks = []
106
+ buf = ""
107
+
108
+ def token_len(s: str) -> int:
109
+ return tokenizer(s, add_special_tokens=False, return_length=True)["length"]
110
+
111
+ for part in rough_parts:
112
+ candidate = (buf + " " + part).strip() if buf else part
113
+ if token_len(candidate) <= max_tokens:
114
+ buf = candidate
115
+ else:
116
+ if buf:
117
+ chunks.append(buf)
118
+ buf = ""
119
+
120
+ # Nếu part tự thân đã quá dài, cắt tiếp theo token
121
+ if token_len(part) <= max_tokens:
122
+ buf = part
123
+ else:
124
+ # Cắt theo token “cứng”
125
+ ids = tokenizer(part, add_special_tokens=False)["input_ids"]
126
+ for i in range(0, len(ids), max_tokens):
127
+ piece_ids = ids[i:i + max_tokens]
128
+ piece = tokenizer.decode(piece_ids, skip_special_tokens=True)
129
+ chunks.append(piece)
130
+ buf = ""
131
+
132
+ if buf:
133
+ chunks.append(buf)
134
+
135
+ # Loại bỏ rỗng
136
+ return [c for c in chunks if c.strip()]
137
+
138
+ # =========================
139
+ # CORE TRANSLATION (SỬ DỤNG CHAT TEMPLATE)
140
+ # =========================
141
+ @torch.inference_mode()
142
+ def translate_text(
143
+ text: str,
144
+ target_lang: str,
145
+ source_lang: Optional[str] = None,
146
+ ) -> str:
147
+ target = normalize_lang_name(target_lang) or "Vietnamese"
148
+ src = normalize_lang_name(source_lang)
149
+
150
+ # Xây prompt: có thể thêm nguồn nếu người dùng cung cấp, còn không để model tự đoán
151
+ if src:
152
+ sys_prompt = f"Translate the following segment from {src} into {target}, without additional explanation."
153
+ else:
154
+ sys_prompt = f"Translate the following segment into {target}, without additional explanation."
155
+
156
+ pieces = chunk_text_by_tokens(text, MAX_INPUT_TOKENS)
157
+ outputs = []
158
+
159
+ for piece in pieces:
160
+ messages = [{"role": "user", "content": f"{sys_prompt}\n\n{piece}"}]
161
+ inputs = tokenizer.apply_chat_template(
162
+ messages, tokenize=True, add_generation_prompt=False, return_tensors="pt"
163
+ )
164
+ out_ids = model.generate(inputs.to(DEVICE), **GEN_KW)
165
+ out_text = tokenizer.decode(out_ids[0], skip_special_tokens=True)
166
+ outputs.append(out_text.strip())
167
+
168
+ return "\n".join(outputs).strip()
169
+
170
+ def translate_batch(
171
+ texts: List[str],
172
+ target_lang: str,
173
+ source_lang: Optional[str] = None,
174
+ ) -> List[str]:
175
+ return [translate_text(t, target_lang, source_lang) for t in texts]
176
+
177
+ # =========================
178
+ # GRADIO UI + API
179
+ # =========================
180
+ LANG_CHOICES = sorted(list(set(LANG_ALIASES.values())))
181
 
182
  with gr.Blocks() as demo:
183
+ gr.Markdown(
184
+ "## Hunyuan-MT (fp8) Multilingual Translation (Trial on CPU)\n"
185
+ "Bản HF Spaces Free (CPU) tốc độ chậm, đã có chia đoạn tự động theo token."
186
+ )
187
+
188
+ with gr.Tab("Single"):
189
+ src = gr.Textbox(label="Văn bản nguồn", lines=10, placeholder="Dán văn bản cần dịch…")
190
+ with gr.Row():
191
+ src_lang = gr.Textbox(label="Ngôn ngữ nguồn (tùy chọn, ví dụ: Vietnamese/Chinese/English…)", placeholder="Để trống nếu không chắc")
192
+ tgt_lang = gr.Dropdown(label="Ngôn ngữ đích", choices=LANG_CHOICES, value="Vietnamese")
193
+ out = gr.Textbox(label="Bản dịch", lines=10)
194
+ btn = gr.Button("Dịch")
195
+ btn.click(fn=translate_text, inputs=[src, tgt_lang, src_lang], outputs=out, api_name="translate_text")
196
+
197
+ with gr.Tab("Batch"):
198
+ src_list = gr.Textbox(
199
+ label="Danh sách câu (mỗi dòng 1 câu/đoạn ngắn)",
200
+ lines=10,
201
+ placeholder="Mỗi dòng là một câu/đoạn…"
202
+ )
203
+ with gr.Row():
204
+ src_lang_b = gr.Textbox(label="Ngôn ngữ nguồn (tuỳ chọn)", placeholder="Để trống nếu không chắc")
205
+ tgt_lang_b = gr.Dropdown(label="Ngôn ngữ đích", choices=LANG_CHOICES, value="Vietnamese")
206
+ out_list = gr.Textbox(label="Kết quả (mỗi dòng tương ứng 1 đầu vào)", lines=10)
207
+ def _batch_wrapper(texts_raw: str, tgt: str, src_: Optional[str]):
208
+ texts = [x for x in texts_raw.splitlines() if x.strip()]
209
+ results = translate_batch(texts, tgt, src_)
210
+ return "\n".join(results)
211
+ btn_b = gr.Button("Dịch Batch")
212
+ btn_b.click(fn=_batch_wrapper, inputs=[src_list, tgt_lang_b, src_lang_b], outputs=out_list, api_name="translate_batch")
213
+
214
+ # Giới hạn tải cho demo
215
+ demo.queue(concurrency_count=1, max_size=2).launch()