Spaces:
Paused
Paused
Update app.py via AI Editor
Browse files
app.py
CHANGED
@@ -178,25 +178,29 @@ def right_main_static():
|
|
178 |
return html.Div([
|
179 |
dbc.Card([
|
180 |
dbc.CardBody([
|
181 |
-
html.Div(id="chat-window", style={
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
182 |
html.Div([
|
183 |
dcc.Textarea(
|
184 |
id="user-input",
|
185 |
placeholder="Type your question...",
|
186 |
style={"width": "100%", "height": "60px", "resize": "vertical", "wordWrap": "break-word"},
|
187 |
wrap="soft",
|
188 |
-
maxLength=1000
|
|
|
189 |
),
|
190 |
html.Div([
|
191 |
dbc.Button("Send", id="send-btn", color="primary", className="mt-2 me-2", style={"minWidth": "100px"}),
|
192 |
dbc.Button("New Chat", id="new-chat-btn", color="secondary", className="mt-2", style={"minWidth": "110px"}),
|
193 |
], style={"float": "right", "display": "flex", "gap": "0.5rem"}),
|
194 |
-
dcc.Input(
|
195 |
-
id="chat-name-input",
|
196 |
-
placeholder="Enter chat name...",
|
197 |
-
type="text",
|
198 |
-
style={"width": "100%", "marginTop": "0.5rem", "display": "none"}
|
199 |
-
),
|
200 |
], style={"marginTop": "1rem"}),
|
201 |
html.Div(id="error-message", style={"color": "#bb2124", "marginTop": "0.5rem"}),
|
202 |
])
|
@@ -211,7 +215,9 @@ app.layout = html.Div([
|
|
211 |
html.Div([
|
212 |
html.Div(left_navbar_static(), id='left-navbar', style={"width": "30vw", "height": "100vh", "position": "fixed", "left": 0, "top": 0, "zIndex": 2, "overflowY": "auto"}),
|
213 |
html.Div(right_main_static(), id='right-main', style={"marginLeft": "30vw", "width": "70vw", "overflowY": "auto"})
|
214 |
-
], style={"display": "flex"})
|
|
|
|
|
215 |
])
|
216 |
|
217 |
def _is_supported_doc(filename):
|
@@ -237,7 +243,7 @@ def assign_session_id(_):
|
|
237 |
Output("error-message", "children"),
|
238 |
Output("stream-interval", "disabled"),
|
239 |
Output("stream-interval", "n_intervals"),
|
240 |
-
Output("
|
241 |
Input("session-id", "data"),
|
242 |
Input("send-btn", "n_clicks"),
|
243 |
Input("file-upload", "contents"),
|
@@ -245,11 +251,9 @@ def assign_session_id(_):
|
|
245 |
Input("stream-interval", "n_intervals"),
|
246 |
State("file-upload", "filename"),
|
247 |
State("user-input", "value"),
|
248 |
-
State("chat-name-input", "value"),
|
249 |
-
State("chat-name-input", "style"),
|
250 |
prevent_initial_call=False
|
251 |
)
|
252 |
-
def main_callback(session_id, send_clicks, file_contents, new_chat_clicks, stream_n, file_names, user_input
|
253 |
trigger = callback_context.triggered[0]['prop_id'].split('.')[0] if callback_context.triggered else ""
|
254 |
if not session_id:
|
255 |
session_id = get_session_id()
|
@@ -334,72 +338,22 @@ def main_callback(session_id, send_clicks, file_contents, new_chat_clicks, strea
|
|
334 |
threading.Thread(target=run_stream, args=(session_id, list(state["messages"])), daemon=True).start()
|
335 |
start_streaming = True
|
336 |
|
337 |
-
# Handle New Chat button logic
|
338 |
-
show_chat_name = chat_name_style if isinstance(chat_name_style, dict) else {}
|
339 |
if trigger == "new-chat-btn":
|
340 |
-
|
341 |
-
if
|
342 |
-
|
343 |
-
upload_cards = [uploaded_file_card(os.path.basename(f["name"]), f["is_img"]) for f in state.get("uploads", [])]
|
344 |
-
chat_history_items = [
|
345 |
-
html.Li(
|
346 |
-
html.Span(
|
347 |
-
chat["name"],
|
348 |
-
style={"fontSize": "0.92rem"}
|
349 |
-
)
|
350 |
-
) for chat in state.get("chat_histories", [])[-6:]
|
351 |
-
]
|
352 |
-
chat_cards = [
|
353 |
-
chat_message_card(msg['content'], is_user=(msg['role'] == "user"))
|
354 |
-
for msg in state.get("messages", [])
|
355 |
-
] + (
|
356 |
-
[chat_message_card(state["stream_buffer"], is_user=False)]
|
357 |
-
if state.get("streaming", False) and state.get("stream_buffer", "") else []
|
358 |
-
)
|
359 |
-
return (
|
360 |
-
upload_cards,
|
361 |
-
chat_history_items,
|
362 |
-
chat_cards,
|
363 |
-
"",
|
364 |
-
not state.get("streaming", False),
|
365 |
-
0,
|
366 |
-
show_chat_name
|
367 |
-
)
|
368 |
-
# If input box is visible and has a value, save chat history
|
369 |
else:
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
state["stream_buffer"] = ""
|
381 |
-
state["streaming"] = False
|
382 |
-
save_session_state(session_id)
|
383 |
-
logger.info(f"Session {session_id}: Saved chat history '{chat_title}'")
|
384 |
-
show_chat_name["display"] = "none"
|
385 |
-
upload_cards = [uploaded_file_card(os.path.basename(f["name"]), f["is_img"]) for f in state.get("uploads", [])]
|
386 |
-
chat_history_items = [
|
387 |
-
html.Li(
|
388 |
-
html.Span(
|
389 |
-
chat["name"],
|
390 |
-
style={"fontSize": "0.92rem", "fontWeight": "bold"}
|
391 |
-
)
|
392 |
-
) for chat in state.get("chat_histories", [])[-6:]
|
393 |
-
]
|
394 |
-
return (
|
395 |
-
upload_cards,
|
396 |
-
chat_history_items,
|
397 |
-
[],
|
398 |
-
error,
|
399 |
-
not state.get("streaming", False),
|
400 |
-
0,
|
401 |
-
show_chat_name
|
402 |
-
)
|
403 |
|
404 |
# Handle polling for streaming
|
405 |
if trigger == "stream-interval":
|
@@ -426,7 +380,7 @@ def main_callback(session_id, send_clicks, file_contents, new_chat_clicks, strea
|
|
426 |
"",
|
427 |
False,
|
428 |
stream_n+1,
|
429 |
-
|
430 |
)
|
431 |
else:
|
432 |
chat_cards = []
|
@@ -448,7 +402,7 @@ def main_callback(session_id, send_clicks, file_contents, new_chat_clicks, strea
|
|
448 |
"",
|
449 |
True,
|
450 |
0,
|
451 |
-
|
452 |
)
|
453 |
|
454 |
# Default: Build Uploads, Chat History and Chat Window
|
@@ -470,8 +424,32 @@ def main_callback(session_id, send_clicks, file_contents, new_chat_clicks, strea
|
|
470 |
if state.get("streaming", False):
|
471 |
if state.get("stream_buffer", ""):
|
472 |
chat_cards.append(chat_message_card(state["stream_buffer"], is_user=False))
|
473 |
-
return upload_cards, chat_history_items, chat_cards, error, False, 0,
|
474 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
475 |
|
476 |
@app_flask.after_request
|
477 |
def set_session_cookie(resp):
|
|
|
178 |
return html.Div([
|
179 |
dbc.Card([
|
180 |
dbc.CardBody([
|
181 |
+
html.Div(id="chat-window", style={
|
182 |
+
"height": "60vh",
|
183 |
+
"overflowY": "auto",
|
184 |
+
"display": "flex",
|
185 |
+
"flexDirection": "column",
|
186 |
+
"justifyContent": "flex-end",
|
187 |
+
"backgroundColor": "#fff",
|
188 |
+
"padding": "0.5rem",
|
189 |
+
"borderRadius": "0.5rem"
|
190 |
+
}),
|
191 |
html.Div([
|
192 |
dcc.Textarea(
|
193 |
id="user-input",
|
194 |
placeholder="Type your question...",
|
195 |
style={"width": "100%", "height": "60px", "resize": "vertical", "wordWrap": "break-word"},
|
196 |
wrap="soft",
|
197 |
+
maxLength=1000,
|
198 |
+
autoFocus=True
|
199 |
),
|
200 |
html.Div([
|
201 |
dbc.Button("Send", id="send-btn", color="primary", className="mt-2 me-2", style={"minWidth": "100px"}),
|
202 |
dbc.Button("New Chat", id="new-chat-btn", color="secondary", className="mt-2", style={"minWidth": "110px"}),
|
203 |
], style={"float": "right", "display": "flex", "gap": "0.5rem"}),
|
|
|
|
|
|
|
|
|
|
|
|
|
204 |
], style={"marginTop": "1rem"}),
|
205 |
html.Div(id="error-message", style={"color": "#bb2124", "marginTop": "0.5rem"}),
|
206 |
])
|
|
|
215 |
html.Div([
|
216 |
html.Div(left_navbar_static(), id='left-navbar', style={"width": "30vw", "height": "100vh", "position": "fixed", "left": 0, "top": 0, "zIndex": 2, "overflowY": "auto"}),
|
217 |
html.Div(right_main_static(), id='right-main', style={"marginLeft": "30vw", "width": "70vw", "overflowY": "auto"})
|
218 |
+
], style={"display": "flex"}),
|
219 |
+
dcc.Store(id="clear-input", data=False),
|
220 |
+
dcc.Store(id="scroll-bottom", data=0)
|
221 |
])
|
222 |
|
223 |
def _is_supported_doc(filename):
|
|
|
243 |
Output("error-message", "children"),
|
244 |
Output("stream-interval", "disabled"),
|
245 |
Output("stream-interval", "n_intervals"),
|
246 |
+
Output("user-input", "value"),
|
247 |
Input("session-id", "data"),
|
248 |
Input("send-btn", "n_clicks"),
|
249 |
Input("file-upload", "contents"),
|
|
|
251 |
Input("stream-interval", "n_intervals"),
|
252 |
State("file-upload", "filename"),
|
253 |
State("user-input", "value"),
|
|
|
|
|
254 |
prevent_initial_call=False
|
255 |
)
|
256 |
+
def main_callback(session_id, send_clicks, file_contents, new_chat_clicks, stream_n, file_names, user_input):
|
257 |
trigger = callback_context.triggered[0]['prop_id'].split('.')[0] if callback_context.triggered else ""
|
258 |
if not session_id:
|
259 |
session_id = get_session_id()
|
|
|
338 |
threading.Thread(target=run_stream, args=(session_id, list(state["messages"])), daemon=True).start()
|
339 |
start_streaming = True
|
340 |
|
341 |
+
# Handle New Chat button logic: auto-name and reset
|
|
|
342 |
if trigger == "new-chat-btn":
|
343 |
+
chat_dialog = list(state.get("messages", []))
|
344 |
+
if not chat_dialog:
|
345 |
+
error = "No chat to save. Start chatting!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
346 |
else:
|
347 |
+
chat_title = "Chat " + datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M")
|
348 |
+
state.setdefault("chat_histories", []).append({
|
349 |
+
"name": chat_title,
|
350 |
+
"dialog": chat_dialog
|
351 |
+
})
|
352 |
+
state["messages"] = []
|
353 |
+
state["stream_buffer"] = ""
|
354 |
+
state["streaming"] = False
|
355 |
+
save_session_state(session_id)
|
356 |
+
logger.info(f"Session {session_id}: Saved chat history '{chat_title}'")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
357 |
|
358 |
# Handle polling for streaming
|
359 |
if trigger == "stream-interval":
|
|
|
380 |
"",
|
381 |
False,
|
382 |
stream_n+1,
|
383 |
+
""
|
384 |
)
|
385 |
else:
|
386 |
chat_cards = []
|
|
|
402 |
"",
|
403 |
True,
|
404 |
0,
|
405 |
+
""
|
406 |
)
|
407 |
|
408 |
# Default: Build Uploads, Chat History and Chat Window
|
|
|
424 |
if state.get("streaming", False):
|
425 |
if state.get("stream_buffer", ""):
|
426 |
chat_cards.append(chat_message_card(state["stream_buffer"], is_user=False))
|
427 |
+
return upload_cards, chat_history_items, chat_cards, error, False, 0, ""
|
428 |
+
# Always clear input after send
|
429 |
+
if trigger == "send-btn":
|
430 |
+
return upload_cards, chat_history_items, chat_cards, error, (not state.get("streaming", False)), 0, ""
|
431 |
+
return upload_cards, chat_history_items, chat_cards, error, (not state.get("streaming", False)), 0, user_input or ""
|
432 |
+
|
433 |
+
# Clientside callback for pressing Enter to send
|
434 |
+
app.clientside_callback(
|
435 |
+
"""
|
436 |
+
function(n_clicks) {
|
437 |
+
var ta = document.getElementById('user-input');
|
438 |
+
if (ta) {
|
439 |
+
ta.addEventListener('keydown', function(e) {
|
440 |
+
if (e.key === 'Enter' && !e.shiftKey) {
|
441 |
+
e.preventDefault();
|
442 |
+
var sendBtn = document.getElementById('send-btn');
|
443 |
+
if(sendBtn){sendBtn.click();}
|
444 |
+
}
|
445 |
+
}, {once: true});
|
446 |
+
}
|
447 |
+
return window.dash_clientside.no_update;
|
448 |
+
}
|
449 |
+
""",
|
450 |
+
Output('user-input', 'value'),
|
451 |
+
[Input('send-btn', 'n_clicks')]
|
452 |
+
)
|
453 |
|
454 |
@app_flask.after_request
|
455 |
def set_session_cookie(resp):
|