nekoniii3 commited on
Commit
2b1ef0b
·
1 Parent(s): 9b1d087
Files changed (2) hide show
  1. app.py +507 -0
  2. requirements.txt +2 -0
app.py ADDED
@@ -0,0 +1,507 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import time
4
+ import datetime
5
+ from zoneinfo import ZoneInfo
6
+ from openai import (
7
+ OpenAI, AuthenticationError, NotFoundError, BadRequestError
8
+ )
9
+
10
+ # 各種設定値
11
+ MAX_TRIAL = int(os.environ["MAX_TRIAL"]) # メッセージ取得最大試行数
12
+ INTER_SEC = int(os.environ["INTER_SEC"]) # 試行間隔(秒)
13
+
14
+ # アシスタント用設定
15
+ DF_MODEL = "gpt-3.5-turbo-0125"
16
+ ASSIST_NAME_JA = "Japanese Assistant"
17
+ ASSIST_NAME_ET = "English Teacher Assistant"
18
+ AST_SYS_PROMPT_JA = "あなたは日本人の優秀なアシスタントです。質問は日本語で回答して下さい。"
19
+ AST_SYS_PROMPT_EN = "You are an English teacher. Please be sure to answer in English."
20
+
21
+ # sys_prompt_default = "あなたは優秀なアシスタントです。日本語で質問に回答してください。"
22
+ # lang_code = {'Japanese': "ja", 'English': "en"}
23
+ auto_play_bl = {'ON': True, 'OFF': False}
24
+ voice_list = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"]
25
+
26
+ def set_state(state, openai_key, voice, auto_play, speed):
27
+
28
+ state["openai_key"] = openai_key
29
+ state["voice"] = voice
30
+ state["auto_play"] = auto_play_bl[auto_play]
31
+ state["speed"] = speed
32
+
33
+ return state
34
+
35
+
36
+ def add_history(history, text_msg):
37
+ """ Chat履歴"history"に追加を行う関数 """
38
+
39
+ print(text_msg)
40
+ print(history)
41
+
42
+
43
+ # ユーザテキストをチャットに追加
44
+ history = history + [(text_msg, None)]
45
+
46
+ # テキスト・オーディオ初期化
47
+ return history, gr.Textbox(value="", interactive=False), gr.components.Audio(value=None, interactive=False)
48
+
49
+
50
+ def init(state, mode, text_msg, voice_msg):
51
+ """ 初期処理(入力チェック・テキスト変換) """
52
+
53
+ err_msg = ""
54
+ text = ""
55
+
56
+ if state["openai_key"] == "":
57
+
58
+ # OpenAI API Key未入力
59
+ err_msg = "OpenAI API Keyを入力してください。(設定タブ)"
60
+
61
+ return state, "", err_msg
62
+
63
+ if text_msg == "" and voice_msg is None:
64
+
65
+ # 何も入力がないならエラーメッセージを返す
66
+ err_msg = "テキストまたは音声を入力してください。"
67
+
68
+ return state, "", err_msg
69
+
70
+ try:
71
+
72
+ if state["client"] is None:
73
+
74
+ # 初回起動時は初期処理をする
75
+ # os.environ["OPENAI_API_KEY"] = os.environ["TEST_OPENAI_KEY"] # テスト時
76
+ os.environ["OPENAI_API_KEY"] = state["openai_key"]
77
+
78
+ # クライアント新規作成
79
+ client = OpenAI()
80
+
81
+ # client作成後は消す
82
+ os.environ["OPENAI_API_KEY"] = ""
83
+
84
+ # セッションにセット
85
+ state["client"] = client
86
+
87
+ # IDとして現在時刻をセット
88
+ dt = datetime.datetime.now(ZoneInfo("Asia/Tokyo"))
89
+ state["user_id"] = dt.strftime("%Y%m%d%H%M%S")
90
+
91
+ else:
92
+
93
+ # 既存のクライアントをセット
94
+ client = state["client"]
95
+
96
+ # モードが翻訳以外はアシスタント・スレッドセット
97
+ if mode != 2:
98
+
99
+ if state["thread_id"] == "":
100
+
101
+ # スレッド作成・セット
102
+ thread = client.beta.threads.create()
103
+
104
+ state["thread_id"] = thread.id
105
+
106
+ if state["assistant_id"] == "":
107
+
108
+ if mode == 0:
109
+
110
+ # 日本語アシスタントをセット
111
+ assistant_id = get_assist_id(client, ASSIST_NAME_JA)
112
+ # state["assistant_id"] = os.environ["ASSIST_JA"]
113
+
114
+ if assistant_id == "":
115
+
116
+ # アシスタント新規作成
117
+ assistant = client.beta.assistants.create(
118
+ name=ASSIST_NAME_JA,
119
+ instructions=AST_SYS_PROMPT_JA,
120
+ model=DF_MODEL,
121
+ )
122
+
123
+ assistant_id = assistant.id
124
+
125
+ elif mode == 1:
126
+
127
+ # 英語教師アシスタントをセット
128
+ assistant_id = get_assist_id(client, ASSIST_NAME_ET)
129
+
130
+ if assistant_id == "":
131
+
132
+ # アシスタント新規作成
133
+ assistant = client.beta.assistants.create(
134
+ name=ASSIST_NAME_ET,
135
+ instructions=AST_SYS_PROMPT_EN,
136
+ model=DF_MODEL,
137
+ )
138
+
139
+ assistant_id = assistant.id
140
+
141
+ state["assistant_id"] = assistant_id
142
+
143
+ # ユーザIDでフォルダ作成
144
+ os.makedirs(state["user_id"], exist_ok=True)
145
+
146
+ if voice_msg is None:
147
+
148
+ # 音声がないならテキストを返す
149
+ text = text_msg
150
+
151
+ else:
152
+
153
+ # 音声があるならwhisperでテキストに変換
154
+ text = exec_transcript(client, voice_msg)
155
+ pass
156
+
157
+ except NotFoundError as e:
158
+ err_msg = "アシスタントIDが間違っています。新しく作成する場合はアシスタントIDを空欄にして下さい。"
159
+ print(e)
160
+ except AuthenticationError as e:
161
+ err_msg = "認証エラーとなりました。OpenAPIKeyが正しいか、支払い方法などが設定されているか確認して下さい。"
162
+ print(e)
163
+ except Exception as e:
164
+ err_msg = "その他のエラーが発生しました。"
165
+ print(e)
166
+ finally:
167
+ return state, text, err_msg
168
+
169
+ def get_assist_id(client, assist_name):
170
+
171
+ assist_id = ""
172
+
173
+ assist_list = client.beta.assistants.list()
174
+
175
+ if len(assist_list.data) == 0:
176
+
177
+ return assist_id
178
+
179
+ for assist in assist_list:
180
+
181
+ if assist.name == assist_name:
182
+
183
+ assist_id = assist.id
184
+
185
+ break
186
+
187
+ return assist_id
188
+
189
+ def raise_exception(err_msg):
190
+ """ エラーの場合例外を起こす関数 """
191
+
192
+ if err_msg != "":
193
+ raise Exception()
194
+
195
+ return
196
+
197
+
198
+ def exec_transcript(client, voice_msg):
199
+ """ whisperで文字に起こす関数 """
200
+
201
+ audio_file= open(voice_msg, "rb")
202
+
203
+ transcript = client.audio.transcriptions.create(
204
+ model="whisper-1",
205
+ file=audio_file,
206
+ language = "ja",
207
+ response_format="text"
208
+ )
209
+
210
+ return transcript
211
+
212
+
213
+ def exec_translation(client, text):
214
+ """ GPTで英語に翻訳する関数 """
215
+
216
+ response = client.chat.completions.create(
217
+ model="gpt-3.5-turbo-1106",
218
+ messages=[
219
+ {"role": "system", "content": "You are a translator. Please translate the Japanese you received into English."},
220
+ {"role": "user", "content": text},
221
+ ]
222
+ )
223
+
224
+ return response.choices[0].message.content
225
+
226
+
227
+ def exec_text_to_speech(client, voice , speed, folder, text):
228
+ """ テキストを音声にする """
229
+
230
+ response = client.audio.speech.create(
231
+ model= "tts-1", # "tts-1-hd",
232
+ voice=voice,
233
+ input=text,
234
+ speed=speed
235
+ )
236
+
237
+ # ファイル名は現在時刻
238
+ dt = datetime.datetime.now(ZoneInfo("Asia/Tokyo"))
239
+ file_name = dt.strftime("%Y%m%d%H%M%S") + ".mp3"
240
+ file_path = folder + "/" + file_name
241
+
242
+ # 音声ファイルに出力
243
+ response.stream_to_file(file_path)
244
+
245
+ return file_path
246
+
247
+
248
+ def bot(state, mode, history):
249
+ """ Chat返答処理 """
250
+
251
+ err_msg = ""
252
+ file_path = None
253
+
254
+ # セッション情報取得
255
+ client = state["client"]
256
+ asist_id = state["assistant_id"]
257
+ thread_id = state["thread_id"]
258
+ last_msg_id = state["last_msg_id"]
259
+ user_id = state["user_id"]
260
+ voice = state["voice"]
261
+ speed = state["speed"]
262
+
263
+ # 最新のユーザからのメッセージ
264
+ user_msg = history[-1][0]
265
+
266
+ print(history)
267
+ print(user_msg)
268
+
269
+ if mode in (0, 1): # 日本語会話モードの場合
270
+
271
+ # メッセージ作成
272
+ message = client.beta.threads.messages.create(
273
+ thread_id=thread_id,
274
+ role="user",
275
+ content=user_msg
276
+ )
277
+
278
+ # RUNスタート
279
+ run = client.beta.threads.runs.create(
280
+ thread_id=thread_id,
281
+ assistant_id=asist_id
282
+ )
283
+
284
+ # "completed"となるまで繰り返す(指定秒おき)
285
+ for i in range(0, MAX_TRIAL, 1):
286
+
287
+ if i > 0:
288
+ time.sleep(INTER_SEC)
289
+
290
+ # メッセージ受け取り
291
+ run = client.beta.threads.runs.retrieve(
292
+ thread_id=thread_id,
293
+ run_id=run.id
294
+ )
295
+
296
+ # 前回のメッセージより後を昇順で取り出す
297
+ messages = client.beta.threads.messages.list(
298
+ thread_id=thread_id,
299
+ after=last_msg_id,
300
+ order="asc"
301
+ )
302
+
303
+ # messageを取り出す
304
+ for msg in messages:
305
+
306
+ if msg.role == "assistant":
307
+
308
+ assist_msg = msg.content[0].text.value
309
+
310
+ if assist_msg != "":
311
+
312
+ history[-1][1] = assist_msg
313
+
314
+ # 音声を作成
315
+ file_path = exec_text_to_speech(client, voice, speed, user_id, assist_msg)
316
+
317
+ if file_path is None:
318
+
319
+ err_msg = "音声作成でエラーが発生しました。"
320
+ history = history + [(None, err_msg)]
321
+
322
+ else:
323
+
324
+ history = history + [(None, (file_path,))]
325
+
326
+ yield gr.Chatbot(label=run.status ,value=history), err_msg
327
+
328
+ # 最終メッセージID更新
329
+ last_msg_id = msg.id
330
+
331
+ # セッション更新
332
+ state["last_msg_id"] = last_msg_id
333
+
334
+ # 完了なら終了
335
+ if run.status == "completed":
336
+
337
+ yield gr.Chatbot(label=run.status ,value=history), err_msg
338
+ break
339
+
340
+ elif run.status == "failed":
341
+
342
+ # エラーとして終了
343
+ err_msg = "※メッセージ取得に失敗しました。"
344
+ yield gr.Chatbot(label=run.status ,value=history), err_msg
345
+ break
346
+
347
+ elif i == MAX_TRIAL:
348
+
349
+ # エラーとして終了
350
+ err_msg = "※メッセージ取得の際にタイムアウトしました。"
351
+ yield gr.Chatbot(label=run.status ,value=history), err_msg
352
+ break
353
+
354
+ elif mode == 2: # 英訳モードの場合
355
+
356
+ # GPTで英文にする
357
+ bot_message = exec_translation(client, user_msg)
358
+
359
+ file_path = exec_text_to_speech(client, voice, speed, user_id, bot_message)
360
+
361
+ # bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"])
362
+ # bot_message = "まず、新宿駅南口に出ます。"
363
+ # voice_path = "nova.mp3"
364
+
365
+ history[-1][1] = bot_message
366
+ # history[-1][1] = [None, (voice_path,)]
367
+ history = history + [(None, (file_path,))]
368
+ # history[-1][1] = (voice_path,)
369
+
370
+ yield history, err_msg
371
+
372
+
373
+ def finally_proc(state, history, err_msg):
374
+ """ 最終処理 """
375
+
376
+ # テキスト・オーディオを使えるように
377
+ interactive = gr.update(interactive = True)
378
+
379
+ if err_msg == "":
380
+
381
+ print(history[-1][1][0])
382
+
383
+ # 出力オーディオをセット
384
+ audio = gr.update(value=history[-1][1][0], autoplay=state["auto_play"])
385
+
386
+ else:
387
+
388
+ audio = None
389
+
390
+ return interactive, interactive, audio
391
+
392
+
393
+ def reset_chat(state):
394
+
395
+ state["assistant_id"] = ""
396
+ state["thread_id"] = ""
397
+ state["last_msg_id"] = ""
398
+
399
+ # Chatも初期化
400
+ return state, None
401
+
402
+ with gr.Blocks() as demo:
403
+
404
+ title = "<h2>GPT 音声対応チャット</h2>"
405
+ message = "<h3>最初に[設定]タブからOpenAIキーを入力してください。<br>"
406
+ message += "</h3>"
407
+
408
+ gr.Markdown(title + message)
409
+
410
+ # セッションの宣言
411
+ state = gr.State({
412
+ # "system_prompt": SYS_PROMPT_DEFAULT,
413
+ "openai_key" : "",
414
+ "client" : None,
415
+ "assistant_id" : "",
416
+ "thread_id" : "",
417
+ "last_msg_id" : "",
418
+ "user_id" : "",
419
+ "auto_play" : True,
420
+ "speed" : 0.8,
421
+ "voice" : "nova"
422
+ })
423
+
424
+ with gr.Tab("GPT-4V 音声入力対応チャット") as maintab:
425
+
426
+ # モード選択
427
+ mode = gr.Radio(["通常(日本語)", "英会話", "英訳"], label="Mode", value="英会話", interactive=True, type="index")
428
+
429
+ # 各コンポーネント定義
430
+ chatbot = gr.Chatbot(label="チャット画面")
431
+ text_msg = gr.Textbox(label="テキスト")
432
+ voice_msg=gr.components.Audio(label="音声入力", sources="microphone", type="filepath")
433
+ with gr.Row():
434
+ btn = gr.Button("送信")
435
+ clear = gr.ClearButton([chatbot, text_msg, voice_msg])
436
+
437
+ sys_msg = gr.Text(label="システムメッセージ", interactive = False)
438
+ out_voice=gr.Audio(label="出力音声", type="filepath", interactive = False, autoplay = True)
439
+
440
+ # 英訳保存用(非表示)
441
+ # text_en = gr.Textbox(visible=False)
442
+
443
+ # メッセージ送信時の処理
444
+ btn.click(init, [state, mode, text_msg, voice_msg],[state, text_msg, sys_msg], queue=False).then(
445
+ raise_exception, sys_msg, None).success(
446
+ add_history, [chatbot, text_msg], [chatbot, text_msg, voice_msg], queue=False).then(
447
+ bot, [state, mode, chatbot], [chatbot, sys_msg]).then(
448
+ finally_proc, [state, chatbot, sys_msg], [text_msg, voice_msg, out_voice], queue=False)
449
+
450
+ # 録音終了時の処理
451
+ # voice_msg.stop_recording(init, [state, mode, text_msg, voice_msg],[state, text_msg, sys_msg], queue=False).then(
452
+ # raise_exception, sys_msg, None).success(
453
+ # add_history, [chatbot, text_msg], [chatbot, text_msg, voice_msg], queue=False).then(
454
+ # bot, [state, mode, chatbot], [chatbot, sys_msg]).then(
455
+ # finally_proc, [state, chatbot, sys_msg], [text_msg, voice_msg, out_voice], queue=False)
456
+
457
+ with gr.Tab("設定"):
458
+ openai_key = gr.Textbox(label="OpenAI API Key", visible=True)
459
+ voice = gr.Dropdown(choices=voice_list, value = "nova", label="Voice", interactive = True)
460
+ auto_play = gr.Dropdown(choices=["ON", "OFF"], value = "ON", label="Auto Play", interactive = True)
461
+ speed = gr.Slider(0, 1, value=0.8, label="Speed", info="1に近づけるほど読むスピードが速くなります。", interactive = True)
462
+ # sys_prompt = gr.Textbox(value = sys_prompt_default,lines = 5, label="Custom instructions", interactive = True)
463
+
464
+ # モード変更時
465
+ mode.change(reset_chat, [state], [state, chatbot])
466
+
467
+ # 設定変更時
468
+ maintab.select(set_state, [state, openai_key, voice, auto_play, speed], state)
469
+
470
+ with gr.Tab("利用上の注意"):
471
+ caution = "・通常モードは日本人アシスタントとしてGPTが日本語で回答��ます。<br>"
472
+ caution += "・英会話モードは英語の教師としてGPTが回答します。<br>"
473
+ caution += "(英語で返答するように設定していますが、まれに日本語で回答するかもしれません。)<br>"
474
+ caution += "・英訳モードはテキスト・音声ともに入力した日本語をそのまま英語に翻訳します。<br>"
475
+ caution += "(GPTでの英訳となり、余計な文章などが含まれる可能性があります、)<br>"
476
+ caution += "・モードを変更すると会話はリセットされます。<br>"
477
+ caution += "・設定タブで声の種類(デフォルトはnova)、回答を自動再生にするか決めることができます。<br>"
478
+ gr.Markdown("<h4>" + caution + "</h4>")
479
+
480
+ with gr.Tab("声サンプル") as voice_chk:
481
+
482
+ gr.Markdown("<h3>Text to speechの声のサンプルです。(速度は0.8です)</h3>")
483
+
484
+ with gr.Row():
485
+ btn_alloy = gr.Button(value="alloy")
486
+ btn_echo = gr.Button(value="echo")
487
+ btn_fable = gr.Button(value="fable")
488
+
489
+ with gr.Row():
490
+ btn_onyx = gr.Button(value="onyx")
491
+ btn_nova = gr.Button(value="nova")
492
+ btn_shimmer = gr.Button(value="shimmer")
493
+
494
+ sample_voice=gr.Audio(type="filepath", interactive = False, autoplay = True)
495
+
496
+ btn_alloy.click(lambda:"voice_sample/alloy.mp3", None, sample_voice)
497
+ btn_echo.click(lambda:"voice_sample/echo.mp3", None, sample_voice)
498
+ btn_fable.click(lambda:"voice_sample/fable.mp3", None, sample_voice)
499
+ btn_onyx.click(lambda:"voice_sample/onyx.mp3", None, sample_voice)
500
+ btn_nova.click(lambda:"voice_sample/nova.mp3", None, sample_voice)
501
+ btn_shimmer.click(lambda:"voice_sample/shimmer.mp3", None, sample_voice)
502
+
503
+
504
+ if __name__ == '__main__':
505
+
506
+ demo.queue()
507
+ demo.launch(debug=False)
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # gradio==4.19.0
2
+ openai==1.12.0