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))
|