bluenevus commited on
Commit
034b23e
·
1 Parent(s): c887bf3

Update app.py via AI Editor

Browse files
Files changed (1) hide show
  1. app.py +28 -31
app.py CHANGED
@@ -10,37 +10,28 @@ import uuid
10
  import time
11
  from flask import Flask
12
 
13
- # Anthropic API stub (replace with real implementation)
14
  import requests
15
 
16
- # For allowed file types and basic file handling
17
  ALLOWED_EXTENSIONS = ('pdf', 'doc', 'docx', 'txt')
18
 
19
- # Logging configuration
20
  logging.basicConfig(
21
  format="%(asctime)s %(levelname)s:%(message)s",
22
  level=logging.INFO
23
  )
24
  logger = logging.getLogger(__name__)
25
 
26
- # In-memory storage for uploaded documents and results
27
  uploaded_documents = {}
28
  generated_content = {}
29
 
30
- # Anthropic API endpoint/config stub
31
  ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages"
32
- ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY", "YOUR_ANTHROPIC_API_KEY") # Replace as appropriate
33
 
34
- # Flask server for Dash
35
  server = Flask(__name__)
36
 
37
- # CUDA GPU configuration (for completeness, if used by downstream libraries)
38
  os.environ["CUDA_VISIBLE_DEVICES"] = "0"
39
 
40
- # External stylesheets
41
  external_stylesheets = [dbc.themes.BOOTSTRAP]
42
 
43
- # Dash app initialization
44
  app = dash.Dash(
45
  __name__,
46
  server=server,
@@ -49,7 +40,6 @@ app = dash.Dash(
49
  title="Proposal Writing Assistant"
50
  )
51
 
52
- # Helper functions
53
  def allowed_file(filename):
54
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
55
 
@@ -64,7 +54,6 @@ def save_uploaded_file(file_content, filename):
64
 
65
  def anthropic_api_call(prompt, files=None, task_type=None, extra_instructions=""):
66
  logger.info(f"Calling Anthropic API for task: {task_type}")
67
- # Stubbed implementation; replace with actual Anthropic API call
68
  headers = {
69
  "x-api-key": ANTHROPIC_API_KEY,
70
  "content-type": "application/json"
@@ -80,7 +69,6 @@ def anthropic_api_call(prompt, files=None, task_type=None, extra_instructions=""
80
  try:
81
  # response = requests.post(ANTHROPIC_API_URL, headers=headers, json=data, timeout=120)
82
  # result = response.json().get('content', ['[Anthropic response placeholder]'])[0]
83
- # Simulated response:
84
  time.sleep(2)
85
  result = f"[Simulated response for {task_type}]"
86
  logger.info(f"Anthropic API success for task: {task_type}")
@@ -92,7 +80,6 @@ def anthropic_api_call(prompt, files=None, task_type=None, extra_instructions=""
92
  def parse_contents(contents, filename):
93
  content_type, content_string = contents.split(',')
94
  decoded = base64.b64decode(content_string)
95
- # Only display first few characters for preview
96
  try:
97
  if filename.lower().endswith('.txt'):
98
  preview = decoded.decode('utf-8')[:2048]
@@ -103,7 +90,6 @@ def parse_contents(contents, filename):
103
  logger.error(f"Could not decode file {filename}: {e}")
104
  return f"Error decoding {filename}"
105
 
106
- # UI Elements
107
  def navbar():
108
  return dbc.Card(
109
  [
@@ -245,7 +231,6 @@ def main_layout():
245
 
246
  app.layout = main_layout
247
 
248
- # Callbacks
249
  @app.callback(
250
  Output("uploaded-doc-list", "children"),
251
  Output("preview-content", "children"),
@@ -285,7 +270,6 @@ def main_callback(
285
  new_chat_history = chat_history if chat_history else []
286
  doc_list_items = []
287
 
288
- # Handle file upload
289
  if triggered_id == "upload-document" and upload_contents and upload_filename:
290
  if not allowed_file(upload_filename):
291
  feedback = dbc.Alert("Unsupported file type. Please upload PDF, Word, or TXT.", color="danger", dismissable=True)
@@ -296,7 +280,6 @@ def main_callback(
296
  feedback = dbc.Alert(f"Uploaded {upload_filename}", color="success", dismissable=True)
297
  logger.info(f"File uploaded: {upload_filename}")
298
 
299
- # Update doc list
300
  for doc_id, doc in uploaded_documents.items():
301
  doc_list_items.append(
302
  html.Li(
@@ -308,7 +291,6 @@ def main_callback(
308
  )
309
  )
310
 
311
- # Handle document deletion
312
  if isinstance(delete_doc_clicks, list) and any(delete_doc_clicks):
313
  idx = delete_doc_clicks.index(max(delete_doc_clicks))
314
  doc_ids = list(uploaded_documents.keys())
@@ -328,13 +310,11 @@ def main_callback(
328
  ]
329
  new_preview_content = "" if not uploaded_documents else no_update
330
 
331
- # If no uploaded documents, block actions
332
  if len(uploaded_documents) == 0 and triggered_id not in ["upload-document", "btn-send-chat"]:
333
  feedback = dbc.Alert("Please upload a document before performing actions.", color="warning", dismissable=True)
334
  logger.warning("Attempted action without documents.")
335
  return doc_list_items, new_preview_content, feedback, loading_message, new_chat_history
336
 
337
- # Handle chat
338
  if triggered_id == "btn-send-chat" and chat_input and chat_input.strip():
339
  if not isinstance(new_chat_history, list):
340
  new_chat_history = []
@@ -343,9 +323,7 @@ def main_callback(
343
  ], style={"marginBottom": "0.25rem"}))
344
  feedback = dbc.Alert("Chat message sent. Instructions will be used in next action.", color="info", dismissable=True)
345
  logger.info(f"Chat message sent: {chat_input}")
346
- # Clear chat input (handled client-side)
347
 
348
- # Identify last chat instructions to use
349
  last_chat = ""
350
  if isinstance(new_chat_history, list) and new_chat_history:
351
  for item in reversed(new_chat_history):
@@ -357,7 +335,6 @@ def main_callback(
357
  elif isinstance(chat_input, str):
358
  last_chat = chat_input
359
 
360
- # Action buttons: always require at least one document
361
  if triggered_id in ["action-shred", "action-generate", "action-compliance", "action-recover", "action-virtual-board", "action-loe"]:
362
  loading_message = dbc.Alert("Processing request, please wait...", color="primary", dismissable=False, style={"textAlign": "center"})
363
  doc_id, doc = next(iter(uploaded_documents.items()))
@@ -365,7 +342,20 @@ def main_callback(
365
  file_content = doc['content']
366
  action_type = triggered_id.replace("action-", "").replace("-", " ").title()
367
 
368
- # Anthropic API call in a thread
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  result_holder = {}
370
 
371
  def threaded_api_call():
@@ -373,27 +363,31 @@ def main_callback(
373
  prompt = (
374
  "Shred this document into requirements, organized by section. "
375
  "Identify requirements by action words (shall, will, perform, etc). "
376
- "Output as spreadsheet: PWS Section, Requirement."
 
377
  )
378
  task_type = "Shred"
379
  elif triggered_id == "action-generate":
380
  prompt = (
381
  "Generate a detailed proposal response, organized by section/subsection. "
382
  "Focus on approach, steps, workflow, people, processes, technology. "
383
- "Include research validation and citations. Address Red Review findings."
 
384
  )
385
  task_type = "Generate Proposal Response"
386
  elif triggered_id == "action-compliance":
387
  prompt = (
388
  "Check compliance of the proposal response against the shredded requirements. "
389
- "Produce a spreadsheet: PWS number, requirement, finding, recommendation."
 
390
  )
391
  task_type = "Check Compliance"
392
  elif triggered_id == "action-recover":
393
  prompt = (
394
  "Using the compliance spreadsheet, improve the document sections. "
395
  "Address recommendations without materially changing content. "
396
- "Organize improvements by PWS section headers/subheaders."
 
397
  )
398
  task_type = "Recover Document"
399
  elif triggered_id == "action-virtual-board":
@@ -401,18 +395,21 @@ def main_callback(
401
  "Evaluate the proposal based on requirements and evaluation criteria. "
402
  "Generate a section-by-section evaluation spreadsheet using ratings: "
403
  "unsatisfactory, satisfactory, good, very good, excellent. Include explanations. "
404
- "Base evaluation on sections L and M."
 
405
  )
406
  task_type = "Virtual Board"
407
  elif triggered_id == "action-loe":
408
  prompt = (
409
  "Estimate Level of Effort for the proposal. Output spreadsheet: "
410
- "PWS task area, brief description, labor categories, estimated hours per category."
 
411
  )
412
  task_type = "Estimate LOE"
413
  else:
414
  prompt = ""
415
  task_type = "Unknown"
 
416
  result_holder["result"] = anthropic_api_call(prompt, files=[file_content], task_type=task_type, extra_instructions=last_chat or "")
417
 
418
  thread = threading.Thread(target=threaded_api_call)
 
10
  import time
11
  from flask import Flask
12
 
 
13
  import requests
14
 
 
15
  ALLOWED_EXTENSIONS = ('pdf', 'doc', 'docx', 'txt')
16
 
 
17
  logging.basicConfig(
18
  format="%(asctime)s %(levelname)s:%(message)s",
19
  level=logging.INFO
20
  )
21
  logger = logging.getLogger(__name__)
22
 
 
23
  uploaded_documents = {}
24
  generated_content = {}
25
 
 
26
  ANTHROPIC_API_URL = "https://api.anthropic.com/v1/messages"
27
+ ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY", "YOUR_ANTHROPIC_API_KEY")
28
 
 
29
  server = Flask(__name__)
30
 
 
31
  os.environ["CUDA_VISIBLE_DEVICES"] = "0"
32
 
 
33
  external_stylesheets = [dbc.themes.BOOTSTRAP]
34
 
 
35
  app = dash.Dash(
36
  __name__,
37
  server=server,
 
40
  title="Proposal Writing Assistant"
41
  )
42
 
 
43
  def allowed_file(filename):
44
  return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
45
 
 
54
 
55
  def anthropic_api_call(prompt, files=None, task_type=None, extra_instructions=""):
56
  logger.info(f"Calling Anthropic API for task: {task_type}")
 
57
  headers = {
58
  "x-api-key": ANTHROPIC_API_KEY,
59
  "content-type": "application/json"
 
69
  try:
70
  # response = requests.post(ANTHROPIC_API_URL, headers=headers, json=data, timeout=120)
71
  # result = response.json().get('content', ['[Anthropic response placeholder]'])[0]
 
72
  time.sleep(2)
73
  result = f"[Simulated response for {task_type}]"
74
  logger.info(f"Anthropic API success for task: {task_type}")
 
80
  def parse_contents(contents, filename):
81
  content_type, content_string = contents.split(',')
82
  decoded = base64.b64decode(content_string)
 
83
  try:
84
  if filename.lower().endswith('.txt'):
85
  preview = decoded.decode('utf-8')[:2048]
 
90
  logger.error(f"Could not decode file {filename}: {e}")
91
  return f"Error decoding {filename}"
92
 
 
93
  def navbar():
94
  return dbc.Card(
95
  [
 
231
 
232
  app.layout = main_layout
233
 
 
234
  @app.callback(
235
  Output("uploaded-doc-list", "children"),
236
  Output("preview-content", "children"),
 
270
  new_chat_history = chat_history if chat_history else []
271
  doc_list_items = []
272
 
 
273
  if triggered_id == "upload-document" and upload_contents and upload_filename:
274
  if not allowed_file(upload_filename):
275
  feedback = dbc.Alert("Unsupported file type. Please upload PDF, Word, or TXT.", color="danger", dismissable=True)
 
280
  feedback = dbc.Alert(f"Uploaded {upload_filename}", color="success", dismissable=True)
281
  logger.info(f"File uploaded: {upload_filename}")
282
 
 
283
  for doc_id, doc in uploaded_documents.items():
284
  doc_list_items.append(
285
  html.Li(
 
291
  )
292
  )
293
 
 
294
  if isinstance(delete_doc_clicks, list) and any(delete_doc_clicks):
295
  idx = delete_doc_clicks.index(max(delete_doc_clicks))
296
  doc_ids = list(uploaded_documents.keys())
 
310
  ]
311
  new_preview_content = "" if not uploaded_documents else no_update
312
 
 
313
  if len(uploaded_documents) == 0 and triggered_id not in ["upload-document", "btn-send-chat"]:
314
  feedback = dbc.Alert("Please upload a document before performing actions.", color="warning", dismissable=True)
315
  logger.warning("Attempted action without documents.")
316
  return doc_list_items, new_preview_content, feedback, loading_message, new_chat_history
317
 
 
318
  if triggered_id == "btn-send-chat" and chat_input and chat_input.strip():
319
  if not isinstance(new_chat_history, list):
320
  new_chat_history = []
 
323
  ], style={"marginBottom": "0.25rem"}))
324
  feedback = dbc.Alert("Chat message sent. Instructions will be used in next action.", color="info", dismissable=True)
325
  logger.info(f"Chat message sent: {chat_input}")
 
326
 
 
327
  last_chat = ""
328
  if isinstance(new_chat_history, list) and new_chat_history:
329
  for item in reversed(new_chat_history):
 
335
  elif isinstance(chat_input, str):
336
  last_chat = chat_input
337
 
 
338
  if triggered_id in ["action-shred", "action-generate", "action-compliance", "action-recover", "action-virtual-board", "action-loe"]:
339
  loading_message = dbc.Alert("Processing request, please wait...", color="primary", dismissable=False, style={"textAlign": "center"})
340
  doc_id, doc = next(iter(uploaded_documents.items()))
 
342
  file_content = doc['content']
343
  action_type = triggered_id.replace("action-", "").replace("-", " ").title()
344
 
345
+ # DECODE the document content for use in the prompt
346
+ try:
347
+ content_type, content_string = file_content.split(',')
348
+ decoded = base64.b64decode(content_string)
349
+ if file_name.lower().endswith('.txt'):
350
+ document_text = decoded.decode('utf-8', errors='replace')
351
+ else:
352
+ document_text = f"[Start of document {file_name} as base64]\n{decoded[:350].hex()}...[truncated]\n[End of document]"
353
+ except Exception as e:
354
+ logger.error(f"Could not decode document {file_name} for Anthropic: {e}")
355
+ document_text = f"[Could not decode {file_name}]"
356
+
357
+ logger.info(f"Sending document content of length {len(document_text)} to Anthropic for {action_type}")
358
+
359
  result_holder = {}
360
 
361
  def threaded_api_call():
 
363
  prompt = (
364
  "Shred this document into requirements, organized by section. "
365
  "Identify requirements by action words (shall, will, perform, etc). "
366
+ "Output as spreadsheet: PWS Section, Requirement.\n\n"
367
+ f"Document Content:\n{document_text}\n"
368
  )
369
  task_type = "Shred"
370
  elif triggered_id == "action-generate":
371
  prompt = (
372
  "Generate a detailed proposal response, organized by section/subsection. "
373
  "Focus on approach, steps, workflow, people, processes, technology. "
374
+ "Include research validation and citations. Address Red Review findings.\n\n"
375
+ f"Document Content:\n{document_text}\n"
376
  )
377
  task_type = "Generate Proposal Response"
378
  elif triggered_id == "action-compliance":
379
  prompt = (
380
  "Check compliance of the proposal response against the shredded requirements. "
381
+ "Produce a spreadsheet: PWS number, requirement, finding, recommendation.\n\n"
382
+ f"Proposal Response Document Content:\n{document_text}\n"
383
  )
384
  task_type = "Check Compliance"
385
  elif triggered_id == "action-recover":
386
  prompt = (
387
  "Using the compliance spreadsheet, improve the document sections. "
388
  "Address recommendations without materially changing content. "
389
+ "Organize improvements by PWS section headers/subheaders.\n\n"
390
+ f"Document Content:\n{document_text}\n"
391
  )
392
  task_type = "Recover Document"
393
  elif triggered_id == "action-virtual-board":
 
395
  "Evaluate the proposal based on requirements and evaluation criteria. "
396
  "Generate a section-by-section evaluation spreadsheet using ratings: "
397
  "unsatisfactory, satisfactory, good, very good, excellent. Include explanations. "
398
+ "Base evaluation on sections L and M.\n\n"
399
+ f"Document Content:\n{document_text}\n"
400
  )
401
  task_type = "Virtual Board"
402
  elif triggered_id == "action-loe":
403
  prompt = (
404
  "Estimate Level of Effort for the proposal. Output spreadsheet: "
405
+ "PWS task area, brief description, labor categories, estimated hours per category.\n\n"
406
+ f"Document Content:\n{document_text}\n"
407
  )
408
  task_type = "Estimate LOE"
409
  else:
410
  prompt = ""
411
  task_type = "Unknown"
412
+ logger.info(f"Prompt to Anthropic for {action_type}: {prompt[:400]}...[truncated]")
413
  result_holder["result"] = anthropic_api_call(prompt, files=[file_content], task_type=task_type, extra_instructions=last_chat or "")
414
 
415
  thread = threading.Thread(target=threaded_api_call)