ZainabFatimaa commited on
Commit
19f2a2d
·
verified ·
1 Parent(s): 1135d36

Update src/app.py

Browse files
Files changed (1) hide show
  1. src/app.py +290 -283
src/app.py CHANGED
@@ -59,207 +59,228 @@ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
59
  from reportlab.lib.styles import getSampleStyleSheet
60
  from reportlab.lib.units import inch
61
 
62
- # Claude Chatbot Class
63
- import time
64
- import os
65
- import requests
66
- import json
67
- import streamlit as st
 
68
 
69
- class ClaudeChatbot:
 
70
  def __init__(self):
71
- self.api_key = os.getenv('OPENROUTER_API_KEY')
72
- self.base_url = "https://openrouter.ai/api/v1/chat/completions"
73
-
74
- # Use a reliable free model
75
- self.model = "meta-llama/llama-3.2-3b-instruct:free"
76
-
77
- # More lenient rate limiting
78
- self.last_request_time = 0
79
- self.min_request_interval = 2 # Reduced to 2 seconds
80
- self.daily_requests = 0
81
- self.max_daily_requests = 200 # Increased limit
82
-
83
- # Debug mode
84
- self.debug = True
85
-
86
- if not self.api_key:
87
- st.error("❌ OPENROUTER_API_KEY not found in environment variables!")
88
- st.info("Please set your OpenRouter API key in the environment variables.")
89
- if self.debug:
90
- st.write("**Debug**: Checked environment variable 'OPENROUTER_API_KEY'")
91
 
92
- def _wait_for_rate_limit(self):
93
- """Ensure we don't exceed rate limits"""
94
- current_time = time.time()
95
- time_since_last_request = current_time - self.last_request_time
96
-
97
- if time_since_last_request < self.min_request_interval:
98
- sleep_time = self.min_request_interval - time_since_last_request
99
- if self.debug:
100
- st.info(f"Rate limiting: waiting {sleep_time:.1f} seconds...")
101
- time.sleep(sleep_time)
102
-
103
- self.last_request_time = time.time()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- def test_connection(self):
106
- """Test the API connection with a simple request"""
107
- if not self.api_key:
108
- return "Error: No API key found"
109
-
110
- headers = {
111
- "Authorization": f"Bearer {self.api_key}",
112
- "Content-Type": "application/json",
113
- "HTTP-Referer": "https://streamlit-resume-analyzer.com",
114
- "X-Title": "AI Resume Analyzer"
115
  }
 
116
 
117
- test_data = {
118
- "model": self.model,
119
- "messages": [
120
- {"role": "system", "content": "You are a helpful assistant."},
121
- {"role": "user", "content": "Say 'Connection test successful'"}
122
- ],
123
- "max_tokens": 50,
124
- "temperature": 0.1
125
- }
 
 
 
 
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  try:
128
- response = requests.post(self.base_url, headers=headers, json=test_data, timeout=10)
129
-
130
- if self.debug:
131
- st.write(f"**Debug - Test Response Status**: {response.status_code}")
132
- st.write(f"**Debug - Response Headers**: {dict(response.headers)}")
133
-
134
- if response.status_code == 200:
135
- result = response.json()
136
- if 'choices' in result and len(result['choices']) > 0:
137
- return f"✅ Connection successful: {result['choices'][0]['message']['content']}"
138
- else:
139
- return f"❌ Unexpected response format: {result}"
140
- else:
141
- error_text = response.text
142
- return f"❌ Connection failed ({response.status_code}): {error_text}"
143
 
144
- except requests.exceptions.Timeout:
145
- return "❌ Connection test timed out"
146
- except requests.exceptions.ConnectionError:
147
- return "❌ Connection error - check internet connection"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  except Exception as e:
149
- return f" Test failed: {str(e)}"
 
150
 
151
- def generate_response(self, prompt, context="", max_tokens=1000):
152
- """Generate response with improved error handling and debugging"""
153
- if not self.api_key:
154
- return "Error: API key not configured. Please set OPENROUTER_API_KEY in your environment variables."
155
-
156
- # Check daily limit
157
- if self.daily_requests >= self.max_daily_requests:
158
- return f"Daily request limit reached ({self.max_daily_requests} requests). Please try again tomorrow."
159
-
160
- # Rate limiting
161
- self._wait_for_rate_limit()
162
-
163
- headers = {
164
- "Authorization": f"Bearer {self.api_key}",
165
- "Content-Type": "application/json",
166
- "HTTP-Referer": "https://streamlit-resume-analyzer.com",
167
- "X-Title": "AI Resume Analyzer"
168
- }
 
 
 
169
 
170
- system_prompt = """You are an expert resume and career consultant with deep knowledge of hiring practices, ATS systems, and industry requirements. You provide actionable, specific advice to help job seekers improve their resumes and career prospects. Always be encouraging but honest in your feedback. Keep responses concise and focused."""
 
 
 
171
 
172
- if context:
173
- # Limit context to avoid token limits
174
- system_prompt += f"\n\nContext about the user's resume:\n{context[:1000]}"
 
175
 
176
- data = {
177
- "model": self.model,
178
- "messages": [
179
- {"role": "system", "content": system_prompt},
180
- {"role": "user", "content": prompt}
181
- ],
182
- "max_tokens": max_tokens,
183
- "temperature": 0.7,
184
- "top_p": 0.9,
185
- "frequency_penalty": 0.1
186
- }
187
 
188
- if self.debug:
189
- st.write(f"**Debug - Request URL**: {self.base_url}")
190
- st.write(f"**Debug - Model**: {self.model}")
191
- st.write(f"**Debug - Prompt length**: {len(prompt)} chars")
192
- st.write(f"**Debug - Context length**: {len(context)} chars")
 
 
 
 
 
 
 
 
 
193
 
194
  try:
195
- if self.debug:
196
- st.info(f"Making API request... (Daily requests used: {self.daily_requests}/{self.max_daily_requests})")
197
 
198
- response = requests.post(self.base_url, headers=headers, json=data, timeout=30)
 
 
 
 
 
 
 
 
199
 
200
- if self.debug:
201
- st.write(f"**Debug - Response Status**: {response.status_code}")
202
- st.write(f"**Debug - Response Headers**: {dict(response.headers)}")
203
 
204
- # Handle different error codes with more detail
205
- if response.status_code == 429:
206
- try:
207
- error_data = response.json()
208
- error_message = error_data.get('error', {}).get('message', 'Rate limit exceeded')
209
- return f"Rate limit exceeded: {error_message}. Please wait and try again."
210
- except:
211
- return "Rate limit exceeded. Please wait a few minutes and try again."
212
-
213
- elif response.status_code == 402:
214
- return "This request requires payment. Please add credits to your OpenRouter account."
215
-
216
- elif response.status_code == 401:
217
- return "Invalid API key. Please check your OPENROUTER_API_KEY environment variable."
218
-
219
- elif response.status_code == 400:
220
- try:
221
- error_data = response.json()
222
- error_message = error_data.get('error', {}).get('message', 'Bad request')
223
- if self.debug:
224
- st.write(f"**Debug - 400 Error Details**: {error_data}")
225
- return f"Bad request: {error_message}"
226
- except:
227
- return f"Bad request. Raw response: {response.text[:200]}"
228
-
229
- elif response.status_code != 200:
230
- try:
231
- error_data = response.json()
232
- error_message = error_data.get('error', {}).get('message', response.text[:200])
233
- return f"API Error {response.status_code}: {error_message}"
234
- except:
235
- return f"API Error {response.status_code}: {response.text[:200]}"
236
 
237
- # Success case
238
- try:
239
- result = response.json()
240
- if self.debug:
241
- st.write(f"**Debug - Response keys**: {list(result.keys())}")
242
-
243
- if 'choices' in result and len(result['choices']) > 0:
244
- self.daily_requests += 1
245
- response_text = result['choices'][0]['message']['content'].strip()
246
- if self.debug:
247
- st.success(f"Response generated successfully! (Remaining: {self.max_daily_requests - self.daily_requests})")
248
- return response_text
249
- else:
250
- return f"Error: Unexpected response format. Response keys: {list(result.keys())}"
251
- except json.JSONDecodeError as e:
252
- return f"Error: Invalid JSON response. Raw response: {response.text[:200]}"
253
-
254
- except requests.exceptions.Timeout:
255
- return "Request timed out after 30 seconds. Please try again with a shorter question."
256
- except requests.exceptions.ConnectionError:
257
- return "Connection error. Please check your internet connection and try again."
258
- except requests.exceptions.RequestException as e:
259
- return f"Request error: {str(e)}"
260
  except Exception as e:
261
- return f"Unexpected error: {str(e)}"
262
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  # Download NLTK data if not already present
264
  @st.cache_resource
265
  def download_nltk_data():
@@ -336,7 +357,7 @@ def basic_grammar_check(text):
336
  class ResumeAnalyzer:
337
  def __init__(self):
338
  self.nlp, self.grammar_tool = init_tools()
339
- self.chatbot = ClaudeChatbot()
340
 
341
  try:
342
  self.stop_words = set(stopwords.words('english'))
@@ -685,6 +706,93 @@ class ResumeAnalyzer:
685
  buffer.seek(0)
686
  return buffer
687
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
688
  def main():
689
  st.set_page_config(
690
  page_title="AI Resume Analyzer with Chatbot",
@@ -915,87 +1023,8 @@ def main():
915
 
916
  # Chat Interface
917
  st.header("💬 Chat with Resume Assistant")
 
918
 
919
- # Show API status
920
- if not os.getenv('OPENROUTER_API_KEY'):
921
- st.error("❌ Chat feature unavailable: OPENROUTER_API_KEY not configured")
922
- st.info("Please set your OpenRouter API key in environment variables to use the chat feature.")
923
- else:
924
- # Debugging information
925
- with st.expander("🔧 Debug Information", expanded=False):
926
- st.write("**API Key Status:**", "✅ Found" if os.getenv('OPENROUTER_API_KEY') else "❌ Missing")
927
- if os.getenv('OPENROUTER_API_KEY'):
928
- api_key = os.getenv('OPENROUTER_API_KEY')
929
- st.write("**API Key Preview:**", f"{api_key[:10]}...{api_key[-4:]}" if len(api_key) > 14 else "Key too short")
930
- st.write("**Model:**", analyzer.chatbot.model)
931
- st.write("**Rate Limit Interval:**", f"{analyzer.chatbot.min_request_interval} seconds")
932
- st.write("**Daily Requests Used:**", f"{analyzer.chatbot.daily_requests}/{analyzer.chatbot.max_daily_requests}")
933
-
934
- st.info(f"🤖 Using model: {analyzer.chatbot.model}")
935
- st.warning("⚠️ OpenRouter free tier limits: 50 requests/day, 6+ seconds between requests")
936
-
937
- # Test API button
938
- if st.button("🧪 Test API Connection", help="Send a simple test request to verify API is working"):
939
- with st.spinner("Testing API connection..."):
940
- test_response = analyzer.chatbot.generate_response(
941
- "Just say 'API test successful' in exactly those words.",
942
- ""
943
- )
944
- st.write("**Test Response:**", test_response)
945
-
946
- # Chat input
947
- user_question = st.text_input(
948
- "Ask about your resume:",
949
- placeholder="e.g., How can I improve my resume for a data scientist role?",
950
- key="chat_input",
951
- help="Ask specific questions about your resume and get personalized advice"
952
- )
953
-
954
- col1, col2 = st.columns([1, 4])
955
- with col1:
956
- send_button = st.button("Send", type="primary")
957
- with col2:
958
- if st.button("Clear Chat"):
959
- st.session_state.chat_history = []
960
- st.rerun()
961
-
962
- if send_button and user_question.strip():
963
- # Add user message to history
964
- st.session_state.chat_history.append(("user", user_question))
965
-
966
- # Get AI response
967
- with st.spinner("Getting AI response..."):
968
- response = analyzer.chatbot.generate_response(
969
- user_question,
970
- st.session_state.resume_context
971
- )
972
-
973
- # Add AI response to history
974
- st.session_state.chat_history.append(("assistant", response))
975
- st.rerun()
976
-
977
- # Display chat history
978
- if st.session_state.chat_history:
979
- st.subheader("Chat History")
980
-
981
- # Display messages in reverse order (newest first) with a limit
982
- chat_display_limit = 10 # Show last 10 messages
983
- recent_messages = st.session_state.chat_history[-chat_display_limit:]
984
-
985
- for role, message in reversed(recent_messages):
986
- if role == "user":
987
- st.markdown(f"**👤 You:** {message}")
988
- else:
989
- st.markdown(f"**🤖 Assistant:** {message}")
990
- st.markdown("---")
991
-
992
- if len(st.session_state.chat_history) > chat_display_limit:
993
- st.info(f"Showing {chat_display_limit} most recent messages. Total messages: {len(st.session_state.chat_history)}")
994
-
995
- except Exception as e:
996
- st.error(f"Error during analysis: {str(e)}")
997
- st.error("Please check your resume format and try again.")
998
-
999
  else:
1000
  st.error("❌ Could not extract text from the uploaded file. Please check the file format and try again.")
1001
 
@@ -1017,31 +1046,9 @@ def main():
1017
 
1018
  **Supported Formats**: PDF, DOCX, TXT
1019
 
1020
- **Chat Feature**: Set OPENROUTER_API_KEY environment variable to enable AI chat assistance
1021
  """)
1022
 
1023
- # API Setup Instructions
1024
- with st.expander("🔧 Setup Instructions for Chat Feature"):
1025
- st.markdown("""
1026
- To enable the AI chat assistant:
1027
-
1028
- 1. **Get an API key from OpenRouter**:
1029
- - Visit https://openrouter.ai
1030
- - Sign up for a free account
1031
- - Get your API key from the dashboard
1032
-
1033
- 2. **Set the environment variable**:
1034
- - Add `OPENROUTER_API_KEY=your_api_key_here` to your environment
1035
- - For local development, you can use a `.env` file
1036
- - For deployment, set it in your hosting platform's environment variables
1037
-
1038
- 3. **Free tier includes**:
1039
- - Limited requests per hour
1040
- - Access to free models like Llama 3.2
1041
- - No credit card required
1042
-
1043
- The app will work without the API key, but chat features will be disabled.
1044
- """)
1045
 
1046
  if __name__ == "__main__":
1047
  main()
 
59
  from reportlab.lib.styles import getSampleStyleSheet
60
  from reportlab.lib.units import inch
61
 
62
+ import nltk
63
+ from nltk.tokenize import word_tokenize
64
+ from nltk.corpus import stopwords
65
+ from nltk.stem import WordNetLemmatizer
66
+ import re
67
+ from datetime import datetime
68
+ from typing import Dict, List
69
 
70
+ # Simple NLP processor
71
+ class SimpleNLPProcessor:
72
  def __init__(self):
73
+ self.setup_nltk()
74
+
75
+ def setup_nltk(self):
76
+ try:
77
+ nltk.download('punkt', quiet=True)
78
+ nltk.download('stopwords', quiet=True)
79
+ nltk.download('wordnet', quiet=True)
80
+ self.stop_words = set(stopwords.words('english'))
81
+ self.lemmatizer = WordNetLemmatizer()
82
+ except:
83
+ self.stop_words = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'with'}
84
+ self.lemmatizer = None
 
 
 
 
 
 
 
 
85
 
86
+ def process_text(self, text: str) -> str:
87
+ """Clean and process text for better context"""
88
+ try:
89
+ tokens = word_tokenize(text.lower())
90
+ except:
91
+ tokens = text.lower().split()
92
+
93
+ # Remove stopwords and short tokens
94
+ filtered_tokens = [token for token in tokens
95
+ if token not in self.stop_words and len(token) > 2]
96
+
97
+ # Lemmatize if available
98
+ if self.lemmatizer:
99
+ try:
100
+ filtered_tokens = [self.lemmatizer.lemmatize(token) for token in filtered_tokens]
101
+ except:
102
+ pass
103
+
104
+ # Return key terms (limit to avoid long prompts)
105
+ return ' '.join(filtered_tokens[:15])
106
+
107
+ # Simple memory management
108
+ class SimpleChatMemory:
109
+ def __init__(self):
110
+ if 'chat_history' not in st.session_state:
111
+ st.session_state.chat_history = []
112
 
113
+ def add_conversation(self, user_msg: str, bot_response: str):
114
+ conversation = {
115
+ 'user': user_msg,
116
+ 'bot': bot_response,
117
+ 'timestamp': datetime.now().strftime("%H:%M:%S")
 
 
 
 
 
118
  }
119
+ st.session_state.chat_history.append(conversation)
120
 
121
+ # Keep only last 8 conversations to save memory
122
+ if len(st.session_state.chat_history) > 8:
123
+ st.session_state.chat_history = st.session_state.chat_history[-8:]
124
+
125
+ def get_recent_context(self, limit: int = 2) -> str:
126
+ """Get recent conversation context"""
127
+ if not st.session_state.chat_history:
128
+ return ""
129
+
130
+ recent = st.session_state.chat_history[-limit:]
131
+ context_parts = []
132
+ for conv in recent:
133
+ context_parts.append(f"User asked: {conv['user'][:50]}...")
134
 
135
+ return " | ".join(context_parts) if context_parts else ""
136
+
137
+ # Main chatbot class
138
+ class SimpleCPUChatbot:
139
+ def __init__(self):
140
+ self.model_name = "distilgpt2" # Fast, CPU-friendly model
141
+ self.model = None
142
+ self.tokenizer = None
143
+ self.pipeline = None
144
+ self.nlp_processor = SimpleNLPProcessor()
145
+ self.memory = SimpleChatMemory()
146
+ self.is_loaded = False
147
+
148
+ @st.cache_resource
149
+ def load_model(_self):
150
+ """Load the model (cached for efficiency)"""
151
  try:
152
+ with st.spinner("Loading AI model (first time may take 2-3 minutes)..."):
153
+ # Load tokenizer
154
+ tokenizer = AutoTokenizer.from_pretrained(_self.model_name)
155
+ tokenizer.pad_token = tokenizer.eos_token
 
 
 
 
 
 
 
 
 
 
 
156
 
157
+ # Load model with CPU optimization
158
+ model = AutoModelForCausalLM.from_pretrained(
159
+ _self.model_name,
160
+ torch_dtype=torch.float32, # Use float32 for CPU
161
+ low_cpu_mem_usage=True
162
+ )
163
+
164
+ # Create pipeline
165
+ text_generator = pipeline(
166
+ "text-generation",
167
+ model=model,
168
+ tokenizer=tokenizer,
169
+ device=-1, # CPU only
170
+ max_new_tokens=80,
171
+ do_sample=True,
172
+ temperature=0.7,
173
+ top_p=0.9,
174
+ pad_token_id=tokenizer.eos_token_id
175
+ )
176
+
177
+ return model, tokenizer, text_generator
178
  except Exception as e:
179
+ st.error(f"Failed to load model: {str(e)}")
180
+ return None, None, None
181
 
182
+ def initialize(self):
183
+ """Initialize the chatbot"""
184
+ if not self.is_loaded:
185
+ result = self.load_model()
186
+ if result[0] is not None:
187
+ self.model, self.tokenizer, self.pipeline = result
188
+ self.is_loaded = True
189
+ st.success("AI model loaded successfully!")
190
+ return True
191
+ else:
192
+ st.error("Failed to load AI model")
193
+ return False
194
+ return True
195
+
196
+ def create_prompt(self, user_input: str, resume_context: str = "") -> str:
197
+ """Create a focused prompt for resume advice"""
198
+ # Process user input for key terms
199
+ key_terms = self.nlp_processor.process_text(user_input)
200
+
201
+ # Get conversation context
202
+ recent_context = self.memory.get_recent_context()
203
 
204
+ # Build prompt
205
+ prompt_parts = [
206
+ "You are a professional resume consultant. Give specific, helpful advice."
207
+ ]
208
 
209
+ # Add resume context if available (limited)
210
+ if resume_context:
211
+ resume_excerpt = resume_context[:200] + "..." if len(resume_context) > 200 else resume_context
212
+ prompt_parts.append(f"Resume excerpt: {resume_excerpt}")
213
 
214
+ # Add conversation context
215
+ if recent_context:
216
+ prompt_parts.append(f"Previous topics: {recent_context}")
 
 
 
 
 
 
 
 
217
 
218
+ # Add key terms from current question
219
+ if key_terms:
220
+ prompt_parts.append(f"Focus areas: {key_terms}")
221
+
222
+ # Add the actual question
223
+ prompt_parts.append(f"Question: {user_input}")
224
+ prompt_parts.append("Advice:")
225
+
226
+ return " | ".join(prompt_parts)
227
+
228
+ def generate_response(self, user_input: str, resume_context: str = "") -> str:
229
+ """Generate response using the loaded model"""
230
+ if not self.is_loaded:
231
+ return "Please initialize the AI model first by clicking 'Initialize AI'."
232
 
233
  try:
234
+ # Create prompt
235
+ prompt = self.create_prompt(user_input, resume_context)
236
 
237
+ # Generate response
238
+ result = self.pipeline(
239
+ prompt,
240
+ max_new_tokens=60, # Keep responses concise
241
+ num_return_sequences=1,
242
+ temperature=0.7,
243
+ do_sample=True,
244
+ top_p=0.9
245
+ )
246
 
247
+ # Extract and clean response
248
+ generated_text = result[0]['generated_text']
249
+ response = generated_text.replace(prompt, "").strip()
250
 
251
+ # Clean up the response
252
+ response = self.clean_response(response, user_input)
253
+
254
+ # Add to memory
255
+ self.memory.add_conversation(user_input, response)
256
+
257
+ return response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  except Exception as e:
260
+ return f"Sorry, I encountered an error: {str(e)}. Please try a simpler question."
261
+
262
+ def clean_response(self, response: str, user_input: str) -> str:
263
+ """Clean and improve the generated response"""
264
+ # Remove extra whitespace and newlines
265
+ response = re.sub(r'\s+', ' ', response).strip()
266
+
267
+ # Split into sentences and take first few good ones
268
+ sentences = [s.strip() for s in response.split('.') if s.strip()]
269
+ good_sentences = []
270
+
271
+ for sentence in sentences[:3]: # Max 3 sentences
272
+ if len(sentence) > 10 and not sentence.lower().startswith(('you are', 'i am', 'as a')):
273
+ good_sentences.append(sentence)
274
+
275
+ if good_sentences:
276
+ response = '. '.join(good_sentences)
277
+ if not response.endswith('.'):
278
+ response += '.'
279
+ else:
280
+ # Fallback response
281
+ response = "I'd be happy to help with your resume. Could you be more specific about what you need assistance with?"
282
+
283
+ return response
284
  # Download NLTK data if not already present
285
  @st.cache_resource
286
  def download_nltk_data():
 
357
  class ResumeAnalyzer:
358
  def __init__(self):
359
  self.nlp, self.grammar_tool = init_tools()
360
+ self.chatbot = SimpleCPUChatbot()
361
 
362
  try:
363
  self.stop_words = set(stopwords.words('english'))
 
706
  buffer.seek(0)
707
  return buffer
708
 
709
+ def create_simple_chat_interface(resume_context: str = ""):
710
+ """Create simple chat interface for the resume analyzer"""
711
+
712
+ st.header("🤖 AI Resume Assistant")
713
+
714
+ # Initialize chatbot
715
+ if 'simple_chatbot' not in st.session_state:
716
+ st.session_state.simple_chatbot = SimpleCPUChatbot()
717
+
718
+ chatbot = st.session_state.simple_chatbot
719
+
720
+ # Model initialization
721
+ col1, col2 = st.columns([3, 1])
722
+
723
+ with col1:
724
+ st.info("Using DistilGPT2 - Fast CPU-only model (≈250MB download)")
725
+
726
+ with col2:
727
+ if st.button("Initialize AI", type="primary"):
728
+ chatbot.initialize()
729
+
730
+ # Chat interface
731
+ if chatbot.is_loaded:
732
+ st.success("✅ AI Ready")
733
+
734
+ # Sample questions
735
+ with st.expander("💡 Try asking"):
736
+ sample_questions = [
737
+ "How can I improve my resume?",
738
+ "What skills should I add?",
739
+ "How do I make it more ATS-friendly?",
740
+ "What's wrong with my experience section?"
741
+ ]
742
+ for q in sample_questions:
743
+ if st.button(q, key=f"sample_{hash(q)}"):
744
+ st.session_state.current_question = q
745
+
746
+ # Chat input
747
+ user_question = st.text_input(
748
+ "Ask about your resume:",
749
+ value=st.session_state.get('current_question', ''),
750
+ placeholder="How can I improve my resume for tech jobs?",
751
+ key="chat_input"
752
+ )
753
+
754
+ # Send button and clear
755
+ col1, col2 = st.columns([1, 3])
756
+ with col1:
757
+ send_clicked = st.button("Send", type="primary")
758
+ with col2:
759
+ if st.button("Clear Chat"):
760
+ st.session_state.chat_history = []
761
+ if 'current_question' in st.session_state:
762
+ del st.session_state.current_question
763
+ st.experimental_rerun()
764
+
765
+ # Generate response
766
+ if send_clicked and user_question.strip():
767
+ with st.spinner("Thinking..."):
768
+ response = chatbot.generate_response(user_question, resume_context)
769
+ if 'current_question' in st.session_state:
770
+ del st.session_state.current_question
771
+ st.experimental_rerun()
772
+
773
+ # Display chat history
774
+ if st.session_state.chat_history:
775
+ st.subheader("💬 Conversation")
776
+
777
+ for conv in reversed(st.session_state.chat_history[-5:]): # Show last 5
778
+ st.markdown(f"**You:** {conv['user']}")
779
+ st.markdown(f"**AI:** {conv['bot']}")
780
+ st.caption(f"Time: {conv['timestamp']}")
781
+ st.divider()
782
+
783
+ else:
784
+ st.warning("Click 'Initialize AI' to start chatting")
785
+
786
+ with st.expander("ℹ️ About this AI"):
787
+ st.markdown("""
788
+ **Model**: DistilGPT2 (CPU-optimized)
789
+ **Size**: ~250MB download
790
+ **Speed**: 2-5 seconds per response
791
+ **Memory**: ~1GB RAM usage
792
+
793
+ This model runs entirely on your CPU and provides helpful resume advice.
794
+ First initialization will download the model files.
795
+ """)
796
  def main():
797
  st.set_page_config(
798
  page_title="AI Resume Analyzer with Chatbot",
 
1023
 
1024
  # Chat Interface
1025
  st.header("💬 Chat with Resume Assistant")
1026
+ create_simple_chat_interface(st.session_state.get('resume_context', ''))
1027
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1028
  else:
1029
  st.error("❌ Could not extract text from the uploaded file. Please check the file format and try again.")
1030
 
 
1046
 
1047
  **Supported Formats**: PDF, DOCX, TXT
1048
 
1049
+ **Chat Feature**: AI chat assistance
1050
  """)
1051
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1052
 
1053
  if __name__ == "__main__":
1054
  main()