bluenevus commited on
Commit
59a9f3a
·
1 Parent(s): 84ddce1

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +62 -201
app.py CHANGED
@@ -8,8 +8,7 @@ import pandas as pd
8
  import logging
9
  from docx import Document
10
  import mimetypes
11
- from threading import Thread, Lock, Event
12
- import threading
13
  import time
14
 
15
  import google.generativeai as genai
@@ -38,8 +37,6 @@ shredded_document = None
38
  generated_response = None
39
 
40
  gemini_lock = Lock()
41
- stream_buffer = {"preview": ""}
42
- stream_event = Event()
43
 
44
  def decode_document(decoded_bytes):
45
  try:
@@ -89,7 +86,7 @@ def upload_to_gemini_file(decoded_bytes, filename):
89
  logging.error(f"Exception during file upload to Gemini: {e}")
90
  return None
91
 
92
- def gemini_generate_content_stream(prompt, file_id=None, chat_input=None, cancel_event=None):
93
  try:
94
  files = []
95
  if file_id:
@@ -111,19 +108,10 @@ def gemini_generate_content_stream(prompt, file_id=None, chat_input=None, cancel
111
  )
112
  )
113
  result = response.text if hasattr(response, "text") else str(response)
114
- chunk_size = 400
115
- for i in range(0, len(result), chunk_size):
116
- if cancel_event is not None and cancel_event.is_set():
117
- logging.info("Gemini stream cancelled by user.")
118
- yield "[Cancelled by user]\n"
119
- break
120
- yield result[:i+chunk_size]
121
- time.sleep(0.08)
122
- if cancel_event is not None and cancel_event.is_set():
123
- yield "[Cancelled by user]\n"
124
  except Exception as e:
125
  logging.error("Error during Gemini generate_content: %s", e)
126
- yield f"Error during Gemini completion: {e}"
127
 
128
  def save_shredded_as_docx(shredded_text, rfp_filename):
129
  doc = Document()
@@ -155,8 +143,8 @@ def save_compliance_as_docx(compliance_text, rfp_filename):
155
  memf.seek(0)
156
  return memf.read()
157
 
158
- def process_document(action, selected_filename=None, chat_input=None, rfp_decoded_bytes=None, cancel_event=None, stream=True, selected_generated_filename=None):
159
- global shredded_document, generated_response, stream_buffer
160
 
161
  logging.info(f"Process document called with action: {action}")
162
 
@@ -186,44 +174,17 @@ def process_document(action, selected_filename=None, chat_input=None, rfp_decode
186
  if chat_input:
187
  prompt += f"User additional instructions: {chat_input}\n"
188
  prompt += f"\nFile Name: {selected_filename}\n\n"
189
- result_holder = {"text": None, "docx_name": None}
190
-
191
- def thread_shred():
192
- global shredded_document, stream_buffer
193
- shredded_document = ""
194
- stream_buffer["preview"] = ""
195
- try:
196
- logging.info("Starting Gemini generate_content for shredding.")
197
- for partial in gemini_generate_content_stream(prompt, file_id=doc_fileid, chat_input=chat_input, cancel_event=cancel_event):
198
- stream_buffer["preview"] = partial
199
- if cancel_event is not None and cancel_event.is_set():
200
- break
201
- result = stream_buffer["preview"]
202
- shredded_document = result
203
- if not cancel_event or not cancel_event.is_set():
204
- docx_bytes = save_shredded_as_docx(result, selected_filename)
205
- generated_docx_name = f"{os.path.splitext(selected_filename)[0]}_shredded.docx"
206
- shredded_documents[generated_docx_name] = docx_bytes
207
- result_holder["text"] = result
208
- result_holder["docx_name"] = generated_docx_name
209
- else:
210
- result_holder["text"] = "[Cancelled by user]\n"
211
- result_holder["docx_name"] = None
212
- logging.info("Document shredded successfully.")
213
- except Exception as e:
214
- shredded_document = f"Error during shredding: {e}"
215
- logging.error("Error in thread_shred: %s", e)
216
- result_holder["text"] = shredded_document
217
-
218
- shredded_document = "Shredding in progress..."
219
- stream_buffer["preview"] = ""
220
- t = Thread(target=thread_shred)
221
- t.start()
222
- t.join()
223
- if result_holder["docx_name"] and result_holder["text"]:
224
- if result_holder["docx_name"] not in uploaded_documents:
225
- uploaded_documents[result_holder["docx_name"]] = result_holder["text"]
226
- return result_holder["text"], None, result_holder["docx_name"], result_holder["text"]
227
 
228
  elif action == 'proposal':
229
  if not doc_content:
@@ -249,42 +210,16 @@ def process_document(action, selected_filename=None, chat_input=None, rfp_decode
249
  if chat_input:
250
  prompt += f"User additional instructions: {chat_input}\n"
251
  prompt += f"\n---\nRFP/SOW/PWS/RFI ({rfp_filename}):\n{doc_content}\n"
252
- logging.info(f"Sending proposal prompt to Gemini. RFP: {rfp_filename}")
253
- result_holder = {"text": None, "docx_name": None}
254
-
255
- def thread_proposal():
256
- global generated_response, stream_buffer
257
- generated_response = ""
258
- stream_buffer["preview"] = ""
259
- try:
260
- logging.info("Connecting to Gemini for proposal.")
261
- for partial in gemini_generate_content_stream(prompt, file_id=rfp_fileid, chat_input=chat_input, cancel_event=cancel_event):
262
- stream_buffer["preview"] = partial
263
- if cancel_event is not None and cancel_event.is_set():
264
- break
265
- response = stream_buffer["preview"]
266
- generated_response = response
267
- if not cancel_event or not cancel_event.is_set():
268
- docx_bytes = save_proposal_as_docx(response, rfp_filename)
269
- generated_docx_name = f"{os.path.splitext(rfp_filename)[0]}_proposal.docx"
270
- uploaded_proposals[generated_docx_name] = response
271
- uploaded_proposals_fileid[generated_docx_name] = None
272
- result_holder["text"] = response
273
- result_holder["docx_name"] = generated_docx_name
274
- else:
275
- result_holder["text"] = "[Cancelled by user]\n"
276
- result_holder["docx_name"] = None
277
- logging.info("Received proposal results from Gemini.")
278
- except Exception as e:
279
- generated_response = f"Error during Gemini completion: {e}"
280
- logging.error("Error during Gemini proposal request: %s", e)
281
- result_holder["text"] = generated_response
282
-
283
- stream_buffer["preview"] = ""
284
- t = Thread(target=thread_proposal)
285
- t.start()
286
- t.join()
287
- return result_holder["text"], None, result_holder["docx_name"], result_holder["text"]
288
 
289
  elif action == 'compliance':
290
  if not selected_generated_filename or selected_generated_filename not in uploaded_proposals:
@@ -308,37 +243,15 @@ def process_document(action, selected_filename=None, chat_input=None, rfp_decode
308
  "---\nGenerated Proposal Document:\n"
309
  f"{proposal_text}\n"
310
  )
311
- result_holder = {"text": None, "docx_name": None}
312
-
313
- def thread_compliance():
314
- global stream_buffer
315
- stream_buffer["preview"] = ""
316
- try:
317
- logging.info("Starting Gemini generate_content for compliance check.")
318
- for partial in gemini_generate_content_stream(prompt, file_id=None, chat_input=None, cancel_event=cancel_event):
319
- stream_buffer["preview"] = partial
320
- if cancel_event is not None and cancel_event.is_set():
321
- break
322
- result = stream_buffer["preview"]
323
- result_holder["text"] = result
324
- if not cancel_event or not cancel_event.is_set():
325
- docx_bytes = save_compliance_as_docx(result, selected_filename)
326
- compliance_docx_name = f"{os.path.splitext(selected_filename)[0]}_compliance_check.docx"
327
- uploaded_documents[compliance_docx_name] = result
328
- shredded_documents[compliance_docx_name] = docx_bytes
329
- result_holder["docx_name"] = compliance_docx_name
330
- else:
331
- result_holder["docx_name"] = None
332
- logging.info("Compliance check completed.")
333
- except Exception as e:
334
- logging.error("Error during compliance check: %s", e)
335
- result_holder["text"] = f"Error during compliance check: {e}"
336
-
337
- stream_buffer["preview"] = ""
338
- t = Thread(target=thread_compliance)
339
- t.start()
340
- t.join()
341
- return result_holder["text"], None, result_holder["docx_name"], result_holder["text"]
342
 
343
  elif action == 'recover':
344
  return "Recovery not implemented yet.", None, None, None
@@ -427,10 +340,6 @@ def get_uploaded_proposal_list(docdict):
427
  return dbc.ListGroup(doc_list, flush=True)
428
 
429
  app.layout = dbc.Container([
430
- dcc.Store(id='shred-store', data={'text': None, 'docx_name': None}),
431
- dcc.Store(id='proposal-store', data={'text': None, 'docx_name': None}),
432
- dcc.Store(id='stream-status', data={'streaming': False}),
433
- dcc.Interval(id='stream-interval', interval=5000, n_intervals=0, disabled=True),
434
  dbc.Row([
435
  dbc.Col([
436
  dbc.Card([
@@ -531,8 +440,6 @@ app.layout = dbc.Container([
531
  ], fluid=True)
532
 
533
  @app.callback(
534
- Output('shred-store', 'data'),
535
- Output('proposal-store', 'data'),
536
  Output('output-data-upload', 'children'),
537
  Output('uploaded-doc-list', 'children'),
538
  Output('select-document-dropdown', 'options'),
@@ -541,8 +448,6 @@ app.layout = dbc.Container([
541
  Output('uploaded-proposal-list', 'children'),
542
  Output('select-proposal-dropdown', 'options'),
543
  Output('select-proposal-dropdown', 'value'),
544
- Output('stream-status', 'data'),
545
- Output('stream-interval', 'disabled'),
546
  [
547
  Input('shred-action-btn', 'n_clicks'),
548
  Input('proposal-action-btn', 'n_clicks'),
@@ -561,9 +466,7 @@ app.layout = dbc.Container([
561
  State('chat-input', 'value'),
562
  State('select-document-dropdown', 'value'),
563
  State('select-proposal-dropdown', 'value'),
564
- Input('cancel-action-btn', 'n_clicks'),
565
- Input('stream-interval', 'n_intervals'),
566
- State('stream-status', 'data') # <-- Moved this to the last State, matching the last argument
567
  ],
568
  prevent_initial_call=True
569
  )
@@ -574,8 +477,7 @@ def master_callback(
574
  shredded_delete_clicks, shredded_doc_children,
575
  select_proposal_dropdown_value,
576
  chat_input, selected_filename, selected_proposal_dropdown, selected_proposal_dropdown_state,
577
- cancel_clicks,
578
- stream_n_intervals, stream_status
579
  ):
580
  ctx = callback_context
581
  triggered_id = ctx.triggered[0]['prop_id'].split('.')[0] if ctx.triggered else None
@@ -588,46 +490,15 @@ def master_callback(
588
 
589
  upload_triggered = False
590
 
591
- shred_store = {'text': None, 'docx_name': None}
592
- proposal_store = {'text': None, 'docx_name': None}
593
- streaming = False
594
-
595
  rfp_delete_clicks = safe_get_n_clicks(ctx, 5)
596
  proposal_delete_clicks = safe_get_n_clicks(ctx, 9)
597
  shredded_delete_clicks = safe_get_n_clicks(ctx, 11)
598
 
599
  uploaded_rfp_decoded_bytes = None
600
 
601
- global gemini_lock, stream_buffer, stream_event
602
-
603
- if triggered_id == 'stream-interval':
604
- if not stream_status or not stream_status.get('streaming'):
605
- return dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update, stream_status, True
606
- preview = stream_buffer.get("preview", "")
607
- still_streaming = gemini_lock.locked()
608
- if not still_streaming:
609
- stream_status['streaming'] = False
610
- output_data_upload = dcc.Markdown(preview, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
611
- doc_options = [{'label': fn, 'value': fn} for fn in uploaded_documents.keys()]
612
- doc_value = selected_doc if selected_doc in uploaded_documents else (next(iter(uploaded_documents), None) if uploaded_documents else None)
613
- shredded_doc_list_items = get_shredded_doc_list(shredded_documents)
614
- uploaded_doc_list = get_uploaded_doc_list(uploaded_documents)
615
- uploaded_proposal_list = get_uploaded_proposal_list(uploaded_proposals)
616
- proposal_options = [{'label': fn, 'value': fn} for fn in uploaded_proposals.keys()]
617
- proposal_value = select_proposal_dropdown_value if select_proposal_dropdown_value in uploaded_proposals else (next(iter(uploaded_proposals), None) if uploaded_proposals else None)
618
- return (
619
- shred_store, proposal_store, output_data_upload,
620
- uploaded_doc_list, doc_options, doc_value,
621
- shredded_doc_list_items,
622
- uploaded_proposal_list, proposal_options, proposal_value,
623
- stream_status,
624
- True
625
- )
626
- return dash.no_update, dash.no_update, dcc.Markdown(preview, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"}), dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update, dash.no_update, stream_status, False
627
 
628
  if triggered_id == 'cancel-action-btn':
629
- stream_event.set()
630
- streaming = False
631
  output_data_upload = html.Div("[Cancelled by user]\n", style={"wordWrap": "break-word"})
632
  doc_options = [{'label': fn, 'value': fn} for fn in uploaded_documents.keys()]
633
  doc_value = selected_doc if selected_doc in uploaded_documents else (next(iter(uploaded_documents), None) if uploaded_documents else None)
@@ -637,12 +508,10 @@ def master_callback(
637
  proposal_options = [{'label': fn, 'value': fn} for fn in uploaded_proposals.keys()]
638
  proposal_value = select_proposal_dropdown_value if select_proposal_dropdown_value in uploaded_proposals else (next(iter(uploaded_proposals), None) if uploaded_proposals else None)
639
  return (
640
- shred_store, proposal_store, output_data_upload,
641
  uploaded_doc_list, doc_options, doc_value,
642
  shredded_doc_list_items,
643
- uploaded_proposal_list, proposal_options, proposal_value,
644
- {'streaming': False},
645
- True
646
  )
647
 
648
  if triggered_id == 'upload-document' and rfp_content is not None and rfp_filename:
@@ -742,33 +611,29 @@ def master_callback(
742
  if not got_lock:
743
  output_data_upload = html.Div("Another Gemini operation is in progress. Please wait or cancel.", style={"wordWrap": "break-word"})
744
  return (
745
- shred_store, proposal_store, output_data_upload,
746
  uploaded_doc_list, doc_options, doc_value,
747
  shredded_doc_list_items,
748
- uploaded_proposal_list, proposal_options, proposal_value,
749
- {'streaming': False},
750
- True
751
  )
752
- stream_event.clear()
753
- stream_buffer["preview"] = ""
754
- streaming = True
755
- def stream_gemini_thread(action, doc_value, chat_input, rfp_decoded_bytes, selected_generated_filename):
756
- try:
757
- process_document(action, doc_value, chat_input, rfp_decoded_bytes, cancel_event=stream_event, stream=True, selected_generated_filename=selected_generated_filename)
758
- finally:
759
- gemini_lock.release()
760
- action_name = "shred" if triggered_id=="shred-action-btn" else ("proposal" if triggered_id=="proposal-action-btn" else "compliance")
761
- t = Thread(target=stream_gemini_thread, args=(action_name, doc_value, chat_input, uploaded_rfp_decoded_bytes, select_proposal_dropdown_value))
762
- t.daemon = True
763
- t.start()
764
- output_data_upload = dcc.Markdown("Starting Gemini operation...", style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
765
  return (
766
- shred_store, proposal_store, output_data_upload,
767
  uploaded_doc_list, doc_options, doc_value,
768
  shredded_doc_list_items,
769
- uploaded_proposal_list, proposal_options, proposal_value,
770
- {'streaming': True},
771
- False
772
  )
773
 
774
  if upload_triggered:
@@ -776,23 +641,19 @@ def master_callback(
776
  proposal_value = proposal_value if proposal_value in uploaded_proposals else (next(iter(uploaded_proposals), None) if uploaded_proposals else None)
777
  output_data_upload = html.Div("Upload/Delete completed.", style={"wordWrap": "break-word"})
778
  return (
779
- shred_store, proposal_store, output_data_upload,
780
  uploaded_doc_list, doc_options, doc_value,
781
  shredded_doc_list_items,
782
- uploaded_proposal_list, proposal_options, proposal_value,
783
- {'streaming': False},
784
- True
785
  )
786
 
787
  doc_value = doc_value if doc_value in uploaded_documents else (next(iter(uploaded_documents), None) if uploaded_documents else None)
788
  proposal_value = proposal_value if proposal_value in uploaded_proposals else (next(iter(uploaded_proposals), None) if uploaded_proposals else None)
789
  return (
790
- shred_store, proposal_store, output_data_upload,
791
  uploaded_doc_list, doc_options, doc_value,
792
  shredded_doc_list_items,
793
- uploaded_proposal_list, proposal_options, proposal_value,
794
- {'streaming': False},
795
- True
796
  )
797
 
798
  if __name__ == '__main__':
 
8
  import logging
9
  from docx import Document
10
  import mimetypes
11
+ from threading import Lock
 
12
  import time
13
 
14
  import google.generativeai as genai
 
37
  generated_response = None
38
 
39
  gemini_lock = Lock()
 
 
40
 
41
  def decode_document(decoded_bytes):
42
  try:
 
86
  logging.error(f"Exception during file upload to Gemini: {e}")
87
  return None
88
 
89
+ def gemini_generate_content(prompt, file_id=None, chat_input=None):
90
  try:
91
  files = []
92
  if file_id:
 
108
  )
109
  )
110
  result = response.text if hasattr(response, "text") else str(response)
111
+ return result
 
 
 
 
 
 
 
 
 
112
  except Exception as e:
113
  logging.error("Error during Gemini generate_content: %s", e)
114
+ return f"Error during Gemini completion: {e}"
115
 
116
  def save_shredded_as_docx(shredded_text, rfp_filename):
117
  doc = Document()
 
143
  memf.seek(0)
144
  return memf.read()
145
 
146
+ def process_document(action, selected_filename=None, chat_input=None, rfp_decoded_bytes=None, selected_generated_filename=None):
147
+ global shredded_document, generated_response
148
 
149
  logging.info(f"Process document called with action: {action}")
150
 
 
174
  if chat_input:
175
  prompt += f"User additional instructions: {chat_input}\n"
176
  prompt += f"\nFile Name: {selected_filename}\n\n"
177
+ result = gemini_generate_content(prompt, file_id=doc_fileid, chat_input=chat_input)
178
+ shredded_document = result
179
+ if result and not result.startswith("Error"):
180
+ docx_bytes = save_shredded_as_docx(result, selected_filename)
181
+ generated_docx_name = f"{os.path.splitext(selected_filename)[0]}_shredded.docx"
182
+ shredded_documents[generated_docx_name] = docx_bytes
183
+ if generated_docx_name not in uploaded_documents:
184
+ uploaded_documents[generated_docx_name] = result
185
+ return result, None, generated_docx_name, result
186
+ else:
187
+ return result, None, None, result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
  elif action == 'proposal':
190
  if not doc_content:
 
210
  if chat_input:
211
  prompt += f"User additional instructions: {chat_input}\n"
212
  prompt += f"\n---\nRFP/SOW/PWS/RFI ({rfp_filename}):\n{doc_content}\n"
213
+ result = gemini_generate_content(prompt, file_id=rfp_fileid, chat_input=chat_input)
214
+ generated_response = result
215
+ if result and not result.startswith("Error"):
216
+ docx_bytes = save_proposal_as_docx(result, rfp_filename)
217
+ generated_docx_name = f"{os.path.splitext(rfp_filename)[0]}_proposal.docx"
218
+ uploaded_proposals[generated_docx_name] = result
219
+ uploaded_proposals_fileid[generated_docx_name] = None
220
+ return result, None, generated_docx_name, result
221
+ else:
222
+ return result, None, None, result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  elif action == 'compliance':
225
  if not selected_generated_filename or selected_generated_filename not in uploaded_proposals:
 
243
  "---\nGenerated Proposal Document:\n"
244
  f"{proposal_text}\n"
245
  )
246
+ result = gemini_generate_content(prompt, file_id=None, chat_input=None)
247
+ if result and not result.startswith("Error"):
248
+ docx_bytes = save_compliance_as_docx(result, selected_filename)
249
+ compliance_docx_name = f"{os.path.splitext(selected_filename)[0]}_compliance_check.docx"
250
+ uploaded_documents[compliance_docx_name] = result
251
+ shredded_documents[compliance_docx_name] = docx_bytes
252
+ return result, None, compliance_docx_name, result
253
+ else:
254
+ return result, None, None, result
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
 
256
  elif action == 'recover':
257
  return "Recovery not implemented yet.", None, None, None
 
340
  return dbc.ListGroup(doc_list, flush=True)
341
 
342
  app.layout = dbc.Container([
 
 
 
 
343
  dbc.Row([
344
  dbc.Col([
345
  dbc.Card([
 
440
  ], fluid=True)
441
 
442
  @app.callback(
 
 
443
  Output('output-data-upload', 'children'),
444
  Output('uploaded-doc-list', 'children'),
445
  Output('select-document-dropdown', 'options'),
 
448
  Output('uploaded-proposal-list', 'children'),
449
  Output('select-proposal-dropdown', 'options'),
450
  Output('select-proposal-dropdown', 'value'),
 
 
451
  [
452
  Input('shred-action-btn', 'n_clicks'),
453
  Input('proposal-action-btn', 'n_clicks'),
 
466
  State('chat-input', 'value'),
467
  State('select-document-dropdown', 'value'),
468
  State('select-proposal-dropdown', 'value'),
469
+ Input('cancel-action-btn', 'n_clicks')
 
 
470
  ],
471
  prevent_initial_call=True
472
  )
 
477
  shredded_delete_clicks, shredded_doc_children,
478
  select_proposal_dropdown_value,
479
  chat_input, selected_filename, selected_proposal_dropdown, selected_proposal_dropdown_state,
480
+ cancel_clicks
 
481
  ):
482
  ctx = callback_context
483
  triggered_id = ctx.triggered[0]['prop_id'].split('.')[0] if ctx.triggered else None
 
490
 
491
  upload_triggered = False
492
 
 
 
 
 
493
  rfp_delete_clicks = safe_get_n_clicks(ctx, 5)
494
  proposal_delete_clicks = safe_get_n_clicks(ctx, 9)
495
  shredded_delete_clicks = safe_get_n_clicks(ctx, 11)
496
 
497
  uploaded_rfp_decoded_bytes = None
498
 
499
+ global gemini_lock
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500
 
501
  if triggered_id == 'cancel-action-btn':
 
 
502
  output_data_upload = html.Div("[Cancelled by user]\n", style={"wordWrap": "break-word"})
503
  doc_options = [{'label': fn, 'value': fn} for fn in uploaded_documents.keys()]
504
  doc_value = selected_doc if selected_doc in uploaded_documents else (next(iter(uploaded_documents), None) if uploaded_documents else None)
 
508
  proposal_options = [{'label': fn, 'value': fn} for fn in uploaded_proposals.keys()]
509
  proposal_value = select_proposal_dropdown_value if select_proposal_dropdown_value in uploaded_proposals else (next(iter(uploaded_proposals), None) if uploaded_proposals else None)
510
  return (
511
+ output_data_upload,
512
  uploaded_doc_list, doc_options, doc_value,
513
  shredded_doc_list_items,
514
+ uploaded_proposal_list, proposal_options, proposal_value
 
 
515
  )
516
 
517
  if triggered_id == 'upload-document' and rfp_content is not None and rfp_filename:
 
611
  if not got_lock:
612
  output_data_upload = html.Div("Another Gemini operation is in progress. Please wait or cancel.", style={"wordWrap": "break-word"})
613
  return (
614
+ output_data_upload,
615
  uploaded_doc_list, doc_options, doc_value,
616
  shredded_doc_list_items,
617
+ uploaded_proposal_list, proposal_options, proposal_value
 
 
618
  )
619
+ try:
620
+ action_name = "shred" if triggered_id=="shred-action-btn" else ("proposal" if triggered_id=="proposal-action-btn" else "compliance")
621
+ result, _, _, _ = process_document(action_name, doc_value, chat_input, uploaded_rfp_decoded_bytes, select_proposal_dropdown_value)
622
+ output_data_upload = dcc.Markdown(result, style={"whiteSpace": "pre-wrap", "wordWrap": "break-word"})
623
+ finally:
624
+ gemini_lock.release()
625
+ doc_options = [{'label': fn, 'value': fn} for fn in uploaded_documents.keys()]
626
+ doc_value = doc_value if doc_value in uploaded_documents else (next(iter(uploaded_documents), None) if uploaded_documents else None)
627
+ proposal_options = [{'label': fn, 'value': fn} for fn in uploaded_proposals.keys()]
628
+ proposal_value = proposal_value if proposal_value in uploaded_proposals else (next(iter(uploaded_proposals), None) if uploaded_proposals else None)
629
+ shredded_doc_list_items = get_shredded_doc_list(shredded_documents)
630
+ uploaded_doc_list = get_uploaded_doc_list(uploaded_documents)
631
+ uploaded_proposal_list = get_uploaded_proposal_list(uploaded_proposals)
632
  return (
633
+ output_data_upload,
634
  uploaded_doc_list, doc_options, doc_value,
635
  shredded_doc_list_items,
636
+ uploaded_proposal_list, proposal_options, proposal_value
 
 
637
  )
638
 
639
  if upload_triggered:
 
641
  proposal_value = proposal_value if proposal_value in uploaded_proposals else (next(iter(uploaded_proposals), None) if uploaded_proposals else None)
642
  output_data_upload = html.Div("Upload/Delete completed.", style={"wordWrap": "break-word"})
643
  return (
644
+ output_data_upload,
645
  uploaded_doc_list, doc_options, doc_value,
646
  shredded_doc_list_items,
647
+ uploaded_proposal_list, proposal_options, proposal_value
 
 
648
  )
649
 
650
  doc_value = doc_value if doc_value in uploaded_documents else (next(iter(uploaded_documents), None) if uploaded_documents else None)
651
  proposal_value = proposal_value if proposal_value in uploaded_proposals else (next(iter(uploaded_proposals), None) if uploaded_proposals else None)
652
  return (
653
+ output_data_upload,
654
  uploaded_doc_list, doc_options, doc_value,
655
  shredded_doc_list_items,
656
+ uploaded_proposal_list, proposal_options, proposal_value
 
 
657
  )
658
 
659
  if __name__ == '__main__':