Update app.py
Browse files
app.py
CHANGED
|
@@ -28,7 +28,8 @@ HF_TOKEN = os.getenv("HF_TOKEN")
|
|
| 28 |
# --- 1. 資料讀取 (主要資料庫 - 使用 restaurants.csv) ---
|
| 29 |
df = pd.DataFrame()
|
| 30 |
try:
|
| 31 |
-
|
|
|
|
| 32 |
df.columns = df.columns.str.strip() # 清理欄位空白
|
| 33 |
|
| 34 |
def classify_mood(row):
|
|
@@ -54,7 +55,8 @@ try:
|
|
| 54 |
|
| 55 |
print("✅ 成功讀取 restaurants.csv 並建立 Mood_Tags")
|
| 56 |
except Exception as e:
|
| 57 |
-
|
|
|
|
| 58 |
|
| 59 |
# --- 2. 資料讀取 (Prompt 資料庫) ---
|
| 60 |
df_prompts = pd.DataFrame()
|
|
@@ -84,51 +86,12 @@ if not df.empty and 'RAG_Content' in df.columns:
|
|
| 84 |
print(f"⚠️ RAG 初始化失敗: {e}")
|
| 85 |
|
| 86 |
# ==========================================
|
| 87 |
-
# 1.
|
| 88 |
-
# ==========================================
|
| 89 |
-
custom_css = """
|
| 90 |
-
.vertical-radio fieldset {
|
| 91 |
-
display: flex !important;
|
| 92 |
-
flex-direction: column !important;
|
| 93 |
-
align-items: flex-start !important;
|
| 94 |
-
gap: 12px !important;
|
| 95 |
-
}
|
| 96 |
-
.vertical-radio label {
|
| 97 |
-
width: 100% !important;
|
| 98 |
-
margin: 0 !important;
|
| 99 |
-
display: flex !important;
|
| 100 |
-
}
|
| 101 |
-
"""
|
| 102 |
-
# 若無圖片自動生成空白圖避免報錯
|
| 103 |
-
img_path_1 = "images/image_0.png"
|
| 104 |
-
img_path_2 = "images/image_1.png"
|
| 105 |
-
if not os.path.exists("images"): os.makedirs("images")
|
| 106 |
-
try:
|
| 107 |
-
if not os.path.exists(img_path_1): Image.new('RGB', (400, 300), color='gray').save(img_path_1)
|
| 108 |
-
if not os.path.exists(img_path_2): Image.new('RGB', (400, 300), color='lightblue').save(img_path_2)
|
| 109 |
-
except: pass
|
| 110 |
-
|
| 111 |
-
q1_options_map = {
|
| 112 |
-
"不要再問白癡問題了我只想吃飯": "現實主義者 (只想吃飯)",
|
| 113 |
-
"去過隱居生活": "隱士 (嚮往平靜)",
|
| 114 |
-
"加入禁衛軍,保衛這個世界": "守護者 (充滿正義感)",
|
| 115 |
-
"神羅天征,毀滅這個世界": "破壞神 (心情可能很差或很中二)"
|
| 116 |
-
}
|
| 117 |
-
|
| 118 |
-
q2_options_map = {
|
| 119 |
-
"不要再問白癡問題了我只想吃飯": "無魔法 (飢餓度MAX)",
|
| 120 |
-
"麵包形狀的魔法炸彈": "爆炸魔法 (喜歡刺激/重口味)",
|
| 121 |
-
"毒氣的生化魔法": "毒氣魔法 (可能想吃臭豆腐或特殊風味)",
|
| 122 |
-
"領域展開:無量空處": "領域展開 (思緒混亂或想放空)"
|
| 123 |
-
}
|
| 124 |
-
|
| 125 |
-
# ==========================================
|
| 126 |
-
# 2. 核心功能函式 (餐廳推薦邏輯)
|
| 127 |
# ==========================================
|
| 128 |
|
| 129 |
def get_restaurant_data(mood_score_str, food_choice):
|
| 130 |
if df.empty: return None, True
|
| 131 |
-
# 定義分數與 Mood_Tags 的對應關係
|
| 132 |
score_map = {
|
| 133 |
"1 (心情差)": ["傷心/疲憊", "生氣/發洩"],
|
| 134 |
"2 (不太好)": ["傷心/疲憊", "生氣/發洩"],
|
|
@@ -157,23 +120,21 @@ def get_restaurant_data(mood_score_str, food_choice):
|
|
| 157 |
if candidates.empty: return df.sample(1).iloc[0], True
|
| 158 |
return candidates.sample(1).iloc[0], False
|
| 159 |
|
| 160 |
-
|
|
|
|
| 161 |
if not GROQ_API_KEY: return "⚠️ 請設定 GROQ_API_KEY (系統變數)"
|
| 162 |
client = OpenAI(api_key=GROQ_API_KEY, base_url="https://api.groq.com/openai/v1")
|
| 163 |
-
system_prompt = "你是一個幽默、懂吃且善解人意的 AI 朋友。請根據使用者的日記
|
| 164 |
user_msg = f"""
|
| 165 |
【使用者狀態】
|
| 166 |
心情分數:{mood_score}
|
| 167 |
心情日記:{user_diary}
|
| 168 |
-
【前測小遊戲結果】
|
| 169 |
-
隱藏人設:{quiz_result.get('q1', '未知')}
|
| 170 |
-
魔法:{quiz_result.get('q2', '未知')}
|
| 171 |
【推薦餐廳】
|
| 172 |
名稱:{restaurant_name}
|
| 173 |
資料:{restaurant_detail}
|
| 174 |
請完成任務:
|
| 175 |
【回應內容】:用繁體中文寫一段溫暖有趣的回覆。
|
| 176 |
-
- 結合「日記」與「
|
| 177 |
- 推薦上述餐廳,說明為什麼這家店適合他。
|
| 178 |
"""
|
| 179 |
try:
|
|
@@ -193,7 +154,8 @@ def generate_image_huggingface(prompt):
|
|
| 193 |
except: return None
|
| 194 |
|
| 195 |
# --- 主邏輯 Agent ---
|
| 196 |
-
|
|
|
|
| 197 |
# 確保 score_input 有值
|
| 198 |
if not score_input:
|
| 199 |
yield "⚠️ 請先選擇心情分數,或使用上方相機偵測!", None, ""
|
|
@@ -230,8 +192,8 @@ def mood_agent_logic(score_input, food_input, diary_input, quiz_state, debug_mod
|
|
| 230 |
prompt_source = "✅ CSV 檔案"
|
| 231 |
except: pass
|
| 232 |
|
| 233 |
-
# 3. 呼叫 LLM
|
| 234 |
-
ai_text = generate_content_with_groq(name, rag_info, diary_input, score_input
|
| 235 |
|
| 236 |
# 4. 組合回應
|
| 237 |
debug_text = ""
|
|
@@ -246,7 +208,7 @@ def mood_agent_logic(score_input, food_input, diary_input, quiz_state, debug_mod
|
|
| 246 |
yield final_response, image_output, map_html
|
| 247 |
|
| 248 |
# ==========================================
|
| 249 |
-
#
|
| 250 |
# ==========================================
|
| 251 |
|
| 252 |
# 定義中文字典 (繪圖用)
|
|
@@ -255,11 +217,10 @@ emotion_text_obj = {
|
|
| 255 |
'happy': '開心', 'sad': '難過', 'surprise': '驚訝', 'neutral': '正常'
|
| 256 |
}
|
| 257 |
|
| 258 |
-
# 繪圖函式
|
| 259 |
def putText(img, x, y, text, size=50, color=(255, 255, 255)):
|
| 260 |
try:
|
| 261 |
fontpath = 'NotoSansTC-VariableFont_wght.ttf'
|
| 262 |
-
# 確保字型存在,若無則回傳原圖
|
| 263 |
if not os.path.exists(fontpath): return img
|
| 264 |
font = ImageFont.truetype(fontpath, size)
|
| 265 |
imgPil = Image.fromarray(img)
|
|
@@ -270,13 +231,13 @@ def putText(img, x, y, text, size=50, color=(255, 255, 255)):
|
|
| 270 |
except:
|
| 271 |
return img
|
| 272 |
|
| 273 |
-
#
|
| 274 |
def detect_emotion_and_map(frame):
|
| 275 |
if frame is None:
|
| 276 |
return frame, None
|
| 277 |
|
| 278 |
-
detected_emotion = "neutral"
|
| 279 |
-
mapped_score = "3 (普通)"
|
| 280 |
|
| 281 |
try:
|
| 282 |
# 1. 辨識情緒
|
|
@@ -288,11 +249,6 @@ def detect_emotion_and_map(frame):
|
|
| 288 |
frame = putText(frame, 20, 40, detected_emotion)
|
| 289 |
|
| 290 |
# 3. 橋樑:將情緒轉換為餐廳系統的分數
|
| 291 |
-
# happy -> 5
|
| 292 |
-
# surprise -> 4
|
| 293 |
-
# neutral -> 3
|
| 294 |
-
# sad, fear -> 2
|
| 295 |
-
# angry, disgust -> 1
|
| 296 |
if detected_emotion == 'happy':
|
| 297 |
mapped_score = "5 (超棒)"
|
| 298 |
elif detected_emotion == 'surprise':
|
|
@@ -311,63 +267,27 @@ def detect_emotion_and_map(frame):
|
|
| 311 |
return frame, mapped_score
|
| 312 |
|
| 313 |
# ==========================================
|
| 314 |
-
#
|
| 315 |
-
# ==========================================
|
| 316 |
-
|
| 317 |
-
def handle_q1_change(selected_label, current_state):
|
| 318 |
-
if not selected_label: return current_state, gr.Button(interactive=False)
|
| 319 |
-
current_state["q1"] = q1_options_map.get(selected_label, selected_label)
|
| 320 |
-
return current_state, gr.Button(interactive=True, variant="primary")
|
| 321 |
-
|
| 322 |
-
def handle_q2_change(selected_label, current_state):
|
| 323 |
-
if not selected_label: return current_state, gr.Button(interactive=False)
|
| 324 |
-
current_state["q2"] = q2_options_map.get(selected_label, selected_label)
|
| 325 |
-
return current_state, gr.Button(interactive=True, variant="primary", value="完成測驗 (前往點餐) ➔")
|
| 326 |
-
|
| 327 |
-
def go_to_page_2(): return gr.update(visible=False), gr.update(visible=True)
|
| 328 |
-
def go_to_main_app(): return gr.update(visible=False), gr.update(visible=True)
|
| 329 |
-
|
| 330 |
-
# ==========================================
|
| 331 |
-
# 5. Gradio 介面建構
|
| 332 |
# ==========================================
|
| 333 |
|
| 334 |
with gr.Blocks(title="AI 心情食堂") as demo:
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
# Page 1
|
| 339 |
-
with gr.Column(visible=True) as page_1_col:
|
| 340 |
-
gr.Markdown("### 🔮 第一題:直覺測試")
|
| 341 |
-
gr.Image(value=img_path_1, type="filepath", label="請觀察圖片", height=300)
|
| 342 |
-
gr.Markdown("**問題:請觀察上方圖片,如果是你,你會怎麼做?**")
|
| 343 |
-
radio_q1 = gr.Radio(choices=list(q1_options_map.keys()), label="請選擇", elem_classes="vertical-radio")
|
| 344 |
-
btn_q1_next = gr.Button("下一頁 ➔", interactive=False)
|
| 345 |
-
|
| 346 |
-
# Page 2
|
| 347 |
-
with gr.Column(visible=False) as page_2_col:
|
| 348 |
-
gr.Markdown("### 🔮 第二題:魔法適性")
|
| 349 |
-
gr.Image(value=img_path_2, type="filepath", label="請觀察圖片", height=300)
|
| 350 |
-
gr.Markdown("**問題:身為魔導士的你,會選擇哪一個法術?**")
|
| 351 |
-
radio_q2 = gr.Radio(choices=list(q2_options_map.keys()), label="請選擇", elem_classes="vertical-radio")
|
| 352 |
-
btn_q2_finish = gr.Button("完成測驗 ➔", interactive=False)
|
| 353 |
-
|
| 354 |
-
# Page 3 (Main)
|
| 355 |
-
with gr.Column(visible=False) as main_app_col:
|
| 356 |
gr.Markdown(f"## 🍱 AI 心情食堂導航")
|
| 357 |
-
gr.Markdown("
|
| 358 |
|
| 359 |
with gr.Row():
|
| 360 |
# 左側:輸入區
|
| 361 |
with gr.Column(scale=1):
|
| 362 |
|
| 363 |
-
#
|
| 364 |
gr.Markdown("### 📸 步驟 1:心情偵測 (選用)")
|
| 365 |
with gr.Group():
|
| 366 |
webcam_input = gr.Image(sources=["webcam"], label="看著鏡頭", streaming=True)
|
| 367 |
detect_btn = gr.Button("📸 偵測心情並自動填入 👇", variant="secondary")
|
| 368 |
|
| 369 |
gr.Markdown("### 📝 步驟 2:確認與補充")
|
| 370 |
-
# score_input 會被 detect_btn 更新
|
| 371 |
score_input = gr.Radio(
|
| 372 |
["1 (心情差)", "2 (不太好)", "3 (普通)", "4 (不錯)", "5 (超棒)"],
|
| 373 |
label="1. 心情分數 (可手動修改)",
|
|
@@ -377,30 +297,29 @@ with gr.Blocks(title="AI 心情食堂") as demo:
|
|
| 377 |
diary_input = gr.Textbox(lines=3, label="3. 心情日記 (選填)", placeholder="例如:今天被老闆罵了,想吃點好料的...")
|
| 378 |
|
| 379 |
debug_mode_btn = gr.Checkbox(label="🔧 顯示 Prompt 除錯資訊", value=False)
|
| 380 |
-
|
|
|
|
| 381 |
|
| 382 |
-
# 右側:輸出區
|
| 383 |
with gr.Column(scale=1):
|
| 384 |
agent_output = gr.Markdown(label="AI 回應")
|
| 385 |
image_output = gr.Image(label="AI 推薦美食圖", type="pil", width=400)
|
| 386 |
map_output = gr.HTML(label="地圖導航")
|
| 387 |
|
| 388 |
-
# Events
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
radio_q2.change(fn=handle_q2_change, inputs=[radio_q2, quiz_state], outputs=[quiz_state, btn_q2_finish])
|
| 392 |
-
btn_q2_finish.click(fn=go_to_main_app, outputs=[page_2_col, main_app_col])
|
| 393 |
-
|
| 394 |
-
# ★ 新增事件:點擊偵測按鈕 -> 執行 DeepFace -> 更新 score_input
|
| 395 |
detect_btn.click(
|
| 396 |
fn=detect_emotion_and_map,
|
| 397 |
inputs=[webcam_input],
|
| 398 |
outputs=[webcam_input, score_input] # 同時更新圖片(畫字)和分數(Radio)
|
| 399 |
)
|
| 400 |
|
|
|
|
|
|
|
| 401 |
submit_btn.click(
|
| 402 |
fn=mood_agent_logic,
|
| 403 |
-
inputs=[score_input, food_input, diary_input,
|
| 404 |
outputs=[agent_output, image_output, map_output]
|
| 405 |
)
|
| 406 |
|
|
|
|
| 28 |
# --- 1. 資料讀取 (主要資料庫 - 使用 restaurants.csv) ---
|
| 29 |
df = pd.DataFrame()
|
| 30 |
try:
|
| 31 |
+
# 確保你有 restaurants.csv 在專案根目錄
|
| 32 |
+
df = pd.read_csv('restaurants.csv', engine='python')
|
| 33 |
df.columns = df.columns.str.strip() # 清理欄位空白
|
| 34 |
|
| 35 |
def classify_mood(row):
|
|
|
|
| 55 |
|
| 56 |
print("✅ 成功讀取 restaurants.csv 並建立 Mood_Tags")
|
| 57 |
except Exception as e:
|
| 58 |
+
# 如果找不到檔案或讀取失敗,這裡會提醒
|
| 59 |
+
print(f"⚠️ 讀取 restaurants.csv 失敗: {e}")
|
| 60 |
|
| 61 |
# --- 2. 資料讀取 (Prompt 資料庫) ---
|
| 62 |
df_prompts = pd.DataFrame()
|
|
|
|
| 86 |
print(f"⚠️ RAG 初始化失敗: {e}")
|
| 87 |
|
| 88 |
# ==========================================
|
| 89 |
+
# 1. 核心功能函式 (餐廳推薦邏輯)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
# ==========================================
|
| 91 |
|
| 92 |
def get_restaurant_data(mood_score_str, food_choice):
|
| 93 |
if df.empty: return None, True
|
| 94 |
+
# 定義分數與 Mood_Tags 的對應關係 (與舊版維持一致)
|
| 95 |
score_map = {
|
| 96 |
"1 (心情差)": ["傷心/疲憊", "生氣/發洩"],
|
| 97 |
"2 (不太好)": ["傷心/疲憊", "生氣/發洩"],
|
|
|
|
| 120 |
if candidates.empty: return df.sample(1).iloc[0], True
|
| 121 |
return candidates.sample(1).iloc[0], False
|
| 122 |
|
| 123 |
+
# 注意:因為移除了測驗,這裡我們用 '無測驗' 作為預設結果
|
| 124 |
+
def generate_content_with_groq(restaurant_name, restaurant_detail, user_diary, mood_score):
|
| 125 |
if not GROQ_API_KEY: return "⚠️ 請設定 GROQ_API_KEY (系統變數)"
|
| 126 |
client = OpenAI(api_key=GROQ_API_KEY, base_url="https://api.groq.com/openai/v1")
|
| 127 |
+
system_prompt = "你是一個幽默、懂吃且善解人意的 AI 朋友。請根據使用者的日記和心情來推薦餐廳。"
|
| 128 |
user_msg = f"""
|
| 129 |
【使用者狀態】
|
| 130 |
心情分數:{mood_score}
|
| 131 |
心情日記:{user_diary}
|
|
|
|
|
|
|
|
|
|
| 132 |
【推薦餐廳】
|
| 133 |
名稱:{restaurant_name}
|
| 134 |
資料:{restaurant_detail}
|
| 135 |
請完成任務:
|
| 136 |
【回應內容】:用繁體中文寫一段溫暖有趣的回覆。
|
| 137 |
+
- 結合「日記」與「心情」給予回應。
|
| 138 |
- 推薦上述餐廳,說明為什麼這家店適合他。
|
| 139 |
"""
|
| 140 |
try:
|
|
|
|
| 154 |
except: return None
|
| 155 |
|
| 156 |
# --- 主邏輯 Agent ---
|
| 157 |
+
# 移除了 quiz_state 參數
|
| 158 |
+
def mood_agent_logic(score_input, food_input, diary_input, debug_mode):
|
| 159 |
# 確保 score_input 有值
|
| 160 |
if not score_input:
|
| 161 |
yield "⚠️ 請先選擇心情分數,或使用上方相機偵測!", None, ""
|
|
|
|
| 192 |
prompt_source = "✅ CSV 檔案"
|
| 193 |
except: pass
|
| 194 |
|
| 195 |
+
# 3. 呼叫 LLM (移除了 quiz_state)
|
| 196 |
+
ai_text = generate_content_with_groq(name, rag_info, diary_input, score_input)
|
| 197 |
|
| 198 |
# 4. 組合回應
|
| 199 |
debug_text = ""
|
|
|
|
| 208 |
yield final_response, image_output, map_html
|
| 209 |
|
| 210 |
# ==========================================
|
| 211 |
+
# 2. 情緒辨識整合模組 (維持不變)
|
| 212 |
# ==========================================
|
| 213 |
|
| 214 |
# 定義中文字典 (繪圖用)
|
|
|
|
| 217 |
'happy': '開心', 'sad': '難過', 'surprise': '驚訝', 'neutral': '正常'
|
| 218 |
}
|
| 219 |
|
| 220 |
+
# 繪圖函式 (維持不變)
|
| 221 |
def putText(img, x, y, text, size=50, color=(255, 255, 255)):
|
| 222 |
try:
|
| 223 |
fontpath = 'NotoSansTC-VariableFont_wght.ttf'
|
|
|
|
| 224 |
if not os.path.exists(fontpath): return img
|
| 225 |
font = ImageFont.truetype(fontpath, size)
|
| 226 |
imgPil = Image.fromarray(img)
|
|
|
|
| 231 |
except:
|
| 232 |
return img
|
| 233 |
|
| 234 |
+
# 關鍵功能:偵測情緒並回傳「分數選項」 (維持不變)
|
| 235 |
def detect_emotion_and_map(frame):
|
| 236 |
if frame is None:
|
| 237 |
return frame, None
|
| 238 |
|
| 239 |
+
detected_emotion = "neutral"
|
| 240 |
+
mapped_score = "3 (普通)"
|
| 241 |
|
| 242 |
try:
|
| 243 |
# 1. 辨識情緒
|
|
|
|
| 249 |
frame = putText(frame, 20, 40, detected_emotion)
|
| 250 |
|
| 251 |
# 3. 橋樑:將情緒轉換為餐廳系統的分數
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
if detected_emotion == 'happy':
|
| 253 |
mapped_score = "5 (超棒)"
|
| 254 |
elif detected_emotion == 'surprise':
|
|
|
|
| 267 |
return frame, mapped_score
|
| 268 |
|
| 269 |
# ==========================================
|
| 270 |
+
# 3. Gradio 介面建構 (移除前兩頁)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
# ==========================================
|
| 272 |
|
| 273 |
with gr.Blocks(title="AI 心情食堂") as demo:
|
| 274 |
+
|
| 275 |
+
# 只有一個主應用程式 Column,並設定為預設顯示
|
| 276 |
+
with gr.Column(visible=True) as main_app_col:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
gr.Markdown(f"## 🍱 AI 心情食堂導航")
|
| 278 |
+
gr.Markdown("請看著鏡頭,讓 AI 幫你判斷今天的心情分數!")
|
| 279 |
|
| 280 |
with gr.Row():
|
| 281 |
# 左側:輸入區
|
| 282 |
with gr.Column(scale=1):
|
| 283 |
|
| 284 |
+
# 情緒偵測區塊 (維持不變)
|
| 285 |
gr.Markdown("### 📸 步驟 1:心情偵測 (選用)")
|
| 286 |
with gr.Group():
|
| 287 |
webcam_input = gr.Image(sources=["webcam"], label="看著鏡頭", streaming=True)
|
| 288 |
detect_btn = gr.Button("📸 偵測心情並自動填入 👇", variant="secondary")
|
| 289 |
|
| 290 |
gr.Markdown("### 📝 步驟 2:確認與補充")
|
|
|
|
| 291 |
score_input = gr.Radio(
|
| 292 |
["1 (心情差)", "2 (不太好)", "3 (普通)", "4 (不錯)", "5 (超棒)"],
|
| 293 |
label="1. 心情分數 (可手動修改)",
|
|
|
|
| 297 |
diary_input = gr.Textbox(lines=3, label="3. 心情日記 (選填)", placeholder="例如:今天被老闆罵了,想吃點好料的...")
|
| 298 |
|
| 299 |
debug_mode_btn = gr.Checkbox(label="🔧 顯示 Prompt 除錯資訊", value=False)
|
| 300 |
+
# 移除了 quiz_state
|
| 301 |
+
submit_btn = gr.Button("🚀 送出給 Agent", variant="primary")
|
| 302 |
|
| 303 |
+
# 右側:輸出區 (維持不變)
|
| 304 |
with gr.Column(scale=1):
|
| 305 |
agent_output = gr.Markdown(label="AI 回應")
|
| 306 |
image_output = gr.Image(label="AI 推薦美食圖", type="pil", width=400)
|
| 307 |
map_output = gr.HTML(label="地圖導航")
|
| 308 |
|
| 309 |
+
# Events (只保留情緒偵測和提交)
|
| 310 |
+
|
| 311 |
+
# 點擊偵測按鈕 -> 執行 DeepFace -> 更新 score_input
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
detect_btn.click(
|
| 313 |
fn=detect_emotion_and_map,
|
| 314 |
inputs=[webcam_input],
|
| 315 |
outputs=[webcam_input, score_input] # 同時更新圖片(畫字)和分數(Radio)
|
| 316 |
)
|
| 317 |
|
| 318 |
+
# 提交按鈕
|
| 319 |
+
# 移除了 quiz_state 的輸入
|
| 320 |
submit_btn.click(
|
| 321 |
fn=mood_agent_logic,
|
| 322 |
+
inputs=[score_input, food_input, diary_input, debug_mode_btn],
|
| 323 |
outputs=[agent_output, image_output, map_output]
|
| 324 |
)
|
| 325 |
|