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("""

🏠 Mortgage Underwriting Automation

Intelligent Document Processing & Risk Assessment Platform

""") # Progress Indicator with gr.Row(): with gr.Column(): gr.HTML("""
1
2
3
4
Document Upload → Data Analysis → Conditions → Final Approval
""") # Section 1: Document Upload with gr.Row(): with gr.Column(): gr.HTML('
') gr.HTML('
📄 Document Upload & Validation
') 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("""
📋 Required Documents Checklist:
• Government-issued ID
• Recent Pay Stubs (2-3 months)
• W-2 Forms (2 years)
• Tax Returns (2 years)
• Bank Statements (3 months)
• Purchase Offer/Contract
• Insurance Documentation
• Property Title Information
""") 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('
') # Section 2: Data Extraction and Analysis with gr.Row(): with gr.Column(): gr.HTML('
') gr.HTML('
🔍 Automated Analysis & Risk Assessment
') 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("""

🎯 Risk Assessment Criteria

📈 DTI Ratio: ≤ 45% (Optimal: ≤ 36%)
💳 Credit Score: ≥ 620 (Preferred: ≥ 680)
🏘️ LTV Ratio: ≤ 95% (Conventional: ≤ 80%)
📋 Documentation: Complete & Verified
🏢 Employment: Stable Income History
""") gr.HTML('
') # Section 3: Conditional Requirements with gr.Row(): with gr.Column(): gr.HTML('
') gr.HTML('
📎 Additional Documentation & Conditions
') 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("""
💡 Common Additional Requirements:
• Gift Letter (for down payment assistance)
• Employment Verification Letter
• Asset Verification Documents
• Property Appraisal Report
• Explanation Letters (for credit items)
• Homeowner's Insurance Proof
""") gr.HTML('
') # Section 4: Final Decision with gr.Row(): with gr.Column(): gr.HTML('
') gr.HTML('
⚖️ Final Underwriting Decision
') 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('
') # Footer with gr.Row(): with gr.Column(): gr.HTML("""

🔒 Secure • 🤖 AI-Powered • ⚡ Real-time Processing

© 2024 Mortgage Underwriting Automation Platform | Version 2.0

""") # 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 )