import gradio as gr import time import datetime from zoneinfo import ZoneInfo import os from openai import ( OpenAI, AuthenticationError, NotFoundError, BadRequestError ) # 各種設定値 MAX_TRIAL = int(os.environ["MAX_TRIAL"]) # メッセージ取得最大試行数 INTER_SEC = int(os.environ["INTER_SEC"]) # 試行間隔(秒) # sys_prompt_default = "あなたは優秀なアシスタントです。日本語で質問に回答してください。" # lang_code = {'Japanese': "ja", 'English': "en"} auto_play_bl = {'ON': True, 'OFF': False} voice_list = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"] def set_state(state, openai_key, voice, auto_play, speed): state["openai_key"] = openai_key state["voice"] = voice state["auto_play"] = auto_play_bl[auto_play] state["speed"] = speed return state def add_history(history, text_msg): """ Chat履歴"history"に追加を行う関数 """ print(text_msg) print(history) # ユーザテキストをチャットに追加 history = history + [(text_msg, None)] # テキスト・オーディオ初期化 return history, gr.Textbox(value="", interactive=False), gr.components.Audio(value=None, interactive=False) def init(state, mode, text_msg, voice_msg): """ 初期処理(入力チェック・テキスト変換) """ print(state) err_msg = "" text = "" if text_msg == "" and voice_msg is None: # 何も入力がないならエラーメッセージを返す err_msg = "テキストまたは音声を入力してください。" return state, "", err_msg try: if state["client"] is None: # 初回起動時は初期処理をする os.environ["OPENAI_API_KEY"] = os.environ["TEST_OPENAI_KEY"] # テスト時 # os.environ["OPENAI_API_KEY"] = state["openai_key"] # クライアント新規作成 client = OpenAI() # client作成後は消す os.environ["OPENAI_API_KEY"] = "" # セッションにセット state["client"] = client # IDとして現在時刻をセット dt = datetime.datetime.now(ZoneInfo("Asia/Tokyo")) state["user_id"] = dt.strftime("%Y%m%d%H%M%S") else: # 既存のクライアントをセット client = state["client"] # モードが翻訳以外はアシスタント・スレッドセット if mode != 2: if state["thread_id"] == "": # スレッド作成・セット thread = client.beta.threads.create() state["thread_id"] = thread.id if state["assistant_id"] == "": # アシスタント作成 # assistant = client.beta.assistants.create( # name="codeinter_test", # instructions=state["system_prompt"], # # model="gpt-4-1106-preview", # model="gpt-3.5-turbo-1106", # tools=[{"type": "code_interpreter"}] # ) # state["assistant_id"] = assistant.id if mode == 0: # 日本語アシスタントをセット state["assistant_id"] = os.environ["ASSIST_JA"] elif mode == 1: # 英語教師アシスタントをセット state["assistant_id"] = os.environ["ASSIST_EN"] else: # アシスタント確認(IDが存在しないならエラーとなる) # assistant = client.beta.assistants.retrieve(state["assistant_id"]) pass # ユーザIDでフォルダ作成 os.makedirs(state["user_id"], exist_ok=True) if voice_msg is None: # 音声がないならテキストを返す text = text_msg else: # 音声があるならwhisperでテキストに変換 text = exec_transcript(client, voice_msg) pass except NotFoundError as e: err_msg = "アシスタントIDが間違っています。新しく作成する場合はアシスタントIDを空欄にして下さい。" print(e) except AuthenticationError as e: err_msg = "認証エラーとなりました。OpenAPIKeyが正しいか、支払い方法などが設定されているか確認して下さい。" print(e) except Exception as e: err_msg = "その他のエラーが発生しました。" print(e) finally: return state, text, err_msg def raise_exception(err_msg): """ エラーの場合例外を起こす関数 """ if err_msg != "": raise Exception() return def exec_transcript(client, voice_msg): """ whisperで文字に起こす関数 """ audio_file= open(voice_msg, "rb") transcript = client.audio.transcriptions.create( model="whisper-1", file=audio_file, language = "ja", response_format="text" ) return transcript def exec_translation(client, text): """ GPTで英語に翻訳する関数 """ response = client.chat.completions.create( model="gpt-3.5-turbo-1106", messages=[ {"role": "system", "content": "You are a translator. Please translate the Japanese you received into English."}, {"role": "user", "content": text}, ] ) return response.choices[0].message.content def exec_text_to_speech(client, voice , speed, folder, text): """ テキストを音声にする """ response = client.audio.speech.create( model= "tts-1", # "tts-1-hd", voice=voice, input=text, speed=speed ) # ファイル名は現在時刻 dt = datetime.datetime.now(ZoneInfo("Asia/Tokyo")) file_name = dt.strftime("%Y%m%d%H%M%S") + ".mp3" file_path = folder + "/" + file_name # 音声ファイルに出力 response.stream_to_file(file_path) return file_path def bot(state, mode, history): """ Chat返答処理 """ err_msg = "" file_path = None # セッション情報取得 client = state["client"] asist_id = state["assistant_id"] thread_id = state["thread_id"] last_msg_id = state["last_msg_id"] user_id = state["user_id"] voice = state["voice"] speed = state["speed"] # 最新のユーザからのメッセージ user_msg = history[-1][0] print(history) print(user_msg) if mode in (0, 1): # 日本語会話モードの場合 # メッセージ作成 message = client.beta.threads.messages.create( thread_id=thread_id, role="user", content=user_msg ) # RUNスタート run = client.beta.threads.runs.create( thread_id=thread_id, assistant_id=asist_id ) # "completed"となるまで繰り返す(指定秒おき) for i in range(0, MAX_TRIAL, 1): if i > 0: time.sleep(INTER_SEC) # メッセージ受け取り run = client.beta.threads.runs.retrieve( thread_id=thread_id, run_id=run.id ) # 前回のメッセージより後を昇順で取り出す messages = client.beta.threads.messages.list( thread_id=thread_id, after=last_msg_id, order="asc" ) # messageを取り出す for msg in messages: if msg.role == "assistant": assist_msg = msg.content[0].text.value if assist_msg != "": history[-1][1] = assist_msg # 音声を作成 file_path = exec_text_to_speech(client, voice, speed, user_id, assist_msg) if file_path is None: err_msg = "音声作成でエラーが発生しました。" history = history + [(None, err_msg)] else: history = history + [(None, (file_path,))] yield gr.Chatbot(label=run.status ,value=history), err_msg # 最終メッセージID更新 last_msg_id = msg.id # セッション更新 state["last_msg_id"] = last_msg_id # 完了なら終了 if run.status == "completed": yield gr.Chatbot(label=run.status ,value=history), err_msg break elif run.status == "failed": # エラーとして終了 err_msg = "※メッセージ取得に失敗しました。" yield gr.Chatbot(label=run.status ,value=history), err_msg break elif i == MAX_TRIAL: # エラーとして終了 err_msg = "※メッセージ取得の際にタイムアウトしました。" yield gr.Chatbot(label=run.status ,value=history), err_msg break elif mode == 2: # 英訳モードの場合 # GPTで英文にする bot_message = exec_translation(client, user_msg) file_path = exec_text_to_speech(client, voice, speed, user_id, bot_message) # bot_message = random.choice(["How are you?", "I love you", "I'm very hungry"]) # bot_message = "まず、新宿駅南口に出ます。" # voice_path = "nova.mp3" history[-1][1] = bot_message # history[-1][1] = [None, (voice_path,)] history = history + [(None, (file_path,))] # history[-1][1] = (voice_path,) yield history, err_msg def finally_proc(state, history, err_msg): """ 最終処理 """ # テキスト・オーディオを使えるように interactive = gr.update(interactive = True) if err_msg == "": print(history[-1][1][0]) # 出力オーディオをセット audio = gr.update(value=history[-1][1][0], autoplay=state["auto_play"]) else: audio = None return interactive, interactive, audio def reset_chat(state): state["assistant_id"] = "" state["thread_id"] = "" state["last_msg_id"] = "" # Chatも初期化 return state, None with gr.Blocks() as demo: title = "