proposal-writer / app.py
bluenevus's picture
Update app.py via AI Editor
34fe234
raw
history blame
6.16 kB
import os
import base64
import io
import dash
from dash import dcc, html, Input, Output, State
import dash_bootstrap_components as dbc
import pandas as pd
import anthropic
from threading import Thread
# Initialize Dash app
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
# Anthropic API setup
ANTHROPIC_KEY = os.environ.get("ANTHROPIC_API_KEY", "")
anthropic_client = anthropic.Anthropic(api_key=ANTHROPIC_KEY)
CLAUDE3_SONNET_MODEL = "claude-3-7-sonnet-20250219"
CLAUDE3_MAX_CONTEXT_TOKENS = 200_000
CLAUDE3_MAX_OUTPUT_TOKENS = 64_000
# Global variables
uploaded_document = None
shredded_document = None
generated_response = None
# Helper functions
def process_document(content, filename, action):
global uploaded_document, shredded_document, generated_response
content_type, content_string = content.split(',')
decoded = base64.b64decode(content_string)
if action == 'upload':
uploaded_document = io.StringIO(decoded.decode('utf-8')).read()
return "Document uploaded successfully."
elif action == 'shred':
prompt = f"Analyze the following RFP/PWS/SOW/RFI and generate a requirements spreadsheet. Identify requirements by action words like 'shall', 'will', 'perform', etc. Organize by PWS section and requirement. Do not write as if responding to the proposal:\n\n{uploaded_document}"
response = anthropic_client.messages.create(
model=CLAUDE3_SONNET_MODEL,
max_tokens=CLAUDE3_MAX_OUTPUT_TOKENS,
messages=[{"role": "user", "content": prompt}]
)
shredded_document = response.content[0].text
return shredded_document
elif action == 'generate':
prompt = f"Create a highly detailed proposal response based on the following PWS requirements. Be compliant and compelling. Focus on describing the approach, steps, workflow, people, processes, and technology. Refer to research that validates the approach and cite sources with measurable outcomes:\n\n{shredded_document}"
response = anthropic_client.messages.create(
model=CLAUDE3_SONNET_MODEL,
max_tokens=CLAUDE3_MAX_OUTPUT_TOKENS,
messages=[{"role": "user", "content": prompt}]
)
generated_response = response.content[0].text
return generated_response
# Implement other actions (check_compliance, recover_document, virtual_board, estimate_loe) similarly
# Layout
app.layout = dbc.Container([
dbc.Row([
dbc.Col([
html.H3("Navigation"),
dbc.Button("Shred RFP/PWS/SOW/RFI", id="shred-btn", className="mb-2 w-100"),
dbc.Button("Generate Proposal Response", id="generate-btn", className="mb-2 w-100"),
dbc.Button("Check Compliance", id="compliance-btn", className="mb-2 w-100"),
dbc.Button("Recover Document", id="recover-btn", className="mb-2 w-100"),
dbc.Button("Virtual Board", id="board-btn", className="mb-2 w-100"),
dbc.Button("Estimate LOE", id="loe-btn", className="mb-2 w-100"),
], width=3),
dbc.Col([
dbc.Input(id="chat-input", type="text", placeholder="Enter additional instructions..."),
dbc.Button("Send", id="chat-send-btn", className="mt-2"),
html.Div([
dbc.Button("Shred", id="shred-action-btn", className="mr-2"),
dbc.Button("Generate", id="generate-action-btn", className="mr-2"),
dbc.Button("Check Compliance", id="compliance-action-btn", className="mr-2"),
dbc.Button("Recover", id="recover-action-btn", className="mr-2"),
dbc.Button("Virtual Board", id="board-action-btn", className="mr-2"),
dbc.Button("LOE", id="loe-action-btn"),
], className="mt-3 mb-3"),
dcc.Upload(
id='upload-document',
children=html.Div([
'Drag and Drop or ',
html.A('Select Files')
]),
style={
'width': '100%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center',
'margin': '10px'
},
multiple=False
),
html.Div(id='output-document-upload'),
dcc.Loading(
id="loading",
type="default",
children=html.Div(id="output-data-upload")
)
], width=9)
])
], fluid=True)
# Callbacks
@app.callback(
Output('output-document-upload', 'children'),
Input('upload-document', 'contents'),
State('upload-document', 'filename')
)
def update_output(content, filename):
if content is not None:
result = process_document(content, filename, 'upload')
return html.Div(result)
@app.callback(
Output('output-data-upload', 'children'),
[Input('shred-action-btn', 'n_clicks'),
Input('generate-action-btn', 'n_clicks'),
Input('compliance-action-btn', 'n_clicks'),
Input('recover-action-btn', 'n_clicks'),
Input('board-action-btn', 'n_clicks'),
Input('loe-action-btn', 'n_clicks')],
State('chat-input', 'value')
)
def update_output(shred_clicks, generate_clicks, compliance_clicks, recover_clicks, board_clicks, loe_clicks, chat_input):
ctx = dash.callback_context
if not ctx.triggered:
return "No action taken yet."
else:
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
if button_id == 'shred-action-btn':
return process_document(None, None, 'shred')
elif button_id == 'generate-action-btn':
return process_document(None, None, 'generate')
# Implement other actions similarly
return "Action not implemented yet."
if __name__ == '__main__':
print("Starting the Dash application...")
app.run(debug=True, host='0.0.0.0', port=7860)
print("Dash application has finished running.")