Spaces:
Paused
Paused
Update app.py via AI Editor
Browse files
app.py
CHANGED
@@ -1 +1,433 @@
|
|
1 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import dash
|
2 |
+
from dash import dcc, html, Input, Output, State, callback_context, no_update
|
3 |
+
import dash_bootstrap_components as dbc
|
4 |
+
import logging
|
5 |
+
import threading
|
6 |
+
import os
|
7 |
+
import base64
|
8 |
+
import io
|
9 |
+
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,
|
47 |
+
external_stylesheets=external_stylesheets,
|
48 |
+
suppress_callback_exceptions=True,
|
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 |
+
|
56 |
+
def save_uploaded_file(file_content, filename):
|
57 |
+
doc_id = str(uuid.uuid4())
|
58 |
+
uploaded_documents[doc_id] = {
|
59 |
+
"filename": filename,
|
60 |
+
"content": file_content
|
61 |
+
}
|
62 |
+
logger.info(f"Uploaded document saved: {filename} with id {doc_id}")
|
63 |
+
return doc_id
|
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"
|
71 |
+
}
|
72 |
+
data = {
|
73 |
+
"model": "claude-3-opus-20240229",
|
74 |
+
"messages": [
|
75 |
+
{"role": "user", "content": prompt + "\n" + extra_instructions}
|
76 |
+
],
|
77 |
+
"max_tokens": 4096,
|
78 |
+
"temperature": 0.2
|
79 |
+
}
|
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}")
|
87 |
+
return result
|
88 |
+
except Exception as e:
|
89 |
+
logger.error(f"Anthropic API error: {str(e)}")
|
90 |
+
return f"Error: {str(e)}"
|
91 |
+
|
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]
|
99 |
+
else:
|
100 |
+
preview = f"Preview not available for {filename}"
|
101 |
+
return preview
|
102 |
+
except Exception as e:
|
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 |
+
[
|
110 |
+
dbc.Nav(
|
111 |
+
[
|
112 |
+
dbc.Button("Shred RFP/PWS/SOW/RFI", id="btn-shred", className="mb-2 btn-primary", style={"width": "100%"}),
|
113 |
+
dbc.Button("Generate Proposal Response", id="btn-generate", className="mb-2 btn-secondary", style={"width": "100%"}),
|
114 |
+
dbc.Button("Check Compliance", id="btn-compliance", className="mb-2 btn-tertiary", style={"width": "100%"}),
|
115 |
+
dbc.Button("Recover Document", id="btn-recover", className="mb-2 btn-primary", style={"width": "100%"}),
|
116 |
+
dbc.Button("Virtual Board", id="btn-virtual-board", className="mb-2 btn-secondary", style={"width": "100%"}),
|
117 |
+
dbc.Button("Estimate LOE", id="btn-loe", className="mb-2 btn-tertiary", style={"width": "100%"}),
|
118 |
+
],
|
119 |
+
vertical=True,
|
120 |
+
pills=False
|
121 |
+
),
|
122 |
+
html.Hr(),
|
123 |
+
html.Div(
|
124 |
+
[
|
125 |
+
html.H6("Uploaded Documents"),
|
126 |
+
html.Ul(
|
127 |
+
id="uploaded-doc-list",
|
128 |
+
style={"listStyleType": "none", "paddingLeft": "0"}
|
129 |
+
),
|
130 |
+
]
|
131 |
+
),
|
132 |
+
],
|
133 |
+
body=True
|
134 |
+
)
|
135 |
+
|
136 |
+
def chat_window():
|
137 |
+
return dbc.Card(
|
138 |
+
[
|
139 |
+
html.Div(
|
140 |
+
[
|
141 |
+
html.Div(id="chat-history", style={"height": "160px", "overflowY": "auto", "padding": "0.5rem"}),
|
142 |
+
dbc.InputGroup(
|
143 |
+
[
|
144 |
+
dbc.Textarea(id="chat-input", placeholder="Send additional instructions...", style={"resize":"vertical", "wordWrap":"break-word", "width": "100%", "height": "60px"}),
|
145 |
+
dbc.Button("Send", id="btn-send-chat", className="btn-secondary", n_clicks=0),
|
146 |
+
],
|
147 |
+
className="mt-2"
|
148 |
+
),
|
149 |
+
]
|
150 |
+
),
|
151 |
+
],
|
152 |
+
body=True,
|
153 |
+
style={"marginBottom": "10px"}
|
154 |
+
)
|
155 |
+
|
156 |
+
def top_action_buttons():
|
157 |
+
return html.Div(
|
158 |
+
[
|
159 |
+
dbc.Button("Shred", id="action-shred", className="me-2 btn-primary", n_clicks=0, style={"minWidth": "120px"}),
|
160 |
+
dbc.Button("Generate Response", id="action-generate", className="me-2 btn-secondary", n_clicks=0, style={"minWidth": "180px"}),
|
161 |
+
dbc.Button("Check Compliance", id="action-compliance", className="me-2 btn-tertiary", n_clicks=0, style={"minWidth": "160px"}),
|
162 |
+
dbc.Button("Recover", id="action-recover", className="me-2 btn-primary", n_clicks=0, style={"minWidth": "120px"}),
|
163 |
+
dbc.Button("Virtual Board", id="action-virtual-board", className="me-2 btn-secondary", n_clicks=0, style={"minWidth": "160px"}),
|
164 |
+
dbc.Button("Estimate LOE", id="action-loe", className="btn-tertiary", n_clicks=0, style={"minWidth": "140px"}),
|
165 |
+
],
|
166 |
+
className="mb-3",
|
167 |
+
style={"display": "flex", "flexWrap": "wrap"}
|
168 |
+
)
|
169 |
+
|
170 |
+
def upload_area():
|
171 |
+
return html.Div(
|
172 |
+
[
|
173 |
+
dcc.Upload(
|
174 |
+
id="upload-document",
|
175 |
+
children=html.Div(["Drag & drop or click to select a file."]),
|
176 |
+
multiple=False,
|
177 |
+
style={
|
178 |
+
"width": "100%",
|
179 |
+
"height": "70px",
|
180 |
+
"lineHeight": "70px",
|
181 |
+
"borderWidth": "1px",
|
182 |
+
"borderStyle": "dashed",
|
183 |
+
"borderRadius": "4px",
|
184 |
+
"textAlign": "center",
|
185 |
+
"marginBottom": "8px"
|
186 |
+
}
|
187 |
+
),
|
188 |
+
html.Div(id="upload-feedback")
|
189 |
+
]
|
190 |
+
)
|
191 |
+
|
192 |
+
def preview_area():
|
193 |
+
return dbc.Card(
|
194 |
+
[
|
195 |
+
html.H6("Document Preview / Output"),
|
196 |
+
html.Pre(id="preview-content", style={"whiteSpace": "pre-wrap", "wordWrap": "break-word", "maxHeight": "340px", "overflowY": "auto"})
|
197 |
+
],
|
198 |
+
body=True
|
199 |
+
)
|
200 |
+
|
201 |
+
def main_layout():
|
202 |
+
return dbc.Container(
|
203 |
+
[
|
204 |
+
dbc.Row(
|
205 |
+
[
|
206 |
+
dbc.Col(
|
207 |
+
html.H2("Proposal Writing Assistant", style={"margin": "12px 0"}),
|
208 |
+
width=12
|
209 |
+
),
|
210 |
+
],
|
211 |
+
align="center",
|
212 |
+
style={"marginBottom": "8px"}
|
213 |
+
),
|
214 |
+
dbc.Row(
|
215 |
+
[
|
216 |
+
dbc.Col(
|
217 |
+
navbar(),
|
218 |
+
width=3,
|
219 |
+
style={"minWidth": "220px", "maxWidth": "400px"}
|
220 |
+
),
|
221 |
+
dbc.Col(
|
222 |
+
dbc.Card(
|
223 |
+
[
|
224 |
+
chat_window(),
|
225 |
+
top_action_buttons(),
|
226 |
+
upload_area(),
|
227 |
+
preview_area(),
|
228 |
+
dcc.Loading(
|
229 |
+
id="loading",
|
230 |
+
type="default",
|
231 |
+
children=html.Div(id="loading-output"),
|
232 |
+
style={"position": "absolute", "top": "6px", "left": "50%"}
|
233 |
+
),
|
234 |
+
],
|
235 |
+
body=True
|
236 |
+
),
|
237 |
+
width=9
|
238 |
+
),
|
239 |
+
],
|
240 |
+
style={"minHeight": "90vh"}
|
241 |
+
),
|
242 |
+
],
|
243 |
+
fluid=True
|
244 |
+
)
|
245 |
+
|
246 |
+
app.layout = main_layout
|
247 |
+
|
248 |
+
# Callbacks
|
249 |
+
@app.callback(
|
250 |
+
Output("uploaded-doc-list", "children"),
|
251 |
+
Output("preview-content", "children"),
|
252 |
+
Output("upload-feedback", "children"),
|
253 |
+
Output("loading-output", "children"),
|
254 |
+
Output("chat-history", "children"),
|
255 |
+
[
|
256 |
+
Input("upload-document", "contents"),
|
257 |
+
Input("action-shred", "n_clicks"),
|
258 |
+
Input("action-generate", "n_clicks"),
|
259 |
+
Input("action-compliance", "n_clicks"),
|
260 |
+
Input("action-recover", "n_clicks"),
|
261 |
+
Input("action-virtual-board", "n_clicks"),
|
262 |
+
Input("action-loe", "n_clicks"),
|
263 |
+
Input("btn-send-chat", "n_clicks"),
|
264 |
+
Input({"type": "delete-doc-btn", "index": dash.ALL}, "n_clicks"),
|
265 |
+
],
|
266 |
+
[
|
267 |
+
State("upload-document", "filename"),
|
268 |
+
State("chat-input", "value"),
|
269 |
+
State("chat-history", "children"),
|
270 |
+
State("preview-content", "children"),
|
271 |
+
State("uploaded-doc-list", "children"),
|
272 |
+
],
|
273 |
+
prevent_initial_call=True
|
274 |
+
)
|
275 |
+
def main_callback(
|
276 |
+
upload_contents, shred, generate, compliance, recover, virtual_board, loe, send_chat, delete_doc_clicks,
|
277 |
+
upload_filename, chat_input, chat_history, preview_content, uploaded_doc_list
|
278 |
+
):
|
279 |
+
triggered_id = callback_context.triggered[0]["prop_id"].split(".")[0] if callback_context.triggered else None
|
280 |
+
logger.info(f"Triggered callback: {triggered_id}")
|
281 |
+
|
282 |
+
feedback = no_update
|
283 |
+
loading_message = ""
|
284 |
+
new_preview_content = no_update
|
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)
|
292 |
+
else:
|
293 |
+
doc_id = save_uploaded_file(upload_contents, upload_filename)
|
294 |
+
preview = parse_contents(upload_contents, upload_filename)
|
295 |
+
new_preview_content = f"{upload_filename}:\n\n{preview}"
|
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(
|
303 |
+
[
|
304 |
+
html.Span(doc['filename'], style={"marginRight": "8px"}),
|
305 |
+
dbc.Button("Delete", id={"type": "delete-doc-btn", "index": doc_id}, color="danger", size="sm", n_clicks=0)
|
306 |
+
],
|
307 |
+
style={"display": "flex", "justifyContent": "space-between", "alignItems": "center", "marginBottom": "5px"}
|
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())
|
315 |
+
if idx < len(doc_ids):
|
316 |
+
deleted_doc = uploaded_documents.pop(doc_ids[idx])
|
317 |
+
feedback = dbc.Alert(f"Deleted {deleted_doc['filename']}", color="info", dismissable=True)
|
318 |
+
logger.info(f"Document deleted: {deleted_doc['filename']}")
|
319 |
+
doc_list_items = [
|
320 |
+
html.Li(
|
321 |
+
[
|
322 |
+
html.Span(doc['filename'], style={"marginRight": "8px"}),
|
323 |
+
dbc.Button("Delete", id={"type": "delete-doc-btn", "index": doc_id}, color="danger", size="sm", n_clicks=0)
|
324 |
+
],
|
325 |
+
style={"display": "flex", "justifyContent": "space-between", "alignItems": "center", "marginBottom": "5px"}
|
326 |
+
)
|
327 |
+
for doc_id, doc in uploaded_documents.items()
|
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 = []
|
341 |
+
new_chat_history.append(html.Div([
|
342 |
+
html.Strong("You: "), html.Span(chat_input)
|
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):
|
352 |
+
if isinstance(item, html.Div):
|
353 |
+
children = item.children
|
354 |
+
if len(children) > 1 and isinstance(children[1], html.Span):
|
355 |
+
last_chat = children[1].children
|
356 |
+
break
|
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()))
|
364 |
+
file_name = doc['filename']
|
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():
|
372 |
+
if triggered_id == "action-shred":
|
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":
|
400 |
+
prompt = (
|
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)
|
419 |
+
thread.start()
|
420 |
+
thread.join()
|
421 |
+
|
422 |
+
result = result_holder.get("result", "[No result]")
|
423 |
+
generated_content[triggered_id] = result
|
424 |
+
new_preview_content = f"{action_type} Output for {file_name}:\n\n{result}"
|
425 |
+
feedback = dbc.Alert(f"{action_type} completed.", color="success", dismissable=True)
|
426 |
+
logger.info(f"{action_type} completed for {file_name}")
|
427 |
+
|
428 |
+
return doc_list_items, new_preview_content, feedback, loading_message, new_chat_history
|
429 |
+
|
430 |
+
if __name__ == '__main__':
|
431 |
+
print("Starting the Dash application...")
|
432 |
+
app.run(debug=True, host='0.0.0.0', port=7860, threaded=True)
|
433 |
+
print("Dash application has finished running.")
|