PhilipL commited on
Commit
e0da6a6
·
verified ·
1 Parent(s): 0d1630d

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -255
app.py DELETED
@@ -1,255 +0,0 @@
1
- import gradio as gr
2
- from openai import OpenAI
3
- from huggingface_hub import InferenceClient
4
- import os
5
- import time
6
- import requests
7
- import urllib.parse
8
- import pandas as pd
9
- from langchain_huggingface import HuggingFaceEmbeddings
10
- from langchain_community.vectorstores import FAISS
11
- from PIL import Image
12
- from dotenv import load_dotenv
13
-
14
- # --- [新增] 匯入組員的模組 ---
15
- import teammate_logic
16
- # ---------------------------
17
-
18
- load_dotenv() # 自動尋找並載入 .env 檔案中的變數
19
-
20
- # ==========================================
21
- # 0. 環境變數
22
- # ==========================================
23
- GROQ_API_KEY = os.getenv("GROQ_API_KEY")
24
- HF_TOKEN = os.getenv("HF_TOKEN")
25
-
26
- # 全域變數
27
- global_df = None
28
- global_mood_df = None
29
- global_retriever = None
30
- rag_initialized = False
31
-
32
- # ==========================================
33
- # 1. 系統初始化 (讀取 CSV + 讀取 FAISS)
34
- # ==========================================
35
- def init_rag_system():
36
- global global_df, global_mood_df, global_retriever, rag_initialized
37
-
38
- if rag_initialized: return
39
-
40
- print("⏳ 正在初始化系統...")
41
-
42
- # --- A. 讀取 CSV (用於篩選與對照) ---
43
- try:
44
- global_df = pd.read_csv('restaurants.csv')
45
- global_df['RAG_Content'] = global_df['RAG_Content'].fillna("")
46
- global_df['Category'] = global_df['Category'].fillna("其他")
47
-
48
- global_mood_df = pd.read_csv('mood_food_guide.csv')
49
- print(f"✅ CSV 資料讀取成功 (餐廳: {len(global_df)} 筆)")
50
- except Exception as e:
51
- print(f"❌ CSV 讀取失敗: {e}")
52
- return
53
-
54
- # --- B. 讀取預先建立好的 FAISS 索引 ---
55
- if os.path.exists("faiss_index"):
56
- try:
57
- print("⏳ 正在載入 FAISS 向量資料庫...")
58
- embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
59
-
60
- vectorstore = FAISS.load_local(
61
- "faiss_index",
62
- embeddings,
63
- allow_dangerous_deserialization=True
64
- )
65
- global_retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
66
- print("✅ FAISS 資料庫載入完成!")
67
- except Exception as e:
68
- print(f"❌ FAISS 載入失敗: {e}")
69
- else:
70
- print("⚠️ 警告:找不到 'faiss_index' 資料夾,RAG 功能將無法使用。")
71
- print("請先執行 build_index.py 並上傳資料夾。")
72
-
73
- rag_initialized = True
74
-
75
- # ==========================================
76
- # 2. 核心功能
77
- # ==========================================
78
-
79
- def get_restaurant_data(mood_score_str, food_choice):
80
- init_rag_system()
81
- if global_df is None or global_df.empty: return None, True, "資料庫未載入", "無建議"
82
-
83
- try:
84
- score = int(str(mood_score_str).split(' ')[0])
85
- except:
86
- score = 3
87
-
88
- mood_info = global_mood_df[global_mood_df['分數'] == score]
89
- if not mood_info.empty:
90
- rec_categories = mood_info.iloc[0]['推薦料理類別']
91
- mood_reason = mood_info.iloc[0]['原因']
92
- else:
93
- rec_categories = ""
94
- mood_reason = "隨意探索"
95
-
96
- candidates = global_df.copy()
97
-
98
- if rec_categories:
99
- candidates = candidates[candidates['Category'].apply(lambda x: str(x) in str(rec_categories) or str(rec_categories) in str(x))]
100
-
101
- food_keyword = "飯" if food_choice == "吃飯" else "麵" if food_choice == "吃麵" else ""
102
- if food_keyword:
103
- candidates = candidates[
104
- candidates['Name'].str.contains(food_keyword, case=False, na=False) |
105
- candidates['RAG_Content'].str.contains(food_keyword, case=False, na=False)
106
- ]
107
-
108
- if candidates.empty:
109
- result = global_df.sample(1).iloc[0]
110
- is_random = True
111
- else:
112
- result = candidates.sample(1).iloc[0]
113
- is_random = False
114
-
115
- return result, is_random, rec_categories, mood_reason
116
-
117
- def generate_content_with_groq(restaurant_name, restaurant_detail, user_diary, mood_score, mood_guide_reason, debug_mode=False):
118
- if not GROQ_API_KEY: return "⚠️ 請設定 GROQ_API_KEY"
119
-
120
- client = OpenAI(api_key=GROQ_API_KEY, base_url="https://api.groq.com/openai/v1")
121
-
122
- system_prompt = "你是一個幽默、懂吃且善解人意的 AI 朋友。請根據使用者的日記、心情以及「心情美食指南」來推薦餐廳。"
123
-
124
- # 移除了測驗相關的 user_msg 內容
125
- user_msg = f"""
126
- 【狀態】心情分數:{mood_score}
127
- 【日記】{user_diary}
128
-
129
- 【心情美食指南建議】
130
- 因為分數是 {mood_score},建議吃這類食物的原因是:「{mood_guide_reason}」。
131
-
132
- 【推薦餐廳】
133
- 名稱:{restaurant_name}
134
- 資料:{restaurant_detail}
135
-
136
- 任務:
137
- 請用繁體中文寫一段溫暖有趣的回覆:
138
- 1. 綜合回應他的日記與心情。
139
- 2. 引用「心情美食指南」的原因。
140
- 3. 介紹這家餐廳的特色。
141
- (只��要回覆文字內容)
142
- """
143
-
144
- try:
145
- response = client.chat.completions.create(
146
- model="llama-3.3-70b-versatile", messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": user_msg}]
147
- )
148
- content = response.choices[0].message.content
149
-
150
- # 如果開啟除錯模式,附加 Prompt 資訊
151
- if debug_mode:
152
- debug_info = f"""
153
- \n\n--- 🛠️ [DEBUG] LLM Prompt 檢查 ---
154
- \n**System Prompt:**\n{system_prompt}
155
- \n**User Message:**\n{user_msg}
156
- \n-----------------------------------
157
- """
158
- return content + debug_info
159
-
160
- return content
161
- except Exception as e:
162
- return f"Groq Error: {str(e)}"
163
-
164
- def generate_image_huggingface(prompt):
165
- if not HF_TOKEN: return None
166
- if not prompt or pd.isna(prompt): prompt = "Delicious gourmet food, photorealistic, 8k"
167
-
168
- try:
169
- hf_client = InferenceClient(token=HF_TOKEN)
170
- return hf_client.text_to_image(prompt=prompt, model="stabilityai/stable-diffusion-xl-base-1.0")
171
- except Exception as e:
172
- print(f"❌ 生圖失敗: {e}")
173
- return None
174
-
175
- def mood_agent_logic(score_input, food_input, diary_input, debug_mode):
176
- restaurant, is_random, rec_categories, mood_reason = get_restaurant_data(score_input, food_input)
177
-
178
- if restaurant is None:
179
- yield "資料庫讀取錯誤", None, ""
180
- return
181
-
182
- name = restaurant['Name']
183
- address = restaurant['Address']
184
- url = restaurant['URL']
185
- img_prompt = restaurant.get('Visual_prompt')
186
- note = "(隨機推薦)" if is_random else ""
187
-
188
- # RAG 檢索
189
- rag_info = str(restaurant.get('RAG_Content', ''))
190
- if global_retriever:
191
- docs = global_retriever.invoke(name)
192
- if docs:
193
- rag_info = "\n".join([d.page_content for d in docs])
194
-
195
- # 呼叫生成函式 (已移除 quiz_state 參數)
196
- ai_text = generate_content_with_groq(name, rag_info, diary_input, score_input, mood_reason, debug_mode)
197
-
198
- # 如果開啟除錯模式,附加圖片 Prompt 資訊
199
- if debug_mode:
200
- img_debug_info = f"""
201
- \n\n--- 🛠️ [DEBUG] 圖片生成檢查 ---
202
- \n**使用的 Visual Prompt:**\n{img_prompt}
203
- \n(如果上方圖片為空白,可能是 HF API 忙碌或 Prompt 無效)
204
- \n-----------------------------------
205
- """
206
- ai_text += img_debug_info
207
-
208
- final_response = f"### 🍽️ 推薦:{name} {note}\n\n{ai_text}"
209
- map_html = f'<div style="text-align:center"><a href="{url}" target="_blank" style="background:#4CAF50;color:white;padding:8px 16px;border-radius:20px;text-decoration:none">🗺️ Google Map 導航</a></div>'
210
-
211
- yield final_response, None, map_html
212
- image_output = generate_image_huggingface(img_prompt)
213
- yield final_response, image_output, map_html
214
-
215
- # ==========================================
216
- # 3. Gradio 介面建構
217
- # ==========================================
218
-
219
- # 結合組員的 CSS
220
- combined_css = teammate_logic.css
221
-
222
- with gr.Blocks(title="AI 心情食堂", css=combined_css) as demo:
223
-
224
- with gr.Tabs():
225
- # Tab 1: 主功能區 (已移除 Q1-Q5,直接顯示點餐介面)
226
- with gr.TabItem("🍽️ AI 心情食堂 (點餐推薦)", id=0):
227
- with gr.Column():
228
- gr.Markdown(f"## 🍱 呷飽沒??!")
229
- with gr.Row():
230
- with gr.Column(scale=1):
231
- score_input = gr.Radio(["1 (心情差)", "2 (不太好)", "3 (普通)", "4 (不錯)", "5 (超棒)"], label="1. 心情分數", value="3 (普通)")
232
- food_input = gr.Radio(["吃飯", "吃麵", "隨便"], label="2. 想吃什麼", value="隨便")
233
- diary_input = gr.Textbox(lines=4, label="3. 心情日記", placeholder="寫下今天發生的事...")
234
-
235
- debug_mode_btn = gr.Checkbox(label="🔧 開啟除錯模式 (顯示 Prompt 與圖片資訊)", value=False)
236
-
237
- submit_btn = gr.Button("送出給 Agent", variant="primary")
238
- with gr.Column(scale=1):
239
- agent_output = gr.Markdown(label="AI 回應")
240
- image_output = gr.Image(label="AI 推薦美食圖", type="pil", width=400)
241
- map_output = gr.HTML(label="地圖導航")
242
-
243
- # Tab 2: 組員功能
244
- with gr.TabItem("情緒辨識 (組員功能)", id=1):
245
- teammate_logic.render_ui() # 呼叫組員的介面渲染函式
246
-
247
- # 事件綁定 (只保留主功能)
248
- submit_btn.click(
249
- fn=mood_agent_logic,
250
- inputs=[score_input, food_input, diary_input, debug_mode_btn],
251
- outputs=[agent_output, image_output, map_output]
252
- )
253
-
254
- if __name__ == "__main__":
255
- demo.launch(ssr_mode=False)