File size: 3,659 Bytes
dc13b6d
 
f358820
dc13b6d
 
 
da40aec
dc13b6d
cc1322f
dc13b6d
 
c97ac97
dc13b6d
 
 
42dc39c
 
 
dc13b6d
 
 
 
 
5799add
dc13b6d
 
 
 
 
da40aec
44ac7e8
da40aec
 
 
 
 
 
 
 
 
8722708
72f7051
 
 
44ac7e8
91852e0
dc13b6d
 
91852e0
f4fd3fb
44ac7e8
dc13b6d
 
44ac7e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
da40aec
1593e08
44ac7e8
da40aec
dc13b6d
44ac7e8
0fd90d3
44ac7e8
 
0fd90d3
 
44ac7e8
7e8e5b2
44ac7e8
 
 
 
dc13b6d
44ac7e8
 
da40aec
44ac7e8
da40aec
 
44ac7e8
da40aec
 
44ac7e8
da40aec
dc13b6d
0fd90d3
dc13b6d
 
 
 
 
 
 
 
 
 
 
 
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import spaces
import gradio as gr
from gradio import update
from functools import lru_cache
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline

# 可選模型列表
MODEL_LIST = [
    "unsloth/gemma-3-1b-pt",
    "ckiplab/gpt2-tiny-chinese",
    "ckiplab/gpt2-base-chinese",
    "liswei/Taiwan-ELM-270M",
    "liswei/Taiwan-ELM-1_1B",
    "benchang1110/Qwen2.5-Taiwan-1.5B-Instruct",
    "benchang1110/Taiwan-tinyllama-v1.0-base",
    "lianghsun/Llama-3.2-Taiwan-3B",
    "twinkle-ai/Llama-3.2-3B-F1-Instruct",
    "Epiculous/Violet_Twilight-v0.2",
]

@lru_cache(maxsize=None)
def get_pipeline(model_name):
    tok = AutoTokenizer.from_pretrained(model_name)
    mdl = AutoModelForCausalLM.from_pretrained(model_name, weights_only=False, trust_remote_code=True)
    mdl.to("cuda")
    return pipeline("text-generation", model=mdl, tokenizer=tok, device=0)

@spaces.GPU
def suggest_next(text, model_name, k, m):
    """
    使用 Beam Search 產生 M 條最可能的下段建議,並一次更新候選列表。
    """
    gen_pipe = get_pipeline(model_name)
    outs = gen_pipe(
        text,
        max_new_tokens=k,
        num_beams=m,
        num_return_sequences=m,
        do_sample=False,
        early_stopping=True
    )
    suggestions = [out["generated_text"][len(text):].strip() for out in outs]
    suggestions = [s for s in suggestions if s]

    # 更新候選條
    return update(choices=suggestions, value=None)

def append_suggestion(current, choice):
    if choice is None:
        return current
    # 模擬輸入法候選選中
    return current + choice

# 自定義 CSS:模擬經典中文輸入法候選欄樣式
custom_css = """
#suggestions-bar .candidate-list {
    display: flex;
    gap: 12px;
    background: #ffffff;
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 6px;
}
#suggestions-bar .candidate-list input[type=radio] {
    display: none;
}
#suggestions-bar .candidate-list label {
    cursor: pointer;
    padding: 2px 6px;
    border-radius: 4px;
}
#suggestions-bar .candidate-list label:hover {
    background: #f0f0f0;
}
#suggestions-bar .candidate-list input[type=radio]:checked + label {
    background: #e0e0e0;
    border: 1px solid #888;
}
"""

with gr.Blocks(css=custom_css) as demo:
    # 標題和說明
    gr.Markdown(
        "## 🇹🇼 台灣中文輸入法加速器  \n"
        "結合小型語言模型與 ZeroGPU,即時 IME 風格候選條。"
    )

    # 經典候選欄:水平排列
    suggestions = gr.Radio(
        [], label="", interactive=True, type="value",
        elem_id="suggestions-bar", elem_classes="candidate-list"
    )

    # 輸入區與按鈕:單行輸入框 + 小按鈕
    with gr.Row():
        input_text = gr.Textbox(
            label="", placeholder="請輸入拼音或文字…", lines=1, max_lines=1
        )
        gpu_button = gr.Button("建議")

    # 進階參數設定(可折疊)
    with gr.Accordion("進階設定", open=False):
        model_selector = gr.Dropdown(
            MODEL_LIST, value=MODEL_LIST[0], label="模型"
        )
        k_slider = gr.Slider(
            minimum=1, maximum=50, step=1, value=1, label="K(最大新詞元數)"
        )
        m_slider = gr.Slider(
            minimum=1, maximum=30, step=1, value=10, label="M(建議數/Beam 數)"
        )

    # 事件綁定
    gpu_button.click(
        fn=suggest_next,
        inputs=[input_text, model_selector, k_slider, m_slider],
        outputs=suggestions,
    )
    suggestions.change(
        fn=append_suggestion,
        inputs=[input_text, suggestions],
        outputs=input_text,
    )

    demo.launch()