Spaces:
Paused
Paused
Update app.py via AI Editor
Browse files
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
|
| 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
|
| 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 |
-
|
| 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 |
-
|
| 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,
|
| 159 |
-
global shredded_document, generated_response
|
| 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 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 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 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 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 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 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
|
| 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 |
-
|
| 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 |
-
|
| 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 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
return (
|
| 766 |
-
|
| 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 |
-
|
| 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 |
-
|
| 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__':
|