File size: 6,570 Bytes
f6bff5d
877774e
 
6180cc7
 
d0c90a3
f0b8f19
6180cc7
 
0ca3860
6180cc7
 
 
0ca3860
6180cc7
 
 
 
 
 
 
 
ebb3019
6180cc7
13a1b41
6180cc7
13a1b41
6180cc7
 
ebb3019
792e10f
 
 
 
 
 
 
 
d0c90a3
792e10f
 
 
 
 
 
 
 
 
 
 
 
 
877774e
c63812d
877774e
 
792e10f
ebb3019
dd25581
 
 
 
 
 
 
 
 
 
b2dbf5f
 
 
 
 
 
 
dd25581
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b2dbf5f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dd25581
d0c90a3
 
dd25581
 
 
 
 
af8e5d3
d0c90a3
 
 
 
 
dd25581
 
 
 
 
 
 
d0c90a3
 
 
 
 
 
53091ac
d0c90a3
dd25581
 
 
53091ac
af8e5d3
d0c90a3
53091ac
 
af8e5d3
53091ac
82b5a0a
dd25581
 
53091ac
 
dd25581
d0c90a3
 
82b5a0a
53091ac
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import gradio as gr
import random
from vocab import get_sources, get_words_from_source
from sentences import generate_sentences
from ai_sentence import MODEL_LIST
from quiz import generate_fill_in_blank_exam, check_exam, render_exam_interface

def process_sentence(mode, word, source, num, use_ai, model_name):
    try:
        if mode == '查詢單字':
            if not word:
                return "<p style='color:red;'>❌ 請輸入單字</p>", "未輸入單字"
            words = [word.strip()]
        elif mode == '隨機抽單字':
            num = int(num)
            if num <= 0:
                return "<p style='color:red;'>❌ 抽取數量須大於0</p>", "數量錯誤"
            words_data = get_words_from_source(source)
            words = [w['word'] for w in words_data]
            words = random.sample(words, num)
        else:
            return "<p style='color:red;'>❌ 模式錯誤</p>", "模式選擇異常"

        result_display, status_log = generate_sentences(words, source, use_ai, model_name)

        return result_display, status_log

    except Exception as e:
        return f"<p style='color:red;'>❌ 發生錯誤:{str(e)}</p>", f"錯誤:{str(e)}"

def project_description():
    return """
    # 📖 VocabLine 單字例句工具
    支援單字例句查詢,AI 自動生成句子。適合作為 LINE 單字推播、英文學習輔助工具。
    ## 🔍 核心功能
    - 查詢單字 → 獲取例句
    - 抽取單字 → 批量獲取例句
    - 可選 AI 生成句子(模型:GPT2 / Pythia)
    - 英文小考 → 單字填空選擇題,自動批改計分
    ## 🧑‍💻 技術架構
    - Gradio Blocks + Transformers (Hugging Face)
    - SQLite 句庫管理
    - 支援多單字庫擴展
    ## 📚 資料來源
    - 常用 3000 單字表
    - 英文例句資料庫 (Tatoeba)
    ## 👨‍💻 開發資訊
    - 開發者:余彦志 (大宇 ian)
    - 信箱:[email protected]
    - GitHub:[https://github.com/dayuian](https://github.com/dayuian)
    """

with gr.Blocks(css="""
    #card-group { padding: 15px; border-radius: 12px; background-color: rgba(255, 255, 255, 0.05); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); margin-bottom: 15px; }
    .gradio-container { max-width: 800px; margin: auto; }
""") as demo:
    gr.Markdown(project_description())

    with gr.Tab("單字查詢/生成例句"):
        with gr.Group():
            with gr.Row():
                mode_radio = gr.Radio(
                    ["查詢單字", "隨機抽單字"],
                    label="選擇模式",
                    value="查詢單字",
                    interactive=True
                )

            word_input = gr.Textbox(label="輸入單字", visible=True)
            num_input = gr.Slider(minimum=1, maximum=10, value=5, step=1, label="抽取單字數量", visible=False)
            source_dropdown = gr.Dropdown(
                choices=get_sources(),
                value="common3000",
                label="選擇單字庫"
            )

        with gr.Group(elem_id="card-group"):
            use_ai_checkbox = gr.Checkbox(label="使用 AI 生成句子(較慢,約 30 秒)", elem_id="use-ai-checkbox")

            with gr.Row():
                model_dropdown = gr.Dropdown(
                    choices=MODEL_LIST,
                    value="gpt2",
                    label="選擇 AI 模型",
                    visible=False
                )
                gr.Markdown("🔷 **建議使用 GPT2(表現較佳),Pythia-410m 很爛慎選!**", visible=False)

        ai_warning = gr.Textbox(
            value="⚠️ 使用 AI 生成句子為功能測試,每一個單字的生成過程可能需要 30 秒以上,請耐心等待。",
            visible=False,
            interactive=False,
            label=""
        )

        result_output = gr.HTML(label="結果")
        status_output = gr.Textbox(label="處理狀態", interactive=False)

        with gr.Row():
            generate_button = gr.Button("✨ 生成句子", elem_id="generate-button")

        def switch_mode(mode):
            return gr.update(visible=(mode == '查詢單字')), gr.update(visible=(mode == '隨機抽單字'))

        mode_radio.change(
            switch_mode,
            inputs=[mode_radio],
            outputs=[word_input, num_input]
        )

        generate_button.click(
            process_sentence,
            inputs=[mode_radio, word_input, source_dropdown, num_input, use_ai_checkbox, model_dropdown],
            outputs=[result_output, status_output]
        )

    with gr.Tab("英文小考"):
        model_note = gr.Markdown(visible=True)

        quiz_source_dropdown = gr.Dropdown(
            choices=get_sources(),
            value="common3000",
            label="選擇單字庫"
        )
        quiz_num_slider = gr.Slider(minimum=1, maximum=5, value=2, step=1, label="題目數量")
        quiz_model_dropdown = gr.Dropdown(
            choices=MODEL_LIST,
            value="gpt2",
            label="選擇 AI 模型"
        )

        quiz_generate_button = gr.Button("📄 生成試卷")
        quiz_submit_button = gr.Button("✅ 提交試卷")

        quiz_score_display = gr.HTML()
        quiz_questions_state = gr.State([])

        radios = [gr.Radio(choices=[], label=f"第 {i + 1} 題", visible=False, interactive=True) for i in range(5)]

        def update_model_note(model):
            return f"本功能目前使用 **{model}** 作為語言模型生成題目。"

        quiz_model_dropdown.change(update_model_note, inputs=[quiz_model_dropdown], outputs=[model_note])

        def display_exam(source, num, model):
            questions = generate_fill_in_blank_exam(source, num)
            quiz_questions_state.value = questions

            updates = []
            for i, q in enumerate(questions):
                updates.append(gr.update(choices=q["options"], label=f"第 {i + 1} 題:{q['sentence']}", visible=True))
            for i in range(len(questions), 5):
                updates.append(gr.update(visible=False))

            return updates

        def submit_exam(*user_answers):
            questions = quiz_questions_state.value
            score_html = check_exam(user_answers[:len(questions)], questions)
            return score_html

        quiz_generate_button.click(display_exam, inputs=[quiz_source_dropdown, quiz_num_slider, quiz_model_dropdown], outputs=radios)
        quiz_submit_button.click(submit_exam, inputs=radios, outputs=quiz_score_display)

    demo.launch()