Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -109,6 +109,40 @@ def save_chat_context(new_context, user_chats, current_chat_title, logged_in_use
|
|
| 109 |
save_chats(logged_in_username, user_chats, auth_globals)
|
| 110 |
return user_chats, "✅ Context saved!"
|
| 111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
# ================== HELPER: No conversion needed (Gradio expects messages format) ==================
|
| 113 |
def format_history_for_chatbot(history):
|
| 114 |
"""Return history as-is (already dict format)"""
|
|
@@ -177,27 +211,37 @@ def handle_chat(msg, chatbot, current_model, logged_in_username, user_chats_stat
|
|
| 177 |
if not current_model:
|
| 178 |
gr.Warning("⚠️ Load a model first!")
|
| 179 |
return chatbot
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
|
|
|
| 183 |
return chatbot
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
|
| 189 |
-
# Chatbot is already in correct dict format (no conversion needed)
|
| 190 |
internal_history = chatbot[:] if chatbot else []
|
| 191 |
|
| 192 |
-
# Call chat_stream with internal dict history
|
| 193 |
try:
|
| 194 |
stream_gen = chat_stream(msg, internal_history, current_model, logged_in_username, user_chats_state, current_chat_title, mode_radio, auth_globals_state, file_upload, current_chat_context)
|
| 195 |
|
| 196 |
for updated_history in stream_gen:
|
| 197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
except Exception as e:
|
| 199 |
-
gr.Warning(f"⚠️
|
| 200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
|
| 202 |
|
| 203 |
# ================== GUI ==================
|
|
@@ -216,6 +260,8 @@ with gr.Blocks(title="NVIDIA AI Chat") as demo:
|
|
| 216 |
auth_globals_state = gr.State(auth_globals)
|
| 217 |
coding_files_state = gr.State({}) # { "filename.py": {"content": "...", "language": "python"} }
|
| 218 |
current_file_name = gr.State(None)
|
|
|
|
|
|
|
| 219 |
status_label = gr.Markdown("⚠️ No model loaded. Login + create/select a chat to begin.", elem_id="status-bold") # Made more prominent
|
| 220 |
|
| 221 |
|
|
@@ -289,6 +335,14 @@ with gr.Blocks(title="NVIDIA AI Chat") as demo:
|
|
| 289 |
)
|
| 290 |
send_btn = gr.Button("➤", variant="primary", scale=1)
|
| 291 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
# Authentication (clean version - no phone/OTP)
|
| 293 |
with gr.Row():
|
| 294 |
username_input = gr.Textbox(label="Username (will be forced lowercase)", scale=2, placeholder="admin")
|
|
@@ -333,6 +387,34 @@ with gr.Blocks(title="NVIDIA AI Chat") as demo:
|
|
| 333 |
confirm_delete_btn.click(delete_account, [confirm_username_input, logged_in_username, auth_globals_state], [status_label, logged_in_username, logout_btn, delete_acc_btn, confirm_row, user_chats_state, current_chat_title, chatbot, chat_dropdown, chat_context_tb, save_context_btn])
|
| 334 |
|
| 335 |
save_context_btn.click(save_chat_context, [chat_context_tb, user_chats_state, current_chat_title, logged_in_username], [user_chats_state, status_label])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
|
| 337 |
msg.submit(
|
| 338 |
handle_chat,
|
|
|
|
| 109 |
save_chats(logged_in_username, user_chats, auth_globals)
|
| 110 |
return user_chats, "✅ Context saved!"
|
| 111 |
|
| 112 |
+
def enforce_cooldown(last_time):
|
| 113 |
+
now = time.time()
|
| 114 |
+
if now - last_time < 3:
|
| 115 |
+
gr.Warning("⏳ Please wait 3 seconds before clicking again.")
|
| 116 |
+
return False, last_time
|
| 117 |
+
return True, now
|
| 118 |
+
|
| 119 |
+
def delete_last_message(user_chats, current_title, logged_in_username, auth_globals):
|
| 120 |
+
if not current_title or not user_chats:
|
| 121 |
+
return user_chats, "Nothing to delete"
|
| 122 |
+
for chat in user_chats:
|
| 123 |
+
if chat["title"] == current_title and len(chat["history"]) >= 2:
|
| 124 |
+
chat["history"] = chat["history"][:-2] # remove last user + assistant
|
| 125 |
+
break
|
| 126 |
+
save_chats(logged_in_username, user_chats, auth_globals)
|
| 127 |
+
return user_chats, "🗑️ Last message deleted"
|
| 128 |
+
|
| 129 |
+
def edit_last_prompt(user_chats, current_title):
|
| 130 |
+
if not current_title or not user_chats:
|
| 131 |
+
return "", "No previous prompt"
|
| 132 |
+
for chat in user_chats:
|
| 133 |
+
if chat["title"] == current_title and len(chat["history"]) >= 2:
|
| 134 |
+
last_user_msg = chat["history"][-2]["content"]
|
| 135 |
+
return last_user_msg, "Ready to edit & re-run"
|
| 136 |
+
return "", "No previous prompt"
|
| 137 |
+
|
| 138 |
+
def update_token_display(user_chats, current_title):
|
| 139 |
+
total = 0
|
| 140 |
+
for chat in user_chats or []:
|
| 141 |
+
if chat["title"] == current_title:
|
| 142 |
+
for msg in chat.get("history", []):
|
| 143 |
+
total += msg.get("tokens", 0)
|
| 144 |
+
return f"**Tokens used this chat:** {total:,}"
|
| 145 |
+
|
| 146 |
# ================== HELPER: No conversion needed (Gradio expects messages format) ==================
|
| 147 |
def format_history_for_chatbot(history):
|
| 148 |
"""Return history as-is (already dict format)"""
|
|
|
|
| 211 |
if not current_model:
|
| 212 |
gr.Warning("⚠️ Load a model first!")
|
| 213 |
return chatbot
|
| 214 |
+
|
| 215 |
+
# Cooldown check
|
| 216 |
+
ok, new_time = enforce_cooldown(last_interaction_time.value)
|
| 217 |
+
if not ok:
|
| 218 |
return chatbot
|
| 219 |
+
last_interaction_time.value = new_time
|
| 220 |
+
|
| 221 |
+
is_generating.value = True
|
| 222 |
+
stop_btn.visible = True
|
| 223 |
|
|
|
|
| 224 |
internal_history = chatbot[:] if chatbot else []
|
| 225 |
|
|
|
|
| 226 |
try:
|
| 227 |
stream_gen = chat_stream(msg, internal_history, current_model, logged_in_username, user_chats_state, current_chat_title, mode_radio, auth_globals_state, file_upload, current_chat_context)
|
| 228 |
|
| 229 |
for updated_history in stream_gen:
|
| 230 |
+
# Add approximate token count to last assistant message
|
| 231 |
+
if updated_history and updated_history[-1]["role"] == "assistant":
|
| 232 |
+
content = updated_history[-1]["content"]
|
| 233 |
+
tokens = len(content) // 4 + 50 # simple but good enough approximation
|
| 234 |
+
updated_history[-1]["tokens"] = tokens
|
| 235 |
+
yield updated_history
|
| 236 |
+
|
| 237 |
except Exception as e:
|
| 238 |
+
gr.Warning(f"⚠️ Error: {str(e)[:200]}")
|
| 239 |
+
finally:
|
| 240 |
+
is_generating.value = False
|
| 241 |
+
stop_btn.visible = False
|
| 242 |
+
|
| 243 |
+
# Update token display
|
| 244 |
+
return token_display.update(value=update_token_display(user_chats_state, current_chat_title))
|
| 245 |
|
| 246 |
|
| 247 |
# ================== GUI ==================
|
|
|
|
| 260 |
auth_globals_state = gr.State(auth_globals)
|
| 261 |
coding_files_state = gr.State({}) # { "filename.py": {"content": "...", "language": "python"} }
|
| 262 |
current_file_name = gr.State(None)
|
| 263 |
+
last_interaction_time = gr.State(0.0) # for 3-second cooldown
|
| 264 |
+
is_generating = gr.State(False) # for Stop button
|
| 265 |
status_label = gr.Markdown("⚠️ No model loaded. Login + create/select a chat to begin.", elem_id="status-bold") # Made more prominent
|
| 266 |
|
| 267 |
|
|
|
|
| 335 |
)
|
| 336 |
send_btn = gr.Button("➤", variant="primary", scale=1)
|
| 337 |
|
| 338 |
+
with gr.Row():
|
| 339 |
+
delete_last_btn = gr.Button("🗑️ Delete Last Message", variant="stop", size="sm")
|
| 340 |
+
edit_last_btn = gr.Button("✏️ Edit & Re-run Last Prompt", variant="secondary", size="sm")
|
| 341 |
+
stop_btn = gr.Button("⏹️ Stop Generation", variant="stop", visible=False)
|
| 342 |
+
|
| 343 |
+
# Token display (optional but useful)
|
| 344 |
+
token_display = gr.Markdown("**Tokens used this chat:** 0", visible=False)
|
| 345 |
+
|
| 346 |
# Authentication (clean version - no phone/OTP)
|
| 347 |
with gr.Row():
|
| 348 |
username_input = gr.Textbox(label="Username (will be forced lowercase)", scale=2, placeholder="admin")
|
|
|
|
| 387 |
confirm_delete_btn.click(delete_account, [confirm_username_input, logged_in_username, auth_globals_state], [status_label, logged_in_username, logout_btn, delete_acc_btn, confirm_row, user_chats_state, current_chat_title, chatbot, chat_dropdown, chat_context_tb, save_context_btn])
|
| 388 |
|
| 389 |
save_context_btn.click(save_chat_context, [chat_context_tb, user_chats_state, current_chat_title, logged_in_username], [user_chats_state, status_label])
|
| 390 |
+
|
| 391 |
+
# Cooldown + action buttons
|
| 392 |
+
delete_last_btn.click(
|
| 393 |
+
delete_last_message,
|
| 394 |
+
[user_chats_state, current_chat_title, logged_in_username, auth_globals_state],
|
| 395 |
+
[user_chats_state, status_label]
|
| 396 |
+
).then(
|
| 397 |
+
lambda: gr.update(visible=True), None, token_display
|
| 398 |
+
).then(update_token_display, [user_chats_state, current_chat_title], token_display)
|
| 399 |
+
|
| 400 |
+
edit_last_btn.click(
|
| 401 |
+
edit_last_prompt,
|
| 402 |
+
[user_chats_state, current_chat_title],
|
| 403 |
+
[msg, status_label]
|
| 404 |
+
)
|
| 405 |
+
|
| 406 |
+
stop_btn.click(
|
| 407 |
+
lambda: (False, gr.update(visible=False)),
|
| 408 |
+
None,
|
| 409 |
+
[is_generating, stop_btn]
|
| 410 |
+
)
|
| 411 |
+
|
| 412 |
+
# Show token display in Coding Mode and after first message
|
| 413 |
+
mode_radio.change(
|
| 414 |
+
lambda m: gr.update(visible=(m == "💻 Coding Mode")),
|
| 415 |
+
mode_radio,
|
| 416 |
+
token_display
|
| 417 |
+
)
|
| 418 |
|
| 419 |
msg.submit(
|
| 420 |
handle_chat,
|