Update app.py
Browse files
app.py
CHANGED
|
@@ -5,11 +5,11 @@ import numpy as np
|
|
| 5 |
from transformers import pipeline
|
| 6 |
|
| 7 |
# ---------------------------
|
| 8 |
-
# Translator Model
|
| 9 |
# ---------------------------
|
| 10 |
translator = pipeline("translation", model="Helsinki-NLP/opus-mt-zh-en")
|
| 11 |
|
| 12 |
-
def
|
| 13 |
try:
|
| 14 |
return translator(cn_text)[0]["translation_text"]
|
| 15 |
except:
|
|
@@ -17,7 +17,7 @@ def translate_text(cn_text):
|
|
| 17 |
|
| 18 |
|
| 19 |
# ---------------------------
|
| 20 |
-
# Politeness
|
| 21 |
# ---------------------------
|
| 22 |
POLITENESS_FEATURES = {
|
| 23 |
"modals": ["could", "would", "may", "might"],
|
|
@@ -34,17 +34,17 @@ def extract_politeness_scores(en_text):
|
|
| 34 |
|
| 35 |
for feat, markers in POLITENESS_FEATURES.items():
|
| 36 |
score = sum(text.count(m) for m in markers)
|
| 37 |
-
score = min(score, 5)
|
| 38 |
scores[feat] = score
|
| 39 |
|
| 40 |
return scores
|
| 41 |
|
| 42 |
|
| 43 |
# ---------------------------
|
| 44 |
-
# TIRA Classification
|
| 45 |
# ---------------------------
|
| 46 |
def classify_tira(cn_text, en_text):
|
| 47 |
-
cn = cn_text.
|
| 48 |
en = en_text.lower()
|
| 49 |
|
| 50 |
cn_amplify = ["能否", "是否方便", "请您帮忙", "抽空", "劳驾", "麻烦帮我"]
|
|
@@ -64,13 +64,13 @@ def classify_tira(cn_text, en_text):
|
|
| 64 |
if any(p in cn for p in cn_attenuate) or any(p in en for p in en_attenuate):
|
| 65 |
return "Attenuate(削弱)", "英文更直接 → PDI 下移。"
|
| 66 |
|
| 67 |
-
return "Retain(保留)", "
|
| 68 |
|
| 69 |
|
| 70 |
# ---------------------------
|
| 71 |
-
# Radar Chart Drawing
|
| 72 |
# ---------------------------
|
| 73 |
-
def draw_radar(scores_dict):
|
| 74 |
labels = list(scores_dict.keys())
|
| 75 |
scores = list(scores_dict.values())
|
| 76 |
|
|
@@ -84,37 +84,46 @@ def draw_radar(scores_dict):
|
|
| 84 |
ax.plot(angles, scores, color="#1B3B6F", linewidth=2)
|
| 85 |
|
| 86 |
ax.set_xticks(angles[:-1])
|
| 87 |
-
ax.set_xticklabels([label.capitalize() for label in labels]
|
| 88 |
ax.set_yticks(range(1, 6))
|
| 89 |
-
ax.set_yticklabels([str(i) for i in range(1, 6)], fontsize=8)
|
| 90 |
ax.set_ylim(0, 5)
|
| 91 |
-
ax.grid(True)
|
| 92 |
|
|
|
|
| 93 |
return fig
|
| 94 |
|
| 95 |
|
| 96 |
# ---------------------------
|
| 97 |
-
# Main
|
| 98 |
# ---------------------------
|
| 99 |
-
def full_process(cn_text):
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
scores = extract_politeness_scores(en)
|
| 102 |
tira, analysis = classify_tira(cn_text, en)
|
| 103 |
|
| 104 |
risk = {
|
| 105 |
"Retain": "低风险(关系保持稳定)",
|
| 106 |
-
"Amplify": "
|
| 107 |
-
"Attenuate": "
|
| 108 |
-
"Redirect": "
|
| 109 |
}
|
| 110 |
-
|
| 111 |
-
|
|
|
|
|
|
|
| 112 |
|
| 113 |
return en, scores, tira, analysis, risk_out, radar
|
| 114 |
|
| 115 |
|
| 116 |
# ---------------------------
|
| 117 |
-
#
|
| 118 |
# ---------------------------
|
| 119 |
css = """
|
| 120 |
h1 {text-align:center; color:#1B3B6F;}
|
|
@@ -122,7 +131,47 @@ h1 {text-align:center; color:#1B3B6F;}
|
|
| 122 |
label {font-weight:bold;}
|
| 123 |
"""
|
| 124 |
|
| 125 |
-
with gr.Blocks(css=css, title="TIRA
|
| 126 |
gr.Markdown("""
|
| 127 |
-
# **📘 TIRA
|
| 128 |
-
### *
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
from transformers import pipeline
|
| 6 |
|
| 7 |
# ---------------------------
|
| 8 |
+
# 1. AI Translator Model
|
| 9 |
# ---------------------------
|
| 10 |
translator = pipeline("translation", model="Helsinki-NLP/opus-mt-zh-en")
|
| 11 |
|
| 12 |
+
def translate_ai(cn_text):
|
| 13 |
try:
|
| 14 |
return translator(cn_text)[0]["translation_text"]
|
| 15 |
except:
|
|
|
|
| 17 |
|
| 18 |
|
| 19 |
# ---------------------------
|
| 20 |
+
# 2. Politeness Marker Extraction
|
| 21 |
# ---------------------------
|
| 22 |
POLITENESS_FEATURES = {
|
| 23 |
"modals": ["could", "would", "may", "might"],
|
|
|
|
| 34 |
|
| 35 |
for feat, markers in POLITENESS_FEATURES.items():
|
| 36 |
score = sum(text.count(m) for m in markers)
|
| 37 |
+
score = min(score, 5)
|
| 38 |
scores[feat] = score
|
| 39 |
|
| 40 |
return scores
|
| 41 |
|
| 42 |
|
| 43 |
# ---------------------------
|
| 44 |
+
# 3. TIRA Classification
|
| 45 |
# ---------------------------
|
| 46 |
def classify_tira(cn_text, en_text):
|
| 47 |
+
cn = cn_text.lower()
|
| 48 |
en = en_text.lower()
|
| 49 |
|
| 50 |
cn_amplify = ["能否", "是否方便", "请您帮忙", "抽空", "劳驾", "麻烦帮我"]
|
|
|
|
| 64 |
if any(p in cn for p in cn_attenuate) or any(p in en for p in en_attenuate):
|
| 65 |
return "Attenuate(削弱)", "英文更直接 → PDI 下移。"
|
| 66 |
|
| 67 |
+
return "Retain(保留)", "礼貌构式基本稳定保留。"
|
| 68 |
|
| 69 |
|
| 70 |
# ---------------------------
|
| 71 |
+
# 4. Radar Chart Drawing
|
| 72 |
# ---------------------------
|
| 73 |
+
def draw_radar(scores_dict, title="Politeness Radar"):
|
| 74 |
labels = list(scores_dict.keys())
|
| 75 |
scores = list(scores_dict.values())
|
| 76 |
|
|
|
|
| 84 |
ax.plot(angles, scores, color="#1B3B6F", linewidth=2)
|
| 85 |
|
| 86 |
ax.set_xticks(angles[:-1])
|
| 87 |
+
ax.set_xticklabels([label.capitalize() for label in labels])
|
| 88 |
ax.set_yticks(range(1, 6))
|
|
|
|
| 89 |
ax.set_ylim(0, 5)
|
|
|
|
| 90 |
|
| 91 |
+
ax.set_title(title, fontsize=14, color="#1B3B6F")
|
| 92 |
return fig
|
| 93 |
|
| 94 |
|
| 95 |
# ---------------------------
|
| 96 |
+
# 5. Main Logic (Supports AI & Human)
|
| 97 |
# ---------------------------
|
| 98 |
+
def full_process(cn_text, mode, human_en=None):
|
| 99 |
+
if mode == "AI翻译(自动)":
|
| 100 |
+
en = translate_ai(cn_text)
|
| 101 |
+
else:
|
| 102 |
+
en = human_en
|
| 103 |
+
|
| 104 |
+
# Empty if user didn't enter human translation
|
| 105 |
+
if not en or en.strip() == "":
|
| 106 |
+
en = "No human translation provided."
|
| 107 |
+
|
| 108 |
scores = extract_politeness_scores(en)
|
| 109 |
tira, analysis = classify_tira(cn_text, en)
|
| 110 |
|
| 111 |
risk = {
|
| 112 |
"Retain": "低风险(关系保持稳定)",
|
| 113 |
+
"Amplify": "中风险(更远、更正式)",
|
| 114 |
+
"Attenuate": "中偏高(更直接)",
|
| 115 |
+
"Redirect": "不稳定(文化错位风险)"
|
| 116 |
}
|
| 117 |
+
key = tira.split("(")[0]
|
| 118 |
+
risk_out = risk[key]
|
| 119 |
+
|
| 120 |
+
radar = draw_radar(scores, title=f"Politeness Radar ({mode})")
|
| 121 |
|
| 122 |
return en, scores, tira, analysis, risk_out, radar
|
| 123 |
|
| 124 |
|
| 125 |
# ---------------------------
|
| 126 |
+
# 6. Deep Blue UI
|
| 127 |
# ---------------------------
|
| 128 |
css = """
|
| 129 |
h1 {text-align:center; color:#1B3B6F;}
|
|
|
|
| 131 |
label {font-weight:bold;}
|
| 132 |
"""
|
| 133 |
|
| 134 |
+
with gr.Blocks(css=css, title="TIRA Dual Translation System") as demo:
|
| 135 |
gr.Markdown("""
|
| 136 |
+
# **📘 TIRA Dual Translation Visualizer**
|
| 137 |
+
### *AI Translation vs Human Translation — TIRA & Politeness Radar*
|
| 138 |
+
---
|
| 139 |
+
|
| 140 |
+
你可以选择:
|
| 141 |
+
✔ 使用 AI 自动翻译
|
| 142 |
+
✔ 输入人工英文译文进行对比
|
| 143 |
+
|
| 144 |
+
系统会自动:
|
| 145 |
+
- 抽取礼貌标记
|
| 146 |
+
- 绘制雷达图
|
| 147 |
+
- 判断 TIRA 类型
|
| 148 |
+
- 评估 PDI 风险
|
| 149 |
+
|
| 150 |
+
---
|
| 151 |
+
""")
|
| 152 |
+
|
| 153 |
+
cn_input = gr.Textbox(lines=4, label="✉ 输入中文邮件")
|
| 154 |
+
|
| 155 |
+
mode_select = gr.Radio(
|
| 156 |
+
["AI翻译(自动)", "人工翻译(手动输入)"],
|
| 157 |
+
label="选择翻译模式"
|
| 158 |
+
)
|
| 159 |
+
|
| 160 |
+
human_input = gr.Textbox(lines=2, label="如果选择人工翻译,请输入英文:", placeholder="请在此输入英文译文")
|
| 161 |
+
|
| 162 |
+
btn = gr.Button("🔍 运行分析", variant="primary")
|
| 163 |
+
|
| 164 |
+
en_out = gr.Textbox(label="🌐 英文结果")
|
| 165 |
+
score_out = gr.JSON(label="🔬 ���貌特征分布")
|
| 166 |
+
tira_out = gr.Textbox(label="📌 TIRA 类型")
|
| 167 |
+
analysis_out = gr.Textbox(label="📘 解释")
|
| 168 |
+
risk_out = gr.Textbox(label="⚠ PDI 风险")
|
| 169 |
+
radar_out = gr.Plot(label="📊 礼貌雷达图")
|
| 170 |
+
|
| 171 |
+
btn.click(
|
| 172 |
+
fn=full_process,
|
| 173 |
+
inputs=[cn_input, mode_select, human_input],
|
| 174 |
+
outputs=[en_out, score_out, tira_out, analysis_out, risk_out, radar_out]
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
demo.launch()
|