Spaces:
Running
Running
update
Browse files- app.py +110 -87
- requirements.txt +2 -1
app.py
CHANGED
|
@@ -20,7 +20,7 @@ st.set_page_config(
|
|
| 20 |
# --- 2. CẤU HÌNH BACKEND ---
|
| 21 |
API_URL = "https://Vanhwbt-TraPhuHoi-Backend.hf.space/predict"
|
| 22 |
|
| 23 |
-
# ---
|
| 24 |
import gspread
|
| 25 |
import json
|
| 26 |
import io
|
|
@@ -180,41 +180,51 @@ disease_info = {
|
|
| 180 |
}
|
| 181 |
}
|
| 182 |
|
| 183 |
-
# --- Cấu hình Gemini (Phiên bản Bất Tử - Fix mọi lỗi Key) ---
|
|
|
|
|
|
|
| 184 |
try:
|
| 185 |
-
# 1.
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
GEMINI_API_KEY = secret_value
|
| 193 |
-
|
| 194 |
-
# Nếu trả về Dictionary (kiểu TOML: [TraPhuHoi] api_key = "...") -> Lấy field api_key
|
| 195 |
-
elif isinstance(secret_value, dict) and "api_key" in secret_value:
|
| 196 |
-
GEMINI_API_KEY = secret_value["api_key"]
|
| 197 |
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
|
|
|
|
|
|
|
|
|
| 201 |
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
else:
|
| 211 |
-
# Không tìm thấy
|
|
|
|
| 212 |
gemini_configured = False
|
| 213 |
model_gemini = None
|
| 214 |
|
| 215 |
except Exception as e:
|
| 216 |
# Log lỗi ra terminal server để debug nếu cần
|
| 217 |
-
print(f"Lỗi cấu hình Gemini: {e}")
|
| 218 |
gemini_configured = False
|
| 219 |
model_gemini = None
|
| 220 |
|
|
@@ -243,69 +253,82 @@ st.markdown("""
|
|
| 243 |
</div>
|
| 244 |
""", unsafe_allow_html=True)
|
| 245 |
|
| 246 |
-
# ================== 🤖 CHATBOT ==================
|
| 247 |
-
|
| 248 |
-
st.subheader("🤖 Trò chuyện cùng Chuyên Gia Trà Phú Hội")
|
| 249 |
-
|
| 250 |
-
if gemini_configured and model_gemini:
|
| 251 |
-
# [FIX] Sửa prompt thành Cây Trà
|
| 252 |
-
personality_prompt = """
|
| 253 |
-
Tôi là Bác sĩ AgriDoc, chuyên gia về CÂY TRÀ PHÚ HỘI.
|
| 254 |
-
Mục tiêu: Hỗ trợ bà con chẩn đoán bệnh trên cây trà.
|
| 255 |
-
Phong cách: Thân thiện, ngắn gọn, thực tế.
|
| 256 |
-
Quy tắc: Chỉ trả lời về cây trà. Dựa vào dữ liệu đã học.
|
| 257 |
-
"""
|
| 258 |
-
knowledge_base = "\n\n# DỮ LIỆU BỆNH:\n"
|
| 259 |
-
for key, info in disease_info.items():
|
| 260 |
-
knowledge_base += f"- {info['ten']}: {info['mo_ta']} | Xử lý: {', '.join(info['xu_ly'])}\n"
|
| 261 |
-
|
| 262 |
-
initial_context = personality_prompt + knowledge_base
|
| 263 |
-
initial_greeting = "Chào bà con! Tui là chuyên gia cây trà đây, bà con cần hỏi bệnh gì?"
|
| 264 |
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
st.session_state.clear()
|
| 268 |
-
st.rerun()
|
| 269 |
|
| 270 |
-
|
| 271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
|
| 310 |
# --- 5. GIAO DIỆN CHÍNH (HTML) ---
|
| 311 |
st.markdown("""
|
|
|
|
| 20 |
# --- 2. CẤU HÌNH BACKEND ---
|
| 21 |
API_URL = "https://Vanhwbt-TraPhuHoi-Backend.hf.space/predict"
|
| 22 |
|
| 23 |
+
# --- 2.5. HÀM LƯU DỮ LIỆU ĐA NĂNG (HF + IMGBB + SHEET) ---
|
| 24 |
import gspread
|
| 25 |
import json
|
| 26 |
import io
|
|
|
|
| 180 |
}
|
| 181 |
}
|
| 182 |
|
| 183 |
+
# --- Cấu hình Gemini (Phiên bản Bất Tử - Fix mọi lỗi Key & Quota) ---
|
| 184 |
+
import random # <--- Nhớ thêm thư viện này để bốc thăm
|
| 185 |
+
|
| 186 |
try:
|
| 187 |
+
# 1. Gom tất cả các Key vào một danh sách
|
| 188 |
+
potential_keys = ["TraPhuHoi", "TraPhuHoi1", "TraPhuHoi2", "TraPhuHoi3"]
|
| 189 |
+
available_keys = []
|
| 190 |
+
|
| 191 |
+
for key_name in potential_keys:
|
| 192 |
+
if key_name in st.secrets:
|
| 193 |
+
secret_value = st.secrets[key_name]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
|
| 195 |
+
# Xử lý: Lấy chuỗi key sạch (dù là String hay Dict)
|
| 196 |
+
real_key = None
|
| 197 |
+
if isinstance(secret_value, str):
|
| 198 |
+
real_key = secret_value
|
| 199 |
+
elif isinstance(secret_value, dict) and "api_key" in secret_value:
|
| 200 |
+
real_key = secret_value["api_key"]
|
| 201 |
|
| 202 |
+
# Nếu lấy được key thì bỏ vào rổ
|
| 203 |
+
if real_key:
|
| 204 |
+
available_keys.append(real_key)
|
| 205 |
+
|
| 206 |
+
# 2. Bốc thăm và Cấu hình
|
| 207 |
+
if available_keys:
|
| 208 |
+
# Chọn ngẫu nhiên 1 key trong danh sách
|
| 209 |
+
chosen_key = random.choice(available_keys)
|
| 210 |
+
|
| 211 |
+
# In log ra terminal để bro biết đang dùng key nào (Debug)
|
| 212 |
+
print(f"🔑 Đang sử dụng Key (đuôi): ...{chosen_key[-6:]}")
|
| 213 |
+
|
| 214 |
+
genai.configure(api_key=chosen_key)
|
| 215 |
+
|
| 216 |
+
# [QUAN TRỌNG] Dùng bản 1.5 Flash để tránh lỗi 400 và tiết kiệm
|
| 217 |
+
model_gemini = genai.GenerativeModel('gemini-1.5-flash')
|
| 218 |
+
gemini_configured = True
|
| 219 |
else:
|
| 220 |
+
# Không tìm thấy bất kỳ key nào
|
| 221 |
+
print("❌ Không tìm thấy key Gemini nào trong Secrets!")
|
| 222 |
gemini_configured = False
|
| 223 |
model_gemini = None
|
| 224 |
|
| 225 |
except Exception as e:
|
| 226 |
# Log lỗi ra terminal server để debug nếu cần
|
| 227 |
+
print(f"🔥 Lỗi cấu hình Gemini: {e}")
|
| 228 |
gemini_configured = False
|
| 229 |
model_gemini = None
|
| 230 |
|
|
|
|
| 253 |
</div>
|
| 254 |
""", unsafe_allow_html=True)
|
| 255 |
|
| 256 |
+
# ================== 🤖 CHATBOT (POWERED BY GEMMA 9B) ==================
|
| 257 |
+
from huggingface_hub import InferenceClient # Nhớ cài thư viện: pip install huggingface_hub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 258 |
|
| 259 |
+
st.divider()
|
| 260 |
+
st.subheader("🤖 Trò chuyện cùng Chuyên Gia Trà Phú Hội (Gemma AI)")
|
|
|
|
|
|
|
| 261 |
|
| 262 |
+
# --- 1. HÀM GỌI GEMMA 9B ---
|
| 263 |
+
def chat_with_gemma(prompt):
|
| 264 |
+
try:
|
| 265 |
+
# Kiểm tra Token
|
| 266 |
+
if "HF_TOKEN" not in st.secrets:
|
| 267 |
+
return "⚠️ Lỗi: Chưa có HF_TOKEN trong Secrets."
|
| 268 |
|
| 269 |
+
# Khởi tạo Client
|
| 270 |
+
client = InferenceClient(api_key=st.secrets["HF_TOKEN"])
|
| 271 |
+
|
| 272 |
+
# Thiết lập tính cách chuyên gia
|
| 273 |
+
system_instruction = """
|
| 274 |
+
Bạn là Kỹ sư Nông nghiệp chuyên về Cây Trà Phú Hội (Nhơn Trạch, Đồng Nai).
|
| 275 |
+
Nhiệm vụ: Tư vấn kỹ thuật trồng, chăm sóc, trị bệnh (Rầy nhảy, Đốm rong, Cháy lá...) cho bà con.
|
| 276 |
+
Phong cách: Thân thiện, ngắn gọn, súc tích, dùng từ ngữ bình dân dễ hiểu.
|
| 277 |
+
Tuyệt đối: Trả lời Tiếng Việt. Nếu không biết thì khuyên bà con hỏi cán bộ khuyến nông.
|
| 278 |
+
"""
|
| 279 |
+
|
| 280 |
+
# Tạo tin nhắn gửi đi
|
| 281 |
+
messages = [
|
| 282 |
+
{"role": "user", "content": f"{system_instruction}\n\nCâu hỏi của bà con: {prompt}"}
|
| 283 |
+
]
|
| 284 |
+
|
| 285 |
+
# Gọi Model Gemma-2-9B (Bản thông minh, miễn phí)
|
| 286 |
+
response = client.chat_completion(
|
| 287 |
+
model="google/gemma-2-9b-it",
|
| 288 |
+
messages=messages,
|
| 289 |
+
max_tokens=800,
|
| 290 |
+
stream=False # Tắt stream để chạy ổn định hơn trên Web
|
| 291 |
+
)
|
| 292 |
+
return response.choices[0].message.content
|
| 293 |
+
|
| 294 |
+
except Exception as e:
|
| 295 |
+
return f"😔 Kỹ sư Gemma đang bận (Lỗi kết nối): {e}"
|
| 296 |
+
|
| 297 |
+
# --- 2. GIAO DIỆN CHAT ---
|
| 298 |
+
|
| 299 |
+
# Nút xóa hội thoại
|
| 300 |
+
if st.button("🗑️ Xóa hội thoại"):
|
| 301 |
+
st.session_state.chat_history_gemini = []
|
| 302 |
+
st.rerun()
|
| 303 |
+
|
| 304 |
+
# Khởi tạo lịch sử nếu chưa có
|
| 305 |
+
if "chat_history_gemini" not in st.session_state:
|
| 306 |
+
st.session_state.chat_history_gemini = [
|
| 307 |
+
{"role": "assistant", "content": "Chào bà con! Tui là Kỹ sư Gemma (Trí tuệ nhân tạo), bà con cần hỏi bệnh gì về cây trà không?"}
|
| 308 |
+
]
|
| 309 |
+
|
| 310 |
+
# Hiện lịch sử chat cũ
|
| 311 |
+
for message in st.session_state.chat_history_gemini:
|
| 312 |
+
with st.chat_message(message["role"]):
|
| 313 |
+
st.markdown(message["content"])
|
| 314 |
+
|
| 315 |
+
# --- 3. XỬ LÝ KHI NGƯỜI DÙNG NHẬP CÂU HỎI ---
|
| 316 |
+
if user_input := st.chat_input("Hỏi về bệnh trà, cách bón phân..."):
|
| 317 |
+
# A. Hiện câu hỏi của người dùng ngay lập tức
|
| 318 |
+
st.session_state.chat_history_gemini.append({"role": "user", "content": user_input})
|
| 319 |
+
with st.chat_message("user"):
|
| 320 |
+
st.markdown(user_input)
|
| 321 |
+
|
| 322 |
+
# B. Gọi Gemma trả lời
|
| 323 |
+
with st.spinner("Kỹ sư Gemma đang suy nghĩ..."):
|
| 324 |
+
bot_reply = chat_with_gemma(user_input)
|
| 325 |
+
|
| 326 |
+
# Hiện câu trả lời của Bot
|
| 327 |
+
with st.chat_message("assistant"):
|
| 328 |
+
st.markdown(bot_reply)
|
| 329 |
+
|
| 330 |
+
# Lưu vào lịch sử
|
| 331 |
+
st.session_state.chat_history_gemini.append({"role": "assistant", "content": bot_reply})
|
| 332 |
|
| 333 |
# --- 5. GIAO DIỆN CHÍNH (HTML) ---
|
| 334 |
st.markdown("""
|
requirements.txt
CHANGED
|
@@ -4,4 +4,5 @@ pillow
|
|
| 4 |
google-generativeai
|
| 5 |
gspread
|
| 6 |
oauth2client
|
| 7 |
-
google-api-python-client
|
|
|
|
|
|
| 4 |
google-generativeai
|
| 5 |
gspread
|
| 6 |
oauth2client
|
| 7 |
+
google-api-python-client
|
| 8 |
+
huggingface_hub
|