bluenevus commited on
Commit
ef148d1
·
1 Parent(s): 469746c

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +224 -194
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 combined_upload_remove_update(
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
- return outputs
590
-
591
- def gemini_generate(prompt, max_tokens=4096, temperature=0.25):
592
- result_holder = {}
593
- def run_gemini():
594
- try:
595
- model = genai.GenerativeModel(GEMINI_MODEL)
596
- response = model.generate_content(
597
- prompt,
598
- generation_config=genai.types.GenerationConfig(
599
- temperature=temperature,
600
- max_output_tokens=max_tokens,
601
- top_p=1
602
  )
603
- )
604
- result_holder['result'] = response.text
605
- logging.info("Gemini response received.")
606
- except Exception as e:
607
- logging.error(f"Gemini error: {e}")
608
- result_holder['error'] = str(e)
609
- thread = threading.Thread(target=run_gemini)
610
- thread.start()
611
- thread.join()
612
- if 'error' in result_holder:
613
- raise Exception(result_holder['error'])
614
- return result_holder['result']
615
-
616
- def generate_document(document_type, file_contents, extra_context=None):
617
- if document_type in spreadsheet_types:
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
- elif document_type in narrative_types:
624
- prompt = f"""Generate a {document_type} document based on the following project artifacts:
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
- else:
634
- prompt = f"""Generate a {document_type} based on the following project artifacts:
635
  {' '.join(file_contents)}
636
  Instructions:
637
  {document_types.get(document_type, '')}
638
  Now, generate the {document_type}:
639
  """
640
- if extra_context:
641
- prompt += f"\n\n{extra_context}"
642
- logging.info(f"Generating document for type: {document_type} using Gemini.")
643
- return gemini_generate(prompt, max_tokens=4096, temperature=0.25)
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
- return [html.Div("Please upload a document before shredding."), "", None, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
753
  file_contents = list(uploaded_files.values())
754
  try:
755
  generated = generate_document(doc_type, file_contents)
756
- shred_doc = generated
757
- preview = markdown_table_preview(generated)
758
- return [preview, "Shred generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
759
  except Exception as e:
760
- return [html.Div(f"Error generating document: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
761
 
762
  if doc_type == "Pink":
763
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
764
  if not shred:
765
- return [html.Div("Please provide a Shred requirements document (either loaded or uploaded) to generate Pink."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
766
  try:
767
  generated = generate_document(doc_type, [shred])
768
- pink_doc = generated
769
- preview = markdown_narrative_preview(generated)
770
- return [preview, "Pink generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
771
  except Exception as e:
772
- return [html.Div(f"Error generating Pink: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
773
 
774
  if doc_type == "Pink Review":
775
- pink = get_doc_from_radio(radio_pink, upload_pink_contents, upload_pink_filenames, pink_doc)
776
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
777
  if not pink or not shred:
778
- return [html.Div("Please provide both Pink and Shred documents (either loaded or uploaded) to generate Pink Review."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
779
  try:
780
  generated = generate_document(doc_type, [pink, shred])
781
- pink_review_doc = generated
782
- preview = markdown_table_preview(generated)
783
- return [preview, "Pink Review generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
784
  except Exception as e:
785
- return [html.Div(f"Error generating Pink Review: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
786
 
787
  if doc_type == "Red":
788
- pink_review = get_doc_from_radio(radio_pink_review, upload_pink_review_contents, upload_pink_review_filenames, pink_review_doc)
789
  if not pink_review:
790
- return [html.Div("Please provide a Pink Review document (either loaded or uploaded) to generate Red."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
791
  try:
792
  generated = generate_document(doc_type, [pink_review])
793
- red_doc = generated
794
- preview = markdown_narrative_preview(generated)
795
- return [preview, "Red generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
796
  except Exception as e:
797
- return [html.Div(f"Error generating Red: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
798
 
799
  if doc_type == "Red Review":
800
- red = get_doc_from_radio(radio_red, upload_red_contents, upload_red_filenames, red_doc)
801
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
802
  if not red or not shred:
803
- return [html.Div("Please provide both Red and Shred documents (either loaded or uploaded) to generate Red Review."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
804
  try:
805
  generated = generate_document(doc_type, [red, shred])
806
- red_review_doc = generated
807
- preview = markdown_table_preview(generated)
808
- return [preview, "Red Review generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
809
  except Exception as e:
810
- return [html.Div(f"Error generating Red Review: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
811
 
812
  if doc_type == "Gold":
813
- red_review = get_doc_from_radio(radio_red_review, upload_red_review_contents, upload_red_review_filenames, red_review_doc)
814
  if not red_review:
815
- return [html.Div("Please provide a Red Review document (either loaded or uploaded) to generate Gold."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
816
  try:
817
  generated = generate_document(doc_type, [red_review])
818
- gold_doc = generated
819
- preview = markdown_narrative_preview(generated)
820
- return [preview, "Gold generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
821
  except Exception as e:
822
- return [html.Div(f"Error generating Gold: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
823
 
824
  if doc_type == "Gold Review":
825
- gold = get_doc_from_radio(radio_gold, upload_gold_contents, upload_gold_filenames, gold_doc)
826
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
827
  if not gold or not shred:
828
- return [html.Div("Please provide both Gold and Shred documents (either loaded or uploaded) to generate Gold Review."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
829
  try:
830
  generated = generate_document(doc_type, [gold, shred])
831
- gold_review_doc = generated
832
- preview = markdown_table_preview(generated)
833
- return [preview, "Gold Review generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
834
  except Exception as e:
835
- return [html.Div(f"Error generating Gold Review: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
836
 
837
  if doc_type == "LOE":
838
- gold = get_doc_from_radio(radio_gold, upload_gold_contents, upload_gold_filenames, gold_doc)
839
  if not gold:
840
- return [html.Div("Please provide a Gold document (either loaded or uploaded) to generate LOE."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
841
  try:
842
  generated = generate_document(doc_type, [gold])
843
- loe_doc = generated
844
- preview = markdown_table_preview(generated)
845
- return [preview, "LOE generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
846
  except Exception as e:
847
- return [html.Div(f"Error generating LOE: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
848
 
849
  if doc_type == "Virtual Board":
850
- shred = get_doc_from_radio(radio_shred, upload_shred_contents, upload_shred_filenames, shred_doc)
851
  if not shred:
852
- return [html.Div("Please provide a Shred requirements document (either loaded or uploaded) to generate Virtual Board."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
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
- virtual_board_doc = generated
862
- preview = markdown_table_preview(generated)
863
- return [preview, "Virtual Board generated", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
864
  except Exception as e:
865
- return [html.Div(f"Error generating Virtual Board: {str(e)}"), "Error", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
866
 
867
- return [html.Div("Unsupported document type or missing required sources."), "", shred_doc, pink_doc, pink_review_doc, red_doc, red_review_doc, gold_doc, gold_review_doc, loe_doc, virtual_board_doc, "", None]
 
 
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
- return [dash.no_update]*11 + ["", None]
 
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
- return [dash.no_update]*11 + ["", None]
 
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
- preview = markdown_table_preview(new_document)
915
  else:
916
- preview = markdown_narrative_preview(new_document)
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
- new_stores = []
920
- for dt, s in zip(doc_types, stores):
921
  if dt == chat_doc_type:
922
- new_stores.append(new_document)
923
- else:
924
- new_stores.append(s)
925
- return [preview, dash.no_update] + new_stores + ["Document updated based on: {}".format(chat_input), None]
926
  except Exception as e:
927
- return [html.Div(f"Error updating document: {str(e)}"), dash.no_update, store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board, f"Error updating document: {str(e)}", None]
 
 
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
- logging.info(f"{dl_doc_type} document downloaded as Excel.")
949
- return [dash.no_update]*11 + ["", dcc.send_bytes(xlsx_bytes.read(), f"{dl_doc_type}.xlsx")]
950
  except Exception as e:
951
- return [dash.no_update]*11 + ["", dcc.send_string(f"Error downloading {dl_doc_type}: {str(e)}", f"{dl_doc_type}_error.txt")]
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
- return [dash.no_update]*11 + ["", dcc.send_bytes(output.read(), f"{dl_doc_type}.docx")]
963
  except Exception as e:
964
- return [dash.no_update]*11 + ["", dcc.send_string(f"Error downloading document: {str(e)}", f"{dl_doc_type}_error.txt")]
 
 
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(