File size: 3,484 Bytes
207cfe1
1056a3c
969fbb5
 
1056a3c
969fbb5
 
 
 
 
 
1056a3c
 
 
93ed77f
 
1056a3c
969fbb5
 
1056a3c
969fbb5
1056a3c
969fbb5
1056a3c
 
 
 
 
 
 
969fbb5
1056a3c
 
11f6802
969fbb5
11f6802
1056a3c
 
 
 
 
 
11f6802
969fbb5
1056a3c
11d802d
1056a3c
 
 
969fbb5
 
1056a3c
 
969fbb5
1056a3c
 
 
969fbb5
1056a3c
 
969fbb5
1056a3c
969fbb5
1056a3c
 
 
 
 
 
 
 
207cfe1
1056a3c
 
 
207cfe1
 
1056a3c
 
207cfe1
1056a3c
 
 
 
969fbb5
 
1056a3c
 
 
 
969fbb5
6052894
93ed77f
59ae941
1056a3c
 
 
 
969fbb5
6052894
1056a3c
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
import os
import gradio as gr
import requests

API_KEY = os.environ.get("SILICONFLOW_API_KEY", "")

ASR_URL = "https://api.siliconflow.cn/v1/audio/transcriptions"
CHAT_URL = "https://api.siliconflow.cn/v1/chat/completions"
ASR_MODEL = "FunAudioLLM/SenseVoiceSmall"
MT_MODEL = "tencent/Hunyuan-MT-7B"

LANG_MAP = {
    "英语 → 中文": {"src": "English", "tgt": "Chinese", "src_tag": "EN", "tgt_tag": "中"},
    "汉语 → 英文": {"src": "Chinese", "tgt": "English", "src_tag": "中", "tgt_tag": "EN"},
    "中文 → 法语": {"src": "Chinese", "tgt": "French", "src_tag": "中", "tgt_tag": "FR"},
    "法语 → 中文": {"src": "French", "tgt": "Chinese", "src_tag": "FR", "tgt_tag": "中"},
}


def transcribe(audio_path):
    with open(audio_path, "rb") as f:
        files = {"file": (os.path.basename(audio_path), f, "audio/wav")}
        data = {"model": ASR_MODEL}
        headers = {"Authorization": "Bearer " + API_KEY}
        r = requests.post(ASR_URL, headers=headers, data=data, files=files, timeout=60)
    r.raise_for_status()
    text = r.json().get("text", "").strip()
    text = text.replace("<|", "").replace("|>", "")  # 简单清理特殊标记
    return text


def translate(text, src, tgt):
    headers = {"Authorization": "Bearer " + API_KEY, "Content-Type": "application/json"}
    payload = {
        "model": MT_MODEL,
        "messages": [
            {
                "role": "system",
                "content": f"You are a professional translator. Translate the user text from {src} to {tgt}. "
                           f"Output ONLY the translated text, no explanations, no quotes, no pinyin.",
            },
            {"role": "user", "content": text},
        ],
        "temperature": 0.3,
        "max_tokens": 512,
    }
    r = requests.post(CHAT_URL, headers=headers, json=payload, timeout=60)
    r.raise_for_status()
    return r.json()["choices"][0]["message"]["content"].strip()


def process(audio_path, mode_label, history):
    history = history or []

    if not API_KEY:
        history.append({"role": "assistant", "content": "⚠️ 服务器未配置 SILICONFLOW_API_KEY,请检查 Space 的 Settings -> Repository secrets"})
        return history, None

    if audio_path is None:
        return history, None

    cfg = LANG_MAP[mode_label]

    try:
        text = transcribe(audio_path)
        if not text:
            return history, None
        trans = translate(text, cfg["src"], cfg["tgt"])
    except Exception as e:
        history.append({"role": "assistant", "content": f"⚠️ 出错了:{e}"})
        return history, None

    history.append({"role": "user", "content": f"[{cfg['src_tag']}] {text}"})
    history.append({"role": "assistant", "content": f"[{cfg['tgt_tag']}] {trans}"})
    return history, None


with gr.Blocks(title="传声筒") as demo:
    gr.Markdown("## 🎙️ 传声筒 · 实时语音传译\n点麦克风开始录音,说一句完整的话,再点一次停止,自动识别+翻译。")

    mode = gr.Radio(
        choices=list(LANG_MAP.keys()),
        value="英语 → 中文",
        label="翻译方向",
    )

    audio_in = gr.Audio(
        sources=["microphone"],
        type="filepath",
        label="点击录音,再点一次停止",
    )

    chatbot = gr.Chatbot(label="传译记录", height=420)

    audio_in.stop_recording(
        fn=process,
        inputs=[audio_in, mode, chatbot],
        outputs=[chatbot, audio_in],
    )

demo.launch()