Spaces:
Paused
Paused
Update app.py via AI Editor
Browse files
app.py
CHANGED
@@ -1,3 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import base64
|
2 |
import io
|
3 |
import os
|
@@ -11,18 +33,20 @@ from dash.dash_table import DataTable
|
|
11 |
from docx.shared import Pt
|
12 |
from docx.enum.style import WD_STYLE_TYPE
|
13 |
from PyPDF2 import PdfReader
|
14 |
-
import openai
|
15 |
import logging
|
16 |
import threading
|
17 |
import re
|
18 |
import markdown
|
19 |
from bs4 import BeautifulSoup
|
|
|
20 |
|
21 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
|
22 |
|
23 |
-
|
|
|
|
|
24 |
|
25 |
-
|
26 |
|
27 |
uploaded_files = {}
|
28 |
uploaded_doc_contents = {}
|
@@ -449,7 +473,7 @@ def update_right_col(selected_type, shred_doc, pink_doc, pink_review_doc, red_do
|
|
449 |
|
450 |
@app.callback(
|
451 |
Output('file-list', 'children'),
|
452 |
-
Output('store-shred', 'data'
|
453 |
Input('upload-document', 'contents'),
|
454 |
State('upload-document', 'filename'),
|
455 |
State('file-list', 'children'),
|
@@ -483,8 +507,8 @@ def update_output(list_of_contents, list_of_names, existing_files, current_shred
|
|
483 |
return existing_files, current_shred
|
484 |
|
485 |
@app.callback(
|
486 |
-
Output('file-list', 'children'
|
487 |
-
Output('store-shred', 'data'
|
488 |
Input({'type': 'remove-file', 'index': ALL}, 'n_clicks'),
|
489 |
State('file-list', 'children'),
|
490 |
State('store-shred', 'data'),
|
@@ -537,6 +561,31 @@ def make_upload_callback(src):
|
|
537 |
for src in ["shred", "pink", "pink_review", "red", "red_review", "gold"]:
|
538 |
make_upload_callback(src)
|
539 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
540 |
def generate_document(document_type, file_contents, extra_context=None):
|
541 |
if document_type in spreadsheet_types:
|
542 |
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.
|
@@ -563,23 +612,8 @@ Now, generate the {document_type}:
|
|
563 |
"""
|
564 |
if extra_context:
|
565 |
prompt += f"\n\n{extra_context}"
|
566 |
-
logging.info(f"Generating document for type: {document_type}")
|
567 |
-
|
568 |
-
response = openai.ChatCompletion.create(
|
569 |
-
model="gpt-4-1106-preview",
|
570 |
-
messages=[
|
571 |
-
{"role": "system", "content": "You are a helpful, expert government proposal writer."},
|
572 |
-
{"role": "user", "content": prompt}
|
573 |
-
],
|
574 |
-
max_tokens=4096,
|
575 |
-
temperature=0.25,
|
576 |
-
)
|
577 |
-
generated_text = response['choices'][0]['message']['content']
|
578 |
-
logging.info("Document generated successfully.")
|
579 |
-
return generated_text
|
580 |
-
except Exception as e:
|
581 |
-
logging.error(f"Error generating document: {str(e)}")
|
582 |
-
raise
|
583 |
|
584 |
@app.callback(
|
585 |
Output('document-preview', 'children'),
|
@@ -803,16 +837,16 @@ def generate_any_doc(
|
|
803 |
|
804 |
@app.callback(
|
805 |
Output('chat-output', 'children'),
|
806 |
-
Output('document-preview', 'children'
|
807 |
-
Output('store-shred', 'data'
|
808 |
-
Output('store-pink', 'data'
|
809 |
-
Output('store-pink-review', 'data'
|
810 |
-
Output('store-red', 'data'
|
811 |
-
Output('store-red-review', 'data'
|
812 |
-
Output('store-gold', 'data'
|
813 |
-
Output('store-gold-review', 'data'
|
814 |
-
Output('store-loe', 'data'
|
815 |
-
Output('store-virtual-board', 'data'
|
816 |
Input('btn-send-chat', 'n_clicks'),
|
817 |
Input('btn-clear-chat', 'n_clicks'),
|
818 |
State('chat-input', 'value'),
|
@@ -876,22 +910,21 @@ Now, provide the updated {selected_doc_type}:
|
|
876 |
"""
|
877 |
logging.info(f"Updating document via chat for {selected_doc_type} instruction: {chat_input}")
|
878 |
try:
|
879 |
-
|
880 |
-
model="gpt-4-1106-preview",
|
881 |
-
messages=[
|
882 |
-
{"role": "system", "content": "You are a helpful, expert government proposal writer."},
|
883 |
-
{"role": "user", "content": prompt}
|
884 |
-
],
|
885 |
-
max_tokens=32768,
|
886 |
-
temperature=0.5,
|
887 |
-
)
|
888 |
-
new_document = response['choices'][0]['message']['content']
|
889 |
if selected_doc_type in spreadsheet_types:
|
890 |
preview = markdown_table_preview(new_document)
|
891 |
else:
|
892 |
preview = markdown_narrative_preview(new_document)
|
893 |
-
|
894 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
895 |
except Exception as e:
|
896 |
logging.error(f"Error updating document via chat: {str(e)}")
|
897 |
return f"Error updating document: {str(e)}", html.Div(f"Error updating document: {str(e)}"), store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board
|
@@ -967,4 +1000,5 @@ def auto_expand_textarea(value, current_rows):
|
|
967 |
if __name__ == '__main__':
|
968 |
print("Starting the Dash application...")
|
969 |
app.run(debug=True, host='0.0.0.0', port=7860, threaded=True)
|
970 |
-
print("Dash application has finished running.")
|
|
|
|
1 |
+
Let's break down the changes needed:
|
2 |
+
|
3 |
+
1. **Switch from OpenAI to Gemini**:
|
4 |
+
- Remove all `openai` imports and usage.
|
5 |
+
- Use `google.generativeai` (Gemini) and set up the API key from `GEMINI_KEY`.
|
6 |
+
- Replace the `openai.ChatCompletion.create` logic with Gemini's chat completion API using the specified model.
|
7 |
+
|
8 |
+
2. **Fix duplicate callback for store-shred.data**:
|
9 |
+
- The callback for document generation (Outputting to many stores) is duplicated with allow_duplicate. Instead, combine all outputs/logic so each store is only output in a single callback. This means the chat update callback must update only the relevant store, and not all stores, and outputs must be refactored to avoid duplication.
|
10 |
+
|
11 |
+
3. **No unnecessary changes**:
|
12 |
+
- No unrelated changes (e.g., don't touch CSS, layout, or unrelated logic).
|
13 |
+
|
14 |
+
4. **Other requirements**:
|
15 |
+
- Use threading for Gemini requests.
|
16 |
+
- Comprehensive logging.
|
17 |
+
- No duplicate callbacks.
|
18 |
+
- All imports, global vars, etc.
|
19 |
+
|
20 |
+
Here is the revised code:
|
21 |
+
|
22 |
+
```
|
23 |
import base64
|
24 |
import io
|
25 |
import os
|
|
|
33 |
from docx.shared import Pt
|
34 |
from docx.enum.style import WD_STYLE_TYPE
|
35 |
from PyPDF2 import PdfReader
|
|
|
36 |
import logging
|
37 |
import threading
|
38 |
import re
|
39 |
import markdown
|
40 |
from bs4 import BeautifulSoup
|
41 |
+
import google.generativeai as genai
|
42 |
|
43 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
|
44 |
|
45 |
+
GEMINI_KEY = os.environ.get("GEMINI_KEY", "")
|
46 |
+
genai.configure(api_key=GEMINI_KEY)
|
47 |
+
GEMINI_MODEL = "gemini-2.5-pro-preview-03-25"
|
48 |
|
49 |
+
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True)
|
50 |
|
51 |
uploaded_files = {}
|
52 |
uploaded_doc_contents = {}
|
|
|
473 |
|
474 |
@app.callback(
|
475 |
Output('file-list', 'children'),
|
476 |
+
Output('store-shred', 'data'),
|
477 |
Input('upload-document', 'contents'),
|
478 |
State('upload-document', 'filename'),
|
479 |
State('file-list', 'children'),
|
|
|
507 |
return existing_files, current_shred
|
508 |
|
509 |
@app.callback(
|
510 |
+
Output('file-list', 'children'),
|
511 |
+
Output('store-shred', 'data'),
|
512 |
Input({'type': 'remove-file', 'index': ALL}, 'n_clicks'),
|
513 |
State('file-list', 'children'),
|
514 |
State('store-shred', 'data'),
|
|
|
561 |
for src in ["shred", "pink", "pink_review", "red", "red_review", "gold"]:
|
562 |
make_upload_callback(src)
|
563 |
|
564 |
+
def gemini_generate(prompt, max_tokens=4096, temperature=0.25):
|
565 |
+
result_holder = {}
|
566 |
+
def run_gemini():
|
567 |
+
try:
|
568 |
+
model = genai.GenerativeModel(GEMINI_MODEL)
|
569 |
+
response = model.generate_content(
|
570 |
+
prompt,
|
571 |
+
generation_config=genai.types.GenerationConfig(
|
572 |
+
temperature=temperature,
|
573 |
+
max_output_tokens=max_tokens,
|
574 |
+
top_p=1
|
575 |
+
)
|
576 |
+
)
|
577 |
+
result_holder['result'] = response.text
|
578 |
+
logging.info("Gemini response received.")
|
579 |
+
except Exception as e:
|
580 |
+
logging.error(f"Gemini error: {e}")
|
581 |
+
result_holder['error'] = str(e)
|
582 |
+
thread = threading.Thread(target=run_gemini)
|
583 |
+
thread.start()
|
584 |
+
thread.join()
|
585 |
+
if 'error' in result_holder:
|
586 |
+
raise Exception(result_holder['error'])
|
587 |
+
return result_holder['result']
|
588 |
+
|
589 |
def generate_document(document_type, file_contents, extra_context=None):
|
590 |
if document_type in spreadsheet_types:
|
591 |
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.
|
|
|
612 |
"""
|
613 |
if extra_context:
|
614 |
prompt += f"\n\n{extra_context}"
|
615 |
+
logging.info(f"Generating document for type: {document_type} using Gemini.")
|
616 |
+
return gemini_generate(prompt, max_tokens=4096, temperature=0.25)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
617 |
|
618 |
@app.callback(
|
619 |
Output('document-preview', 'children'),
|
|
|
837 |
|
838 |
@app.callback(
|
839 |
Output('chat-output', 'children'),
|
840 |
+
Output('document-preview', 'children'),
|
841 |
+
Output('store-shred', 'data'),
|
842 |
+
Output('store-pink', 'data'),
|
843 |
+
Output('store-pink-review', 'data'),
|
844 |
+
Output('store-red', 'data'),
|
845 |
+
Output('store-red-review', 'data'),
|
846 |
+
Output('store-gold', 'data'),
|
847 |
+
Output('store-gold-review', 'data'),
|
848 |
+
Output('store-loe', 'data'),
|
849 |
+
Output('store-virtual-board', 'data'),
|
850 |
Input('btn-send-chat', 'n_clicks'),
|
851 |
Input('btn-clear-chat', 'n_clicks'),
|
852 |
State('chat-input', 'value'),
|
|
|
910 |
"""
|
911 |
logging.info(f"Updating document via chat for {selected_doc_type} instruction: {chat_input}")
|
912 |
try:
|
913 |
+
new_document = gemini_generate(prompt, max_tokens=4096, temperature=0.5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
914 |
if selected_doc_type in spreadsheet_types:
|
915 |
preview = markdown_table_preview(new_document)
|
916 |
else:
|
917 |
preview = markdown_narrative_preview(new_document)
|
918 |
+
# Only update the relevant store
|
919 |
+
stores = [store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board]
|
920 |
+
doc_types = ["Shred", "Pink", "Pink Review", "Red", "Red Review", "Gold", "Gold Review", "LOE", "Virtual Board"]
|
921 |
+
new_stores = []
|
922 |
+
for dt, s in zip(doc_types, stores):
|
923 |
+
if dt == selected_doc_type:
|
924 |
+
new_stores.append(new_document)
|
925 |
+
else:
|
926 |
+
new_stores.append(s)
|
927 |
+
return f"Document updated based on: {chat_input}", preview, *new_stores
|
928 |
except Exception as e:
|
929 |
logging.error(f"Error updating document via chat: {str(e)}")
|
930 |
return f"Error updating document: {str(e)}", html.Div(f"Error updating document: {str(e)}"), store_shred, store_pink, store_pink_review, store_red, store_red_review, store_gold, store_gold_review, store_loe, store_virtual_board
|
|
|
1000 |
if __name__ == '__main__':
|
1001 |
print("Starting the Dash application...")
|
1002 |
app.run(debug=True, host='0.0.0.0', port=7860, threaded=True)
|
1003 |
+
print("Dash application has finished running.")
|
1004 |
+
```
|