Sensei13k commited on
Commit
0335d8f
·
verified ·
1 Parent(s): 97e44de

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +455 -451
app.py CHANGED
@@ -1,452 +1,456 @@
1
- import os
2
- import json
3
- import gradio as gr
4
- import google.generativeai as genai
5
- from datetime import datetime
6
- import time
7
- from typing import List, Dict, Any, Tuple
8
-
9
- # Import your existing modules
10
- try:
11
- from intent_detector import detect_intent #type:ignore
12
- from rag_utils import retrieve_profiles, retrieve_jobs, get_stats
13
- except ImportError:
14
- print("Warning: Could not import custom modules. Some features may not work.")
15
- def detect_intent(text, model=None):
16
- return "SEARCH"
17
- def retrieve_profiles(query, top_k=3):
18
- return []
19
- def retrieve_jobs(query, top_k=3):
20
- return []
21
- def get_stats():
22
- return {"profiles_loaded": 0, "jobs_loaded": 0}
23
-
24
- # Configure Gemini
25
- API_KEY = os.getenv("GOOGLE_API_KEY")
26
- if API_KEY:
27
- genai.configure(api_key=API_KEY) #type:ignore
28
-
29
- # System prompt
30
- SYSTEM_PROMPT = """
31
- You are a professional AI assistant that can perform three main tasks:
32
-
33
- 1. **ONBOARD** - Help professionals create their profile (name, role, skills, experience, location)
34
- 2. **SEARCH** - Find relevant job opportunities based on user requirements
35
- 3. **POST** - Create structured job postings from client descriptions
36
-
37
- Respond in a friendly, professional manner. Keep responses concise but informative.
38
- """
39
-
40
- def get_intent_emoji(intent: str) -> str:
41
- """Get emoji for intent type."""
42
- emoji_map = {
43
- "ONBOARD": "👤",
44
- "SEARCH": "🔍",
45
- "POST": "📝"
46
- }
47
- return emoji_map.get(intent, "🤖")
48
-
49
- def format_response(response: str, intent: str) -> str:
50
- """Format response with intent indicator."""
51
- emoji = get_intent_emoji(intent)
52
- return f"{emoji} **{intent}**\n\n{response}"
53
-
54
- def process_message(message: str, history: List[List[str]], model_name: str) -> Tuple[str, List[List[str]]]:
55
- """Process user message and return response with updated history."""
56
- if not message.strip():
57
- return "", history
58
-
59
- if not API_KEY:
60
- error_msg = "❌ **Error**: Please set your GOOGLE_API_KEY environment variable."
61
- history.append([message, error_msg])
62
- return "", history
63
-
64
- try:
65
- # Detect intent
66
- intent = detect_intent(message, model=model_name)
67
-
68
- # Get context based on intent
69
- context_block = ""
70
- if intent == "SEARCH":
71
- job_results = retrieve_jobs(message, top_k=3)
72
- if job_results:
73
- context_lines = []
74
- for idx, job in enumerate(job_results, 1):
75
- context_lines.append(f"{idx}. {job['text'][:200]}...")
76
- context_block = f"\n\n**Available Jobs:**\n" + "\n".join(context_lines)
77
- elif intent == "ONBOARD":
78
- profile_results = retrieve_profiles(message, top_k=3)
79
- if profile_results:
80
- context_lines = []
81
- for idx, prof in enumerate(profile_results, 1):
82
- context_lines.append(f"{idx}. {prof['text'][:200]}...")
83
- context_block = f"\n\n**Existing Profiles:**\n" + "\n".join(context_lines)
84
-
85
- # Prepare prompt
86
- full_prompt = SYSTEM_PROMPT + context_block + f"\n\nUser: {message}\nAssistant:"
87
-
88
- # Generate response
89
- model = genai.GenerativeModel(model_name) #type:ignore
90
- response = model.generate_content(full_prompt)
91
-
92
- # Format response
93
- formatted_response = format_response(response.text, intent)
94
-
95
- # Update history
96
- history.append([message, formatted_response])
97
-
98
- return "", history
99
-
100
- except Exception as e:
101
- error_msg = f"❌ **Error**: {str(e)}"
102
- history.append([message, error_msg])
103
- return "", history
104
-
105
- def get_system_stats() -> str:
106
- """Get system statistics."""
107
- try:
108
- stats = get_stats()
109
- return f"""
110
- 📊 **System Status**
111
- - Profiles loaded: {stats['profiles_loaded']}
112
- - Jobs loaded: {stats['jobs_loaded']}
113
- - API Status: {'✅ Connected' if API_KEY else '❌ Not configured'}
114
- - Last updated: {datetime.now().strftime('%H:%M:%S')}
115
- """
116
- except:
117
- return "📊 **System Status**: Unable to fetch stats"
118
-
119
- def create_sample_profiles() -> str:
120
- """Generate sample profile data for testing."""
121
- sample_data = [
122
- {
123
- "id": "prof_001",
124
- "name": "Alice Johnson",
125
- "role": "Full Stack Developer",
126
- "skills": ["React", "Node.js", "Python", "PostgreSQL"],
127
- "experience": "5 years",
128
- "location": "San Francisco, CA"
129
- },
130
- {
131
- "id": "prof_002",
132
- "name": "Bob Smith",
133
- "role": "Data Scientist",
134
- "skills": ["Python", "Machine Learning", "TensorFlow", "SQL"],
135
- "experience": "3 years",
136
- "location": "New York, NY"
137
- }
138
- ]
139
-
140
- os.makedirs("data", exist_ok=True)
141
- with open("data/onboarding_profiles.jsonl", "w") as f:
142
- for profile in sample_data:
143
- f.write(json.dumps(profile) + "\n")
144
-
145
- return "✅ Sample profiles created successfully!"
146
-
147
- def create_sample_jobs() -> str:
148
- """Generate sample job data for testing."""
149
- sample_data = [
150
- {
151
- "id": "job_001",
152
- "title": "Senior React Developer",
153
- "company": "TechCorp",
154
- "type": "Remote",
155
- "skills": ["React", "TypeScript", "Node.js"],
156
- "description": "Build scalable web applications using React and TypeScript",
157
- "salary": "$80k-$120k",
158
- "location": "Remote"
159
- },
160
- {
161
- "id": "job_002",
162
- "title": "Python Data Engineer",
163
- "company": "DataFlow Inc",
164
- "type": "Hybrid",
165
- "skills": ["Python", "Apache Spark", "AWS"],
166
- "description": "Design and maintain data pipelines for machine learning models",
167
- "salary": "$90k-$130k",
168
- "location": "Seattle, WA"
169
- }
170
- ]
171
-
172
- os.makedirs("data", exist_ok=True)
173
- with open("data/job_listings.jsonl", "w") as f:
174
- for job in sample_data:
175
- f.write(json.dumps(job) + "\n")
176
-
177
- return "✅ Sample jobs created successfully!"
178
-
179
- # Custom CSS for styling
180
- custom_css = """
181
- /* Main container styling */
182
- .gradio-container {
183
- max-width: 1200px !important;
184
- margin: 0 auto;
185
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
186
- min-height: 100vh;
187
- }
188
-
189
- /* Header styling */
190
- .header-section {
191
- background: rgba(255, 255, 255, 0.1);
192
- backdrop-filter: blur(10px);
193
- padding: 20px;
194
- border-radius: 15px;
195
- margin-bottom: 20px;
196
- border: 1px solid rgba(255, 255, 255, 0.2);
197
- }
198
-
199
- .header-section h1 {
200
- color: white;
201
- text-align: center;
202
- font-size: 2.5em;
203
- margin-bottom: 10px;
204
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
205
- }
206
-
207
- .header-section p {
208
- color: rgba(255, 255, 255, 0.9);
209
- text-align: center;
210
- font-size: 1.1em;
211
- }
212
-
213
- /* Chat interface styling */
214
- .chat-container {
215
- background: rgba(255, 255, 255, 0.95);
216
- border-radius: 20px;
217
- padding: 20px;
218
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
219
- border: 1px solid rgba(255, 255, 255, 0.3);
220
- }
221
-
222
- /* Message styling */
223
- .message {
224
- padding: 15px;
225
- margin: 10px 0;
226
- border-radius: 15px;
227
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
228
- }
229
-
230
- .user-message {
231
- background: linear-gradient(135deg, #4f46e5, #7c3aed);
232
- color: white;
233
- margin-left: 20%;
234
- }
235
-
236
- .assistant-message {
237
- background: linear-gradient(135deg, #f8fafc, #e2e8f0);
238
- color: #1e293b;
239
- margin-right: 20%;
240
- }
241
-
242
- /* Button styling */
243
- .action-button {
244
- background: linear-gradient(135deg, #10b981, #059669);
245
- color: white;
246
- border: none;
247
- padding: 12px 24px;
248
- border-radius: 25px;
249
- font-weight: 600;
250
- transition: all 0.3s ease;
251
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
252
- }
253
-
254
- .action-button:hover {
255
- transform: translateY(-2px);
256
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
257
- }
258
-
259
- /* Input styling */
260
- .input-box {
261
- border: 2px solid #e5e7eb;
262
- border-radius: 15px;
263
- padding: 15px;
264
- font-size: 16px;
265
- transition: border-color 0.3s ease;
266
- }
267
-
268
- .input-box:focus {
269
- border-color: #4f46e5;
270
- box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1);
271
- }
272
-
273
- /* Stats panel styling */
274
- .stats-panel {
275
- background: rgba(255, 255, 255, 0.1);
276
- backdrop-filter: blur(10px);
277
- border-radius: 15px;
278
- padding: 15px;
279
- border: 1px solid rgba(255, 255, 255, 0.2);
280
- color: white;
281
- }
282
-
283
- /* Responsive design */
284
- @media (max-width: 768px) {
285
- .gradio-container {
286
- padding: 10px;
287
- }
288
-
289
- .header-section h1 {
290
- font-size: 2em;
291
- }
292
-
293
- .user-message, .assistant-message {
294
- margin-left: 5%;
295
- margin-right: 5%;
296
- }
297
- }
298
- """
299
-
300
- # Sample conversation starters
301
- sample_conversations = [
302
- "I'm a Python developer with 5 years experience in Django and React, based in San Francisco",
303
- "Show me remote React developer jobs paying over $80k",
304
- "I need to hire a full-stack developer for my startup, budget is $5000 for 3 months",
305
- "Find me data science positions in New York",
306
- "Looking for freelance web development work"
307
- ]
308
-
309
- def create_interface():
310
- """Create the Gradio interface."""
311
-
312
- with gr.Blocks(css=custom_css, title="Multi-Task Gemini Assistant") as demo:
313
- # Header
314
- gr.HTML("""
315
- <div class="header-section">
316
- <h1>🚀 Multi-Task Gemini Assistant</h1>
317
- <p>Your AI-powered career companion for onboarding, job search, and posting opportunities</p>
318
- </div>
319
- """)
320
-
321
- with gr.Row():
322
- with gr.Column(scale=3):
323
- # Main chat interface
324
- chatbot = gr.Chatbot(
325
- height=500,
326
- label="💬 Chat with Gemini",
327
- elem_classes=["chat-container"],
328
- bubble_full_width=False,
329
- show_label=True,
330
- avatar_images=("👤", "🤖")
331
- )
332
-
333
- with gr.Row():
334
- msg = gr.Textbox(
335
- placeholder="Type your message here... (e.g., 'I'm a Python developer looking for work')",
336
- label="Your Message",
337
- elem_classes=["input-box"],
338
- scale=4
339
- )
340
- send_btn = gr.Button("Send 🚀", elem_classes=["action-button"], scale=1)
341
-
342
- # Sample conversation starters
343
- gr.Markdown("### 💡 Try these examples:")
344
- sample_buttons = []
345
- for i, sample in enumerate(sample_conversations):
346
- btn = gr.Button(f"💬 {sample[:50]}{'...' if len(sample) > 50 else ''}",
347
- size="sm", variant="secondary")
348
- sample_buttons.append(btn)
349
-
350
- with gr.Column(scale=1):
351
- # Control panel
352
- gr.Markdown("### ⚙️ Settings")
353
-
354
- model_dropdown = gr.Dropdown(
355
- choices=["gemini-1.5-flash", "gemini-1.5-pro", "gemini-pro"],
356
- value="gemini-1.5-flash",
357
- label="🧠 Model",
358
- info="Choose your Gemini model"
359
- )
360
-
361
- # System stats
362
- stats_display = gr.Markdown(
363
- get_system_stats(),
364
- label="📊 System Status",
365
- elem_classes=["stats-panel"]
366
- )
367
-
368
- refresh_stats_btn = gr.Button("🔄 Refresh Stats", size="sm")
369
-
370
- gr.Markdown("### 🛠️ Quick Actions")
371
-
372
- create_profiles_btn = gr.Button("👥 Create Sample Profiles", variant="secondary")
373
- create_jobs_btn = gr.Button("💼 Create Sample Jobs", variant="secondary")
374
- clear_chat_btn = gr.Button("🗑️ Clear Chat", variant="secondary")
375
-
376
- # Status messages
377
- status_msg = gr.Markdown("", elem_classes=["stats-panel"])
378
-
379
- # Event handlers
380
- def send_message(message, history, model):
381
- return process_message(message, history, model)
382
-
383
- def set_sample_message(sample_text):
384
- return sample_text
385
-
386
- def clear_chat():
387
- return []
388
-
389
- def refresh_stats():
390
- return get_system_stats()
391
-
392
- # Bind events
393
- send_btn.click(
394
- send_message,
395
- inputs=[msg, chatbot, model_dropdown],
396
- outputs=[msg, chatbot]
397
- )
398
-
399
- msg.submit(
400
- send_message,
401
- inputs=[msg, chatbot, model_dropdown],
402
- outputs=[msg, chatbot]
403
- )
404
-
405
- # Sample button events
406
- for i, btn in enumerate(sample_buttons):
407
- btn.click(
408
- lambda sample=sample_conversations[i]: sample,
409
- outputs=msg
410
- )
411
-
412
- refresh_stats_btn.click(refresh_stats, outputs=stats_display)
413
- clear_chat_btn.click(clear_chat, outputs=chatbot)
414
- create_profiles_btn.click(create_sample_profiles, outputs=status_msg)
415
- create_jobs_btn.click(create_sample_jobs, outputs=status_msg)
416
-
417
- # Footer
418
- gr.HTML("""
419
- <div style="text-align: center; padding: 20px; color: rgba(255,255,255,0.8);">
420
- <p>🌟 Powered by Google's Gemini AI | Built with ❤️ using Gradio</p>
421
- <p>💡 <strong>Tips:</strong> Use natural language to describe your needs. The AI will automatically detect if you want to onboard, search for jobs, or post a job.</p>
422
- </div>
423
- """)
424
-
425
- return demo
426
-
427
- def main():
428
- """Main function to launch the interface."""
429
- print("🚀 Starting Multi-Task Gemini Assistant...")
430
-
431
- # Check API key
432
- if not API_KEY:
433
- print("⚠️ Warning: GOOGLE_API_KEY not found. Please set it as an environment variable.")
434
- print(" Example: export GOOGLE_API_KEY='your-api-key-here'")
435
-
436
- # Create and launch interface
437
- demo = create_interface()
438
-
439
- # Launch with custom settings
440
- demo.launch(
441
- server_name="0.0.0.0", # Allow external access
442
- server_port=7860, # Default Gradio port
443
- share=False, # Set to True to create public link
444
- debug=True, # Enable debug mode
445
- show_error=True, # Show errors in interface
446
- inbrowser=True, # Open in browser automatically
447
- favicon_path=None, # Add custom favicon if needed
448
- ssl_verify=False # Disable SSL verification for development
449
- )
450
-
451
- if __name__ == "__main__":
 
 
 
 
452
  main()
 
1
+ import os
2
+ import sys
3
+ import json
4
+ import time
5
+ import gradio as gr
6
+ import google.generativeai as genai
7
+
8
+ from intent_detector import detect_intent
9
+ from rag_utils import retrieve_profiles, retrieve_jobs
10
+
11
+ # 1. Configure Gemini
12
+ API_KEY = os.getenv("GOOGLE_API_KEY")
13
+ if not API_KEY:
14
+ raise ValueError("Error: Set GOOGLE_API_KEY environment variable before running.")
15
+ genai.configure(api_key=API_KEY)
16
+
17
+ # 2. System prompt with conversational onboarding flow
18
+ SYSTEM_PROMPT = """
19
+ You are a helpful AI assistant that can perform three main tasks:
20
+
21
+ 1. ONBOARD – Help professionals create their profile by asking questions ONE AT A TIME in a natural conversation flow
22
+ 2. SEARCH Help find job opportunities based on user requirements
23
+ 3. POST – Help clients create structured job postings
24
+
25
+ ONBOARDING CONVERSATION FLOW:
26
+ When onboarding a user, ask questions in this specific order, ONE QUESTION AT A TIME:
27
+ 1. First, ask for their full name
28
+ 2. Then ask what their current role/job title is
29
+ 3. Then ask about their key skills (programming languages, technologies, etc.)
30
+ 4. Then ask about their years of experience
31
+ 5. Then ask about their preferred location or if they want remote work
32
+ 6. Finally, summarize their profile and confirm if everything looks correct
33
+
34
+ IMPORTANT ONBOARDING RULES:
35
+ - Ask ONLY ONE question at a time
36
+ - Wait for the user's response before asking the next question
37
+ - Be conversational and friendly
38
+ - If they provide multiple pieces of information at once, acknowledge what they shared and ask for what's still missing
39
+ - Keep questions simple and clear
40
+ - After collecting all information, show a summary and ask for confirmation
41
+
42
+ SEARCH BEHAVIOR:
43
+ For job searches, retrieve relevant listings and present them clearly with title, company, and key details.
44
+
45
+ POST BEHAVIOR:
46
+ For job postings, help create structured JSON format job listings.
47
+
48
+ Always be helpful, conversational, and ask follow-up questions when needed.
49
+ """
50
+
51
+ class ChatbotState:
52
+ """Manages the conversation state for onboarding flow."""
53
+
54
+ def __init__(self):
55
+ self.reset()
56
+
57
+ def reset(self):
58
+ """Reset the conversation state."""
59
+ self.mode = None # "onboarding", "search", "post", or None
60
+ self.onboarding_step = 0
61
+ self.user_data = {}
62
+ self.onboarding_complete = False
63
+
64
+ def is_onboarding(self):
65
+ """Check if currently in onboarding mode."""
66
+ return self.mode == "onboarding" and not self.onboarding_complete
67
+
68
+ def get_next_onboarding_question(self):
69
+ """Get the next question in the onboarding flow."""
70
+ questions = [
71
+ "What's your full name?",
72
+ "What's your current role or job title?",
73
+ "What are your key skills? (For example: Python, React, project management, etc.)",
74
+ "How many years of experience do you have in your field?",
75
+ "What's your preferred work location? (Or would you prefer remote work?)"
76
+ ]
77
+
78
+ if self.onboarding_step < len(questions):
79
+ return questions[self.onboarding_step]
80
+ return None
81
+
82
+ def save_onboarding_data(self, field, value):
83
+ """Save user response to onboarding data."""
84
+ fields = ["name", "role", "skills", "experience", "location"]
85
+ if self.onboarding_step < len(fields):
86
+ self.user_data[fields[self.onboarding_step]] = value
87
+
88
+ def get_onboarding_summary(self):
89
+ """Generate a summary of collected onboarding data."""
90
+ summary = "Here's your profile summary:\n"
91
+ summary += f"• Name: {self.user_data.get('name', 'Not provided')}\n"
92
+ summary += f"• Role: {self.user_data.get('role', 'Not provided')}\n"
93
+ summary += f"• Skills: {self.user_data.get('skills', 'Not provided')}\n"
94
+ summary += f"• Experience: {self.user_data.get('experience', 'Not provided')}\n"
95
+ summary += f"• Location: {self.user_data.get('location', 'Not provided')}\n"
96
+ summary += "\nDoes this look correct? (yes/no)"
97
+ return summary
98
+
99
+ def save_user_profile(user_data, filename="data/user_profiles.jsonl"):
100
+ """Save user profile to file."""
101
+ os.makedirs(os.path.dirname(filename), exist_ok=True)
102
+
103
+ # Create a unique ID for the user
104
+ user_id = f"user_{int(time.time())}"
105
+
106
+ profile_entry = {
107
+ "id": user_id,
108
+ "name": user_data.get("name", ""),
109
+ "role": user_data.get("role", ""),
110
+ "skills": user_data.get("skills", "").split(", ") if user_data.get("skills") else [],
111
+ "experience": user_data.get("experience", ""),
112
+ "location": user_data.get("location", ""),
113
+ "timestamp": time.time()
114
+ }
115
+
116
+ try:
117
+ with open(filename, "a", encoding="utf-8") as f:
118
+ f.write(json.dumps(profile_entry) + "\n")
119
+ return True
120
+ except Exception as e:
121
+ print(f"Error saving profile: {e}")
122
+ return False
123
+
124
+ # Global state for the chatbot
125
+ chatbot_state = ChatbotState()
126
+ model = None
127
+
128
+ def initialize_model(model_name="gemini-1.5-flash"):
129
+ """Initialize the Gemini model."""
130
+ global model
131
+ model = genai.GenerativeModel(model_name)
132
+ return model
133
+
134
+ def process_message(message, history, model_name):
135
+ """Process user message and return bot response."""
136
+ global chatbot_state, model
137
+
138
+ if model is None:
139
+ initialize_model(model_name)
140
+
141
+ try:
142
+ # Handle onboarding flow
143
+ if chatbot_state.is_onboarding():
144
+ # Handle onboarding responses
145
+ if chatbot_state.onboarding_step < 5: # Still collecting basic info
146
+ # Save the user's response
147
+ chatbot_state.save_onboarding_data(None, message)
148
+ chatbot_state.onboarding_step += 1
149
+
150
+ # Check if we need to ask another question
151
+ if chatbot_state.onboarding_step < 5:
152
+ next_question = chatbot_state.get_next_onboarding_question()
153
+ return f"Great! {next_question}"
154
+ else:
155
+ # All info collected, show summary
156
+ summary = chatbot_state.get_onboarding_summary()
157
+ return summary
158
+
159
+ elif chatbot_state.onboarding_step == 5: # Waiting for confirmation
160
+ if message.lower() in ["yes", "y", "correct", "looks good"]:
161
+ # Save profile
162
+ if save_user_profile(chatbot_state.user_data):
163
+ response = "Perfect! Your profile has been saved successfully. You're now onboarded and ready to search for jobs! 🎉"
164
+ else:
165
+ response = "Your profile information has been recorded. You're now onboarded! 🎉"
166
+
167
+ chatbot_state.onboarding_complete = True
168
+ chatbot_state.mode = None
169
+ return response
170
+
171
+ elif message.lower() in ["no", "n", "incorrect", "wrong"]:
172
+ chatbot_state.reset()
173
+ chatbot_state.mode = "onboarding"
174
+ chatbot_state.onboarding_step = 0
175
+ return "No problem! Let's start over. What's your full name?"
176
+
177
+ else:
178
+ return "Please answer 'yes' if the information is correct, or 'no' if you'd like to start over."
179
+
180
+ # Regular intent detection for non-onboarding messages
181
+ intent = detect_intent(message, model=model_name)
182
+ print(f"[Debug] Detected intent: {intent}")
183
+
184
+ # Handle different intents
185
+ if intent == "ONBOARD":
186
+ # Start onboarding flow
187
+ chatbot_state.reset()
188
+ chatbot_state.mode = "onboarding"
189
+
190
+ # Check if user already provided some info in their first message
191
+ first_question = chatbot_state.get_next_onboarding_question()
192
+ return f"I'd be happy to help you create your professional profile! Let's start with some basic information.\n\n{first_question}"
193
+
194
+ elif intent == "SEARCH":
195
+ # Handle job search
196
+ context_block = None
197
+ job_results = retrieve_jobs(message, top_k=3)
198
+
199
+ if job_results:
200
+ lines = []
201
+ for idx, job in enumerate(job_results, start=1):
202
+ lines.append(f"{idx}) {job['text']}")
203
+ block_text = "\n".join(lines)
204
+ context_block = f"Relevant job listings:\n{block_text}"
205
+ print(f"[Debug] Found {len(job_results)} relevant jobs")
206
+ else:
207
+ print("[Debug] No relevant jobs found")
208
+
209
+ # Prepare prompt for job search
210
+ search_prompt = f"""
211
+ {SYSTEM_PROMPT}
212
+
213
+ {"Context Information: " + context_block if context_block else "No relevant jobs found in the database."}
214
+
215
+ User is looking for jobs: {message}
216
+
217
+ Please provide a helpful response about job opportunities. If jobs were found, present them clearly. If no jobs were found, provide helpful guidance on how they might refine their search.
218
+ """
219
+
220
+ response = model.generate_content(search_prompt)
221
+ return response.text
222
+
223
+ elif intent == "POST":
224
+ # Handle job posting creation
225
+ post_prompt = f"""
226
+ {SYSTEM_PROMPT}
227
+
228
+ User wants to create a job posting: {message}
229
+
230
+ Help them create a structured job posting. Ask for any missing information needed to create a complete job post (title, description, required skills, budget, timeline, etc.).
231
+ """
232
+
233
+ response = model.generate_content(post_prompt)
234
+ return response.text
235
+
236
+ else:
237
+ # General conversation
238
+ general_prompt = f"""
239
+ {SYSTEM_PROMPT}
240
+
241
+ User message: {message}
242
+
243
+ Respond helpfully. If they seem to want to get started with onboarding, job searching, or job posting, guide them appropriately.
244
+ """
245
+
246
+ response = model.generate_content(general_prompt)
247
+ return response.text
248
+
249
+ except Exception as e:
250
+ return f"Sorry, I encountered an error: {str(e)}. Please try again!"
251
+
252
+ def reset_conversation():
253
+ """Reset the conversation state."""
254
+ global chatbot_state
255
+ chatbot_state.reset()
256
+ return [], ""
257
+
258
+ def get_welcome_message():
259
+ """Get the initial welcome message."""
260
+ return """👋 **Welcome to AI Job Assistant!**
261
+
262
+ I can help you with:
263
+ 🎯 **Creating your professional profile** - Let me guide you through a quick onboarding
264
+ • 🔍 **Finding job opportunities** - Search for jobs that match your skills
265
+ 📝 **Creating job postings** - Help employers post structured job listings
266
+
267
+ What would you like to do today?"""
268
+
269
+ # Custom CSS for a modern look
270
+ custom_css = """
271
+ .gradio-container {
272
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
273
+ }
274
+
275
+ .chat-message {
276
+ padding: 1rem;
277
+ margin: 0.5rem 0;
278
+ border-radius: 1rem;
279
+ max-width: 80%;
280
+ }
281
+
282
+ .user-message {
283
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
284
+ color: white;
285
+ margin-left: auto;
286
+ }
287
+
288
+ .bot-message {
289
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
290
+ color: white;
291
+ }
292
+
293
+ /* Dark mode support */
294
+ .dark .chat-message {
295
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
296
+ }
297
+
298
+ .gradio-chatbot {
299
+ height: 500px;
300
+ }
301
+
302
+ /* Custom button styles */
303
+ .primary-button {
304
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
305
+ border: none;
306
+ color: white;
307
+ padding: 0.5rem 1rem;
308
+ border-radius: 0.5rem;
309
+ font-weight: 500;
310
+ transition: all 0.3s ease;
311
+ }
312
+
313
+ .primary-button:hover {
314
+ transform: translateY(-2px);
315
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
316
+ }
317
+ """
318
+
319
+ def create_interface():
320
+ """Create the Gradio interface."""
321
+
322
+ with gr.Blocks(
323
+ css=custom_css,
324
+ theme=gr.themes.Soft(
325
+ primary_hue="blue",
326
+ secondary_hue="purple",
327
+ neutral_hue="slate",
328
+ ),
329
+ title="AI Job Assistant"
330
+ ) as interface:
331
+
332
+ gr.HTML("""
333
+ <div style="text-align: center; padding: 2rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 1rem; margin-bottom: 2rem;">
334
+ <h1 style="margin: 0; font-size: 2.5rem; font-weight: bold;">🤖 AI Job Assistant</h1>
335
+ <p style="margin: 0.5rem 0 0 0; font-size: 1.2rem; opacity: 0.9;">Your intelligent career companion</p>
336
+ </div>
337
+ """)
338
+
339
+ with gr.Row():
340
+ with gr.Column(scale=4):
341
+ chatbot = gr.Chatbot(
342
+ value=[[None, get_welcome_message()]],
343
+ height=500,
344
+ bubble_full_width=False,
345
+ show_label=False,
346
+ container=True,
347
+ avatar_images=("👤", "🤖")
348
+ )
349
+
350
+ with gr.Row():
351
+ msg = gr.Textbox(
352
+ placeholder="Type your message here...",
353
+ show_label=False,
354
+ scale=4,
355
+ container=False
356
+ )
357
+ send_btn = gr.Button(
358
+ "Send",
359
+ variant="primary",
360
+ scale=1,
361
+ elem_classes=["primary-button"]
362
+ )
363
+
364
+ with gr.Column(scale=1, min_width=250):
365
+ gr.HTML("""
366
+ <div style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; padding: 1.5rem; border-radius: 1rem; margin-bottom: 1rem;">
367
+ <h3 style="margin: 0 0 1rem 0;">🚀 Quick Actions</h3>
368
+ <div style="font-size: 0.9rem; line-height: 1.6;">
369
+ <div style="margin-bottom: 0.8rem;">
370
+ <strong>💼 Get Started:</strong><br>
371
+ "I want to create my profile"
372
+ </div>
373
+ <div style="margin-bottom: 0.8rem;">
374
+ <strong>🔍 Find Jobs:</strong><br>
375
+ "Show me Python developer jobs"
376
+ </div>
377
+ <div>
378
+ <strong>📝 Post Job:</strong><br>
379
+ "I want to post a job opening"
380
+ </div>
381
+ </div>
382
+ </div>
383
+ """)
384
+
385
+ model_selector = gr.Dropdown(
386
+ choices=["gemini-1.5-flash", "gemini-1.5-pro"],
387
+ value="gemini-1.5-flash",
388
+ label="🧠 AI Model",
389
+ info="Choose your preferred AI model"
390
+ )
391
+
392
+ clear_btn = gr.Button(
393
+ "🗑️ Clear Chat",
394
+ variant="secondary",
395
+ size="sm"
396
+ )
397
+
398
+ gr.HTML("""
399
+ <div style="background: rgba(102, 126, 234, 0.1); padding: 1rem; border-radius: 0.5rem; margin-top: 1rem;">
400
+ <h4 style="margin: 0 0 0.5rem 0; color: #667eea;">ℹ️ Tips</h4>
401
+ <ul style="margin: 0; padding-left: 1.2rem; font-size: 0.85rem; color: #666;">
402
+ <li>Be specific about your skills and preferences</li>
403
+ <li>Use natural language - I understand context!</li>
404
+ <li>Ask follow-up questions anytime</li>
405
+ </ul>
406
+ </div>
407
+ """)
408
+
409
+ # Event handlers
410
+ def respond(message, chat_history, model_name):
411
+ if not message.strip():
412
+ return chat_history, ""
413
+
414
+ bot_message = process_message(message, chat_history, model_name)
415
+ chat_history.append([message, bot_message])
416
+ return chat_history, ""
417
+
418
+ def clear_chat():
419
+ reset_conversation()
420
+ return [[None, get_welcome_message()]], ""
421
+
422
+ # Wire up the events
423
+ msg.submit(respond, [msg, chatbot, model_selector], [chatbot, msg])
424
+ send_btn.click(respond, [msg, chatbot, model_selector], [chatbot, msg])
425
+ clear_btn.click(clear_chat, None, [chatbot, msg])
426
+
427
+ # Auto-focus on textbox
428
+ interface.load(lambda: gr.update(focus=True), None, msg)
429
+
430
+ return interface
431
+
432
+ def main():
433
+ """Main function to launch the Gradio interface."""
434
+
435
+ # Initialize the model
436
+ chosen_model = sys.argv[1] if len(sys.argv) > 1 else "gemini-1.5-flash"
437
+ initialize_model(chosen_model)
438
+
439
+ # Create and launch the interface
440
+ interface = create_interface()
441
+
442
+ print("🚀 Launching AI Job Assistant...")
443
+ print("🌐 Opening in your default browser...")
444
+
445
+ interface.launch(
446
+ server_name="0.0.0.0", # Allow external access
447
+ server_port=7860, # Default Gradio port
448
+ share=False, # Set to True to create public link
449
+ show_error=True,
450
+ show_tips=True,
451
+ enable_queue=True,
452
+ max_threads=10
453
+ )
454
+
455
+ if __name__ == "__main__":
456
  main()