adamtobegreat commited on
Commit
d5b36d8
·
verified ·
1 Parent(s): 3c83449

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -29
app.py CHANGED
@@ -1,12 +1,13 @@
1
  """
2
  ======================================================
3
  📘 金融客服小智(Fintech Assistant)
4
- 版本:v2.1 (Hugging Face 部署版)
5
- 改進重點:
6
- 1. 改用記憶體型 Chroma,避免 PersistentClient 錯誤
7
- 2. 路徑使用 os.getcwd() 以符合 HF Spaces
8
- 3. 加入 QA 檔案容錯與模擬模式
9
- 4. GOOGLE_API_KEY 以 Secrets 管理
 
10
  ======================================================
11
  """
12
 
@@ -39,7 +40,7 @@ LOGO_PATH = os.path.join(BASE_DIR, "mega.png")
39
 
40
  API_KEY = os.getenv("GOOGLE_API_KEY")
41
  if not API_KEY:
42
- print("⚠️ 尚未設定 GOOGLE_API_KEY,將使用模擬模式。")
43
 
44
 
45
  # =============================================
@@ -74,23 +75,16 @@ else:
74
 
75
 
76
  # =============================================
77
- # 3️⃣ 向量資料庫初始化(記憶體型)
78
  # =============================================
79
- try:
80
- client = chromadb.Client()
81
- except Exception:
82
- import chromadb.api
83
- client = chromadb.api.Client()
84
-
85
  collection_map = {"證券": "stocks", "期貨": "futures", "複委託": "overseas"}
86
  vectordbs = {}
87
-
88
  for cat, docs in qa_docs.items():
89
  vectordb = Chroma(client=client, collection_name=collection_map[cat], embedding_function=embedding)
90
  if hasattr(vectordb._collection, "count") and vectordb._collection.count() == 0 and docs:
91
  vectordb.add_documents(docs)
92
  vectordbs[cat] = vectordb
93
-
94
  print("✅ 向量資料庫初始化完成。")
95
 
96
 
@@ -137,7 +131,7 @@ def chat_fn(message, history):
137
  response = llm.invoke(prompt)
138
  reply = getattr(response, "content", None) or getattr(response, "text", "⚠️ 無回覆")
139
  else:
140
- reply = "(模擬模式)這是示範回覆,請確認已設定 GOOGLE_API_KEY。"
141
  except Exception as e:
142
  reply = f"⚠️ 生成錯誤:{e}"
143
 
@@ -147,7 +141,7 @@ def chat_fn(message, history):
147
 
148
 
149
  # =============================================
150
- # 6️⃣ Gradio 介面
151
  # =============================================
152
  logo_base64 = ""
153
  if os.path.exists(LOGO_PATH):
@@ -164,22 +158,80 @@ with gr.Blocks(
164
  pointer-events: none;
165
  }
166
  #logo-top img { width: 120px; height: auto; display: block; }
 
167
  #footer { text-align:center; font-size:13px; color:#aaa; margin-top: 20px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  """
169
  ) as demo:
170
  if logo_base64:
171
  gr.HTML(f"<div id='logo-top'><img src='data:image/png;base64,{logo_base64}'></div>")
172
 
173
- gr.Markdown("## 👨‍💼 我是小智 · 您的金融好幫手 🫰")
174
- gr.Markdown("Powered by Gemini & LangChain")
175
-
176
- with gr.Row():
177
- with gr.Column(scale=4):
 
 
 
 
 
 
178
  chatbot = gr.Chatbot(label="💬 對話紀錄", type="messages", height=500)
179
- user_input = gr.Textbox(placeholder="請輸入問題...", show_label=False)
180
- send_btn = gr.Button("送出", variant="primary")
 
 
 
181
 
182
  def handle_input(message, history):
 
 
183
  if not message.strip():
184
  return history, gr.update(value="")
185
  reply = chat_fn(message, history)
@@ -190,9 +242,8 @@ with gr.Blocks(
190
  user_input.submit(handle_input, [user_input, chatbot], [chatbot, user_input])
191
  send_btn.click(handle_input, [user_input, chatbot], [chatbot, user_input])
192
 
193
- gr.Button("🧹 清除對話").click(lambda: ([], gr.update(value="")), outputs=[chatbot, user_input])
194
-
195
- with gr.Column(scale=1):
196
  gr.Markdown("### 🔍 常見問題")
197
  examples = [
198
  "未成年可以開戶嗎?",
@@ -203,7 +254,18 @@ with gr.Blocks(
203
  "美股可以定期定額嗎?"
204
  ]
205
  for q in examples:
206
- gr.Button(q).click(lambda h, q=q: handle_input(q, h), [chatbot], [chatbot, user_input])
 
 
 
 
 
 
 
 
 
 
 
207
 
208
  gr.HTML("<div id='footer'>© Fintech Assistant — 僅業務使用,非官方授權</div>")
209
 
 
1
  """
2
  ======================================================
3
  📘 金融客服小智(Fintech Assistant)
4
+ 版本:v3 (Hugging Face 行動優化版)
5
+ 更新重點:
6
+ 1. 手機/桌面自適應 (自動換行 + Logo 縮放)
7
+ 2. 桌面標題置中、手機上下排列
8
+ 3. LINE 風格輸入框
9
+ 4. 常見問題按鈕可直接提問不報錯
10
+ 5. 清除對話按鈕移到右側欄位底部
11
  ======================================================
12
  """
13
 
 
40
 
41
  API_KEY = os.getenv("GOOGLE_API_KEY")
42
  if not API_KEY:
43
+ print("⚠️ 尚未設定 GOOGLE_API_KEY,系統將以模擬模式運行。")
44
 
45
 
46
  # =============================================
 
75
 
76
 
77
  # =============================================
78
+ # 3️⃣ 向量資料庫初始化
79
  # =============================================
80
+ client = chromadb.Client()
 
 
 
 
 
81
  collection_map = {"證券": "stocks", "期貨": "futures", "複委託": "overseas"}
82
  vectordbs = {}
 
83
  for cat, docs in qa_docs.items():
84
  vectordb = Chroma(client=client, collection_name=collection_map[cat], embedding_function=embedding)
85
  if hasattr(vectordb._collection, "count") and vectordb._collection.count() == 0 and docs:
86
  vectordb.add_documents(docs)
87
  vectordbs[cat] = vectordb
 
88
  print("✅ 向量資料庫初始化完成。")
89
 
90
 
 
131
  response = llm.invoke(prompt)
132
  reply = getattr(response, "content", None) or getattr(response, "text", "⚠️ 無回覆")
133
  else:
134
+ reply = "(模擬模式)這是示範回覆,請確認是否已設定 GOOGLE_API_KEY。"
135
  except Exception as e:
136
  reply = f"⚠️ 生成錯誤:{e}"
137
 
 
141
 
142
 
143
  # =============================================
144
+ # 6️⃣ Gradio 介面(手機自適應 + LINE樣式 + 清除鍵右欄)
145
  # =============================================
146
  logo_base64 = ""
147
  if os.path.exists(LOGO_PATH):
 
158
  pointer-events: none;
159
  }
160
  #logo-top img { width: 120px; height: auto; display: block; }
161
+
162
  #footer { text-align:center; font-size:13px; color:#aaa; margin-top: 20px; }
163
+
164
+ /* 💬 LINE風格輸入列 */
165
+ .input-container {
166
+ display: flex;
167
+ flex-direction: row;
168
+ align-items: center;
169
+ border: 1px solid #ccc;
170
+ border-radius: 25px;
171
+ padding: 5px 8px;
172
+ background-color: #fff;
173
+ }
174
+ .input-container textarea {
175
+ border: none !important;
176
+ outline: none !important;
177
+ resize: none;
178
+ box-shadow: none !important;
179
+ font-size: 15px;
180
+ padding: 8px 10px;
181
+ width: 100%;
182
+ }
183
+ .send-btn {
184
+ border-radius: 20px !important;
185
+ padding: 6px 14px !important;
186
+ margin-left: 6px !important;
187
+ background-color: #007bff !important;
188
+ color: white !important;
189
+ border: none !important;
190
+ }
191
+
192
+ /* 🔸 手機版自適應設定 */
193
+ @media (max-width: 768px) {
194
+ #logo-top img { width: 90px; }
195
+ .gradio-container { padding: 8px; }
196
+ #footer { font-size: 12px; margin-top: 10px; }
197
+ #main-title { font-size: 22px !important; line-height: 1.4; }
198
+ #main-title br { display: inline; }
199
+ }
200
+
201
+ /* 桌面版標題置中 */
202
+ #main-title {
203
+ text-align: center;
204
+ font-size: 26px;
205
+ margin-top: 60px;
206
+ margin-bottom: 6px;
207
+ font-weight: bold;
208
+ }
209
  """
210
  ) as demo:
211
  if logo_base64:
212
  gr.HTML(f"<div id='logo-top'><img src='data:image/png;base64,{logo_base64}'></div>")
213
 
214
+ # 🔹 標題(桌面置中、手機換行)
215
+ gr.HTML("""
216
+ <div id="main-title">
217
+ 👨‍💼 我是小智<br>您的金融好幫手 🫰
218
+ </div>
219
+ """)
220
+ gr.Markdown("<div style='text-align:center; color:gray;'>Powered by Gemini & LangChain</div>")
221
+
222
+ with gr.Row(equal_height=False, wrap=True):
223
+ # 左側:聊天區
224
+ with gr.Column(scale=4, min_width=300):
225
  chatbot = gr.Chatbot(label="💬 對話紀錄", type="messages", height=500)
226
+
227
+ # 🟢 LINE風格輸入框
228
+ with gr.Group(elem_classes="input-container"):
229
+ user_input = gr.Textbox(placeholder="輸入訊息...", show_label=False)
230
+ send_btn = gr.Button("送出", elem_classes="send-btn")
231
 
232
  def handle_input(message, history):
233
+ if history is None:
234
+ history = []
235
  if not message.strip():
236
  return history, gr.update(value="")
237
  reply = chat_fn(message, history)
 
242
  user_input.submit(handle_input, [user_input, chatbot], [chatbot, user_input])
243
  send_btn.click(handle_input, [user_input, chatbot], [chatbot, user_input])
244
 
245
+ # 右側:常見問題 + 清除對話
246
+ with gr.Column(scale=1, min_width=200):
 
247
  gr.Markdown("### 🔍 常見問題")
248
  examples = [
249
  "未成年可以開戶嗎?",
 
254
  "美股可以定期定額嗎?"
255
  ]
256
  for q in examples:
257
+ gr.Button(q).click(
258
+ fn=lambda q=q, history=[]: handle_input(q, history),
259
+ inputs=[],
260
+ outputs=[chatbot, user_input]
261
+ )
262
+
263
+ # ✅ 清除按鈕
264
+ def clear_all():
265
+ memory.clear()
266
+ return [], gr.update(value="")
267
+ gr.Markdown("---")
268
+ gr.Button("🧹 清除對話").click(clear_all, outputs=[chatbot, user_input])
269
 
270
  gr.HTML("<div id='footer'>© Fintech Assistant — 僅業務使用,非官方授權</div>")
271