bluenevus commited on
Commit
831a192
·
1 Parent(s): 72252ab

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +121 -31
app.py CHANGED
@@ -23,7 +23,7 @@ CLAUDE3_SONNET_MODEL = "claude-3-7-sonnet-20250219"
23
  CLAUDE3_MAX_CONTEXT_TOKENS = 200_000
24
  CLAUDE3_MAX_OUTPUT_TOKENS = 64_000
25
 
26
- uploaded_document = None
27
  shredded_document = None
28
  generated_response = None
29
 
@@ -60,31 +60,33 @@ def anthropic_stream_generate(prompt):
60
  logging.error("Error during anthropic streaming request: %s", e)
61
  return f"Error during streaming: {e}"
62
 
63
- def process_document(content, filename, action):
64
- global uploaded_document, shredded_document, generated_response
65
  logging.info(f"Process document called with action: {action}")
66
 
67
- if action == 'upload':
68
- if content is None:
69
- return "No content uploaded."
70
- content_type, content_string = content.split(',')
71
- decoded = base64.b64decode(content_string)
72
- text = decode_document(decoded)
73
- if text is None:
74
- return "Error: Could not decode document. Please upload a valid text file."
75
- uploaded_document = text
76
- logging.info("Document uploaded successfully.")
77
- return "Document uploaded successfully."
78
 
79
- elif action == 'shred':
80
- if not uploaded_document:
81
  logging.warning("No uploaded document found for shredding.")
82
  return "No document uploaded."
83
  prompt = (
84
  "Analyze the following RFP/PWS/SOW/RFI and generate a requirements spreadsheet. "
85
  "Identify requirements by action words like 'shall', 'will', 'perform', etc. Organize by PWS section and requirement. "
86
- "Do not write as if responding to the proposal:\n\n" + uploaded_document
87
  )
 
 
 
88
  def thread_shred():
89
  global shredded_document
90
  shredded_document = ""
@@ -109,8 +111,11 @@ def process_document(content, filename, action):
109
  prompt = (
110
  "Create a highly detailed proposal response based on the following PWS requirements. "
111
  "Be compliant and compelling. Focus on describing the approach, steps, workflow, people, processes, and technology. "
112
- "Refer to research that validates the approach and cite sources with measurable outcomes:\n\n" + shredded_document
113
  )
 
 
 
114
  def thread_generate():
115
  global generated_response
116
  generated_response = ""
@@ -128,8 +133,29 @@ def process_document(content, filename, action):
128
  t.join()
129
  return generated_response
130
 
 
 
 
 
 
 
 
 
131
  return "Action not implemented yet."
132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  app.layout = dbc.Container([
134
  dbc.Row([
135
  dbc.Col([
@@ -143,6 +169,12 @@ app.layout = dbc.Container([
143
  dbc.Button("Virtual Board", id="board-btn", className="mb-2 w-100 btn-tertiary"),
144
  dbc.Button("Estimate LOE", id="loe-btn", className="mb-2 w-100 btn-tertiary"),
145
  ])
 
 
 
 
 
 
146
  ])
147
  ], width=3, style={'minWidth': '260px'}),
148
  dbc.Col([
@@ -151,7 +183,6 @@ app.layout = dbc.Container([
151
  dbc.CardBody([
152
  dbc.Form([
153
  dbc.Textarea(id="chat-input", placeholder="Enter additional instructions...", style={"width":"100%", "wordWrap": "break-word"}, className="mb-2"),
154
- dbc.Button("Send", id="chat-send-btn", className="mt-2 btn-primary")
155
  ]),
156
  html.Div([
157
  dbc.Button("Shred", id="shred-action-btn", className="mr-2 btn-primary"),
@@ -161,6 +192,13 @@ app.layout = dbc.Container([
161
  dbc.Button("Virtual Board", id="board-action-btn", className="mr-2 btn-tertiary"),
162
  dbc.Button("LOE", id="loe-action-btn", className="btn-tertiary"),
163
  ], className="mt-3 mb-3"),
 
 
 
 
 
 
 
164
  dcc.Upload(
165
  id='upload-document',
166
  children=html.Div([
@@ -192,6 +230,51 @@ app.layout = dbc.Container([
192
  ], style={'marginTop':'20px'})
193
  ], fluid=True)
194
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  @app.callback(
196
  Output('output-document-upload', 'children'),
197
  Input('upload-document', 'contents'),
@@ -199,10 +282,16 @@ app.layout = dbc.Container([
199
  prevent_initial_call=True
200
  )
201
  def handle_upload(content, filename):
202
- logging.info("Upload callback triggered.")
203
- if content is not None:
204
- result = process_document(content, filename, 'upload')
205
- return html.Div(result, style={"wordWrap": "break-word"})
 
 
 
 
 
 
206
  return ""
207
 
208
  @app.callback(
@@ -213,12 +302,13 @@ def handle_upload(content, filename):
213
  Input('compliance-action-btn', 'n_clicks'),
214
  Input('recover-action-btn', 'n_clicks'),
215
  Input('board-action-btn', 'n_clicks'),
216
- Input('loe-action-btn', 'n_clicks')
217
  ],
218
  State('chat-input', 'value'),
 
219
  prevent_initial_call=True
220
  )
221
- def handle_actions(shred_clicks, generate_clicks, compliance_clicks, recover_clicks, board_clicks, loe_clicks, chat_input):
222
  ctx = callback_context
223
  if not ctx.triggered:
224
  logging.info("No action triggered yet.")
@@ -226,17 +316,17 @@ def handle_actions(shred_clicks, generate_clicks, compliance_clicks, recover_cli
226
  button_id = ctx.triggered[0]['prop_id'].split('.')[0]
227
  logging.info(f"Button pressed: {button_id}")
228
  if button_id == 'shred-action-btn':
229
- return process_document(None, None, 'shred')
230
  elif button_id == 'generate-action-btn':
231
- return process_document(None, None, 'generate')
232
  elif button_id == 'compliance-action-btn':
233
- return "Compliance checking not implemented yet."
234
  elif button_id == 'recover-action-btn':
235
- return "Recovery not implemented yet."
236
  elif button_id == 'board-action-btn':
237
- return "Virtual board not implemented yet."
238
  elif button_id == 'loe-action-btn':
239
- return "LOE estimation not implemented yet."
240
  return "Action not implemented yet."
241
 
242
  if __name__ == '__main__':
 
23
  CLAUDE3_MAX_CONTEXT_TOKENS = 200_000
24
  CLAUDE3_MAX_OUTPUT_TOKENS = 64_000
25
 
26
+ uploaded_documents = {} # filename: content
27
  shredded_document = None
28
  generated_response = None
29
 
 
60
  logging.error("Error during anthropic streaming request: %s", e)
61
  return f"Error during streaming: {e}"
62
 
63
+ def process_document(action, selected_filename=None, chat_input=None):
64
+ global shredded_document, generated_response
65
  logging.info(f"Process document called with action: {action}")
66
 
67
+ # Select document content to use, if needed
68
+ doc_content = None
69
+ if selected_filename and selected_filename in uploaded_documents:
70
+ doc_content = uploaded_documents[selected_filename]
71
+ elif uploaded_documents:
72
+ # Default to the first document if not specified
73
+ doc_content = next(iter(uploaded_documents.values()))
74
+ selected_filename = next(iter(uploaded_documents.keys()))
75
+ else:
76
+ doc_content = None
 
77
 
78
+ if action == 'shred':
79
+ if not doc_content:
80
  logging.warning("No uploaded document found for shredding.")
81
  return "No document uploaded."
82
  prompt = (
83
  "Analyze the following RFP/PWS/SOW/RFI and generate a requirements spreadsheet. "
84
  "Identify requirements by action words like 'shall', 'will', 'perform', etc. Organize by PWS section and requirement. "
85
+ "Do not write as if responding to the proposal.\n"
86
  )
87
+ if chat_input:
88
+ prompt += f"User additional instructions: {chat_input}\n"
89
+ prompt += f"\nFile Name: {selected_filename}\n\n{doc_content}"
90
  def thread_shred():
91
  global shredded_document
92
  shredded_document = ""
 
111
  prompt = (
112
  "Create a highly detailed proposal response based on the following PWS requirements. "
113
  "Be compliant and compelling. Focus on describing the approach, steps, workflow, people, processes, and technology. "
114
+ "Refer to research that validates the approach and cite sources with measurable outcomes.\n"
115
  )
116
+ if chat_input:
117
+ prompt += f"User additional instructions: {chat_input}\n"
118
+ prompt += f"\nFile Name: {selected_filename}\n\n{shredded_document}"
119
  def thread_generate():
120
  global generated_response
121
  generated_response = ""
 
133
  t.join()
134
  return generated_response
135
 
136
+ elif action == 'compliance':
137
+ return "Compliance checking not implemented yet."
138
+ elif action == 'recover':
139
+ return "Recovery not implemented yet."
140
+ elif action == 'board':
141
+ return "Virtual board not implemented yet."
142
+ elif action == 'loe':
143
+ return "LOE estimation not implemented yet."
144
  return "Action not implemented yet."
145
 
146
+ def get_uploaded_doc_list():
147
+ if not uploaded_documents:
148
+ return html.Div("No documents uploaded.", style={"wordWrap": "break-word"})
149
+ doc_list = []
150
+ for filename in uploaded_documents:
151
+ doc_list.append(
152
+ dbc.ListGroupItem([
153
+ html.Span(filename, style={"wordWrap": "break-word"}),
154
+ dbc.Button("Delete", id={'type': 'delete-doc-btn', 'index': filename}, size="sm", color="danger", className="float-end ms-2")
155
+ ], className="d-flex justify-content-between align-items-center")
156
+ )
157
+ return dbc.ListGroup(doc_list, flush=True)
158
+
159
  app.layout = dbc.Container([
160
  dbc.Row([
161
  dbc.Col([
 
169
  dbc.Button("Virtual Board", id="board-btn", className="mb-2 w-100 btn-tertiary"),
170
  dbc.Button("Estimate LOE", id="loe-btn", className="mb-2 w-100 btn-tertiary"),
171
  ])
172
+ ], className="mb-3"),
173
+ dbc.Card([
174
+ dbc.CardHeader(html.H5("Uploaded Documents")),
175
+ dbc.CardBody([
176
+ html.Div(id='uploaded-doc-list')
177
+ ])
178
  ])
179
  ], width=3, style={'minWidth': '260px'}),
180
  dbc.Col([
 
183
  dbc.CardBody([
184
  dbc.Form([
185
  dbc.Textarea(id="chat-input", placeholder="Enter additional instructions...", style={"width":"100%", "wordWrap": "break-word"}, className="mb-2"),
 
186
  ]),
187
  html.Div([
188
  dbc.Button("Shred", id="shred-action-btn", className="mr-2 btn-primary"),
 
192
  dbc.Button("Virtual Board", id="board-action-btn", className="mr-2 btn-tertiary"),
193
  dbc.Button("LOE", id="loe-action-btn", className="btn-tertiary"),
194
  ], className="mt-3 mb-3"),
195
+ dcc.Dropdown(
196
+ id='select-document-dropdown',
197
+ options=[{'label': fn, 'value': fn} for fn in uploaded_documents.keys()],
198
+ placeholder="Select a document to work with",
199
+ value=next(iter(uploaded_documents), None),
200
+ style={"marginBottom": "10px"}
201
+ ),
202
  dcc.Upload(
203
  id='upload-document',
204
  children=html.Div([
 
230
  ], style={'marginTop':'20px'})
231
  ], fluid=True)
232
 
233
+ @app.callback(
234
+ Output('uploaded-doc-list', 'children'),
235
+ Output('select-document-dropdown', 'options'),
236
+ Output('select-document-dropdown', 'value'),
237
+ Input('upload-document', 'contents'),
238
+ State('upload-document', 'filename'),
239
+ Input({'type': 'delete-doc-btn', 'index': dash.ALL}, 'n_clicks'),
240
+ State('uploaded-doc-list', 'children'),
241
+ State('select-document-dropdown', 'value'),
242
+ prevent_initial_call=True
243
+ )
244
+ def update_uploaded_docs(content, filename, delete_clicks, children, selected_doc):
245
+ ctx = callback_context
246
+ triggered = ctx.triggered
247
+ changed_id = ""
248
+ if triggered:
249
+ changed_id = triggered[0]['prop_id'].split('.')[0]
250
+ # Handle upload
251
+ if content is not None and filename:
252
+ content_type, content_string = content.split(',')
253
+ decoded = base64.b64decode(content_string)
254
+ text = decode_document(decoded)
255
+ if text is not None:
256
+ uploaded_documents[filename] = text
257
+ logging.info(f"Document uploaded: {filename}")
258
+ else:
259
+ logging.error(f"Failed to decode uploaded document: {filename}")
260
+ # Handle delete
261
+ if delete_clicks:
262
+ for i, n_click in enumerate(delete_clicks):
263
+ if n_click:
264
+ # The delete button's id is a dict with 'index' = filename
265
+ btn_id = ctx.inputs_list[2][i]['id']
266
+ del_filename = btn_id['index']
267
+ if del_filename in uploaded_documents:
268
+ del uploaded_documents[del_filename]
269
+ logging.info(f"Document deleted: {del_filename}")
270
+ # If the selected doc is deleted, choose another
271
+ if selected_doc == del_filename:
272
+ selected_doc = next(iter(uploaded_documents), None)
273
+ break
274
+ options = [{'label': fn, 'value': fn} for fn in uploaded_documents.keys()]
275
+ value = selected_doc if selected_doc in uploaded_documents else (next(iter(uploaded_documents), None) if uploaded_documents else None)
276
+ return get_uploaded_doc_list(), options, value
277
+
278
  @app.callback(
279
  Output('output-document-upload', 'children'),
280
  Input('upload-document', 'contents'),
 
282
  prevent_initial_call=True
283
  )
284
  def handle_upload(content, filename):
285
+ logging.info("Upload callback triggered (output-document-upload).")
286
+ if content is not None and filename:
287
+ content_type, content_string = content.split(',')
288
+ decoded = base64.b64decode(content_string)
289
+ text = decode_document(decoded)
290
+ if text is None:
291
+ return html.Div("Error: Could not decode document. Please upload a valid text file.", style={"wordWrap": "break-word"})
292
+ else:
293
+ # Already handled and added in update_uploaded_docs
294
+ return html.Div(f"Document '{filename}' uploaded successfully.", style={"wordWrap": "break-word"})
295
  return ""
296
 
297
  @app.callback(
 
302
  Input('compliance-action-btn', 'n_clicks'),
303
  Input('recover-action-btn', 'n_clicks'),
304
  Input('board-action-btn', 'n_clicks'),
305
+ Input('loe-action-btn', 'n_clicks'),
306
  ],
307
  State('chat-input', 'value'),
308
+ State('select-document-dropdown', 'value'),
309
  prevent_initial_call=True
310
  )
311
+ def handle_actions(shred_clicks, generate_clicks, compliance_clicks, recover_clicks, board_clicks, loe_clicks, chat_input, selected_filename):
312
  ctx = callback_context
313
  if not ctx.triggered:
314
  logging.info("No action triggered yet.")
 
316
  button_id = ctx.triggered[0]['prop_id'].split('.')[0]
317
  logging.info(f"Button pressed: {button_id}")
318
  if button_id == 'shred-action-btn':
319
+ return process_document('shred', selected_filename, chat_input)
320
  elif button_id == 'generate-action-btn':
321
+ return process_document('generate', selected_filename, chat_input)
322
  elif button_id == 'compliance-action-btn':
323
+ return process_document('compliance', selected_filename, chat_input)
324
  elif button_id == 'recover-action-btn':
325
+ return process_document('recover', selected_filename, chat_input)
326
  elif button_id == 'board-action-btn':
327
+ return process_document('board', selected_filename, chat_input)
328
  elif button_id == 'loe-action-btn':
329
+ return process_document('loe', selected_filename, chat_input)
330
  return "Action not implemented yet."
331
 
332
  if __name__ == '__main__':