Spaces:
Paused
Paused
Update app.py via AI Editor
Browse files
app.py
CHANGED
|
@@ -49,6 +49,7 @@ def get_session_state(session_id):
|
|
| 49 |
"created": datetime.datetime.utcnow().isoformat(),
|
| 50 |
"streaming": False,
|
| 51 |
"stream_buffer": "",
|
|
|
|
| 52 |
}
|
| 53 |
return SESSION_DATA[session_id]
|
| 54 |
|
|
@@ -149,7 +150,16 @@ def right_main_static():
|
|
| 149 |
wrap="soft",
|
| 150 |
maxLength=1000
|
| 151 |
),
|
| 152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
], style={"marginTop": "1rem"}),
|
| 154 |
html.Div(id="error-message", style={"color": "#bb2124", "marginTop": "0.5rem"}),
|
| 155 |
])
|
|
@@ -190,15 +200,19 @@ def assign_session_id(_):
|
|
| 190 |
Output("error-message", "children"),
|
| 191 |
Output("stream-interval", "disabled"),
|
| 192 |
Output("stream-interval", "n_intervals"),
|
|
|
|
| 193 |
Input("session-id", "data"),
|
| 194 |
Input("send-btn", "n_clicks"),
|
| 195 |
Input("file-upload", "contents"),
|
|
|
|
| 196 |
State("file-upload", "filename"),
|
| 197 |
State("user-input", "value"),
|
|
|
|
| 198 |
State("stream-interval", "n_intervals"),
|
|
|
|
| 199 |
prevent_initial_call=False
|
| 200 |
)
|
| 201 |
-
def main_callback(session_id, send_clicks, file_contents, file_names, user_input, stream_n):
|
| 202 |
trigger = callback_context.triggered[0]['prop_id'].split('.')[0] if callback_context.triggered else ""
|
| 203 |
if not session_id:
|
| 204 |
session_id = get_session_id()
|
|
@@ -209,6 +223,7 @@ def main_callback(session_id, send_clicks, file_contents, file_names, user_input
|
|
| 209 |
error = ""
|
| 210 |
start_streaming = False
|
| 211 |
|
|
|
|
| 212 |
if trigger == "file-upload" and file_contents and file_names:
|
| 213 |
uploads = []
|
| 214 |
if not isinstance(file_contents, list):
|
|
@@ -228,6 +243,7 @@ def main_callback(session_id, send_clicks, file_contents, file_names, user_input
|
|
| 228 |
save_session_state(session_id)
|
| 229 |
logger.info(f"Session {session_id}: Uploaded files {[u['name'] for u in uploads]}")
|
| 230 |
|
|
|
|
| 231 |
if trigger == "send-btn" and user_input and user_input.strip():
|
| 232 |
state["messages"].append({"role": "user", "content": user_input})
|
| 233 |
state["streaming"] = True
|
|
@@ -253,14 +269,12 @@ def main_callback(session_id, send_clicks, file_contents, file_names, user_input
|
|
| 253 |
content = delta.get("content", "")
|
| 254 |
if content:
|
| 255 |
reply += content
|
| 256 |
-
# Update buffer in session state
|
| 257 |
session_lock = get_session_lock(session_id)
|
| 258 |
with session_lock:
|
| 259 |
load_session_state(session_id)
|
| 260 |
state = get_session_state(session_id)
|
| 261 |
state["stream_buffer"] = reply
|
| 262 |
save_session_state(session_id)
|
| 263 |
-
# Finalize message
|
| 264 |
session_lock = get_session_lock(session_id)
|
| 265 |
with session_lock:
|
| 266 |
load_session_state(session_id)
|
|
@@ -283,22 +297,89 @@ def main_callback(session_id, send_clicks, file_contents, file_names, user_input
|
|
| 283 |
threading.Thread(target=run_stream, args=(session_id, list(state["messages"])), daemon=True).start()
|
| 284 |
start_streaming = True
|
| 285 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 286 |
chat_history = state.get("messages", [])
|
| 287 |
uploads = state.get("uploads", [])
|
|
|
|
| 288 |
upload_cards = [uploaded_file_card(os.path.basename(f["name"]), f["is_img"]) for f in uploads]
|
| 289 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 290 |
chat_cards = []
|
| 291 |
-
for
|
| 292 |
-
|
| 293 |
-
chat_cards.append(chat_message_card(msg['content'], is_user=True))
|
| 294 |
-
elif msg['role'] == "assistant":
|
| 295 |
-
chat_cards.append(chat_message_card(msg['content'], is_user=False))
|
| 296 |
if state.get("streaming", False):
|
| 297 |
-
# Add a partial assistant message at the end
|
| 298 |
if state.get("stream_buffer", ""):
|
| 299 |
chat_cards.append(chat_message_card(state["stream_buffer"], is_user=False))
|
| 300 |
-
return upload_cards,
|
| 301 |
-
return upload_cards,
|
| 302 |
|
| 303 |
@app.callback(
|
| 304 |
Output("chat-window", "children"),
|
|
@@ -314,11 +395,8 @@ def poll_stream(n_intervals, session_id):
|
|
| 314 |
state = get_session_state(session_id)
|
| 315 |
chat_history = state.get("messages", [])
|
| 316 |
chat_cards = []
|
| 317 |
-
for
|
| 318 |
-
|
| 319 |
-
chat_cards.append(chat_message_card(msg['content'], is_user=True))
|
| 320 |
-
elif msg['role'] == "assistant":
|
| 321 |
-
chat_cards.append(chat_message_card(msg['content'], is_user=False))
|
| 322 |
if state.get("streaming", False):
|
| 323 |
if state.get("stream_buffer", ""):
|
| 324 |
chat_cards.append(chat_message_card(state["stream_buffer"], is_user=False))
|
|
|
|
| 49 |
"created": datetime.datetime.utcnow().isoformat(),
|
| 50 |
"streaming": False,
|
| 51 |
"stream_buffer": "",
|
| 52 |
+
"chat_histories": []
|
| 53 |
}
|
| 54 |
return SESSION_DATA[session_id]
|
| 55 |
|
|
|
|
| 150 |
wrap="soft",
|
| 151 |
maxLength=1000
|
| 152 |
),
|
| 153 |
+
html.Div([
|
| 154 |
+
dbc.Button("Send", id="send-btn", color="primary", className="mt-2 me-2", style={"minWidth": "100px"}),
|
| 155 |
+
dbc.Button("New Chat", id="new-chat-btn", color="secondary", className="mt-2", style={"minWidth": "110px"}),
|
| 156 |
+
], style={"float": "right", "display": "flex", "gap": "0.5rem"}),
|
| 157 |
+
dcc.Input(
|
| 158 |
+
id="chat-name-input",
|
| 159 |
+
placeholder="Enter chat name...",
|
| 160 |
+
type="text",
|
| 161 |
+
style={"width": "100%", "marginTop": "0.5rem", "display": "none"}
|
| 162 |
+
),
|
| 163 |
], style={"marginTop": "1rem"}),
|
| 164 |
html.Div(id="error-message", style={"color": "#bb2124", "marginTop": "0.5rem"}),
|
| 165 |
])
|
|
|
|
| 200 |
Output("error-message", "children"),
|
| 201 |
Output("stream-interval", "disabled"),
|
| 202 |
Output("stream-interval", "n_intervals"),
|
| 203 |
+
Output("chat-name-input", "style"),
|
| 204 |
Input("session-id", "data"),
|
| 205 |
Input("send-btn", "n_clicks"),
|
| 206 |
Input("file-upload", "contents"),
|
| 207 |
+
Input("new-chat-btn", "n_clicks"),
|
| 208 |
State("file-upload", "filename"),
|
| 209 |
State("user-input", "value"),
|
| 210 |
+
State("chat-name-input", "value"),
|
| 211 |
State("stream-interval", "n_intervals"),
|
| 212 |
+
State("chat-name-input", "style"),
|
| 213 |
prevent_initial_call=False
|
| 214 |
)
|
| 215 |
+
def main_callback(session_id, send_clicks, file_contents, new_chat_clicks, file_names, user_input, chat_name, stream_n, chat_name_style):
|
| 216 |
trigger = callback_context.triggered[0]['prop_id'].split('.')[0] if callback_context.triggered else ""
|
| 217 |
if not session_id:
|
| 218 |
session_id = get_session_id()
|
|
|
|
| 223 |
error = ""
|
| 224 |
start_streaming = False
|
| 225 |
|
| 226 |
+
# Handle File Upload
|
| 227 |
if trigger == "file-upload" and file_contents and file_names:
|
| 228 |
uploads = []
|
| 229 |
if not isinstance(file_contents, list):
|
|
|
|
| 243 |
save_session_state(session_id)
|
| 244 |
logger.info(f"Session {session_id}: Uploaded files {[u['name'] for u in uploads]}")
|
| 245 |
|
| 246 |
+
# Handle Send
|
| 247 |
if trigger == "send-btn" and user_input and user_input.strip():
|
| 248 |
state["messages"].append({"role": "user", "content": user_input})
|
| 249 |
state["streaming"] = True
|
|
|
|
| 269 |
content = delta.get("content", "")
|
| 270 |
if content:
|
| 271 |
reply += content
|
|
|
|
| 272 |
session_lock = get_session_lock(session_id)
|
| 273 |
with session_lock:
|
| 274 |
load_session_state(session_id)
|
| 275 |
state = get_session_state(session_id)
|
| 276 |
state["stream_buffer"] = reply
|
| 277 |
save_session_state(session_id)
|
|
|
|
| 278 |
session_lock = get_session_lock(session_id)
|
| 279 |
with session_lock:
|
| 280 |
load_session_state(session_id)
|
|
|
|
| 297 |
threading.Thread(target=run_stream, args=(session_id, list(state["messages"])), daemon=True).start()
|
| 298 |
start_streaming = True
|
| 299 |
|
| 300 |
+
# Handle New Chat button logic
|
| 301 |
+
show_chat_name = chat_name_style if isinstance(chat_name_style, dict) else {}
|
| 302 |
+
if trigger == "new-chat-btn":
|
| 303 |
+
# If chat name input box is not yet visible, show it
|
| 304 |
+
if show_chat_name.get("display", "none") == "none":
|
| 305 |
+
show_chat_name["display"] = "block"
|
| 306 |
+
return (
|
| 307 |
+
[uploaded_file_card(os.path.basename(f["name"]), f["is_img"]) for f in state.get("uploads", [])],
|
| 308 |
+
[
|
| 309 |
+
html.Li(
|
| 310 |
+
html.Span(
|
| 311 |
+
chat["name"],
|
| 312 |
+
style={"fontSize": "0.92rem"}
|
| 313 |
+
)
|
| 314 |
+
) for chat in state.get("chat_histories", [])[-6:]
|
| 315 |
+
],
|
| 316 |
+
[
|
| 317 |
+
chat_message_card(msg['content'], is_user=(msg['role'] == "user"))
|
| 318 |
+
for msg in state.get("messages", [])
|
| 319 |
+
] + (
|
| 320 |
+
[chat_message_card(state["stream_buffer"], is_user=False)]
|
| 321 |
+
if state.get("streaming", False) and state.get("stream_buffer", "") else []
|
| 322 |
+
),
|
| 323 |
+
"",
|
| 324 |
+
not state.get("streaming", False),
|
| 325 |
+
0,
|
| 326 |
+
show_chat_name
|
| 327 |
+
)
|
| 328 |
+
# If input box is visible and has a value, save chat history
|
| 329 |
+
else:
|
| 330 |
+
chat_dialog = list(state.get("messages", []))
|
| 331 |
+
if not chat_dialog:
|
| 332 |
+
error = "Cannot save empty chat."
|
| 333 |
+
else:
|
| 334 |
+
chat_title = chat_name if chat_name and chat_name.strip() else "Chat " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M")
|
| 335 |
+
state.setdefault("chat_histories", []).append({
|
| 336 |
+
"name": chat_title,
|
| 337 |
+
"dialog": chat_dialog
|
| 338 |
+
})
|
| 339 |
+
state["messages"] = []
|
| 340 |
+
state["stream_buffer"] = ""
|
| 341 |
+
state["streaming"] = False
|
| 342 |
+
save_session_state(session_id)
|
| 343 |
+
logger.info(f"Session {session_id}: Saved chat history '{chat_title}'")
|
| 344 |
+
show_chat_name["display"] = "none"
|
| 345 |
+
return (
|
| 346 |
+
[uploaded_file_card(os.path.basename(f["name"]), f["is_img"]) for f in state.get("uploads", [])],
|
| 347 |
+
[
|
| 348 |
+
html.Li(
|
| 349 |
+
html.Span(
|
| 350 |
+
chat["name"],
|
| 351 |
+
style={"fontSize": "0.92rem", "fontWeight": "bold"}
|
| 352 |
+
)
|
| 353 |
+
) for chat in state.get("chat_histories", [])[-6:]
|
| 354 |
+
],
|
| 355 |
+
[],
|
| 356 |
+
error,
|
| 357 |
+
not state.get("streaming", False),
|
| 358 |
+
0,
|
| 359 |
+
show_chat_name
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
# Build Uploads, Chat History and Chat Window
|
| 363 |
chat_history = state.get("messages", [])
|
| 364 |
uploads = state.get("uploads", [])
|
| 365 |
+
chat_histories = state.get("chat_histories", [])
|
| 366 |
upload_cards = [uploaded_file_card(os.path.basename(f["name"]), f["is_img"]) for f in uploads]
|
| 367 |
+
chat_history_items = [
|
| 368 |
+
html.Li(
|
| 369 |
+
html.Span(
|
| 370 |
+
chat["name"],
|
| 371 |
+
style={"fontSize": "0.92rem", "fontWeight": "bold" if i == len(chat_histories)-1 else "normal"}
|
| 372 |
+
)
|
| 373 |
+
) for i, chat in enumerate(chat_histories[-6:])
|
| 374 |
+
]
|
| 375 |
chat_cards = []
|
| 376 |
+
for msg in chat_history:
|
| 377 |
+
chat_cards.append(chat_message_card(msg['content'], is_user=(msg['role'] == "user")))
|
|
|
|
|
|
|
|
|
|
| 378 |
if state.get("streaming", False):
|
|
|
|
| 379 |
if state.get("stream_buffer", ""):
|
| 380 |
chat_cards.append(chat_message_card(state["stream_buffer"], is_user=False))
|
| 381 |
+
return upload_cards, chat_history_items, chat_cards, error, False, 0, show_chat_name
|
| 382 |
+
return upload_cards, chat_history_items, chat_cards, error, (not state.get("streaming", False)), 0, show_chat_name
|
| 383 |
|
| 384 |
@app.callback(
|
| 385 |
Output("chat-window", "children"),
|
|
|
|
| 395 |
state = get_session_state(session_id)
|
| 396 |
chat_history = state.get("messages", [])
|
| 397 |
chat_cards = []
|
| 398 |
+
for msg in chat_history:
|
| 399 |
+
chat_cards.append(chat_message_card(msg['content'], is_user=(msg['role'] == "user")))
|
|
|
|
|
|
|
|
|
|
| 400 |
if state.get("streaming", False):
|
| 401 |
if state.get("stream_buffer", ""):
|
| 402 |
chat_cards.append(chat_message_card(state["stream_buffer"], is_user=False))
|