junaid17 commited on
Commit
55f820f
·
verified ·
1 Parent(s): 02c9eb9

Upload 6 files

Browse files
advisor_bot.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_groq import ChatGroq
2
+ from langchain_core.prompts import PromptTemplate
3
+ import os
4
+ from dotenv import load_dotenv
5
+ load_dotenv()
6
+
7
+ llm = ChatGroq(model="llama-3.1-8b-instant", api_key=os.getenv("GROQ_API_KEY"))
8
+
9
+ prompt = PromptTemplate.from_template("""
10
+ You are RiskGuard AI, a professional digital bank assistant.
11
+
12
+ Below is the user's credit evaluation:
13
+
14
+ • Probability of Default: {probability}%
15
+ • Credit Score: {credit_score}
16
+ • Rating Category: {rating}
17
+
18
+ Write a short, friendly message (4–6 lines) following this format:
19
+
20
+ 1) A polite greeting such as:
21
+ "Thank you for using RiskGuard AI for your loan assessment."
22
+
23
+ 2) A clear decision tone:
24
+ - If the score and risk level are strong: indicate the loan is likely suitable for approval.
25
+ - If risk is high: indicate that approval may be difficult at this time.
26
+
27
+ 3) Give one or two simple, actionable suggestions for improvement (if needed).
28
+
29
+ 4) Close with a short support line such as:
30
+ "If you have questions or want guidance, feel free to talk to our loan advisor chatbot."
31
+
32
+ Tone: concise, professional, supportive. No long bullet lists, no emojis, no legal claims.
33
+ """)
34
+
35
+
36
+ def generate_advice(probability, credit_score, rating):
37
+ formatted_prompt = prompt.format(
38
+ probability=round(probability * 100, 2),
39
+ credit_score=credit_score,
40
+ rating=rating
41
+ )
42
+
43
+ result = llm.invoke(formatted_prompt)
44
+ return result.content
45
+
46
+
artifacts/model_data.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e127bcce15dacbe33e11988528e5219f89a1ef04f19662ab688d334a7fe90c49
3
+ size 4769
chatbot_advisor.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langgraph.graph import StateGraph, START, END
2
+ from typing import TypedDict,Annotated
3
+ from langchain_core.messages import HumanMessage, BaseMessage, AIMessage, SystemMessage
4
+ from langchain_openai import ChatOpenAI
5
+ from langgraph.checkpoint.memory import MemorySaver
6
+ from dotenv import load_dotenv
7
+ from langgraph.graph.message import add_messages
8
+ load_dotenv()
9
+
10
+ class ChatState(TypedDict):
11
+ messages : Annotated[list[BaseMessage], add_messages]
12
+
13
+ llm = ChatOpenAI(model='gpt-4.1-nano', streaming=True)
14
+
15
+ SYSTEM_MESSAGE = SystemMessage(
16
+ content=(
17
+ "You are RiskGuard AI, an intelligent credit risk and financial guidance assistant. "
18
+ "Your role is to help users understand their credit standing, loan eligibility, and financial risk profile "
19
+ "based on the information provided to you. "
20
+
21
+ "Your responses should be:\n"
22
+ "- Clear, concise, and easy to understand (avoid technical jargon unless needed)\n"
23
+ "- Professional and non-judgmental in tone\n"
24
+ "- Supportive, encouraging, and solution-focused\n"
25
+ "- Insightful, offering actionable steps the user can take to improve\n"
26
+ "- Aligned with responsible financial communication (no promises, guarantees, or legal statements)\n\n"
27
+
28
+ "When answering:\n"
29
+ "- Reference relevant financial data if provided\n"
30
+ "- Offer practical recommendations that feel personalized\n"
31
+ "- Keep responses conversational, modern, and human-like, similar to a digital bank assistant or financial coach\n\n"
32
+
33
+ "If the user asks about next steps, provide helpful financial strategies such as improving repayment history, "
34
+ "reducing utilization, maintaining fewer inquiries, or improving documentation.\n\n"
35
+
36
+ "If you do not have enough information to answer accurately, ask a clarifying question.\n\n"
37
+
38
+ "Never provide legal, tax, or investment guarantees.\n\n"
39
+
40
+ "Your priority is to help the user feel informed, supported, and confident in managing their credit journey."
41
+ )
42
+ )
43
+
44
+
45
+ def chat_node(state : ChatState):
46
+ user_query = state['messages']
47
+ query = [SYSTEM_MESSAGE]+user_query
48
+ response = llm.invoke(query)
49
+ return {'messages': [response]}
50
+
51
+ checkpointer = MemorySaver()
52
+ graph = StateGraph(ChatState)
53
+ graph.add_node("chat_node", chat_node)
54
+ graph.add_edge(START, 'chat_node')
55
+ graph.add_edge('chat_node', END)
56
+
57
+ financial_advisor_chatbot = graph.compile(checkpointer=checkpointer)
58
+
59
+ thread_id='1'
60
+ config = {'configurable' : {'thread_id' : thread_id}}
61
+
62
+ def format_chat_input(probability, credit_score, rating, advisor_reply, user_message):
63
+ return f"""
64
+ Below is the most recent loan evaluation details. Use them when responding.
65
+
66
+ CREDIT ANALYSIS
67
+ ---------------
68
+ • Credit Score: {credit_score}
69
+ • Default Probability: {probability:.2%}
70
+ • Rating: {rating}
71
+
72
+ AI ADVISOR SUMMARY
73
+ ------------------
74
+ {advisor_reply}
75
+
76
+ USER QUESTION
77
+ -------------
78
+ {user_message}
79
+
80
+ Respond as RiskGuard AI in a clear, concise and helpful tone.
81
+ """
82
+
83
+ def ask_chatbot(probability, credit_score, rating, advisor_reply, user_message, thread_id):
84
+ formatted_msg = format_chat_input(probability, credit_score, rating, advisor_reply, user_message)
85
+
86
+ initial_state = {
87
+ "messages": [HumanMessage(content=formatted_msg)]
88
+ }
89
+
90
+ config = {"configurable": {"thread_id": thread_id}}
91
+
92
+ response = financial_advisor_chatbot.invoke(initial_state, config=config)
93
+ return response["messages"][-1].content
94
+
95
+
96
+
97
+
credit_risk_model.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
main.py ADDED
@@ -0,0 +1,456 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import requests
3
+ import uuid
4
+ from datetime import datetime
5
+
6
+ # Page configuration
7
+ st.set_page_config(
8
+ page_title="RiskGuard AI: Credit Risk Modelling",
9
+ page_icon="🛡️",
10
+ layout="wide"
11
+ )
12
+
13
+ # Initialize session storage
14
+ if "chat_history" not in st.session_state:
15
+ st.session_state.chat_history = []
16
+ if "thread_id" not in st.session_state:
17
+ st.session_state.thread_id = str(uuid.uuid4())
18
+ if "analysis_done" not in st.session_state:
19
+ st.session_state.analysis_done = False
20
+ if "show_info" not in st.session_state:
21
+ st.session_state.show_info = False
22
+
23
+ # ========================= ENHANCED UI STYLING (UPDATED) =========================
24
+ st.markdown("""
25
+ <style>
26
+ @import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap');
27
+ * {
28
+ font-family: 'Montserrat', 'Inter', sans-serif;
29
+ }
30
+ .main {
31
+ background: linear-gradient(135deg, #212d3b 0%, #223a57 100%);
32
+ padding: 2rem 0;
33
+ }
34
+ .header-container {
35
+ background: linear-gradient(135deg, #243b55 0%, #141e30 100%);
36
+ padding: 2rem;
37
+ border-radius: 16px;
38
+ box-shadow: 0 8px 32px rgba(36, 59, 85,0.13);
39
+ margin-bottom: 2rem;
40
+ text-align: center;
41
+ }
42
+ .header-title {
43
+ color: white;
44
+ font-size: 2.5rem;
45
+ font-weight: 700;
46
+ margin: 0;
47
+ text-shadow: 2px 2px 6px rgba(20,30,48,0.2);
48
+ font-family: 'Montserrat', sans-serif;
49
+ }
50
+ .header-subtitle {
51
+ color: #aab3cf;
52
+ font-size: 1.1rem;
53
+ margin-top: 0.5rem;
54
+ font-family: 'Montserrat', sans-serif;
55
+ }
56
+ .section-card {
57
+ background: #25304b;
58
+ padding: 1.5rem;
59
+ border-radius: 12px;
60
+ box-shadow: 0 4px 8px rgba(36, 59, 85,0.12);
61
+ margin-bottom: 1.5rem;
62
+ border-left: 4px solid #2952a3;
63
+ color: #f3f4fa;
64
+ }
65
+ .section-title {
66
+ color: #f1f7fc;
67
+ font-size: 1.3rem;
68
+ font-weight: 700;
69
+ margin-bottom: 1rem;
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 0.5rem;
73
+ font-family: 'Montserrat', sans-serif;
74
+ }
75
+ .info-box {
76
+ background: linear-gradient(135deg, #344667 0%, #2952a3 100%);
77
+ color: #f1f7fc;
78
+ padding: 1rem;
79
+ border-radius: 10px;
80
+ margin: 1rem 0;
81
+ border-left: 4px solid #18aad5;
82
+ animation: slideIn 0.5s ease-out;
83
+ }
84
+ @keyframes slideIn {
85
+ from { opacity: 0; transform: translateY(-10px); }
86
+ to { opacity: 1; transform: translateY(0); }
87
+ }
88
+ .metric-card {
89
+ background: linear-gradient(135deg, #2952a3 0%, #243b55 100%);
90
+ padding: 1.2rem;
91
+ border-radius: 12px;
92
+ color: #f3f4fa;
93
+ text-align: center;
94
+ box-shadow: 0 4px 15px rgba(41, 82, 163, 0.2);
95
+ transition: transform 0.3s ease;
96
+ }
97
+ .metric-card:hover {
98
+ transform: translateY(-5px);
99
+ }
100
+ .metric-value {
101
+ font-size: 2rem;
102
+ font-weight: 700;
103
+ margin: 0.5rem 0;
104
+ font-family: 'Montserrat', sans-serif;
105
+ }
106
+ .metric-label {
107
+ font-size: 0.9rem;
108
+ opacity: 0.92;
109
+ }
110
+ .result-card {
111
+ background: linear-gradient(135deg, #333b9b 0%, #2d375b 100%);
112
+ padding: 2rem;
113
+ border-radius: 16px;
114
+ color: #f4f8ff;
115
+ box-shadow: 0 8px 32px rgba(51, 59, 155, 0.10);
116
+ margin: 1.5rem 0;
117
+ }
118
+ .result-grid {
119
+ display: grid;
120
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
121
+ gap: 1.5rem;
122
+ margin-top: 1rem;
123
+ }
124
+ .result-item {
125
+ background: rgba(25,40,65,0.2);
126
+ padding: 1.5rem;
127
+ border-radius: 12px;
128
+ backdrop-filter: blur(10px);
129
+ }
130
+ .result-item-label {
131
+ font-size: 0.9rem;
132
+ opacity: 0.9;
133
+ margin-bottom: 0.5rem;
134
+ }
135
+ .result-item-value {
136
+ font-size: 1.8rem;
137
+ font-weight: 700;
138
+ }
139
+ .advisor-box {
140
+ background: linear-gradient(135deg, #2e8bcb 0%, #243b55 100%);
141
+ color: #f5f5fa;
142
+ padding: 1.5rem;
143
+ border-radius: 12px;
144
+ margin-top: 1.5rem;
145
+ border-left: 4px solid #18aad5;
146
+ box-shadow: 0 4px 15px rgba(46, 139, 203, 0.1);
147
+ }
148
+ .advisor-box h4 {
149
+ color: #f1f7fc;
150
+ margin-top: 0;
151
+ }
152
+ .chat-container {
153
+ background: #24304e;
154
+ color: #fff;
155
+ padding: 1.5rem;
156
+ border-radius: 12px;
157
+ box-shadow: 0 4px 6px rgba(36, 48, 78,0.12);
158
+ max-height: 400px;
159
+ overflow-y: auto;
160
+ margin-bottom: 1rem;
161
+ }
162
+ .chat-bubble-user {
163
+ background: linear-gradient(135deg, #2952a3 0%, #243b55 100%);
164
+ color: white;
165
+ padding: 12px 18px;
166
+ border-radius: 18px 18px 4px 18px;
167
+ margin: 8px 0 8px auto;
168
+ max-width: 70%;
169
+ text-align: right;
170
+ box-shadow: 0 2px 8px rgba(41,82,163, 0.13);
171
+ animation: slideInRight 0.3s ease-out;
172
+ font-family: 'Montserrat', sans-serif;
173
+ }
174
+ .chat-bubble-bot {
175
+ background: #20293b;
176
+ border: 2px solid #2a3d6a;
177
+ color: #ebf2f8;
178
+ padding: 12px 18px;
179
+ border-radius: 18px 18px 18px 4px;
180
+ margin: 8px auto 8px 0;
181
+ max-width: 70%;
182
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
183
+ animation: slideInLeft 0.3s ease-out;
184
+ font-family: 'Montserrat', sans-serif;
185
+ }
186
+ @keyframes slideInRight {
187
+ from { opacity: 0; transform: translateX(20px); }
188
+ to { opacity: 1; transform: translateX(0); }
189
+ }
190
+ @keyframes slideInLeft {
191
+ from { opacity: 0; transform: translateX(-20px); }
192
+ to { opacity: 1; transform: translateX(0); }
193
+ }
194
+ .stButton>button {
195
+ width: 100%;
196
+ background: linear-gradient(135deg, #285aeb 0%, #223a57 100%);
197
+ color: white;
198
+ font-weight: 600;
199
+ border: none;
200
+ border-radius: 10px;
201
+ padding: 0.8rem;
202
+ font-size: 1rem;
203
+ font-family: 'Montserrat', sans-serif;
204
+ transition: all 0.3s ease;
205
+ box-shadow: 0 4px 15px rgba(40, 90, 235, 0.13);
206
+ }
207
+ .stButton>button:hover {
208
+ transform: translateY(-2px);
209
+ box-shadow: 0 6px 20px rgba(40, 90, 235, 0.20);
210
+ }
211
+ .progress-container {
212
+ margin: 1rem 0;
213
+ }
214
+ .stNumberInput>div>div>input,
215
+ .stSelectbox>div>div>select,
216
+ .stTextInput>div>div>input {
217
+ border-radius: 8px;
218
+ border: 2px solid #233269;
219
+ padding: 0.5rem;
220
+ transition: border-color 0.3s ease;
221
+ background: #2d375b;
222
+ color: #e9ecfa;
223
+ font-family: 'Montserrat', sans-serif;
224
+ }
225
+ .stNumberInput>div>div>input:focus,
226
+ .stSelectbox>div>div>select:focus,
227
+ .stTextInput>div>div>input:focus {
228
+ border-color: #285aeb;
229
+ box-shadow: 0 0 0 3px rgba(40, 90, 235, 0.1);
230
+ }
231
+ .alert-banner {
232
+ background: linear-gradient(135deg, #233269 0%, #285aeb 100%);
233
+ color: #fff;
234
+ padding: 1rem;
235
+ border-radius: 10px;
236
+ margin: 1rem 0;
237
+ border-left: 4px solid #18aad5;
238
+ display: flex;
239
+ align-items: center;
240
+ gap: 0.5rem;
241
+ }
242
+ .chat-container::-webkit-scrollbar {
243
+ width: 8px;
244
+ }
245
+ .chat-container::-webkit-scrollbar-track {
246
+ background: #1a2336;
247
+ border-radius: 10px;
248
+ }
249
+ .chat-container::-webkit-scrollbar-thumb {
250
+ background: #2952a3;
251
+ border-radius: 10px;
252
+ }
253
+ .chat-container::-webkit-scrollbar-thumb:hover {
254
+ background: #223a57;
255
+ }
256
+ </style>
257
+ """, unsafe_allow_html=True)
258
+
259
+ # ========================= HEADER =========================
260
+ st.markdown("""
261
+ <div class="header-container">
262
+ <h1 class="header-title">🛡️ RiskGuard AI</h1>
263
+ <p class="header-subtitle">Advanced AI-Powered Credit Risk Assessment Platform</p>
264
+ </div>
265
+ """, unsafe_allow_html=True)
266
+
267
+ # Alert Banner
268
+ st.markdown("""
269
+ <div class="alert-banner">
270
+ ⚠️ <strong>Note:</strong> First request may take up to 20 seconds (API cold start).
271
+ </div>
272
+ """, unsafe_allow_html=True)
273
+
274
+ # Info Toggle
275
+ if st.button("ℹ️ How It Works"):
276
+ st.session_state.show_info = not st.session_state.show_info
277
+
278
+ if st.session_state.show_info:
279
+ st.markdown("""
280
+ <div class="info-box">
281
+ <h4>📊 About RiskGuard AI</h4>
282
+ <p><strong>What we analyze:</strong></p>
283
+ <ul>
284
+ <li>Personal financial profile and credit history</li>
285
+ <li>Loan-to-income ratio and debt burden</li>
286
+ <li>Payment patterns and delinquency records</li>
287
+ <li>Credit utilization and account management</li>
288
+ </ul>
289
+ <p><strong>Our AI provides:</strong></p>
290
+ <ul>
291
+ <li>Default probability predictions</li>
292
+ <li>Credit score assessment</li>
293
+ <li>Risk rating classification</li>
294
+ <li>Personalized recommendations</li>
295
+ </ul>
296
+ </div>
297
+ """, unsafe_allow_html=True)
298
+
299
+ # ========================= INPUT FORM =========================
300
+ col_left, col_right = st.columns([1, 1], gap="large")
301
+
302
+ with col_left:
303
+ st.markdown('<div class="section-card">', unsafe_allow_html=True)
304
+ st.markdown('<div class="section-title">👤 Personal & Loan Information</div>', unsafe_allow_html=True)
305
+
306
+ age = st.number_input("Age", 18, 100, 28, help="Applicant's age in years")
307
+ income = st.number_input("Annual Income (₹)", 0, 50000000, 1200000, step=50000, help="Total annual income")
308
+ loan_amount = st.number_input("Loan Amount (₹)", 0, 50000000, 2500000, step=100000, help="Requested loan amount")
309
+ loan_tenure_months = st.number_input("Loan Tenure (months)", 0, 480, 36, help="Loan repayment period")
310
+
311
+ loan_purpose = st.selectbox("Loan Purpose", ["Education", "Home", "Auto", "Personal"])
312
+ residence_type = st.selectbox("Residence Type", ["Owned", "Rented", "Mortgage"])
313
+ loan_type = st.selectbox("Loan Type", ["Secured", "Unsecured"])
314
+
315
+ st.markdown('</div>', unsafe_allow_html=True)
316
+
317
+ loan_to_income_ratio = loan_amount / income if income > 0 else 0
318
+ st.markdown(f"""
319
+ <div class="metric-card">
320
+ <div class="metric-label">Loan-to-Income Ratio</div>
321
+ <div class="metric-value">{loan_to_income_ratio:.2f}x</div>
322
+ </div>
323
+ """, unsafe_allow_html=True)
324
+
325
+ with col_right:
326
+ st.markdown('<div class="section-card">', unsafe_allow_html=True)
327
+ st.markdown('<div class="section-title">💳 Credit Profile</div>', unsafe_allow_html=True)
328
+ avg_dpd_per_delinquency = st.number_input("Average Days Past Due", 0, 200, 20, help="Average days past due per delinquency")
329
+ delinquency_ratio = st.number_input("Delinquency Ratio (%)", 0, 100, 30, help="Percentage of delinquent accounts")
330
+ credit_utilization_ratio = st.number_input("Credit Utilization (%)", 0, 100, 30, help="Percentage of available credit used")
331
+ num_open_accounts = st.number_input("Open Loan Accounts", 0, 20, 2, help="Number of currently active loan accounts")
332
+ st.markdown('</div>', unsafe_allow_html=True)
333
+
334
+ # ========================= ANALYSIS BUTTON =========================
335
+ st.markdown("<br>", unsafe_allow_html=True)
336
+
337
+ if st.button("🔍 Analyze Credit Risk", use_container_width=True):
338
+ API_URL = st.secrets["API_URL"]
339
+
340
+ payload = {
341
+ "age": age,
342
+ "income": income,
343
+ "loan_amount": loan_amount,
344
+ "loan_tenure_months": loan_tenure_months,
345
+ "avg_dpd_per_delinquency": avg_dpd_per_delinquency,
346
+ "delinquency_ratio": delinquency_ratio,
347
+ "credit_utilization_ratio": credit_utilization_ratio,
348
+ "num_open_accounts": num_open_accounts,
349
+ "residence_type": residence_type,
350
+ "loan_purpose": loan_purpose,
351
+ "loan_type": loan_type
352
+ }
353
+
354
+ with st.spinner("🤖 Analyzing your credit profile..."):
355
+ try:
356
+ r = requests.post(API_URL, json=payload, timeout=30)
357
+ if r.status_code == 200:
358
+ result = r.json()
359
+
360
+ # Display Results
361
+ st.markdown('<div class="result-card">', unsafe_allow_html=True)
362
+ st.markdown('<h3 style="margin-top:0; color:white;">📊 Assessment Results</h3>', unsafe_allow_html=True)
363
+ st.markdown(f"""
364
+ <div class="result-grid">
365
+ <div class="result-item">
366
+ <div class="result-item-label">Default Probability</div>
367
+ <div class="result-item-value">{result['probability']:.2%}</div>
368
+ </div>
369
+ <div class="result-item">
370
+ <div class="result-item-label">Credit Score</div>
371
+ <div class="result-item-value">{result['credit_score']}</div>
372
+ </div>
373
+ <div class="result-item">
374
+ <div class="result-item-label">Risk Rating</div>
375
+ <div class="result-item-value">{result['rating']}</div>
376
+ </div>
377
+ </div>
378
+ """, unsafe_allow_html=True)
379
+ st.markdown('</div>', unsafe_allow_html=True)
380
+
381
+ if result.get("advisor_response"):
382
+ st.markdown(f"""
383
+ <div class="advisor-box">
384
+ <h4>🤖 AI Credit Advisor Insights</h4>
385
+ <p>{result['advisor_response']}</p>
386
+ </div>
387
+ """, unsafe_allow_html=True)
388
+
389
+ st.session_state.probability = result["probability"]
390
+ st.session_state.credit_score = result["credit_score"]
391
+ st.session_state.rating = result["rating"]
392
+ st.session_state.advisor_reply = result["advisor_response"]
393
+ st.session_state.analysis_done = True
394
+
395
+ st.success("✅ Analysis complete! You can now chat with our AI assistant below.")
396
+
397
+ else:
398
+ st.error(f"❌ API Error: {r.status_code} - {r.text}")
399
+ except requests.exceptions.Timeout:
400
+ st.error("⏱️ Request timed out. Please try again.")
401
+ except Exception as e:
402
+ st.error(f"❌ Request failed: {e}")
403
+
404
+ # ========================= CHATBOT =========================
405
+ if st.session_state.analysis_done:
406
+ st.markdown("<br><br>", unsafe_allow_html=True)
407
+ st.markdown('<div class="section-card">', unsafe_allow_html=True)
408
+ st.markdown('<div class="section-title">💬 Interactive Loan Chat Assistant</div>', unsafe_allow_html=True)
409
+ if st.session_state.chat_history:
410
+ st.markdown('<div class="chat-container">', unsafe_allow_html=True)
411
+ for role, msg in st.session_state.chat_history:
412
+ bubble = "chat-bubble-user" if role == "user" else "chat-bubble-bot"
413
+ prefix = "You: " if role == "user" else "🤖 Assistant: "
414
+ st.markdown(f"<div class='{bubble}'><strong>{prefix}</strong>{msg}</div>", unsafe_allow_html=True)
415
+ st.markdown('</div>', unsafe_allow_html=True)
416
+ user_query = st.text_input("Ask a question about your credit assessment:", placeholder="e.g., How can I improve my credit score?")
417
+ col_send, col_clear = st.columns([3, 1])
418
+ with col_send:
419
+ send_button = st.button("📤 Send Message", use_container_width=True)
420
+ with col_clear:
421
+ if st.button("🗑️ Clear Chat", use_container_width=True):
422
+ st.session_state.chat_history = []
423
+ st.session_state.thread_id = str(uuid.uuid4())
424
+ st.experimental_rerun()
425
+ if send_button and user_query.strip():
426
+ CHAT_URL = st.secrets["CHAT_URL"]
427
+ payload = {
428
+ "thread_id": st.session_state.thread_id,
429
+ "message": user_query,
430
+ "probability": st.session_state.probability,
431
+ "credit_score": st.session_state.credit_score,
432
+ "rating": st.session_state.rating,
433
+ "advisor_reply": st.session_state.advisor_reply
434
+ }
435
+ with st.spinner("🤖 Thinking..."):
436
+ try:
437
+ r = requests.post(CHAT_URL, json=payload, timeout=30)
438
+ if r.status_code == 200:
439
+ reply = r.json()["response"]
440
+ st.session_state.chat_history.append(("user", user_query))
441
+ st.session_state.chat_history.append(("bot", reply))
442
+ st.experimental_rerun()
443
+ else:
444
+ st.error(f"❌ Chat server error: {r.status_code}")
445
+ except Exception as e:
446
+ st.error(f"❌ Chat failed: {e}")
447
+ st.markdown('</div>', unsafe_allow_html=True)
448
+
449
+ # Footer
450
+ st.markdown("<br><br>", unsafe_allow_html=True)
451
+ st.markdown("""
452
+ <div style='text-align: center; color: #7f8c8d; font-size: 0.9rem;'>
453
+ <p>🛡️ RiskGuard AI © 2025 | Powered by Advanced Machine Learning</p>
454
+ <p style='font-size: 0.8rem;'>For demonstration purposes only. Not financial advice.</p>
455
+ </div>
456
+ """, unsafe_allow_html=True)
prediction_helper.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import joblib
2
+ import numpy as np
3
+ import pandas as pd
4
+ from sklearn.preprocessing import MinMaxScaler
5
+
6
+ MODEL_PATH = 'artifacts/model_data.joblib'
7
+
8
+ model_data = joblib.load(MODEL_PATH)
9
+ model = model_data['model']
10
+ scaler = model_data['scaler']
11
+ features = model_data['features']
12
+ cols_to_scale = model_data['cols_to_scale']
13
+
14
+ def prepare_input(age, income, loan_amount, loan_tenure_months, avg_dpd_per_delinquency,
15
+ delinquency_ratio, credit_utilization_ratio, num_open_accounts, residence_type,
16
+ loan_purpose, loan_type):
17
+ input_data = {
18
+ 'age': age,
19
+ 'loan_tenure_months': loan_tenure_months,
20
+ 'number_of_open_accounts': num_open_accounts,
21
+ 'credit_utilization_ratio': credit_utilization_ratio,
22
+ 'loan_to_income': loan_amount / income if income > 0 else 0,
23
+ 'delinquency_ratio': delinquency_ratio,
24
+ 'avg_dpd_per_delinquency': avg_dpd_per_delinquency,
25
+ 'residence_type_Owned': 1 if residence_type == 'Owned' else 0,
26
+ 'residence_type_Rented': 1 if residence_type == 'Rented' else 0,
27
+ 'loan_purpose_Education': 1 if loan_purpose == 'Education' else 0,
28
+ 'loan_purpose_Home': 1 if loan_purpose == 'Home' else 0,
29
+ 'loan_purpose_Personal': 1 if loan_purpose == 'Personal' else 0,
30
+ 'loan_type_Unsecured': 1 if loan_type == 'Unsecured' else 0,
31
+ 'number_of_dependants': 1,
32
+ 'years_at_current_address': 1,
33
+ 'zipcode': 1,
34
+ 'sanction_amount': 1,
35
+ 'processing_fee': 1,
36
+ 'gst': 1,
37
+ 'net_disbursement': 1,
38
+ 'principal_outstanding': 1,
39
+ 'bank_balance_at_application': 1,
40
+ 'number_of_closed_accounts': 1,
41
+ 'enquiry_count': 1
42
+ }
43
+
44
+ df = pd.DataFrame([input_data])
45
+ df[cols_to_scale] = scaler.transform(df[cols_to_scale])
46
+ df = df[features]
47
+ return df
48
+
49
+ def predict(age, income, loan_amount, loan_tenure_months, avg_dpd_per_delinquency,
50
+ delinquency_ratio, credit_utilization_ratio, num_open_accounts,
51
+ residence_type, loan_purpose, loan_type):
52
+ input_df = prepare_input(age, income, loan_amount, loan_tenure_months, avg_dpd_per_delinquency,
53
+ delinquency_ratio, credit_utilization_ratio, num_open_accounts, residence_type,
54
+ loan_purpose, loan_type)
55
+
56
+ probability, credit_score, rating = calculate_credit_score(input_df)
57
+
58
+ return probability, credit_score, rating
59
+
60
+ def calculate_credit_score(input_df, base_score=300, scale_length=600):
61
+ x = np.dot(input_df.values, model.coef_.T) + model.intercept_
62
+
63
+ default_probability = 1 / (1 + np.exp(-x))
64
+ non_default_probability = 1 - default_probability
65
+ credit_score = base_score + non_default_probability.flatten() * scale_length
66
+ def get_rating(score):
67
+ if 300 <= score < 500:
68
+ return 'Poor'
69
+ elif 500 <= score < 650:
70
+ return 'Average'
71
+ elif 650 <= score < 750:
72
+ return 'Good'
73
+ elif 750 <= score <= 900:
74
+ return 'Excellent'
75
+ else:
76
+ return 'Undefined'
77
+ rating = get_rating(credit_score[0])
78
+ return default_probability.flatten()[0], int(credit_score[0]), rating
79
+
80
+