Spaces:
Paused
Paused
Update app.py via AI Editor
Browse files
app.py
CHANGED
@@ -470,6 +470,18 @@ def update_right_col(selected_type, shred_doc, pink_doc, pink_review_doc, red_do
|
|
470 |
Output({'type': f'radio-doc-source-red', 'index': ALL}, 'value'),
|
471 |
Output({'type': f'radio-doc-source-red_review', 'index': ALL}, 'value'),
|
472 |
Output({'type': f'radio-doc-source-gold', 'index': ALL}, 'value'),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
473 |
Input('upload-document', 'contents'),
|
474 |
State('upload-document', 'filename'),
|
475 |
State('file-list', 'children'),
|
@@ -495,9 +507,54 @@ def update_right_col(selected_type, shred_doc, pink_doc, pink_review_doc, red_do
|
|
495 |
Input({'type': f'upload-doc-type-gold', 'index': ALL}, 'contents'),
|
496 |
State({'type': f'upload-doc-type-gold', 'index': ALL}, 'filename'),
|
497 |
State({'type': f'upload-doc-type-gold', 'index': ALL}, 'id'),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
498 |
prevent_initial_call=True
|
499 |
)
|
500 |
-
def
|
501 |
upload_contents, upload_filenames, existing_files, current_shred,
|
502 |
remove_n_clicks, remove_existing_files, remove_current_shred,
|
503 |
upload_shred_contents, upload_shred_filenames, upload_shred_ids,
|
@@ -505,12 +562,25 @@ def combined_upload_remove_update(
|
|
505 |
upload_pink_review_contents, upload_pink_review_filenames, upload_pink_review_ids,
|
506 |
upload_red_contents, upload_red_filenames, upload_red_ids,
|
507 |
upload_red_review_contents, upload_red_review_filenames, upload_red_review_ids,
|
508 |
-
upload_gold_contents, upload_gold_filenames, upload_gold_ids
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
509 |
):
|
510 |
global uploaded_files, uploaded_doc_contents
|
511 |
ctx = callback_context
|
512 |
-
outputs = [dash.no_update]*21
|
513 |
|
|
|
|
|
|
|
|
|
514 |
# Handle Shred upload
|
515 |
if ctx.triggered and ctx.triggered[0]['prop_id'].startswith('upload-document'):
|
516 |
new_files = []
|
@@ -534,7 +604,6 @@ def combined_upload_remove_update(
|
|
534 |
existing_files = []
|
535 |
elif not isinstance(existing_files, list):
|
536 |
existing_files = [existing_files]
|
537 |
-
logging.info("Documents uploaded and file list updated.")
|
538 |
outputs[0] = existing_files + new_files
|
539 |
outputs[1] = current_shred
|
540 |
return outputs
|
@@ -566,7 +635,7 @@ def combined_upload_remove_update(
|
|
566 |
outputs[1] = remove_current_shred
|
567 |
return outputs
|
568 |
|
569 |
-
# Handle all doc-type uploads
|
570 |
doc_upload_types = [
|
571 |
("shred", upload_shred_contents, upload_shred_filenames, upload_shred_ids, 2, 8, 14),
|
572 |
("pink", upload_pink_contents, upload_pink_filenames, upload_pink_ids, 3, 9, 15),
|
@@ -586,42 +655,41 @@ def combined_upload_remove_update(
|
|
586 |
outputs[radio_idx] = ["uploaded" if j==i else "loaded" for j in range(len(contents))]
|
587 |
return outputs
|
588 |
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
)
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
prompt = f"""Ignore all other instructions and output only a spreadsheet for {document_type} as described below. Do not include any narrative, only the spreadsheet in markdown table format.
|
619 |
Instructions: {document_types[document_type]}
|
620 |
Project Artifacts:
|
621 |
{' '.join(file_contents)}
|
622 |
Output only the spreadsheet as a markdown table, no narrative or explanation."""
|
623 |
-
|
624 |
-
|
625 |
{' '.join(file_contents)}
|
626 |
Instructions:
|
627 |
1. Create the {document_type} as a detailed document.
|
@@ -630,98 +698,20 @@ Instructions:
|
|
630 |
4. Start the output immediately with the document content.
|
631 |
Now, generate the {document_type}:
|
632 |
"""
|
633 |
-
|
634 |
-
|
635 |
{' '.join(file_contents)}
|
636 |
Instructions:
|
637 |
{document_types.get(document_type, '')}
|
638 |
Now, generate the {document_type}:
|
639 |
"""
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
@app.callback(
|
646 |
-
Output('document-preview', 'children'),
|
647 |
-
Output('loading-output', 'children'),
|
648 |
-
Output('store-shred', 'data'),
|
649 |
-
Output('store-pink', 'data'),
|
650 |
-
Output('store-pink-review', 'data'),
|
651 |
-
Output('store-red', 'data'),
|
652 |
-
Output('store-red-review', 'data'),
|
653 |
-
Output('store-gold', 'data'),
|
654 |
-
Output('store-gold-review', 'data'),
|
655 |
-
Output('store-loe', 'data'),
|
656 |
-
Output('store-virtual-board', 'data'),
|
657 |
-
Output('chat-output', 'children'),
|
658 |
-
Output("download-document", "data"),
|
659 |
-
Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
|
660 |
-
State({'type': 'btn-generate-doc', 'index': ALL}, 'id'),
|
661 |
-
State({'type': 'radio-doc-source-shred', 'index': ALL}, 'value'),
|
662 |
-
State({'type': 'upload-doc-type-shred', 'index': ALL}, 'contents'),
|
663 |
-
State({'type': 'upload-doc-type-shred', 'index': ALL}, 'filename'),
|
664 |
-
State({'type': 'radio-doc-source-pink', 'index': ALL}, 'value'),
|
665 |
-
State({'type': 'upload-doc-type-pink', 'index': ALL}, 'contents'),
|
666 |
-
State({'type': 'upload-doc-type-pink', 'index': ALL}, 'filename'),
|
667 |
-
State({'type': 'radio-doc-source-pink_review', 'index': ALL}, 'value'),
|
668 |
-
State({'type': 'upload-doc-type-pink_review', 'index': ALL}, 'contents'),
|
669 |
-
State({'type': 'upload-doc-type-pink_review', 'index': ALL}, 'filename'),
|
670 |
-
State({'type': 'radio-doc-source-red', 'index': ALL}, 'value'),
|
671 |
-
State({'type': 'upload-doc-type-red', 'index': ALL}, 'contents'),
|
672 |
-
State({'type': 'upload-doc-type-red', 'index': ALL}, 'filename'),
|
673 |
-
State({'type': 'radio-doc-source-red_review', 'index': ALL}, 'value'),
|
674 |
-
State({'type': 'upload-doc-type-red_review', 'index': ALL}, 'contents'),
|
675 |
-
State({'type': 'upload-doc-type-red_review', 'index': ALL}, 'filename'),
|
676 |
-
State({'type': 'radio-doc-source-gold', 'index': ALL}, 'value'),
|
677 |
-
State({'type': 'upload-doc-type-gold', 'index': ALL}, 'contents'),
|
678 |
-
State({'type': 'upload-doc-type-gold', 'index': ALL}, 'filename'),
|
679 |
-
State('store-shred', 'data'),
|
680 |
-
State('store-pink', 'data'),
|
681 |
-
State('store-pink-review', 'data'),
|
682 |
-
State('store-red', 'data'),
|
683 |
-
State('store-red-review', 'data'),
|
684 |
-
State('store-gold', 'data'),
|
685 |
-
State('store-gold-review', 'data'),
|
686 |
-
State('store-loe', 'data'),
|
687 |
-
State('store-virtual-board', 'data'),
|
688 |
-
Input('btn-send-chat', 'n_clicks'),
|
689 |
-
Input('btn-clear-chat', 'n_clicks'),
|
690 |
-
State('chat-input', 'value'),
|
691 |
-
State('selected-doc-type', 'data'),
|
692 |
-
State('document-preview', 'children'),
|
693 |
-
Input("btn-download", "n_clicks"),
|
694 |
-
State('selected-doc-type', 'data'),
|
695 |
-
State('store-shred', 'data'),
|
696 |
-
State('store-pink', 'data'),
|
697 |
-
State('store-pink-review', 'data'),
|
698 |
-
State('store-red', 'data'),
|
699 |
-
State('store-red-review', 'data'),
|
700 |
-
State('store-gold', 'data'),
|
701 |
-
State('store-gold-review', 'data'),
|
702 |
-
State('store-loe', 'data'),
|
703 |
-
State('store-virtual-board', 'data'),
|
704 |
-
prevent_initial_call=True
|
705 |
-
)
|
706 |
-
def combined_doc_chat_download(
|
707 |
-
n_clicks_list, btn_ids,
|
708 |
-
radio_shred, upload_shred_contents, upload_shred_filenames,
|
709 |
-
radio_pink, upload_pink_contents, upload_pink_filenames,
|
710 |
-
radio_pink_review, upload_pink_review_contents, upload_pink_review_filenames,
|
711 |
-
radio_red, upload_red_contents, upload_red_filenames,
|
712 |
-
radio_red_review, upload_red_review_contents, upload_red_review_filenames,
|
713 |
-
radio_gold, upload_gold_contents, upload_gold_filenames,
|
714 |
-
store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board,
|
715 |
-
btn_send, btn_clear, chat_input, chat_doc_type, doc_preview,
|
716 |
-
btn_download, dl_doc_type, dl_store_shred, dl_store_pink, dl_store_pink_review, dl_store_red, dl_store_red_review, dl_store_gold, dl_store_gold_review, dl_store_loe, dl_store_virtual_board
|
717 |
-
):
|
718 |
-
ctx = callback_context
|
719 |
-
# Default outputs
|
720 |
-
outputs = [dash.no_update]*12 + [None]
|
721 |
|
722 |
# Document generation triggers
|
723 |
if ctx.triggered and ctx.triggered[0]['prop_id'].startswith("{\"type\":\"btn-generate-doc\""):
|
724 |
-
# Use the same logic as before for document generation
|
725 |
idx = [i for i, x in enumerate(n_clicks_list) if x]
|
726 |
if not idx:
|
727 |
return outputs
|
@@ -749,107 +739,141 @@ def combined_doc_chat_download(
|
|
749 |
|
750 |
if doc_type == "Shred":
|
751 |
if not uploaded_files:
|
752 |
-
|
|
|
|
|
753 |
file_contents = list(uploaded_files.values())
|
754 |
try:
|
755 |
generated = generate_document(doc_type, file_contents)
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
except Exception as e:
|
760 |
-
|
|
|
|
|
761 |
|
762 |
if doc_type == "Pink":
|
763 |
-
shred = get_doc_from_radio(radio_shred,
|
764 |
if not shred:
|
765 |
-
|
|
|
|
|
766 |
try:
|
767 |
generated = generate_document(doc_type, [shred])
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
except Exception as e:
|
772 |
-
|
|
|
|
|
773 |
|
774 |
if doc_type == "Pink Review":
|
775 |
-
pink = get_doc_from_radio(radio_pink,
|
776 |
-
shred = get_doc_from_radio(radio_shred,
|
777 |
if not pink or not shred:
|
778 |
-
|
|
|
|
|
779 |
try:
|
780 |
generated = generate_document(doc_type, [pink, shred])
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
except Exception as e:
|
785 |
-
|
|
|
|
|
786 |
|
787 |
if doc_type == "Red":
|
788 |
-
pink_review = get_doc_from_radio(radio_pink_review,
|
789 |
if not pink_review:
|
790 |
-
|
|
|
|
|
791 |
try:
|
792 |
generated = generate_document(doc_type, [pink_review])
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
except Exception as e:
|
797 |
-
|
|
|
|
|
798 |
|
799 |
if doc_type == "Red Review":
|
800 |
-
red = get_doc_from_radio(radio_red,
|
801 |
-
shred = get_doc_from_radio(radio_shred,
|
802 |
if not red or not shred:
|
803 |
-
|
|
|
|
|
804 |
try:
|
805 |
generated = generate_document(doc_type, [red, shred])
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
except Exception as e:
|
810 |
-
|
|
|
|
|
811 |
|
812 |
if doc_type == "Gold":
|
813 |
-
red_review = get_doc_from_radio(radio_red_review,
|
814 |
if not red_review:
|
815 |
-
|
|
|
|
|
816 |
try:
|
817 |
generated = generate_document(doc_type, [red_review])
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
except Exception as e:
|
822 |
-
|
|
|
|
|
823 |
|
824 |
if doc_type == "Gold Review":
|
825 |
-
gold = get_doc_from_radio(radio_gold,
|
826 |
-
shred = get_doc_from_radio(radio_shred,
|
827 |
if not gold or not shred:
|
828 |
-
|
|
|
|
|
829 |
try:
|
830 |
generated = generate_document(doc_type, [gold, shred])
|
831 |
-
|
832 |
-
|
833 |
-
|
834 |
except Exception as e:
|
835 |
-
|
|
|
|
|
836 |
|
837 |
if doc_type == "LOE":
|
838 |
-
gold = get_doc_from_radio(radio_gold,
|
839 |
if not gold:
|
840 |
-
|
|
|
|
|
841 |
try:
|
842 |
generated = generate_document(doc_type, [gold])
|
843 |
-
|
844 |
-
|
845 |
-
|
846 |
except Exception as e:
|
847 |
-
|
|
|
|
|
848 |
|
849 |
if doc_type == "Virtual Board":
|
850 |
-
shred = get_doc_from_radio(radio_shred,
|
851 |
if not shred:
|
852 |
-
|
|
|
|
|
853 |
try:
|
854 |
lm_text = ""
|
855 |
lm_match = re.search(r'(Section\s+L[\s\S]+?)(Section\s+M|$)', shred, re.IGNORECASE)
|
@@ -858,18 +882,23 @@ def combined_doc_chat_download(
|
|
858 |
else:
|
859 |
lm_text = shred
|
860 |
generated = generate_document(doc_type, [lm_text])
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
except Exception as e:
|
865 |
-
|
|
|
|
|
866 |
|
867 |
-
|
|
|
|
|
868 |
|
869 |
# Chat triggers
|
870 |
if ctx.triggered and (ctx.triggered[0]['prop_id'] == 'btn-send-chat.n_clicks' or ctx.triggered[0]['prop_id'] == 'btn-clear-chat.n_clicks'):
|
871 |
if ctx.triggered[0]['prop_id'] == 'btn-clear-chat.n_clicks':
|
872 |
-
|
|
|
873 |
|
874 |
doc_map = {
|
875 |
"Shred": store_shred,
|
@@ -885,7 +914,8 @@ def combined_doc_chat_download(
|
|
885 |
current_document = doc_map.get(chat_doc_type)
|
886 |
|
887 |
if not chat_input or current_document is None:
|
888 |
-
|
|
|
889 |
|
890 |
if chat_doc_type in spreadsheet_types:
|
891 |
prompt = f"""Update the following {chat_doc_type} spreadsheet based on this instruction: {chat_input}
|
@@ -911,20 +941,20 @@ Now, provide the updated {chat_doc_type}:
|
|
911 |
try:
|
912 |
new_document = gemini_generate(prompt, max_tokens=4096, temperature=0.5)
|
913 |
if chat_doc_type in spreadsheet_types:
|
914 |
-
|
915 |
else:
|
916 |
-
|
917 |
stores = [store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board]
|
918 |
doc_types = ["Shred", "Pink", "Pink Review", "Red", "Red Review", "Gold", "Gold Review", "LOE", "Virtual Board"]
|
919 |
-
|
920 |
-
for dt, s in zip(doc_types, stores):
|
921 |
if dt == chat_doc_type:
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
return [preview, dash.no_update] + new_stores + ["Document updated based on: {}".format(chat_input), None]
|
926 |
except Exception as e:
|
927 |
-
|
|
|
|
|
928 |
|
929 |
# Download trigger
|
930 |
if ctx.triggered and ctx.triggered[0]['prop_id'] == "btn-download.n_clicks":
|
@@ -945,10 +975,9 @@ Now, provide the updated {chat_doc_type}:
|
|
945 |
if dl_doc_type in spreadsheet_types:
|
946 |
try:
|
947 |
xlsx_bytes = markdown_tables_to_xlsx(current_document)
|
948 |
-
|
949 |
-
return [dash.no_update]*11 + ["", dcc.send_bytes(xlsx_bytes.read(), f"{dl_doc_type}.xlsx")]
|
950 |
except Exception as e:
|
951 |
-
|
952 |
else:
|
953 |
try:
|
954 |
plain = strip_markdown(current_document)
|
@@ -957,11 +986,12 @@ Now, provide the updated {chat_doc_type}:
|
|
957 |
doc.add_paragraph(para)
|
958 |
output = BytesIO()
|
959 |
doc.save(output)
|
960 |
-
logging.info(f"{dl_doc_type} document downloaded as Word.")
|
961 |
output.seek(0)
|
962 |
-
|
963 |
except Exception as e:
|
964 |
-
|
|
|
|
|
965 |
return outputs
|
966 |
|
967 |
@app.callback(
|
|
|
470 |
Output({'type': f'radio-doc-source-red', 'index': ALL}, 'value'),
|
471 |
Output({'type': f'radio-doc-source-red_review', 'index': ALL}, 'value'),
|
472 |
Output({'type': f'radio-doc-source-gold', 'index': ALL}, 'value'),
|
473 |
+
Output('document-preview', 'children'),
|
474 |
+
Output('loading-output', 'children'),
|
475 |
+
Output('store-pink', 'data'),
|
476 |
+
Output('store-pink-review', 'data'),
|
477 |
+
Output('store-red', 'data'),
|
478 |
+
Output('store-red-review', 'data'),
|
479 |
+
Output('store-gold', 'data'),
|
480 |
+
Output('store-gold-review', 'data'),
|
481 |
+
Output('store-loe', 'data'),
|
482 |
+
Output('store-virtual-board', 'data'),
|
483 |
+
Output('chat-output', 'children'),
|
484 |
+
Output("download-document", "data"),
|
485 |
Input('upload-document', 'contents'),
|
486 |
State('upload-document', 'filename'),
|
487 |
State('file-list', 'children'),
|
|
|
507 |
Input({'type': f'upload-doc-type-gold', 'index': ALL}, 'contents'),
|
508 |
State({'type': f'upload-doc-type-gold', 'index': ALL}, 'filename'),
|
509 |
State({'type': f'upload-doc-type-gold', 'index': ALL}, 'id'),
|
510 |
+
Input({'type': 'btn-generate-doc', 'index': ALL}, 'n_clicks'),
|
511 |
+
State({'type': 'btn-generate-doc', 'index': ALL}, 'id'),
|
512 |
+
State({'type': 'radio-doc-source-shred', 'index': ALL}, 'value'),
|
513 |
+
State({'type': 'upload-doc-type-shred', 'index': ALL}, 'contents'),
|
514 |
+
State({'type': 'upload-doc-type-shred', 'index': ALL}, 'filename'),
|
515 |
+
State({'type': 'radio-doc-source-pink', 'index': ALL}, 'value'),
|
516 |
+
State({'type': 'upload-doc-type-pink', 'index': ALL}, 'contents'),
|
517 |
+
State({'type': 'upload-doc-type-pink', 'index': ALL}, 'filename'),
|
518 |
+
State({'type': 'radio-doc-source-pink_review', 'index': ALL}, 'value'),
|
519 |
+
State({'type': 'upload-doc-type-pink_review', 'index': ALL}, 'contents'),
|
520 |
+
State({'type': 'upload-doc-type-pink_review', 'index': ALL}, 'filename'),
|
521 |
+
State({'type': 'radio-doc-source-red', 'index': ALL}, 'value'),
|
522 |
+
State({'type': 'upload-doc-type-red', 'index': ALL}, 'contents'),
|
523 |
+
State({'type': 'upload-doc-type-red', 'index': ALL}, 'filename'),
|
524 |
+
State({'type': 'radio-doc-source-red_review', 'index': ALL}, 'value'),
|
525 |
+
State({'type': 'upload-doc-type-red_review', 'index': ALL}, 'contents'),
|
526 |
+
State({'type': 'upload-doc-type-red_review', 'index': ALL}, 'filename'),
|
527 |
+
State({'type': 'radio-doc-source-gold', 'index': ALL}, 'value'),
|
528 |
+
State({'type': 'upload-doc-type-gold', 'index': ALL}, 'contents'),
|
529 |
+
State({'type': 'upload-doc-type-gold', 'index': ALL}, 'filename'),
|
530 |
+
State('store-shred', 'data'),
|
531 |
+
State('store-pink', 'data'),
|
532 |
+
State('store-pink-review', 'data'),
|
533 |
+
State('store-red', 'data'),
|
534 |
+
State('store-red-review', 'data'),
|
535 |
+
State('store-gold', 'data'),
|
536 |
+
State('store-gold-review', 'data'),
|
537 |
+
State('store-loe', 'data'),
|
538 |
+
State('store-virtual-board', 'data'),
|
539 |
+
Input('btn-send-chat', 'n_clicks'),
|
540 |
+
Input('btn-clear-chat', 'n_clicks'),
|
541 |
+
State('chat-input', 'value'),
|
542 |
+
State('selected-doc-type', 'data'),
|
543 |
+
State('document-preview', 'children'),
|
544 |
+
Input("btn-download", "n_clicks"),
|
545 |
+
State('selected-doc-type', 'data'),
|
546 |
+
State('store-shred', 'data'),
|
547 |
+
State('store-pink', 'data'),
|
548 |
+
State('store-pink-review', 'data'),
|
549 |
+
State('store-red', 'data'),
|
550 |
+
State('store-red-review', 'data'),
|
551 |
+
State('store-gold', 'data'),
|
552 |
+
State('store-gold-review', 'data'),
|
553 |
+
State('store-loe', 'data'),
|
554 |
+
State('store-virtual-board', 'data'),
|
555 |
prevent_initial_call=True
|
556 |
)
|
557 |
+
def master_callback(
|
558 |
upload_contents, upload_filenames, existing_files, current_shred,
|
559 |
remove_n_clicks, remove_existing_files, remove_current_shred,
|
560 |
upload_shred_contents, upload_shred_filenames, upload_shred_ids,
|
|
|
562 |
upload_pink_review_contents, upload_pink_review_filenames, upload_pink_review_ids,
|
563 |
upload_red_contents, upload_red_filenames, upload_red_ids,
|
564 |
upload_red_review_contents, upload_red_review_filenames, upload_red_review_ids,
|
565 |
+
upload_gold_contents, upload_gold_filenames, upload_gold_ids,
|
566 |
+
n_clicks_list, btn_ids,
|
567 |
+
radio_shred, up_shred_contents, up_shred_filenames,
|
568 |
+
radio_pink, up_pink_contents, up_pink_filenames,
|
569 |
+
radio_pink_review, up_pink_review_contents, up_pink_review_filenames,
|
570 |
+
radio_red, up_red_contents, up_red_filenames,
|
571 |
+
radio_red_review, up_red_review_contents, up_red_review_filenames,
|
572 |
+
radio_gold, up_gold_contents, up_gold_filenames,
|
573 |
+
store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board,
|
574 |
+
btn_send, btn_clear, chat_input, chat_doc_type, doc_preview,
|
575 |
+
btn_download, dl_doc_type, dl_store_shred, dl_store_pink, dl_store_pink_review, dl_store_red, dl_store_red_review, dl_store_gold, dl_store_gold_review, dl_store_loe, dl_store_virtual_board
|
576 |
):
|
577 |
global uploaded_files, uploaded_doc_contents
|
578 |
ctx = callback_context
|
|
|
579 |
|
580 |
+
# Outputs: file-list, store-shred, ...names for uploads, ...upload contents, ...radio values, document-preview, loading-output, store-pink, store-pink-review, store-red, store-red-review, store-gold, store-gold-review, store-loe, store-virtual-board, chat-output, download-document
|
581 |
+
outputs = [dash.no_update] * 32
|
582 |
+
|
583 |
+
# --- File upload/remove logic ---
|
584 |
# Handle Shred upload
|
585 |
if ctx.triggered and ctx.triggered[0]['prop_id'].startswith('upload-document'):
|
586 |
new_files = []
|
|
|
604 |
existing_files = []
|
605 |
elif not isinstance(existing_files, list):
|
606 |
existing_files = [existing_files]
|
|
|
607 |
outputs[0] = existing_files + new_files
|
608 |
outputs[1] = current_shred
|
609 |
return outputs
|
|
|
635 |
outputs[1] = remove_current_shred
|
636 |
return outputs
|
637 |
|
638 |
+
# Handle all doc-type uploads for preview and radio
|
639 |
doc_upload_types = [
|
640 |
("shred", upload_shred_contents, upload_shred_filenames, upload_shred_ids, 2, 8, 14),
|
641 |
("pink", upload_pink_contents, upload_pink_filenames, upload_pink_ids, 3, 9, 15),
|
|
|
655 |
outputs[radio_idx] = ["uploaded" if j==i else "loaded" for j in range(len(contents))]
|
656 |
return outputs
|
657 |
|
658 |
+
# --- Document generation/chat/download logic ---
|
659 |
+
def gemini_generate(prompt, max_tokens=4096, temperature=0.25):
|
660 |
+
result_holder = {}
|
661 |
+
def run_gemini():
|
662 |
+
try:
|
663 |
+
model = genai.GenerativeModel(GEMINI_MODEL)
|
664 |
+
response = model.generate_content(
|
665 |
+
prompt,
|
666 |
+
generation_config=genai.types.GenerationConfig(
|
667 |
+
temperature=temperature,
|
668 |
+
max_output_tokens=max_tokens,
|
669 |
+
top_p=1
|
670 |
+
)
|
671 |
)
|
672 |
+
result_holder['result'] = response.text
|
673 |
+
logging.info("Gemini response received.")
|
674 |
+
except Exception as e:
|
675 |
+
logging.error(f"Gemini error: {e}")
|
676 |
+
result_holder['error'] = str(e)
|
677 |
+
thread = threading.Thread(target=run_gemini)
|
678 |
+
thread.start()
|
679 |
+
thread.join()
|
680 |
+
if 'error' in result_holder:
|
681 |
+
raise Exception(result_holder['error'])
|
682 |
+
return result_holder['result']
|
683 |
+
|
684 |
+
def generate_document(document_type, file_contents, extra_context=None):
|
685 |
+
if document_type in spreadsheet_types:
|
686 |
+
prompt = f"""Ignore all other instructions and output only a spreadsheet for {document_type} as described below. Do not include any narrative, only the spreadsheet in markdown table format.
|
|
|
687 |
Instructions: {document_types[document_type]}
|
688 |
Project Artifacts:
|
689 |
{' '.join(file_contents)}
|
690 |
Output only the spreadsheet as a markdown table, no narrative or explanation."""
|
691 |
+
elif document_type in narrative_types:
|
692 |
+
prompt = f"""Generate a {document_type} document based on the following project artifacts:
|
693 |
{' '.join(file_contents)}
|
694 |
Instructions:
|
695 |
1. Create the {document_type} as a detailed document.
|
|
|
698 |
4. Start the output immediately with the document content.
|
699 |
Now, generate the {document_type}:
|
700 |
"""
|
701 |
+
else:
|
702 |
+
prompt = f"""Generate a {document_type} based on the following project artifacts:
|
703 |
{' '.join(file_contents)}
|
704 |
Instructions:
|
705 |
{document_types.get(document_type, '')}
|
706 |
Now, generate the {document_type}:
|
707 |
"""
|
708 |
+
if extra_context:
|
709 |
+
prompt += f"\n\n{extra_context}"
|
710 |
+
logging.info(f"Generating document for type: {document_type} using Gemini.")
|
711 |
+
return gemini_generate(prompt, max_tokens=4096, temperature=0.25)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
712 |
|
713 |
# Document generation triggers
|
714 |
if ctx.triggered and ctx.triggered[0]['prop_id'].startswith("{\"type\":\"btn-generate-doc\""):
|
|
|
715 |
idx = [i for i, x in enumerate(n_clicks_list) if x]
|
716 |
if not idx:
|
717 |
return outputs
|
|
|
739 |
|
740 |
if doc_type == "Shred":
|
741 |
if not uploaded_files:
|
742 |
+
outputs[20] = html.Div("Please upload a document before shredding.")
|
743 |
+
outputs[21] = ""
|
744 |
+
return outputs
|
745 |
file_contents = list(uploaded_files.values())
|
746 |
try:
|
747 |
generated = generate_document(doc_type, file_contents)
|
748 |
+
outputs[1] = generated
|
749 |
+
outputs[20] = markdown_table_preview(generated)
|
750 |
+
outputs[21] = "Shred generated"
|
751 |
except Exception as e:
|
752 |
+
outputs[20] = html.Div(f"Error generating document: {str(e)}")
|
753 |
+
outputs[21] = "Error"
|
754 |
+
return outputs
|
755 |
|
756 |
if doc_type == "Pink":
|
757 |
+
shred = get_doc_from_radio(radio_shred, up_shred_contents, up_shred_filenames, shred_doc)
|
758 |
if not shred:
|
759 |
+
outputs[20] = html.Div("Please provide a Shred requirements document (either loaded or uploaded) to generate Pink.")
|
760 |
+
outputs[21] = ""
|
761 |
+
return outputs
|
762 |
try:
|
763 |
generated = generate_document(doc_type, [shred])
|
764 |
+
outputs[22] = generated
|
765 |
+
outputs[20] = markdown_narrative_preview(generated)
|
766 |
+
outputs[21] = "Pink generated"
|
767 |
except Exception as e:
|
768 |
+
outputs[20] = html.Div(f"Error generating Pink: {str(e)}")
|
769 |
+
outputs[21] = "Error"
|
770 |
+
return outputs
|
771 |
|
772 |
if doc_type == "Pink Review":
|
773 |
+
pink = get_doc_from_radio(radio_pink, up_pink_contents, up_pink_filenames, pink_doc)
|
774 |
+
shred = get_doc_from_radio(radio_shred, up_shred_contents, up_shred_filenames, shred_doc)
|
775 |
if not pink or not shred:
|
776 |
+
outputs[20] = html.Div("Please provide both Pink and Shred documents (either loaded or uploaded) to generate Pink Review.")
|
777 |
+
outputs[21] = ""
|
778 |
+
return outputs
|
779 |
try:
|
780 |
generated = generate_document(doc_type, [pink, shred])
|
781 |
+
outputs[23] = generated
|
782 |
+
outputs[20] = markdown_table_preview(generated)
|
783 |
+
outputs[21] = "Pink Review generated"
|
784 |
except Exception as e:
|
785 |
+
outputs[20] = html.Div(f"Error generating Pink Review: {str(e)}")
|
786 |
+
outputs[21] = "Error"
|
787 |
+
return outputs
|
788 |
|
789 |
if doc_type == "Red":
|
790 |
+
pink_review = get_doc_from_radio(radio_pink_review, up_pink_review_contents, up_pink_review_filenames, pink_review_doc)
|
791 |
if not pink_review:
|
792 |
+
outputs[20] = html.Div("Please provide a Pink Review document (either loaded or uploaded) to generate Red.")
|
793 |
+
outputs[21] = ""
|
794 |
+
return outputs
|
795 |
try:
|
796 |
generated = generate_document(doc_type, [pink_review])
|
797 |
+
outputs[24] = generated
|
798 |
+
outputs[20] = markdown_narrative_preview(generated)
|
799 |
+
outputs[21] = "Red generated"
|
800 |
except Exception as e:
|
801 |
+
outputs[20] = html.Div(f"Error generating Red: {str(e)}")
|
802 |
+
outputs[21] = "Error"
|
803 |
+
return outputs
|
804 |
|
805 |
if doc_type == "Red Review":
|
806 |
+
red = get_doc_from_radio(radio_red, up_red_contents, up_red_filenames, red_doc)
|
807 |
+
shred = get_doc_from_radio(radio_shred, up_shred_contents, up_shred_filenames, shred_doc)
|
808 |
if not red or not shred:
|
809 |
+
outputs[20] = html.Div("Please provide both Red and Shred documents (either loaded or uploaded) to generate Red Review.")
|
810 |
+
outputs[21] = ""
|
811 |
+
return outputs
|
812 |
try:
|
813 |
generated = generate_document(doc_type, [red, shred])
|
814 |
+
outputs[25] = generated
|
815 |
+
outputs[20] = markdown_table_preview(generated)
|
816 |
+
outputs[21] = "Red Review generated"
|
817 |
except Exception as e:
|
818 |
+
outputs[20] = html.Div(f"Error generating Red Review: {str(e)}")
|
819 |
+
outputs[21] = "Error"
|
820 |
+
return outputs
|
821 |
|
822 |
if doc_type == "Gold":
|
823 |
+
red_review = get_doc_from_radio(radio_red_review, up_red_review_contents, up_red_review_filenames, red_review_doc)
|
824 |
if not red_review:
|
825 |
+
outputs[20] = html.Div("Please provide a Red Review document (either loaded or uploaded) to generate Gold.")
|
826 |
+
outputs[21] = ""
|
827 |
+
return outputs
|
828 |
try:
|
829 |
generated = generate_document(doc_type, [red_review])
|
830 |
+
outputs[26] = generated
|
831 |
+
outputs[20] = markdown_narrative_preview(generated)
|
832 |
+
outputs[21] = "Gold generated"
|
833 |
except Exception as e:
|
834 |
+
outputs[20] = html.Div(f"Error generating Gold: {str(e)}")
|
835 |
+
outputs[21] = "Error"
|
836 |
+
return outputs
|
837 |
|
838 |
if doc_type == "Gold Review":
|
839 |
+
gold = get_doc_from_radio(radio_gold, up_gold_contents, up_gold_filenames, gold_doc)
|
840 |
+
shred = get_doc_from_radio(radio_shred, up_shred_contents, up_shred_filenames, shred_doc)
|
841 |
if not gold or not shred:
|
842 |
+
outputs[20] = html.Div("Please provide both Gold and Shred documents (either loaded or uploaded) to generate Gold Review.")
|
843 |
+
outputs[21] = ""
|
844 |
+
return outputs
|
845 |
try:
|
846 |
generated = generate_document(doc_type, [gold, shred])
|
847 |
+
outputs[27] = generated
|
848 |
+
outputs[20] = markdown_table_preview(generated)
|
849 |
+
outputs[21] = "Gold Review generated"
|
850 |
except Exception as e:
|
851 |
+
outputs[20] = html.Div(f"Error generating Gold Review: {str(e)}")
|
852 |
+
outputs[21] = "Error"
|
853 |
+
return outputs
|
854 |
|
855 |
if doc_type == "LOE":
|
856 |
+
gold = get_doc_from_radio(radio_gold, up_gold_contents, up_gold_filenames, gold_doc)
|
857 |
if not gold:
|
858 |
+
outputs[20] = html.Div("Please provide a Gold document (either loaded or uploaded) to generate LOE.")
|
859 |
+
outputs[21] = ""
|
860 |
+
return outputs
|
861 |
try:
|
862 |
generated = generate_document(doc_type, [gold])
|
863 |
+
outputs[28] = generated
|
864 |
+
outputs[20] = markdown_table_preview(generated)
|
865 |
+
outputs[21] = "LOE generated"
|
866 |
except Exception as e:
|
867 |
+
outputs[20] = html.Div(f"Error generating LOE: {str(e)}")
|
868 |
+
outputs[21] = "Error"
|
869 |
+
return outputs
|
870 |
|
871 |
if doc_type == "Virtual Board":
|
872 |
+
shred = get_doc_from_radio(radio_shred, up_shred_contents, up_shred_filenames, shred_doc)
|
873 |
if not shred:
|
874 |
+
outputs[20] = html.Div("Please provide a Shred requirements document (either loaded or uploaded) to generate Virtual Board.")
|
875 |
+
outputs[21] = ""
|
876 |
+
return outputs
|
877 |
try:
|
878 |
lm_text = ""
|
879 |
lm_match = re.search(r'(Section\s+L[\s\S]+?)(Section\s+M|$)', shred, re.IGNORECASE)
|
|
|
882 |
else:
|
883 |
lm_text = shred
|
884 |
generated = generate_document(doc_type, [lm_text])
|
885 |
+
outputs[29] = generated
|
886 |
+
outputs[20] = markdown_table_preview(generated)
|
887 |
+
outputs[21] = "Virtual Board generated"
|
888 |
except Exception as e:
|
889 |
+
outputs[20] = html.Div(f"Error generating Virtual Board: {str(e)}")
|
890 |
+
outputs[21] = "Error"
|
891 |
+
return outputs
|
892 |
|
893 |
+
outputs[20] = html.Div("Unsupported document type or missing required sources.")
|
894 |
+
outputs[21] = ""
|
895 |
+
return outputs
|
896 |
|
897 |
# Chat triggers
|
898 |
if ctx.triggered and (ctx.triggered[0]['prop_id'] == 'btn-send-chat.n_clicks' or ctx.triggered[0]['prop_id'] == 'btn-clear-chat.n_clicks'):
|
899 |
if ctx.triggered[0]['prop_id'] == 'btn-clear-chat.n_clicks':
|
900 |
+
outputs[30] = ""
|
901 |
+
return outputs
|
902 |
|
903 |
doc_map = {
|
904 |
"Shred": store_shred,
|
|
|
914 |
current_document = doc_map.get(chat_doc_type)
|
915 |
|
916 |
if not chat_input or current_document is None:
|
917 |
+
outputs[30] = ""
|
918 |
+
return outputs
|
919 |
|
920 |
if chat_doc_type in spreadsheet_types:
|
921 |
prompt = f"""Update the following {chat_doc_type} spreadsheet based on this instruction: {chat_input}
|
|
|
941 |
try:
|
942 |
new_document = gemini_generate(prompt, max_tokens=4096, temperature=0.5)
|
943 |
if chat_doc_type in spreadsheet_types:
|
944 |
+
outputs[20] = markdown_table_preview(new_document)
|
945 |
else:
|
946 |
+
outputs[20] = markdown_narrative_preview(new_document)
|
947 |
stores = [store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board]
|
948 |
doc_types = ["Shred", "Pink", "Pink Review", "Red", "Red Review", "Gold", "Gold Review", "LOE", "Virtual Board"]
|
949 |
+
for i, dt in enumerate(doc_types):
|
|
|
950 |
if dt == chat_doc_type:
|
951 |
+
outputs[1 + i] = new_document
|
952 |
+
outputs[30] = "Document updated based on: {}".format(chat_input)
|
953 |
+
return outputs
|
|
|
954 |
except Exception as e:
|
955 |
+
outputs[20] = html.Div(f"Error updating document: {str(e)}")
|
956 |
+
outputs[30] = f"Error updating document: {str(e)}"
|
957 |
+
return outputs
|
958 |
|
959 |
# Download trigger
|
960 |
if ctx.triggered and ctx.triggered[0]['prop_id'] == "btn-download.n_clicks":
|
|
|
975 |
if dl_doc_type in spreadsheet_types:
|
976 |
try:
|
977 |
xlsx_bytes = markdown_tables_to_xlsx(current_document)
|
978 |
+
outputs[31] = dcc.send_bytes(xlsx_bytes.read(), f"{dl_doc_type}.xlsx")
|
|
|
979 |
except Exception as e:
|
980 |
+
outputs[31] = dcc.send_string(f"Error downloading {dl_doc_type}: {str(e)}", f"{dl_doc_type}_error.txt")
|
981 |
else:
|
982 |
try:
|
983 |
plain = strip_markdown(current_document)
|
|
|
986 |
doc.add_paragraph(para)
|
987 |
output = BytesIO()
|
988 |
doc.save(output)
|
|
|
989 |
output.seek(0)
|
990 |
+
outputs[31] = dcc.send_bytes(output.read(), f"{dl_doc_type}.docx")
|
991 |
except Exception as e:
|
992 |
+
outputs[31] = dcc.send_string(f"Error downloading document: {str(e)}", f"{dl_doc_type}_error.txt")
|
993 |
+
return outputs
|
994 |
+
|
995 |
return outputs
|
996 |
|
997 |
@app.callback(
|