File size: 7,788 Bytes
c8fdedf
58978b9
e3bfb15
00605bc
b90df65
635f86b
 
16e04ca
e3bfb15
 
16e04ca
 
c8fdedf
3f9e47f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b90df65
 
 
 
 
 
 
 
 
bc52e2a
ea01f67
00605bc
8109133
bc52e2a
1f9274c
acdf0a8
ea01f67
61cea89
77a2db1
61cea89
 
8109133
6deb073
 
 
 
 
00605bc
 
 
 
 
8109133
00605bc
b90df65
 
00605bc
3f9e47f
c17ed50
8109133
b90df65
 
bc52e2a
8109133
523f3ec
ea01f67
261c7e8
ea01f67
261c7e8
f4819db
c8fdedf
 
ea01f67
e3bfb15
d4dc5f0
71cee5c
ea01f67
77a2db1
f2bc1ef
 
 
 
cb56cc7
 
f2bc1ef
 
 
d4dc5f0
52d5ca6
edeee5f
58978b9
830e7f1
82ec76e
58978b9
 
3dae297
a2e3e4d
8d75709
656113e
d292fe4
8d56c8c
759d6ff
0de648e
 
 
 
 
8d56c8c
0de648e
 
 
ea01f67
0de648e
 
536f8a9
 
 
 
8d56c8c
eee5af3
cc390c1
b052980
536f8a9
 
 
 
 
ea01f67
 
536f8a9
 
 
 
 
 
 
 
 
 
 
 
 
e9a7746
eee5af3
0de648e
 
8d56c8c
 
eee5af3
50c9cb2
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
167
168
169
170
171
172
173
174
import gradio as gr
import json
import os
from openai import OpenAI
import re
from azure.cognitiveservices.speech import SpeechConfig, SpeechSynthesizer, AudioConfig


PASSWORD = os.environ['PASSWORD']
OPEN_AI_KEY = os.environ['OPEN_AI_KEY']
AZURE_REGION = os.environ['AZURE_REGION']
AZURE_API_KEY = os.environ['AZURE_API_KEY']

def validate_and_correct_chat(data, roles=["A", "B"], rounds=2):
    """
    Corrects the chat data to ensure proper roles and number of rounds.

    Parameters:
    - data (list): The chat data list of dicts, e.g. [{"role": "A", "content": "Hi"}, ...]
    - roles (list): The expected roles, default is ["A", "B"]
    - rounds (int): The number of rounds expected

    Returns:
    - list: Corrected chat data
    """
    
    # Validate role names
    for item in data:
        if item['role'] not in roles:
            print(f"Invalid role '{item['role']}' detected. Correcting it.")
            # We will change the role to the next expected role in the sequence.
            prev_index = roles.index(data[data.index(item) - 1]['role'])
            next_index = (prev_index + 1) % len(roles)
            item['role'] = roles[next_index]

    # Validate number of rounds
    expected_entries = rounds * len(roles)
    if len(data) > expected_entries:
        print(f"Too many rounds detected. Trimming the chat to {rounds} rounds.")
        data = data[:expected_entries]

    return data

def extract_json_from_response(response_text):
    # 使用正則表達式匹配 JSON 格式的對話
    match = re.search(r'\[\s*\{.*?\}\s*\]', response_text, re.DOTALL)
    if match:
        json_str = match.group(0)
        return json.loads(json_str)
    else:
        raise ValueError("JSON dialogue not found in the response.")


def create_chat_dialogue(rounds, role1, role1_gender, role2, role2_gender, theme, language, cefr_level):
    client = OpenAI(api_key=OPEN_AI_KEY)

    # 初始化對話
    sentenses_count = int(rounds) * 2
    sys_content = f"你是一個{language}家教,請用{language}生成對話"
    prompt = f"您將進行一場以{theme}為主題的對話,請用 cefr_level:{cefr_level} 為對話的程度。{role1} (gender: {role1_gender}) 和{role2} (gender: {role2_gender})將是參與者。請依次交談{rounds}輪。(1輪對話的定義是 {role1}{role2} 各說一句話,總共 {sentenses_count} 句話。)以json格式儲存對話。並回傳對話JSON文件。格式為:[{{role:\"{role1}\", \"gender\": {role1_gender} , content: \".....\"}}, {{role:\"{role2}\", \"gender\": {role2_gender}, content: \".....\"}}]"
    messages = [
        {"role": "system", "content": sys_content},
        {"role": "user", "content": prompt}
    ]

    print("=====messages=====")
    print(messages)
    print("=====messages=====")


    request_payload = {
        "model": "gpt-4-1106-preview",
        "messages": messages,
        "max_tokens": int(500 * int(rounds))  # 設定一個較大的值,可根據需要調整
    }

    response = client.chat.completions.create(**request_payload)
    print(response)
    
    response_text = response.choices[0].message.content.strip()
    extract_json = extract_json_from_response(response_text)
    dialogue = validate_and_correct_chat(data=extract_json, roles=[role1, role2], rounds=rounds)

    print(dialogue)
    # 這裡直接返回JSON格式的對話,但考慮到這可能只是一個字符串,您可能還需要將它解析為一個Python對象
    return dialogue


def generate_dialogue(rounds, method, role1, role1_gender, role2, role2_gender, theme, language, cefr_level):
    if method == "auto":
        dialogue = create_chat_dialogue(rounds, role1, role1_gender, role2, role2_gender, theme, language, cefr_level)
    else:
        dialogue = [{"role": role1, "gender": role1_gender, "content": "手動輸入文本 1"}, {"role": role2, "gender": role2_gender , "content": "手動輸入文本 2"}]
    return dialogue

def main_function(password: str, theme: str, language: str, cefr_level: str, method: str, rounds: int, role1: str, role1_gender: str, role2: str, role2_gender: str):
    if password != os.environ.get("PASSWORD", ""):
        return "错误的密码,请重新输入。", ""

    structured_dialogue = generate_dialogue(rounds, method, role1, role1_gender, role2, role2_gender, theme, language, cefr_level)

    # Convert structured dialogue for Chatbot component to show "role1: content1" and "role2: content2" side by side
    chatbot_dialogue = []
    for i in range(0, len(structured_dialogue), 2):  # We iterate with a step of 2 to take pairs
        # Get the content for the two roles in the pair
        role1_content = f"{structured_dialogue[i]['content']}"
        role2_content = f"{structured_dialogue[i+1]['content']}" if i+1 < len(structured_dialogue) else ""
        
        chatbot_dialogue.append((role1_content, role2_content))

    # audio_path = dialogue_to_audio(structured_dialogue, role1_gender, role2_gender)
    json_output = json.dumps({"dialogue": structured_dialogue}, ensure_ascii=False, indent=4)

    # 儲存對話為 JSON 文件
    file_name = "dialogue_output.txt"
    with open(file_name, "w", encoding="utf-8") as f:
        f.write(json_output)

    return chatbot_dialogue, file_name, json_output


if __name__ == "__main__":
    with gr.Blocks(theme=gr.themes.Soft()) as demo:  # 使用 'light' 主题作为默认值
        # Header 或其他组件可以在这里添加,如果有需要
        with gr.Row():
            with gr.Column(scale=2):  # 2/3 的宽度
                chat_output = gr.Chatbot(label="生成的對話")
                json_file = gr.File(label="下載對話 JSON 文件")
                json_textbox = gr.Textbox(readonly=True, label="對話 JSON 內容", lines=10)

            with gr.Column(scale=1):  # 1/3 的宽度
                password = gr.Textbox(label="输入密码", type="password")
                theme = gr.Textbox(label="對話主題")  # 加入 theme 的輸入框,設定預設值為 '購物'
                language = gr.Dropdown(choices=["中文", "英文"], label="語言")
                cefr_level = gr.Dropdown(choices=["A1", "A2", "B1", "B2", "C1", "C2"], label="CEFR Level")
                generation_mode = gr.Dropdown(choices=["auto", "manual"], label="生成方式")
                rounds = gr.Slider(minimum=2, maximum=6, step=2, label="對話輪數")
                role1_name = gr.Textbox(label="角色 1 名稱")
                role1_gender = gr.Dropdown(choices=["male", "female"], label="角色 1 性別")
                role2_name = gr.Textbox(label="角色 2 名稱")
                role2_gender = gr.Dropdown(choices=["male", "female"], label="角色 2 性別")

                # 在这里添加提交和清除按鈕
                submit_button = gr.Button("Submit")
                clear_button = gr.Button("Clear")
                submit_button.click(
                    main_function, 
                    [
                        password, 
                        theme, 
                        language,
                        cefr_level, 
                        generation_mode, 
                        rounds, 
                        role1_name, 
                        role1_gender, 
                        role2_name, 
                        role2_gender
                    ], 
                    [
                        chat_output, 
                        json_file, 
                        json_textbox
                    ]
                )
                clear_button.click(lambda: [[],None,""], None, [chat_output, json_file, json_textbox], queue=False)


            
    # 可以添加其他交互逻辑和按钮事件,如果有需要

    demo.launch(inline=False, share=True)