Update app.py
Browse files
app.py
CHANGED
|
@@ -115,7 +115,7 @@ def debate_turn(topic, user_stance, user_input_text, user_input_audio, history):
|
|
| 115 |
# 將用戶原始語音(路徑)加入歷史
|
| 116 |
history.append(((processed_audio_path,), None)) # 用戶音訊路徑作為輸入顯示
|
| 117 |
if user_text and not user_text.startswith("["):
|
| 118 |
-
#
|
| 119 |
pass
|
| 120 |
elif user_text.startswith("["): # 辨識失敗
|
| 121 |
history.append((user_text, None)) # 顯示錯誤訊息
|
|
@@ -127,66 +127,73 @@ def debate_turn(topic, user_stance, user_input_text, user_input_audio, history):
|
|
| 127 |
if user_input_text:
|
| 128 |
user_text = user_input_text
|
| 129 |
# 將用戶文字輸入加入 history,這會是這一輪的開始
|
| 130 |
-
history
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
else:
|
| 132 |
-
# 如果兩者都無效 (
|
| 133 |
if not processed_audio_path: # 僅當連音訊都沒嘗試時才報錯
|
| 134 |
history.append(("[錯誤:請提供文字或語音論點]", None))
|
| 135 |
-
|
| 136 |
-
|
| 137 |
|
| 138 |
# 確保 user_text 是有效的字串才繼續
|
| 139 |
if not isinstance(user_text, str) or user_text.startswith("["):
|
| 140 |
print("Invalid user text, stopping turn.")
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
| 142 |
|
| 143 |
# --- 準備呼叫 LLM ---
|
| 144 |
-
# 從 history 整理出 LLM 需要的 messages 格式
|
| 145 |
llm_messages = []
|
|
|
|
| 146 |
for i, turn in enumerate(history):
|
| 147 |
user_msg, ai_msg = turn
|
| 148 |
-
|
| 149 |
-
# --- 處理用戶訊息 ---
|
| 150 |
user_content = None
|
| 151 |
-
if isinstance(user_msg, str):
|
| 152 |
if not user_msg.startswith("[") and not user_msg.startswith("(語音辨識結果:"):
|
| 153 |
user_content = user_msg
|
| 154 |
-
elif isinstance(user_msg, tuple):
|
| 155 |
-
#
|
| 156 |
-
#
|
| 157 |
-
if i == len(history) - 1 and not user_text.startswith("["):
|
| 158 |
user_content = user_text
|
| 159 |
-
#
|
| 160 |
|
| 161 |
if user_content:
|
| 162 |
llm_messages.append({"role": "user", "content": user_content})
|
| 163 |
|
| 164 |
-
|
| 165 |
-
# --- 處理 AI 訊息 ---
|
| 166 |
ai_content = None
|
| 167 |
-
if isinstance(ai_msg, str):
|
| 168 |
if not ai_msg.startswith("["):
|
| 169 |
ai_content = ai_msg
|
| 170 |
-
elif isinstance(ai_msg, tuple) and len(ai_msg) > 0:
|
| 171 |
-
|
| 172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
if ai_content:
|
| 175 |
llm_messages.append({"role": "assistant", "content": ai_content})
|
| 176 |
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
else:
|
| 185 |
-
print("Skipping LLM call due to invalid user text.")
|
| 186 |
-
return history, None, "" # 無法呼叫 LLM
|
| 187 |
-
elif not llm_messages and not user_text.startswith("["):
|
| 188 |
-
# 如果是第一輪
|
| 189 |
-
llm_messages.append({"role": "user", "content": user_text})
|
| 190 |
|
| 191 |
# --- 呼叫 LLM ---
|
| 192 |
ai_response_text = call_llm(topic, user_stance, llm_messages)
|
|
@@ -198,21 +205,27 @@ def debate_turn(topic, user_stance, user_input_text, user_input_audio, history):
|
|
| 198 |
# 找到用戶最新輸入的那一條記錄(它還沒有 AI 回應)
|
| 199 |
last_user_turn_index = -1
|
| 200 |
for i in range(len(history) - 1, -1, -1):
|
| 201 |
-
if history[i][1] is None
|
| 202 |
last_user_turn_index = i
|
| 203 |
break
|
| 204 |
|
| 205 |
if last_user_turn_index != -1:
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
|
|
|
|
|
|
|
|
|
| 212 |
else:
|
| 213 |
-
#
|
| 214 |
-
print("
|
| 215 |
-
history.append(("[
|
|
|
|
|
|
|
|
|
|
| 216 |
|
| 217 |
# 清空輸入框
|
| 218 |
return history, None, ""
|
|
|
|
| 115 |
# 將用戶原始語音(路徑)加入歷史
|
| 116 |
history.append(((processed_audio_path,), None)) # 用戶音訊路徑作為輸入顯示
|
| 117 |
if user_text and not user_text.startswith("["):
|
| 118 |
+
# 成功辨識,在下面會統一處理
|
| 119 |
pass
|
| 120 |
elif user_text.startswith("["): # 辨識失敗
|
| 121 |
history.append((user_text, None)) # 顯示錯誤訊息
|
|
|
|
| 127 |
if user_input_text:
|
| 128 |
user_text = user_input_text
|
| 129 |
# 將用戶文字輸入加入 history,這會是這一輪的開始
|
| 130 |
+
# 如果前面已經因為 audio 加入了 history,這裡就不重複加
|
| 131 |
+
if not processed_audio_path:
|
| 132 |
+
history.append((user_text, None))
|
| 133 |
+
# 如果 audio 辨識失敗,且有文字輸入,用文字覆蓋辨識結果 (或者提示用戶?)
|
| 134 |
+
# 這裡選擇使用文字輸入(如果有的話)
|
| 135 |
+
elif user_input_text:
|
| 136 |
+
user_text = user_input_text
|
| 137 |
+
# 替換掉 history 中 audio 辨識失敗的訊息,或者追加? 這裡選擇追加文字輸入
|
| 138 |
+
history.append((user_text, None))
|
| 139 |
+
|
| 140 |
+
|
| 141 |
else:
|
| 142 |
+
# 如果兩者都無效 (沒有輸入文字,且音訊無效/未提供/辨識失敗)
|
| 143 |
if not processed_audio_path: # 僅當連音訊都沒嘗試時才報錯
|
| 144 |
history.append(("[錯誤:請提供文字或語音論點]", None))
|
| 145 |
+
# 如果是語音辨識失敗,前面已加入錯誤訊息
|
| 146 |
+
return history, None, ""
|
| 147 |
|
| 148 |
# 確保 user_text 是有效的字串才繼續
|
| 149 |
if not isinstance(user_text, str) or user_text.startswith("["):
|
| 150 |
print("Invalid user text, stopping turn.")
|
| 151 |
+
# 如果 history 最後一筆是 audio tuple,且辨識失敗,避免錯誤繼續
|
| 152 |
+
if history and isinstance(history[-1][0], tuple) and history[-1][1] is None:
|
| 153 |
+
history.append((f"[無法處理用戶輸入: {user_text}]", None))
|
| 154 |
+
return history, None, ""
|
| 155 |
|
| 156 |
# --- 準備呼叫 LLM ---
|
|
|
|
| 157 |
llm_messages = []
|
| 158 |
+
# 從 history 整理出 LLM 需要的 messages 格式 (保持不變)
|
| 159 |
for i, turn in enumerate(history):
|
| 160 |
user_msg, ai_msg = turn
|
|
|
|
|
|
|
| 161 |
user_content = None
|
| 162 |
+
if isinstance(user_msg, str):
|
| 163 |
if not user_msg.startswith("[") and not user_msg.startswith("(語音辨識結果:"):
|
| 164 |
user_content = user_msg
|
| 165 |
+
elif isinstance(user_msg, tuple):
|
| 166 |
+
# 如果是最新一輪的音訊,用 call_asr 的結果
|
| 167 |
+
# 這裡需要更可靠的方式找到辨識結果,但為了簡化,先假設 user_text 是對的
|
| 168 |
+
if i == len(history) - 1 and not user_text.startswith("["): # 假設 user_text 是剛辨識的
|
| 169 |
user_content = user_text
|
| 170 |
+
# 注意:如果歷史中有多次語音,這裡的邏輯需要加強才能正確配對
|
| 171 |
|
| 172 |
if user_content:
|
| 173 |
llm_messages.append({"role": "user", "content": user_content})
|
| 174 |
|
|
|
|
|
|
|
| 175 |
ai_content = None
|
| 176 |
+
if isinstance(ai_msg, str):
|
| 177 |
if not ai_msg.startswith("["):
|
| 178 |
ai_content = ai_msg
|
| 179 |
+
elif isinstance(ai_msg, tuple) and len(ai_msg) > 0:
|
| 180 |
+
# 之前的結構是 (文字, (音訊,)),現在已修改,但先保留兼容舊結構的解析
|
| 181 |
+
if isinstance(ai_msg[0], str) and not ai_msg[0].startswith("["):
|
| 182 |
+
ai_content = ai_msg[0]
|
| 183 |
+
# 新結構是 (音訊路徑,)
|
| 184 |
+
elif isinstance(ai_msg[0], str) and ai_msg[0].endswith(".mp3"):
|
| 185 |
+
pass # 這是音訊,不是文本內容
|
| 186 |
|
| 187 |
if ai_content:
|
| 188 |
llm_messages.append({"role": "assistant", "content": ai_content})
|
| 189 |
|
| 190 |
+
# 確保最後是 user message
|
| 191 |
+
if not llm_messages or llm_messages[-1]["role"] == "assistant":
|
| 192 |
+
if not user_text.startswith("["):
|
| 193 |
+
llm_messages.append({"role": "user", "content": user_text})
|
| 194 |
+
else:
|
| 195 |
+
print("Skipping LLM call due to invalid final user text.")
|
| 196 |
+
return history, None, ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 197 |
|
| 198 |
# --- 呼叫 LLM ---
|
| 199 |
ai_response_text = call_llm(topic, user_stance, llm_messages)
|
|
|
|
| 205 |
# 找到用戶最新輸入的那一條記錄(它還沒有 AI 回應)
|
| 206 |
last_user_turn_index = -1
|
| 207 |
for i in range(len(history) - 1, -1, -1):
|
| 208 |
+
if history[i][1] is None and not history[i][0] is None: # 找到最後一個用戶有輸入但AI無回應的地方
|
| 209 |
last_user_turn_index = i
|
| 210 |
break
|
| 211 |
|
| 212 |
if last_user_turn_index != -1:
|
| 213 |
+
# ****** 修改核心 ******
|
| 214 |
+
# 1. 更新找到的那一回合,填入 AI 的文字回應
|
| 215 |
+
history[last_user_turn_index] = (history[last_user_turn_index][0], ai_response_text)
|
| 216 |
+
|
| 217 |
+
# 2. 如果 TTS 成功,*追加*一個新的回合,只包含 AI 的音訊
|
| 218 |
+
if ai_response_audio_path and not ai_response_text.startswith("["):
|
| 219 |
+
# AI 的音訊回應格式應該是 (filepath,)
|
| 220 |
+
history.append((None, (ai_response_audio_path,))) # User 設為 None, AI 設為音訊元組
|
| 221 |
+
# ****** 修改結束 ******
|
| 222 |
else:
|
| 223 |
+
# 如果找不到用戶回合(理論上不該發生),直接追加
|
| 224 |
+
print("Warning: Could not find user's turn. Appending AI response.")
|
| 225 |
+
history.append(("[用戶回合丟失?]", ai_response_text))
|
| 226 |
+
if ai_response_audio_path and not ai_response_text.startswith("["):
|
| 227 |
+
history.append((None, (ai_response_audio_path,)))
|
| 228 |
+
|
| 229 |
|
| 230 |
# 清空輸入框
|
| 231 |
return history, None, ""
|