Agnets / app.py
sagar007's picture
Update app.py
363f959 verified
import gradio as gr
import os
from typing import List, Dict, Tuple
import json
import datetime
# Mock backend functions to simulate AWS Bedrock Agents
def validate_documents(files: List[str]) -> Tuple[str, str]:
"""Simulate Intake Agent: Check if all required docs are present."""
if not files:
return "❌ No documents uploaded", "error"
required = ["ID", "Pay Stubs", "W-2", "Tax Returns", "Bank Statements", "Offer", "Insurance", "Title"]
uploaded = [os.path.basename(f).split(".")[0] for f in files]
missing = [doc for doc in required if doc not in uploaded]
if missing:
return f"⚠️ Missing documents: {', '.join(missing)}", "warning"
return "βœ… All documents validated successfully", "success"
def extract_data(files: List[str], ssn: str) -> Dict:
"""Simulate Extraction Agent: Extract data from documents."""
if not files or not ssn:
return {}
return {
"application_id": "APP-2024-001234",
"ssn": ssn,
"applicant_name": "John Doe",
"monthly_income": 8500,
"monthly_debts": 2100,
"credit_score": 720,
"property_value": 450000,
"loan_amount": 360000,
"insurance_coverage": "12_months",
"title_status": "Clear",
"employment_status": "Full-time",
"years_employed": 3.5
}
def analyze_data(data: Dict) -> Dict:
"""Simulate Credit/Capacity/Collateral/Compliance Agents: Analyze data."""
if not data:
return {}
dti = (data["monthly_debts"] / data["monthly_income"]) * 100
ltv = (data["loan_amount"] / data["property_value"]) * 100
flags = []
risk_level = "Low"
if dti > 45:
flags.append("High DTI Ratio")
risk_level = "High"
elif dti > 36:
flags.append("Elevated DTI Ratio")
risk_level = "Medium"
if data["credit_score"] < 620:
flags.append("Low Credit Score")
risk_level = "High"
elif data["credit_score"] < 680:
flags.append("Fair Credit Score")
if risk_level != "High":
risk_level = "Medium"
if ltv > 95:
flags.append("High LTV Ratio")
risk_level = "High"
elif ltv > 80:
flags.append("Elevated LTV Ratio")
if risk_level == "Low":
risk_level = "Medium"
if "Clear" not in data["title_status"]:
flags.append("Title Issues")
risk_level = "High"
status = "Ready for Approval" if not flags else "Requires Review"
if risk_level == "High":
status = "High Risk - Manual Review Required"
return {
"application_id": data["application_id"],
"applicant_name": data["applicant_name"],
"dti_ratio": round(dti, 2),
"ltv_ratio": round(ltv, 2),
"credit_score": data["credit_score"],
"monthly_income": f"${data['monthly_income']:,}",
"monthly_debts": f"${data['monthly_debts']:,}",
"loan_amount": f"${data['loan_amount']:,}",
"property_value": f"${data['property_value']:,}",
"risk_level": risk_level,
"flags": flags,
"status": status,
"processed_date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
def handle_conditions(additional_files: List[str], comments: str) -> str:
"""Simulate Conditions Agent: Process additional uploads."""
if additional_files and comments.strip():
file_names = [os.path.basename(f) for f in additional_files]
return f"βœ… Additional documentation received: {', '.join(file_names)}\n\nπŸ“ Comments: {comments}\n\n⏳ Status: Under Review"
elif additional_files:
file_names = [os.path.basename(f) for f in additional_files]
return f"βœ… Additional files uploaded: {', '.join(file_names)}\n\n⚠️ Please provide comments for review"
elif comments.strip():
return f"πŸ“ Comments noted: {comments}\n\n⚠️ Please upload supporting documents"
return "❌ No additional files or comments provided"
def finalize_approval(decision: str, comments: str, analysis: Dict) -> str:
"""Simulate Final Approval Agent: Finalize decision."""
if not analysis:
return "❌ Error: No analysis data available. Please complete analysis first."
app_id = analysis.get("application_id", "N/A")
applicant = analysis.get("applicant_name", "N/A")
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if decision == "Approve":
if not analysis.get("flags", []):
return f"""
πŸŽ‰ **APPLICATION APPROVED**
πŸ“‹ **Application ID:** {app_id}
πŸ‘€ **Applicant:** {applicant}
πŸ“… **Decision Date:** {timestamp}
βœ… **Status:** Clear to Close
πŸ“¦ **Next Steps:**
- Closing package will be generated automatically
- Borrower will be contacted within 24 hours
- Expected closing date: {(datetime.datetime.now() + datetime.timedelta(days=14)).strftime("%Y-%m-%d")}
πŸ’¬ **Comments:** {comments if comments.strip() else 'Standard approval - all criteria met'}
"""
else:
return f"""
βœ… **APPLICATION APPROVED WITH CONDITIONS**
πŸ“‹ **Application ID:** {app_id}
πŸ‘€ **Applicant:** {applicant}
πŸ“… **Decision Date:** {timestamp}
⚠️ **Conditions:** {', '.join(analysis['flags'])}
πŸ“‹ **Required Actions:**
- Address flagged conditions before closing
- Submit additional documentation if required
- Schedule manual review if needed
πŸ’¬ **Comments:** {comments}
"""
else: # Reject
return f"""
❌ **APPLICATION REJECTED**
πŸ“‹ **Application ID:** {app_id}
πŸ‘€ **Applicant:** {applicant}
πŸ“… **Decision Date:** {timestamp}
🚫 **Rejection Reasons:**
{chr(10).join([f"β€’ {flag}" for flag in analysis.get('flags', ['Manual rejection'])])}
πŸ“ž **Next Steps:**
- Applicant will be contacted within 48 hours
- Adverse action notice will be sent
- Reapplication possible after addressing issues
πŸ’¬ **Comments:** {comments}
"""
# Custom CSS for production-grade styling
custom_css = """
/* Global Styles */
.gradio-container {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
min-height: 100vh;
}
.main {
background: rgba(255, 255, 255, 0.95) !important;
border-radius: 20px !important;
box-shadow: 0 20px 40px rgba(0,0,0,0.1) !important;
margin: 20px !important;
padding: 0 !important;
}
/* Header Styling */
.header-section {
background: linear-gradient(90deg, #1e3c72 0%, #2a5298 100%) !important;
color: white !important;
padding: 30px !important;
border-radius: 20px 20px 0 0 !important;
margin-bottom: 0 !important;
text-align: center !important;
}
.header-section h1 {
margin: 0 !important;
font-size: 2.5rem !important;
font-weight: 700 !important;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3) !important;
}
.header-section p {
margin: 10px 0 0 0 !important;
font-size: 1.1rem !important;
opacity: 0.9 !important;
}
/* Section Styling */
.section-card {
background: white !important;
border-radius: 15px !important;
padding: 25px !important;
margin: 20px !important;
box-shadow: 0 8px 25px rgba(0,0,0,0.1) !important;
border: 1px solid rgba(0,0,0,0.05) !important;
transition: all 0.3s ease !important;
}
.section-card:hover {
transform: translateY(-2px) !important;
box-shadow: 0 12px 35px rgba(0,0,0,0.15) !important;
}
.section-title {
color: #2c3e50 !important;
font-size: 1.5rem !important;
font-weight: 600 !important;
margin-bottom: 20px !important;
padding-bottom: 10px !important;
border-bottom: 3px solid #3498db !important;
display: flex !important;
align-items: center !important;
gap: 10px !important;
}
/* Button Styling */
.btn-primary {
background: linear-gradient(45deg, #667eea, #764ba2) !important;
border: none !important;
border-radius: 10px !important;
padding: 12px 30px !important;
font-weight: 600 !important;
font-size: 1rem !important;
transition: all 0.3s ease !important;
text-transform: uppercase !important;
letter-spacing: 0.5px !important;
}
.btn-primary:hover {
transform: translateY(-2px) !important;
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4) !important;
}
.btn-success {
background: linear-gradient(45deg, #56ab2f, #a8e6cf) !important;
border: none !important;
border-radius: 10px !important;
padding: 12px 30px !important;
font-weight: 600 !important;
}
.btn-danger {
background: linear-gradient(45deg, #ff416c, #ff4b2b) !important;
border: none !important;
border-radius: 10px !important;
padding: 12px 30px !important;
font-weight: 600 !important;
}
/* Input Styling */
input, textarea, select {
border-radius: 8px !important;
border: 2px solid #e1e8ed !important;
padding: 12px !important;
font-size: 1rem !important;
transition: all 0.3s ease !important;
}
input:focus, textarea:focus, select:focus {
border-color: #667eea !important;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
outline: none !important;
}
/* Status Messages */
.status-success {
background: linear-gradient(45deg, #56ab2f, #a8e6cf) !important;
color: white !important;
padding: 15px !important;
border-radius: 10px !important;
font-weight: 600 !important;
}
.status-warning {
background: linear-gradient(45deg, #f7971e, #ffd200) !important;
color: #333 !important;
padding: 15px !important;
border-radius: 10px !important;
font-weight: 600 !important;
}
.status-error {
background: linear-gradient(45deg, #ff416c, #ff4b2b) !important;
color: white !important;
padding: 15px !important;
border-radius: 10px !important;
font-weight: 600 !important;
}
/* Progress Indicators */
.step-indicator {
display: flex !important;
justify-content: space-between !important;
margin: 20px 0 !important;
position: relative !important;
}
.step {
background: #e1e8ed !important;
border-radius: 50% !important;
width: 40px !important;
height: 40px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
font-weight: bold !important;
color: #666 !important;
z-index: 2 !important;
}
.step.active {
background: linear-gradient(45deg, #667eea, #764ba2) !important;
color: white !important;
}
.step.completed {
background: linear-gradient(45deg, #56ab2f, #a8e6cf) !important;
color: white !important;
}
/* JSON Display Enhancement */
.json-display {
background: #f8f9fa !important;
border: 1px solid #e9ecef !important;
border-radius: 10px !important;
padding: 20px !important;
font-family: 'Monaco', 'Consolas', monospace !important;
max-height: 400px !important;
overflow-y: auto !important;
}
/* File Upload Styling */
.file-upload {
border: 2px dashed #667eea !important;
border-radius: 10px !important;
padding: 30px !important;
text-align: center !important;
background: rgba(102, 126, 234, 0.05) !important;
transition: all 0.3s ease !important;
}
.file-upload:hover {
background: rgba(102, 126, 234, 0.1) !important;
border-color: #5a67d8 !important;
}
/* Responsive Design */
@media (max-width: 768px) {
.main {
margin: 10px !important;
}
.section-card {
margin: 10px !important;
padding: 20px !important;
}
.header-section h1 {
font-size: 2rem !important;
}
}
"""
# Gradio Interface
with gr.Blocks(css=custom_css, title="Mortgage Underwriting System", theme=gr.themes.Soft()) as app:
# Header Section
with gr.Row():
with gr.Column():
gr.HTML("""
<div class="header-section">
<h1>🏠 Mortgage Underwriting Automation</h1>
<p>Intelligent Document Processing & Risk Assessment Platform</p>
</div>
""")
# Progress Indicator
with gr.Row():
with gr.Column():
gr.HTML("""
<div class="section-card">
<div class="step-indicator">
<div class="step active">1</div>
<div class="step">2</div>
<div class="step">3</div>
<div class="step">4</div>
</div>
<div style="text-align: center; margin-top: 10px; color: #666;">
<strong>Document Upload</strong> β†’ Data Analysis β†’ Conditions β†’ Final Approval
</div>
</div>
""")
# Section 1: Document Upload
with gr.Row():
with gr.Column():
gr.HTML('<div class="section-card">')
gr.HTML('<div class="section-title">πŸ“„ Document Upload & Validation</div>')
with gr.Row():
with gr.Column(scale=2):
files_input = gr.File(
label="Required Documents",
file_count="multiple",
file_types=[".pdf", ".png", ".jpg", ".jpeg"],
elem_classes=["file-upload"]
)
gr.HTML("""
<div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 10px 0;">
<strong>πŸ“‹ Required Documents Checklist:</strong><br>
β€’ Government-issued ID<br>
β€’ Recent Pay Stubs (2-3 months)<br>
β€’ W-2 Forms (2 years)<br>
β€’ Tax Returns (2 years)<br>
β€’ Bank Statements (3 months)<br>
β€’ Purchase Offer/Contract<br>
β€’ Insurance Documentation<br>
β€’ Property Title Information
</div>
""")
with gr.Column(scale=1):
ssn_input = gr.Textbox(
label="Social Security Number",
placeholder="XXX-XX-XXXX",
type="password"
)
validate_btn = gr.Button("πŸ” Validate Documents", variant="primary", size="lg")
validate_output = gr.Textbox(
label="Validation Status",
interactive=False,
lines=2
)
gr.HTML('</div>')
# Section 2: Data Extraction and Analysis
with gr.Row():
with gr.Column():
gr.HTML('<div class="section-card">')
gr.HTML('<div class="section-title">πŸ” Automated Analysis & Risk Assessment</div>')
with gr.Row():
with gr.Column():
extract_btn = gr.Button("⚑ Extract Data & Analyze Risk", variant="primary", size="lg")
with gr.Row():
with gr.Column():
analysis_output = gr.JSON(
label="πŸ“Š Comprehensive Analysis Results",
elem_classes=["json-display"]
)
with gr.Column():
gr.HTML("""
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px;">
<h3 style="margin: 0 0 15px 0;">🎯 Risk Assessment Criteria</h3>
<div style="background: rgba(255,255,255,0.1); padding: 15px; border-radius: 8px;">
<strong>πŸ“ˆ DTI Ratio:</strong> ≀ 45% (Optimal: ≀ 36%)<br>
<strong>πŸ’³ Credit Score:</strong> β‰₯ 620 (Preferred: β‰₯ 680)<br>
<strong>🏘️ LTV Ratio:</strong> ≀ 95% (Conventional: ≀ 80%)<br>
<strong>πŸ“‹ Documentation:</strong> Complete & Verified<br>
<strong>🏒 Employment:</strong> Stable Income History
</div>
</div>
""")
gr.HTML('</div>')
# Section 3: Conditional Requirements
with gr.Row():
with gr.Column():
gr.HTML('<div class="section-card">')
gr.HTML('<div class="section-title">πŸ“Ž Additional Documentation & Conditions</div>')
with gr.Row():
with gr.Column():
additional_files = gr.File(
label="Upload Additional Documents",
file_count="multiple",
file_types=[".pdf", ".png", ".jpg", ".jpeg"],
elem_classes=["file-upload"]
)
condition_comments = gr.Textbox(
label="Underwriter Comments & Instructions",
placeholder="Provide detailed comments about additional requirements...",
lines=4
)
condition_btn = gr.Button("πŸ“€ Submit Additional Documentation", variant="secondary", size="lg")
with gr.Column():
condition_output = gr.Textbox(
label="πŸ“‹ Conditions Status",
interactive=False,
lines=6
)
gr.HTML("""
<div style="background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 8px; color: #856404;">
<strong>πŸ’‘ Common Additional Requirements:</strong><br>
β€’ Gift Letter (for down payment assistance)<br>
β€’ Employment Verification Letter<br>
β€’ Asset Verification Documents<br>
β€’ Property Appraisal Report<br>
β€’ Explanation Letters (for credit items)<br>
β€’ Homeowner's Insurance Proof
</div>
""")
gr.HTML('</div>')
# Section 4: Final Decision
with gr.Row():
with gr.Column():
gr.HTML('<div class="section-card">')
gr.HTML('<div class="section-title">βš–οΈ Final Underwriting Decision</div>')
with gr.Row():
with gr.Column():
approve_radio = gr.Radio(
choices=["Approve", "Reject"],
label="πŸ“‹ Underwriting Decision",
info="Select final decision based on comprehensive analysis"
)
approval_comments = gr.Textbox(
label="πŸ’¬ Decision Comments & Rationale",
placeholder="Provide detailed reasoning for the decision...",
lines=4
)
approve_btn = gr.Button("βœ… Submit Final Decision", variant="primary", size="lg")
with gr.Column():
final_output = gr.Textbox(
label="πŸ† Final Approval Status",
interactive=False,
lines=12,
elem_classes=["status-display"]
)
gr.HTML('</div>')
# Footer
with gr.Row():
with gr.Column():
gr.HTML("""
<div style="text-align: center; padding: 20px; color: #666; background: rgba(0,0,0,0.05); border-radius: 0 0 20px 20px;">
<p>πŸ”’ Secure β€’ πŸ€– AI-Powered β€’ ⚑ Real-time Processing</p>
<p><small>Β© 2024 Mortgage Underwriting Automation Platform | Version 2.0</small></p>
</div>
""")
# Event Handlers
validate_btn.click(
fn=lambda files: validate_documents(files)[0],
inputs=files_input,
outputs=validate_output
)
extract_btn.click(
fn=lambda files, ssn: analyze_data(extract_data(files, ssn)),
inputs=[files_input, ssn_input],
outputs=analysis_output
)
condition_btn.click(
fn=handle_conditions,
inputs=[additional_files, condition_comments],
outputs=condition_output
)
approve_btn.click(
fn=finalize_approval,
inputs=[approve_radio, approval_comments, analysis_output],
outputs=final_output
)
if __name__ == "__main__":
app.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True,
favicon_path=None,
ssl_verify=False,
debug=True
)