Aqso commited on
Commit
8916ddc
Β·
verified Β·
1 Parent(s): 9fcd5e1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +50 -31
app.py CHANGED
@@ -3,12 +3,11 @@ from huggingface_hub import InferenceClient
3
  import os
4
  import json
5
  import threading
6
- from datetime import datetime
7
  import firebase_admin
8
  from firebase_admin import credentials, firestore
9
 
10
  # ==========================================
11
- # 1. ARSITEKTUR DATABASE (FIREBASE)
12
  # ==========================================
13
  firebase_initialized = False
14
  db = None
@@ -17,16 +16,20 @@ try:
17
  firebase_cred_raw = os.getenv("FIREBASE_CREDENTIALS")
18
  if firebase_cred_raw:
19
  cred_dict = json.loads(firebase_cred_raw)
 
20
  if not firebase_admin._apps:
21
  cred = credentials.Certificate(cred_dict)
22
  firebase_admin.initialize_app(cred)
23
  db = firestore.client()
24
  firebase_initialized = True
 
 
 
25
  except Exception as e:
26
- print(f"[Arsitektur Sistem] Gagal inisiasi Firebase: {e}")
27
 
28
  def push_to_firebase(user_message, ai_response):
29
- """Menulis log secara asynchronous."""
30
  if not firebase_initialized or not db: return
31
  try:
32
  db.collection("aqso_chat_logs").add({
@@ -38,39 +41,41 @@ def push_to_firebase(user_message, ai_response):
38
  print(f"[Firebase Write Error] {e}")
39
 
40
  def fetch_history_from_firebase():
41
- """Mengambil riwayat percakapan untuk Sidebar UI."""
42
  if not firebase_initialized or not db:
43
- return "⚠️ Firebase belum terhubung. Cek FIREBASE_CREDENTIALS di Secrets."
44
  try:
45
  docs = db.collection("aqso_chat_logs").order_by("timestamp", direction=firestore.Query.DESCENDING).limit(10).stream()
46
  history_text = ""
47
  for doc in docs:
48
  data = doc.to_dict()
49
- time_str = data.get('timestamp')
50
- user_msg = data.get('user', '')[:30] + "..." # Truncate panjang
51
  history_text += f"πŸ•’ {time_str}\nπŸ‘€: {user_msg}\n---\n"
52
  return history_text if history_text else "πŸ“­ Belum ada riwayat."
53
  except Exception as e:
54
  return f"❌ Gagal memuat riwayat: {e}"
55
 
56
  # ==========================================
57
- # 2. LOGIKA INFERENCE & PARSER (LAZY LOAD)
58
  # ==========================================
59
  def parse_thinking_ui(text: str) -> str:
 
60
  text = text.replace("<think>", "<details open>\n<summary>🧠 <b>Thinking Process...</b></summary>\n\n> ")
61
  text = text.replace("</think>", "\n</details>\n\n---\n\n")
62
  return text
63
 
64
  def respond(message, history, system_message, max_tokens, temperature, top_p):
65
- # Lazy Initialization untuk mencegah Bug API Key saat Docker Build
66
  hf_token = os.getenv("HF_TOKEN")
67
  if not hf_token:
68
- yield "❌ **FATAL ERROR:** HF_TOKEN tidak ditemukan di sistem. Pastikan variabel Secrets sudah diset dan Space telah di-rebuild."
69
  return
70
 
 
71
  client = InferenceClient("deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", token=hf_token)
72
  messages = [{"role": "system", "content": system_message}]
73
 
 
74
  for user_msg, ai_msg in history:
75
  if user_msg: messages.append({"role": "user", "content": user_msg})
76
  if ai_msg: messages.append({"role": "assistant", "content": ai_msg})
@@ -84,51 +89,64 @@ def respond(message, history, system_message, max_tokens, temperature, top_p):
84
  temperature=temperature, top_p=top_p
85
  )
86
  for msg in stream:
87
- # Bypass "List Index Out of Range" dengan validasi obyek statis
88
- if hasattr(msg, 'choices') and len(msg.choices) > 0:
89
- delta = msg.choices[0].delta
90
- if hasattr(delta, 'content') and delta.content is not None:
91
- response += delta.content
92
- yield parse_thinking_ui(response)
93
-
 
 
 
 
 
 
94
  if response:
95
  threading.Thread(target=push_to_firebase, args=(message, response)).start()
96
 
97
  except Exception as e:
98
- yield f"⚠️ **Inference API Error:** {str(e)}\n\n*Jika API membalas error, model di backend HF mungkin sedang Cold Start (memuat). Coba lagi dalam 1 menit.*"
 
 
99
 
100
  # ==========================================
101
- # 3. ARSITEKTUR UI (PREMIUM CLAUDE UX)
102
  # ==========================================
103
  custom_css = """
104
- /* CSS Injeksi untuk UI Premium */
105
  .gradio-container { background-color: #0B0F19 !important; font-family: 'Inter', sans-serif; }
106
  .contain { border-radius: 12px !important; border: 1px solid #1E293B !important; background-color: #111827 !important;}
107
  button { border-radius: 8px !important; }
108
  textarea { background-color: #1F2937 !important; color: white !important; border: 1px solid #374151 !important; }
109
- .message.user { background-color: #3B82F6 !important; color: white; }
110
- .message.bot { background-color: transparent !important; border: 1px solid #374151 !important; }
111
  """
112
 
113
- default_prompt = "You are an elite Senior Developer and Expert Scraper built for AQSO. Prioritize absolute logical accuracy."
 
 
 
 
 
114
 
115
  with gr.Blocks(theme=gr.themes.Monochrome(), css=custom_css, title="AQSO Engine") as demo:
116
  with gr.Row():
117
- # Kolom Kiri: Sidebar Riwayat
118
  with gr.Column(scale=1, min_width=250):
119
- gr.Markdown("### πŸ—„οΈ Riwayat Sesi")
120
  btn_refresh = gr.Button("πŸ”„ Muat Ulang Riwayat", variant="primary")
121
- txt_history = gr.Textbox(label="10 Percakapan Terakhir", lines=25, interactive=False, value="Klik tombol muat ulang...")
122
 
123
- # Action Listener Sidebar
124
  btn_refresh.click(fn=fetch_history_from_firebase, inputs=None, outputs=txt_history)
125
 
126
- # Kolom Kanan: Main Chat Engine
127
  with gr.Column(scale=4):
128
- gr.Markdown("## ⚑ AQSO Thinking Engine")
129
 
130
  with gr.Accordion("βš™οΈ Konfigurasi Arsitektur Engine", open=False):
131
- sys_prompt = gr.Textbox(value=default_prompt, label="System Prompt", lines=2)
132
  with gr.Row():
133
  max_tok = gr.Slider(1, 8192, 4096, step=1, label="Max Tokens")
134
  temp = gr.Slider(0.1, 2.0, 0.6, step=0.1, label="Temperature")
@@ -141,5 +159,6 @@ with gr.Blocks(theme=gr.themes.Monochrome(), css=custom_css, title="AQSO Engine"
141
  )
142
 
143
  if __name__ == "__main__":
 
144
  demo.launch(server_name="0.0.0.0", server_port=7860)
145
 
 
3
  import os
4
  import json
5
  import threading
 
6
  import firebase_admin
7
  from firebase_admin import credentials, firestore
8
 
9
  # ==========================================
10
+ # 1. ARSITEKTUR DATABASE (FIREBASE FIRESTORE)
11
  # ==========================================
12
  firebase_initialized = False
13
  db = None
 
16
  firebase_cred_raw = os.getenv("FIREBASE_CREDENTIALS")
17
  if firebase_cred_raw:
18
  cred_dict = json.loads(firebase_cred_raw)
19
+ # Mencegah error inisialisasi ganda di environment Gradio
20
  if not firebase_admin._apps:
21
  cred = credentials.Certificate(cred_dict)
22
  firebase_admin.initialize_app(cred)
23
  db = firestore.client()
24
  firebase_initialized = True
25
+ print("[System Architect] Firebase berhasil terhubung.")
26
+ else:
27
+ print("[System Architect Warning] FIREBASE_CREDENTIALS tidak ditemukan di Secrets.")
28
  except Exception as e:
29
+ print(f"[System Architect Error] Gagal inisiasi Firebase: {e}")
30
 
31
  def push_to_firebase(user_message, ai_response):
32
+ """Menulis log secara asynchronous tanpa memblokir thread UI Gradio."""
33
  if not firebase_initialized or not db: return
34
  try:
35
  db.collection("aqso_chat_logs").add({
 
41
  print(f"[Firebase Write Error] {e}")
42
 
43
  def fetch_history_from_firebase():
44
+ """Mengambil 10 riwayat terakhir untuk ditampilkan di Sidebar UI."""
45
  if not firebase_initialized or not db:
46
+ return "⚠️ Firebase belum terhubung. Cek FIREBASE_CREDENTIALS di Secrets Space."
47
  try:
48
  docs = db.collection("aqso_chat_logs").order_by("timestamp", direction=firestore.Query.DESCENDING).limit(10).stream()
49
  history_text = ""
50
  for doc in docs:
51
  data = doc.to_dict()
52
+ time_str = data.get('timestamp', 'Waktu Tidak Diketahui')
53
+ user_msg = str(data.get('user', ''))[:40] + "..."
54
  history_text += f"πŸ•’ {time_str}\nπŸ‘€: {user_msg}\n---\n"
55
  return history_text if history_text else "πŸ“­ Belum ada riwayat."
56
  except Exception as e:
57
  return f"❌ Gagal memuat riwayat: {e}"
58
 
59
  # ==========================================
60
+ # 2. LOGIKA INFERENCE & PARSER (DEFENSIVE ZERO ERROR)
61
  # ==========================================
62
  def parse_thinking_ui(text: str) -> str:
63
+ """Mengubah tag <think> bawaan model menjadi UI Collapsible ala Claude."""
64
  text = text.replace("<think>", "<details open>\n<summary>🧠 <b>Thinking Process...</b></summary>\n\n> ")
65
  text = text.replace("</think>", "\n</details>\n\n---\n\n")
66
  return text
67
 
68
  def respond(message, history, system_message, max_tokens, temperature, top_p):
 
69
  hf_token = os.getenv("HF_TOKEN")
70
  if not hf_token:
71
+ yield "❌ **FATAL ERROR:** HF_TOKEN tidak ditemukan. Pastikan variabel Secrets sudah diset dan Space telah di-Factory Rebuild."
72
  return
73
 
74
+ # Lazy Initialization: Hanya panggil client saat fungsi dieksekusi, mencegah error Docker Build
75
  client = InferenceClient("deepseek-ai/DeepSeek-R1-Distill-Qwen-32B", token=hf_token)
76
  messages = [{"role": "system", "content": system_message}]
77
 
78
+ # Reconstruct history memori sesi saat ini
79
  for user_msg, ai_msg in history:
80
  if user_msg: messages.append({"role": "user", "content": user_msg})
81
  if ai_msg: messages.append({"role": "assistant", "content": ai_msg})
 
89
  temperature=temperature, top_p=top_p
90
  )
91
  for msg in stream:
92
+ try:
93
+ # Validasi Null-Check (Zero Error Logic) untuk bypass error "list index out of range"
94
+ if hasattr(msg, 'choices') and isinstance(msg.choices, list) and len(msg.choices) > 0:
95
+ delta = msg.choices[0].delta
96
+ if hasattr(delta, 'content') and delta.content is not None:
97
+ response += delta.content
98
+ yield parse_thinking_ui(response)
99
+ except Exception as chunk_err:
100
+ # Jika server HF mengirim chunk rusak, hiraukan dan lanjut ke chunk berikutnya
101
+ print(f"[System Architect Warning] Malformed stream chunk ignored: {chunk_err}")
102
+ continue
103
+
104
+ # Simpan ke Database Firebase hanya jika generate selesai dan memiliki response
105
  if response:
106
  threading.Thread(target=push_to_firebase, args=(message, response)).start()
107
 
108
  except Exception as e:
109
+ # Menyelamatkan teks yang sudah sempat diketik sebelum koneksi HF terputus
110
+ error_msg = f"\n\n⚠️ **[Koneksi HF API Terputus]:** Server kelebihan beban atau timeout. Teks diselamatkan sejauh ini. Error internal: {str(e)}"
111
+ yield parse_thinking_ui(response) + error_msg
112
 
113
  # ==========================================
114
+ # 3. ARSITEKTUR UI (PREMIUM LAYOUT)
115
  # ==========================================
116
  custom_css = """
117
+ /* Injeksi CSS Premium Dark Mode */
118
  .gradio-container { background-color: #0B0F19 !important; font-family: 'Inter', sans-serif; }
119
  .contain { border-radius: 12px !important; border: 1px solid #1E293B !important; background-color: #111827 !important;}
120
  button { border-radius: 8px !important; }
121
  textarea { background-color: #1F2937 !important; color: white !important; border: 1px solid #374151 !important; }
122
+ .message.user { background-color: #3B82F6 !important; color: white !important; }
123
+ .message.bot { background-color: transparent !important; border: 1px solid #374151 !important; color: white !important; }
124
  """
125
 
126
+ default_prompt = (
127
+ "You are an elite Senior Developer, System Architect, and Expert Scraper built for AQSO. "
128
+ "Prioritize Absolute Logical Accuracy & Zero Error. "
129
+ "Always simulate logic internally before outputting. For scraping tasks, proactively implement stealth mitigations, "
130
+ "WAF/Cloudflare bypasses, and dynamic rendering handling."
131
+ )
132
 
133
  with gr.Blocks(theme=gr.themes.Monochrome(), css=custom_css, title="AQSO Engine") as demo:
134
  with gr.Row():
135
+ # Kolom Kiri: Sidebar Riwayat Firebase
136
  with gr.Column(scale=1, min_width=250):
137
+ gr.Markdown("### πŸ—„οΈ Riwayat Sesi (Firebase)")
138
  btn_refresh = gr.Button("πŸ”„ Muat Ulang Riwayat", variant="primary")
139
+ txt_history = gr.Textbox(label="10 Percakapan Terakhir", lines=25, interactive=False, value="Klik tombol muat ulang untuk mengambil data dari Firestore...")
140
 
141
+ # Binding tombol ke fungsi fetch Firestore
142
  btn_refresh.click(fn=fetch_history_from_firebase, inputs=None, outputs=txt_history)
143
 
144
+ # Kolom Kanan: Engine Chat & Konfigurasi
145
  with gr.Column(scale=4):
146
+ gr.Markdown("## ⚑ AQSO Thinking Engine (Defensive Architecture)")
147
 
148
  with gr.Accordion("βš™οΈ Konfigurasi Arsitektur Engine", open=False):
149
+ sys_prompt = gr.Textbox(value=default_prompt, label="System Prompt", lines=4)
150
  with gr.Row():
151
  max_tok = gr.Slider(1, 8192, 4096, step=1, label="Max Tokens")
152
  temp = gr.Slider(0.1, 2.0, 0.6, step=0.1, label="Temperature")
 
159
  )
160
 
161
  if __name__ == "__main__":
162
+ # Server binding wajib untuk HF Spaces Docker
163
  demo.launch(server_name="0.0.0.0", server_port=7860)
164