Update app.py
Browse files
app.py
CHANGED
@@ -15,9 +15,14 @@ import logging
|
|
15 |
from dotenv import load_dotenv
|
16 |
import pytz
|
17 |
import uuid
|
18 |
-
import
|
19 |
-
|
20 |
-
from
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
# Load environment variables
|
23 |
load_dotenv()
|
@@ -28,38 +33,33 @@ logging.basicConfig(level=logging.INFO,
|
|
28 |
logger = logging.getLogger(__name__)
|
29 |
|
30 |
# Configure API keys
|
31 |
-
|
32 |
SERPER_API_KEY = os.getenv("SERPER_API_KEY", "your-serper-api-key")
|
33 |
|
34 |
-
# Configure Google
|
35 |
-
genai.configure(api_key=
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
# Constants for global app
|
38 |
EMOTIONS = ["Unmotivated", "Anxious", "Confused", "Excited", "Overwhelmed", "Discouraged"]
|
39 |
GOAL_TYPES = ["Get a job at a big company", "Find an internship", "Change careers", "Improve skills", "Network better"]
|
40 |
USER_DB_PATH = "user_database.json"
|
|
|
|
|
41 |
|
42 |
-
#
|
43 |
-
|
44 |
-
|
45 |
-
generation_config={
|
46 |
-
"temperature": 0.7,
|
47 |
-
"top_p": 0.95,
|
48 |
-
"top_k": 40,
|
49 |
-
"max_output_tokens": 2048,
|
50 |
-
},
|
51 |
-
safety_settings={
|
52 |
-
HarmCategory.HARM_CATEGORY_HATE_SPEECH: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
53 |
-
HarmCategory.HARM_CATEGORY_HARASSMENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
54 |
-
HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
55 |
-
HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_ONLY_HIGH,
|
56 |
-
}
|
57 |
-
)
|
58 |
|
59 |
-
# Function declarations for
|
60 |
-
|
61 |
-
name="
|
62 |
-
description="
|
63 |
parameters={
|
64 |
"type": "OBJECT",
|
65 |
"properties": {
|
@@ -80,7 +80,7 @@ search_jobs = FunctionDeclaration(
|
|
80 |
},
|
81 |
)
|
82 |
|
83 |
-
generate_document = FunctionDeclaration(
|
84 |
name="generate_document_template",
|
85 |
description="Generate a document template for job applications",
|
86 |
parameters={
|
@@ -103,7 +103,7 @@ generate_document = FunctionDeclaration(
|
|
103 |
},
|
104 |
)
|
105 |
|
106 |
-
create_routine = FunctionDeclaration(
|
107 |
name="create_personalized_routine",
|
108 |
description="Create a personalized career development routine",
|
109 |
parameters={
|
@@ -130,11 +130,54 @@ create_routine = FunctionDeclaration(
|
|
130 |
},
|
131 |
)
|
132 |
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
138 |
|
139 |
# User database functions
|
140 |
def load_user_database():
|
@@ -168,6 +211,10 @@ def get_user_profile(user_id):
|
|
168 |
"upcoming_events": [],
|
169 |
"routine_history": [],
|
170 |
"daily_emotions": [],
|
|
|
|
|
|
|
|
|
171 |
"joined_date": datetime.now().strftime("%Y-%m-%d")
|
172 |
}
|
173 |
save_user_database(db)
|
@@ -231,6 +278,68 @@ def add_routine_to_user(user_id, routine):
|
|
231 |
save_user_database(db)
|
232 |
return db['users'][user_id]
|
233 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
234 |
# API Helper Functions
|
235 |
def search_jobs_with_serper(query, location, max_results=5):
|
236 |
"""Search for job opportunities using Serper API"""
|
@@ -288,8 +397,8 @@ def extract_company_from_title(title):
|
|
288 |
return title.split(' - ')[1].strip()
|
289 |
return "Unknown Company"
|
290 |
|
291 |
-
def get_ai_response(user_id, user_input, context=None):
|
292 |
-
"""Get AI response using Google
|
293 |
try:
|
294 |
user_profile = get_user_profile(user_id)
|
295 |
|
@@ -299,42 +408,178 @@ def get_ai_response(user_id, user_input, context=None):
|
|
299 |
1. Recognize and acknowledge the user's emotion
|
300 |
2. Respond with high-empathy message
|
301 |
3. Suggest specific action based on their input
|
302 |
-
4. Offer document support or routine
|
|
|
|
|
|
|
303 |
|
304 |
-
|
|
|
305 |
"""
|
306 |
|
307 |
# Build conversation context
|
308 |
-
|
309 |
-
if context:
|
310 |
-
messages.extend(context)
|
311 |
|
312 |
# Add user profile information as context
|
313 |
profile_context = f"""
|
314 |
User Profile Information:
|
|
|
315 |
- Current emotion: {user_profile.get('current_emotion', '')}
|
316 |
- Career goal: {user_profile.get('career_goal', '')}
|
317 |
- Location: {user_profile.get('location', '')}
|
318 |
"""
|
319 |
|
320 |
-
|
321 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
322 |
|
323 |
-
|
324 |
-
|
325 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
system_instruction=system_instruction,
|
327 |
-
tools=tools
|
|
|
|
|
|
|
|
|
|
|
|
|
328 |
)
|
329 |
|
330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
331 |
except Exception as e:
|
332 |
logger.error(f"Error in get_ai_response: {str(e)}")
|
333 |
return "I apologize, but I'm having trouble processing your request right now. Please try again later."
|
334 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
335 |
def create_personalized_routine_with_ai(user_id, emotion, goal, available_time=60, days=7):
|
336 |
"""Create a personalized routine using AI"""
|
337 |
try:
|
|
|
|
|
338 |
prompt = f"""
|
339 |
Create a personalized {days}-day career development routine for a user who is feeling {emotion} and has a goal to {goal}.
|
340 |
They have about {available_time} minutes per day to dedicate to this routine.
|
@@ -347,10 +592,51 @@ def create_personalized_routine_with_ai(user_id, emotion, goal, available_time=6
|
|
347 |
3. Points value (between 10-50)
|
348 |
4. A brief description of why this task is valuable
|
349 |
|
350 |
-
Format the routine as a JSON object
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
351 |
"""
|
352 |
|
353 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
354 |
routine_text = response.text
|
355 |
|
356 |
# Extract JSON portion from the response
|
@@ -458,12 +744,88 @@ def generate_document_template_with_ai(document_type, career_field="", experienc
|
|
458 |
Format it in markdown.
|
459 |
"""
|
460 |
|
461 |
-
response =
|
|
|
|
|
|
|
|
|
462 |
return response.text
|
463 |
except Exception as e:
|
464 |
logger.error(f"Error in generate_document_template_with_ai: {str(e)}")
|
465 |
return f"Error generating {document_type} template. Please try again later."
|
466 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
467 |
# Chart and visualization functions
|
468 |
def create_emotion_chart(user_id):
|
469 |
"""Create a chart of user's emotions over time"""
|
@@ -594,6 +956,109 @@ def create_routine_completion_gauge(user_id):
|
|
594 |
|
595 |
return fig
|
596 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
597 |
# Gradio interface components
|
598 |
def create_interface():
|
599 |
"""Create the Gradio interface for Aishura MVP"""
|
@@ -630,19 +1095,20 @@ def create_interface():
|
|
630 |
|
631 |
# Chat function
|
632 |
def chat(message, history):
|
633 |
-
#
|
634 |
-
|
635 |
-
history = []
|
636 |
|
637 |
-
#
|
638 |
-
|
|
|
|
|
|
|
639 |
|
640 |
# Get AI response
|
641 |
-
response = get_ai_response(session_user_id, message,
|
642 |
-
|
643 |
-
# Add AI's response to history
|
644 |
-
history.append({"role": "assistant", "parts": [response]})
|
645 |
|
|
|
|
|
646 |
return history, ""
|
647 |
|
648 |
# Function to search for jobs
|
@@ -683,11 +1149,28 @@ def create_interface():
|
|
683 |
result += f"- **{task['name']}** ({task['duration']} mins, {task['points']} points)\n"
|
684 |
result += f" *{task['description']}*\n\n"
|
685 |
|
686 |
-
# Add the routine to user's profile
|
687 |
-
add_routine_to_user(session_user_id, routine)
|
688 |
-
|
689 |
return result
|
690 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
691 |
# Function to mark a task as complete
|
692 |
def complete_task(task_name):
|
693 |
if not task_name:
|
@@ -728,6 +1211,29 @@ def create_interface():
|
|
728 |
emotion_fig
|
729 |
)
|
730 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
731 |
# Create the interface
|
732 |
with gr.Blocks(theme=gr.themes.Soft()) as app:
|
733 |
gr.Markdown("# Aishura - Your AI Career Assistant")
|
@@ -750,10 +1256,49 @@ def create_interface():
|
|
750 |
with gr.Tabs() as tabs:
|
751 |
# Chat tab
|
752 |
with gr.TabItem("Chat with Aishura"):
|
753 |
-
|
754 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
755 |
|
756 |
msg.submit(chat, [msg, chatbot], [chatbot, msg])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
757 |
|
758 |
# Job Search tab
|
759 |
with gr.TabItem("Find Opportunities"):
|
@@ -771,7 +1316,7 @@ def create_interface():
|
|
771 |
with gr.TabItem("Document Templates"):
|
772 |
gr.Markdown("## Generate Document Templates")
|
773 |
doc_type = gr.Dropdown(
|
774 |
-
choices=["Resume", "Cover Letter", "Self-Introduction", "LinkedIn Profile", "Portfolio"],
|
775 |
label="Document Type"
|
776 |
)
|
777 |
career_field = gr.Textbox(label="Career Field/Industry")
|
@@ -843,6 +1388,13 @@ def create_interface():
|
|
843 |
[name_input, location_input, emotion_dropdown, goal_dropdown],
|
844 |
[welcome_output, welcome_group, main_interface]
|
845 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
846 |
|
847 |
return app
|
848 |
|
|
|
15 |
from dotenv import load_dotenv
|
16 |
import pytz
|
17 |
import uuid
|
18 |
+
import re
|
19 |
+
import base64
|
20 |
+
from io import BytesIO
|
21 |
+
from PIL import Image
|
22 |
+
|
23 |
+
# Import the updated Google GenAI SDK
|
24 |
+
from google import genai
|
25 |
+
from google.genai import types
|
26 |
|
27 |
# Load environment variables
|
28 |
load_dotenv()
|
|
|
33 |
logger = logging.getLogger(__name__)
|
34 |
|
35 |
# Configure API keys
|
36 |
+
GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "your-api-key")
|
37 |
SERPER_API_KEY = os.getenv("SERPER_API_KEY", "your-serper-api-key")
|
38 |
|
39 |
+
# Configure Google GenAI
|
40 |
+
genai.configure(api_key=GOOGLE_API_KEY)
|
41 |
+
|
42 |
+
# Init the client
|
43 |
+
client = genai.Client()
|
44 |
+
|
45 |
+
# Model configuration - using Gemini latest model
|
46 |
+
MODEL_ID = "gemini-2.0-flash-001"
|
47 |
|
48 |
# Constants for global app
|
49 |
EMOTIONS = ["Unmotivated", "Anxious", "Confused", "Excited", "Overwhelmed", "Discouraged"]
|
50 |
GOAL_TYPES = ["Get a job at a big company", "Find an internship", "Change careers", "Improve skills", "Network better"]
|
51 |
USER_DB_PATH = "user_database.json"
|
52 |
+
RESUME_FOLDER = "user_resumes"
|
53 |
+
PORTFOLIO_FOLDER = "user_portfolios"
|
54 |
|
55 |
+
# Ensure folders exist
|
56 |
+
os.makedirs(RESUME_FOLDER, exist_ok=True)
|
57 |
+
os.makedirs(PORTFOLIO_FOLDER, exist_ok=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
+
# Function declarations for tools
|
60 |
+
get_job_opportunities = types.FunctionDeclaration(
|
61 |
+
name="get_job_opportunities",
|
62 |
+
description="Get relevant job opportunities based on location and career goals",
|
63 |
parameters={
|
64 |
"type": "OBJECT",
|
65 |
"properties": {
|
|
|
80 |
},
|
81 |
)
|
82 |
|
83 |
+
generate_document = types.FunctionDeclaration(
|
84 |
name="generate_document_template",
|
85 |
description="Generate a document template for job applications",
|
86 |
parameters={
|
|
|
103 |
},
|
104 |
)
|
105 |
|
106 |
+
create_routine = types.FunctionDeclaration(
|
107 |
name="create_personalized_routine",
|
108 |
description="Create a personalized career development routine",
|
109 |
parameters={
|
|
|
130 |
},
|
131 |
)
|
132 |
|
133 |
+
analyze_resume = types.FunctionDeclaration(
|
134 |
+
name="analyze_resume",
|
135 |
+
description="Analyze a user's resume and provide feedback",
|
136 |
+
parameters={
|
137 |
+
"type": "OBJECT",
|
138 |
+
"properties": {
|
139 |
+
"resume_text": {
|
140 |
+
"type": "STRING",
|
141 |
+
"description": "The full text of the user's resume",
|
142 |
+
},
|
143 |
+
"career_goal": {
|
144 |
+
"type": "STRING",
|
145 |
+
"description": "The user's career goal or job interest",
|
146 |
+
},
|
147 |
+
},
|
148 |
+
"required": ["resume_text"],
|
149 |
+
},
|
150 |
+
)
|
151 |
+
|
152 |
+
analyze_portfolio = types.FunctionDeclaration(
|
153 |
+
name="analyze_portfolio",
|
154 |
+
description="Analyze a user's portfolio and provide feedback",
|
155 |
+
parameters={
|
156 |
+
"type": "OBJECT",
|
157 |
+
"properties": {
|
158 |
+
"portfolio_url": {
|
159 |
+
"type": "STRING",
|
160 |
+
"description": "URL to the user's portfolio",
|
161 |
+
},
|
162 |
+
"portfolio_description": {
|
163 |
+
"type": "STRING",
|
164 |
+
"description": "Description of the portfolio content",
|
165 |
+
},
|
166 |
+
"career_goal": {
|
167 |
+
"type": "STRING",
|
168 |
+
"description": "The user's career goal or job interest",
|
169 |
+
},
|
170 |
+
},
|
171 |
+
"required": ["portfolio_description"],
|
172 |
+
},
|
173 |
+
)
|
174 |
+
|
175 |
+
# Combine tools
|
176 |
+
job_tool = types.Tool(function_declarations=[get_job_opportunities])
|
177 |
+
document_tool = types.Tool(function_declarations=[generate_document])
|
178 |
+
routine_tool = types.Tool(function_declarations=[create_routine])
|
179 |
+
resume_tool = types.Tool(function_declarations=[analyze_resume])
|
180 |
+
portfolio_tool = types.Tool(function_declarations=[analyze_portfolio])
|
181 |
|
182 |
# User database functions
|
183 |
def load_user_database():
|
|
|
211 |
"upcoming_events": [],
|
212 |
"routine_history": [],
|
213 |
"daily_emotions": [],
|
214 |
+
"resume_path": "",
|
215 |
+
"portfolio_path": "",
|
216 |
+
"recommendations": [],
|
217 |
+
"chat_history": [],
|
218 |
"joined_date": datetime.now().strftime("%Y-%m-%d")
|
219 |
}
|
220 |
save_user_database(db)
|
|
|
278 |
save_user_database(db)
|
279 |
return db['users'][user_id]
|
280 |
|
281 |
+
def save_user_resume(user_id, resume_text):
|
282 |
+
"""Save user's resume to file and update profile"""
|
283 |
+
# Create filename
|
284 |
+
filename = f"{user_id}_resume.txt"
|
285 |
+
filepath = os.path.join(RESUME_FOLDER, filename)
|
286 |
+
|
287 |
+
# Save resume text to file
|
288 |
+
with open(filepath, 'w') as file:
|
289 |
+
file.write(resume_text)
|
290 |
+
|
291 |
+
# Update user profile
|
292 |
+
update_user_profile(user_id, {"resume_path": filepath})
|
293 |
+
|
294 |
+
return filepath
|
295 |
+
|
296 |
+
def save_user_portfolio(user_id, portfolio_content):
|
297 |
+
"""Save user's portfolio info to file and update profile"""
|
298 |
+
# Create filename
|
299 |
+
filename = f"{user_id}_portfolio.json"
|
300 |
+
filepath = os.path.join(PORTFOLIO_FOLDER, filename)
|
301 |
+
|
302 |
+
# Save portfolio content to file
|
303 |
+
with open(filepath, 'w') as file:
|
304 |
+
json.dump(portfolio_content, file, indent=4)
|
305 |
+
|
306 |
+
# Update user profile
|
307 |
+
update_user_profile(user_id, {"portfolio_path": filepath})
|
308 |
+
|
309 |
+
return filepath
|
310 |
+
|
311 |
+
def add_recommendation_to_user(user_id, recommendation):
|
312 |
+
"""Add a new recommendation to user's recommendations list"""
|
313 |
+
db = load_user_database()
|
314 |
+
if user_id in db['users']:
|
315 |
+
if 'recommendations' not in db['users'][user_id]:
|
316 |
+
db['users'][user_id]['recommendations'] = []
|
317 |
+
|
318 |
+
recommendation_with_date = {
|
319 |
+
"recommendation": recommendation,
|
320 |
+
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
321 |
+
"status": "pending" # pending, completed, dismissed
|
322 |
+
}
|
323 |
+
db['users'][user_id]['recommendations'].append(recommendation_with_date)
|
324 |
+
save_user_database(db)
|
325 |
+
return db['users'][user_id]
|
326 |
+
|
327 |
+
def add_chat_message(user_id, role, message):
|
328 |
+
"""Add a message to the user's chat history"""
|
329 |
+
db = load_user_database()
|
330 |
+
if user_id in db['users']:
|
331 |
+
if 'chat_history' not in db['users'][user_id]:
|
332 |
+
db['users'][user_id]['chat_history'] = []
|
333 |
+
|
334 |
+
chat_message = {
|
335 |
+
"role": role, # user or assistant
|
336 |
+
"message": message,
|
337 |
+
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
338 |
+
}
|
339 |
+
db['users'][user_id]['chat_history'].append(chat_message)
|
340 |
+
save_user_database(db)
|
341 |
+
return db['users'][user_id]
|
342 |
+
|
343 |
# API Helper Functions
|
344 |
def search_jobs_with_serper(query, location, max_results=5):
|
345 |
"""Search for job opportunities using Serper API"""
|
|
|
397 |
return title.split(' - ')[1].strip()
|
398 |
return "Unknown Company"
|
399 |
|
400 |
+
def get_ai_response(user_id, user_input, context=None, generate_recommendations=True):
|
401 |
+
"""Get AI response using Google GenAI"""
|
402 |
try:
|
403 |
user_profile = get_user_profile(user_id)
|
404 |
|
|
|
408 |
1. Recognize and acknowledge the user's emotion
|
409 |
2. Respond with high-empathy message
|
410 |
3. Suggest specific action based on their input
|
411 |
+
4. Offer document support, job opportunities, or personalized routine
|
412 |
+
|
413 |
+
Remember to be proactive and preemptive - suggest actions before the user asks. Your goal is to provide
|
414 |
+
end-to-end support for the user's career journey, from emotional support to concrete action.
|
415 |
|
416 |
+
If the user has shared a resume or portfolio, refer to insights from those documents to provide
|
417 |
+
personalized guidance.
|
418 |
"""
|
419 |
|
420 |
# Build conversation context
|
421 |
+
contents = []
|
|
|
|
|
422 |
|
423 |
# Add user profile information as context
|
424 |
profile_context = f"""
|
425 |
User Profile Information:
|
426 |
+
- Name: {user_profile.get('name', '')}
|
427 |
- Current emotion: {user_profile.get('current_emotion', '')}
|
428 |
- Career goal: {user_profile.get('career_goal', '')}
|
429 |
- Location: {user_profile.get('location', '')}
|
430 |
"""
|
431 |
|
432 |
+
# Add resume context if available
|
433 |
+
if user_profile.get('resume_path') and os.path.exists(user_profile.get('resume_path')):
|
434 |
+
try:
|
435 |
+
with open(user_profile.get('resume_path'), 'r') as file:
|
436 |
+
resume_text = file.read()
|
437 |
+
profile_context += f"\nUser Resume Summary: The user has shared their resume. They have experience in {resume_text[:100]}..."
|
438 |
+
except Exception as e:
|
439 |
+
logger.error(f"Error reading resume: {str(e)}")
|
440 |
+
|
441 |
+
# Add portfolio context if available
|
442 |
+
if user_profile.get('portfolio_path') and os.path.exists(user_profile.get('portfolio_path')):
|
443 |
+
try:
|
444 |
+
with open(user_profile.get('portfolio_path'), 'r') as file:
|
445 |
+
portfolio_data = json.load(file)
|
446 |
+
profile_context += f"\nUser Portfolio: The user has shared their portfolio with URL: {portfolio_data.get('url', 'Not provided')}."
|
447 |
+
except Exception as e:
|
448 |
+
logger.error(f"Error reading portfolio: {str(e)}")
|
449 |
+
|
450 |
+
# Start with context
|
451 |
+
user_context = types.Content(
|
452 |
+
role="user",
|
453 |
+
parts=[types.Part.from_text(profile_context)]
|
454 |
+
)
|
455 |
+
contents.append(user_context)
|
456 |
|
457 |
+
# Add previous context if provided
|
458 |
+
if context:
|
459 |
+
for msg in context:
|
460 |
+
if msg["role"] == "user":
|
461 |
+
contents.append(types.Content(
|
462 |
+
role="user",
|
463 |
+
parts=[types.Part.from_text(msg["message"])]
|
464 |
+
))
|
465 |
+
else:
|
466 |
+
contents.append(types.Content(
|
467 |
+
role="model",
|
468 |
+
parts=[types.Part.from_text(msg["message"])]
|
469 |
+
))
|
470 |
+
|
471 |
+
# Add current user input
|
472 |
+
contents.append(types.Content(
|
473 |
+
role="user",
|
474 |
+
parts=[types.Part.from_text(user_input)]
|
475 |
+
))
|
476 |
+
|
477 |
+
# Configure tools
|
478 |
+
tools = [job_tool, document_tool, routine_tool, resume_tool, portfolio_tool]
|
479 |
+
|
480 |
+
# Get response
|
481 |
+
response = client.models.generate_content(
|
482 |
+
model=MODEL_ID,
|
483 |
+
contents=contents,
|
484 |
system_instruction=system_instruction,
|
485 |
+
tools=tools,
|
486 |
+
generation_config=types.GenerationConfig(
|
487 |
+
temperature=0.7,
|
488 |
+
max_output_tokens=2048,
|
489 |
+
top_p=0.95,
|
490 |
+
top_k=40
|
491 |
+
)
|
492 |
)
|
493 |
|
494 |
+
ai_response_text = response.text
|
495 |
+
|
496 |
+
# Log the message in chat history
|
497 |
+
add_chat_message(user_id, "user", user_input)
|
498 |
+
add_chat_message(user_id, "assistant", ai_response_text)
|
499 |
+
|
500 |
+
# Generate recommendations if enabled
|
501 |
+
if generate_recommendations:
|
502 |
+
gen_recommendations(user_id, user_input, ai_response_text)
|
503 |
+
|
504 |
+
return ai_response_text
|
505 |
except Exception as e:
|
506 |
logger.error(f"Error in get_ai_response: {str(e)}")
|
507 |
return "I apologize, but I'm having trouble processing your request right now. Please try again later."
|
508 |
|
509 |
+
def gen_recommendations(user_id, user_input, ai_response):
|
510 |
+
"""Generate recommendations based on conversation"""
|
511 |
+
try:
|
512 |
+
user_profile = get_user_profile(user_id)
|
513 |
+
|
514 |
+
prompt = f"""
|
515 |
+
Based on the following conversation between a user and Aishura (an AI career assistant),
|
516 |
+
generate 1-3 specific, actionable recommendations for the user's next steps in their career journey.
|
517 |
+
|
518 |
+
User Profile:
|
519 |
+
- Current emotion: {user_profile.get('current_emotion', '')}
|
520 |
+
- Career goal: {user_profile.get('career_goal', '')}
|
521 |
+
- Location: {user_profile.get('location', '')}
|
522 |
+
|
523 |
+
Recent Conversation:
|
524 |
+
User: {user_input}
|
525 |
+
|
526 |
+
Aishura: {ai_response}
|
527 |
+
|
528 |
+
Generate specific, actionable recommendations in JSON format:
|
529 |
+
```json
|
530 |
+
[
|
531 |
+
{{
|
532 |
+
"title": "Brief recommendation title",
|
533 |
+
"description": "Detailed recommendation description",
|
534 |
+
"action_type": "job_search|skill_building|networking|resume|portfolio|interview_prep|other",
|
535 |
+
"priority": "high|medium|low"
|
536 |
+
}}
|
537 |
+
]
|
538 |
+
```
|
539 |
+
|
540 |
+
Focus on immediate, practical next steps that align with the user's goals and emotional state.
|
541 |
+
"""
|
542 |
+
|
543 |
+
response = client.models.generate_content(
|
544 |
+
model=MODEL_ID,
|
545 |
+
contents=prompt
|
546 |
+
)
|
547 |
+
|
548 |
+
recommendation_text = response.text
|
549 |
+
|
550 |
+
# Extract JSON from response
|
551 |
+
try:
|
552 |
+
# Find JSON content between ```json and ``` if present
|
553 |
+
if "```json" in recommendation_text and "```" in recommendation_text.split("```json")[1]:
|
554 |
+
json_str = recommendation_text.split("```json")[1].split("```")[0].strip()
|
555 |
+
else:
|
556 |
+
# Otherwise try to find anything that looks like JSON array
|
557 |
+
import re
|
558 |
+
json_match = re.search(r'(\[.*\])', recommendation_text, re.DOTALL)
|
559 |
+
if json_match:
|
560 |
+
json_str = json_match.group(1)
|
561 |
+
else:
|
562 |
+
json_str = recommendation_text
|
563 |
+
|
564 |
+
recommendations = json.loads(json_str)
|
565 |
+
|
566 |
+
# Add recommendations to user profile
|
567 |
+
for rec in recommendations:
|
568 |
+
add_recommendation_to_user(user_id, rec)
|
569 |
+
|
570 |
+
return recommendations
|
571 |
+
except json.JSONDecodeError:
|
572 |
+
logger.error(f"Failed to parse JSON from AI response: {recommendation_text}")
|
573 |
+
return []
|
574 |
+
except Exception as e:
|
575 |
+
logger.error(f"Error in gen_recommendations: {str(e)}")
|
576 |
+
return []
|
577 |
+
|
578 |
def create_personalized_routine_with_ai(user_id, emotion, goal, available_time=60, days=7):
|
579 |
"""Create a personalized routine using AI"""
|
580 |
try:
|
581 |
+
user_profile = get_user_profile(user_id)
|
582 |
+
|
583 |
prompt = f"""
|
584 |
Create a personalized {days}-day career development routine for a user who is feeling {emotion} and has a goal to {goal}.
|
585 |
They have about {available_time} minutes per day to dedicate to this routine.
|
|
|
592 |
3. Points value (between 10-50)
|
593 |
4. A brief description of why this task is valuable
|
594 |
|
595 |
+
Format the routine as a JSON object with this structure:
|
596 |
+
```json
|
597 |
+
{{
|
598 |
+
"name": "Routine name",
|
599 |
+
"description": "Brief description of the routine",
|
600 |
+
"days": {days},
|
601 |
+
"daily_tasks": [
|
602 |
+
{{
|
603 |
+
"day": 1,
|
604 |
+
"tasks": [
|
605 |
+
{{
|
606 |
+
"name": "Task name",
|
607 |
+
"points": 20,
|
608 |
+
"duration": 30,
|
609 |
+
"description": "Why this task is valuable"
|
610 |
+
}}
|
611 |
+
]
|
612 |
+
}}
|
613 |
+
]
|
614 |
+
}}
|
615 |
+
```
|
616 |
"""
|
617 |
|
618 |
+
# Use resume and portfolio info if available
|
619 |
+
if user_profile.get('resume_path') and os.path.exists(user_profile.get('resume_path')):
|
620 |
+
try:
|
621 |
+
with open(user_profile.get('resume_path'), 'r') as file:
|
622 |
+
resume_text = file.read()
|
623 |
+
prompt += f"\n\nTailor the routine based on the user's resume. Here's a summary: {resume_text[:500]}..."
|
624 |
+
except Exception as e:
|
625 |
+
logger.error(f"Error reading resume: {str(e)}")
|
626 |
+
|
627 |
+
if user_profile.get('portfolio_path') and os.path.exists(user_profile.get('portfolio_path')):
|
628 |
+
try:
|
629 |
+
with open(user_profile.get('portfolio_path'), 'r') as file:
|
630 |
+
portfolio_data = json.load(file)
|
631 |
+
prompt += f"\n\nConsider the user's portfolio when creating the routine. Portfolio URL: {portfolio_data.get('url', 'Not provided')}"
|
632 |
+
except Exception as e:
|
633 |
+
logger.error(f"Error reading portfolio: {str(e)}")
|
634 |
+
|
635 |
+
response = client.models.generate_content(
|
636 |
+
model=MODEL_ID,
|
637 |
+
contents=prompt
|
638 |
+
)
|
639 |
+
|
640 |
routine_text = response.text
|
641 |
|
642 |
# Extract JSON portion from the response
|
|
|
744 |
Format it in markdown.
|
745 |
"""
|
746 |
|
747 |
+
response = client.models.generate_content(
|
748 |
+
model=MODEL_ID,
|
749 |
+
contents=prompt
|
750 |
+
)
|
751 |
+
|
752 |
return response.text
|
753 |
except Exception as e:
|
754 |
logger.error(f"Error in generate_document_template_with_ai: {str(e)}")
|
755 |
return f"Error generating {document_type} template. Please try again later."
|
756 |
|
757 |
+
def analyze_resume_with_ai(user_id, resume_text):
|
758 |
+
"""Analyze resume with AI and provide feedback"""
|
759 |
+
try:
|
760 |
+
user_profile = get_user_profile(user_id)
|
761 |
+
|
762 |
+
prompt = f"""
|
763 |
+
Analyze the following resume for a user who has the career goal of: {user_profile.get('career_goal', 'improving their career')}
|
764 |
+
|
765 |
+
Resume Text:
|
766 |
+
{resume_text}
|
767 |
+
|
768 |
+
Provide detailed feedback on:
|
769 |
+
1. Overall strengths and weaknesses
|
770 |
+
2. Format and organization
|
771 |
+
3. Content effectiveness for their career goal
|
772 |
+
4. Specific improvement suggestions
|
773 |
+
5. Keywords and skills that should be highlighted
|
774 |
+
|
775 |
+
Format your analysis with markdown headings and bullet points.
|
776 |
+
"""
|
777 |
+
|
778 |
+
response = client.models.generate_content(
|
779 |
+
model=MODEL_ID,
|
780 |
+
contents=prompt
|
781 |
+
)
|
782 |
+
|
783 |
+
# Save resume
|
784 |
+
save_user_resume(user_id, resume_text)
|
785 |
+
|
786 |
+
return response.text
|
787 |
+
except Exception as e:
|
788 |
+
logger.error(f"Error in analyze_resume_with_ai: {str(e)}")
|
789 |
+
return "I apologize, but I'm having trouble analyzing your resume right now. Please try again later."
|
790 |
+
|
791 |
+
def analyze_portfolio_with_ai(user_id, portfolio_url, portfolio_description):
|
792 |
+
"""Analyze portfolio with AI and provide feedback"""
|
793 |
+
try:
|
794 |
+
user_profile = get_user_profile(user_id)
|
795 |
+
|
796 |
+
prompt = f"""
|
797 |
+
Analyze the following portfolio for a user who has the career goal of: {user_profile.get('career_goal', 'improving their career')}
|
798 |
+
|
799 |
+
Portfolio URL: {portfolio_url}
|
800 |
+
Portfolio Description: {portfolio_description}
|
801 |
+
|
802 |
+
Based on the description provided, analyze:
|
803 |
+
1. How well the portfolio aligns with their career goal
|
804 |
+
2. Strengths of the portfolio
|
805 |
+
3. Areas for improvement
|
806 |
+
4. Specific suggestions to enhance the portfolio
|
807 |
+
5. How to better showcase skills relevant to their goal
|
808 |
+
|
809 |
+
Format your analysis with markdown headings and bullet points.
|
810 |
+
"""
|
811 |
+
|
812 |
+
response = client.models.generate_content(
|
813 |
+
model=MODEL_ID,
|
814 |
+
contents=prompt
|
815 |
+
)
|
816 |
+
|
817 |
+
# Save portfolio info
|
818 |
+
portfolio_content = {
|
819 |
+
"url": portfolio_url,
|
820 |
+
"description": portfolio_description
|
821 |
+
}
|
822 |
+
save_user_portfolio(user_id, portfolio_content)
|
823 |
+
|
824 |
+
return response.text
|
825 |
+
except Exception as e:
|
826 |
+
logger.error(f"Error in analyze_portfolio_with_ai: {str(e)}")
|
827 |
+
return "I apologize, but I'm having trouble analyzing your portfolio right now. Please try again later."
|
828 |
+
|
829 |
# Chart and visualization functions
|
830 |
def create_emotion_chart(user_id):
|
831 |
"""Create a chart of user's emotions over time"""
|
|
|
956 |
|
957 |
return fig
|
958 |
|
959 |
+
def create_skill_radar_chart(user_id):
|
960 |
+
"""Create a radar chart of user's skills based on resume analysis"""
|
961 |
+
user_profile = get_user_profile(user_id)
|
962 |
+
|
963 |
+
# If no resume, return empty chart
|
964 |
+
if not user_profile.get('resume_path') or not os.path.exists(user_profile.get('resume_path')):
|
965 |
+
fig = go.Figure()
|
966 |
+
fig.add_annotation(text="No resume data available yet", showarrow=False)
|
967 |
+
return fig
|
968 |
+
|
969 |
+
# Read resume
|
970 |
+
try:
|
971 |
+
with open(user_profile.get('resume_path'), 'r') as file:
|
972 |
+
resume_text = file.read()
|
973 |
+
except Exception as e:
|
974 |
+
logger.error(f"Error reading resume: {str(e)}")
|
975 |
+
fig = go.Figure()
|
976 |
+
fig.add_annotation(text="Error reading resume data", showarrow=False)
|
977 |
+
return fig
|
978 |
+
|
979 |
+
# Use AI to extract and score skills
|
980 |
+
prompt = f"""
|
981 |
+
Based on the following resume, identify 5-8 key skills and rate them on a scale of 1-10.
|
982 |
+
|
983 |
+
Resume:
|
984 |
+
{resume_text[:2000]}...
|
985 |
+
|
986 |
+
Return the results as a JSON object with this structure:
|
987 |
+
```json
|
988 |
+
{{
|
989 |
+
"skills": [
|
990 |
+
{{"name": "Skill Name", "score": 7}},
|
991 |
+
{{"name": "Another Skill", "score": 9}}
|
992 |
+
]
|
993 |
+
}}
|
994 |
+
```
|
995 |
+
"""
|
996 |
+
|
997 |
+
try:
|
998 |
+
response = client.models.generate_content(
|
999 |
+
model=MODEL_ID,
|
1000 |
+
contents=prompt
|
1001 |
+
)
|
1002 |
+
|
1003 |
+
skill_text = response.text
|
1004 |
+
|
1005 |
+
# Extract JSON
|
1006 |
+
if "```json" in skill_text and "```" in skill_text.split("```json")[1]:
|
1007 |
+
json_str = skill_text.split("```json")[1].split("```")[0].strip()
|
1008 |
+
else:
|
1009 |
+
import re
|
1010 |
+
json_match = re.search(r'(\{.*\})', skill_text, re.DOTALL)
|
1011 |
+
if json_match:
|
1012 |
+
json_str = json_match.group(1)
|
1013 |
+
else:
|
1014 |
+
json_str = skill_text
|
1015 |
+
|
1016 |
+
skill_data = json.loads(json_str)
|
1017 |
+
|
1018 |
+
# Create radar chart
|
1019 |
+
if 'skills' in skill_data and skill_data['skills']:
|
1020 |
+
skills = skill_data['skills']
|
1021 |
+
|
1022 |
+
# Prepare data for radar chart
|
1023 |
+
categories = [skill['name'] for skill in skills]
|
1024 |
+
values = [skill['score'] for skill in skills]
|
1025 |
+
|
1026 |
+
# Add the first point at the end to close the loop
|
1027 |
+
categories.append(categories[0])
|
1028 |
+
values.append(values[0])
|
1029 |
+
|
1030 |
+
fig = go.Figure()
|
1031 |
+
|
1032 |
+
fig.add_trace(go.Scatterpolar(
|
1033 |
+
r=values,
|
1034 |
+
theta=categories,
|
1035 |
+
fill='toself',
|
1036 |
+
name='Skills'
|
1037 |
+
))
|
1038 |
+
|
1039 |
+
fig.update_layout(
|
1040 |
+
polar=dict(
|
1041 |
+
radialaxis=dict(
|
1042 |
+
visible=True,
|
1043 |
+
range=[0, 10]
|
1044 |
+
)
|
1045 |
+
),
|
1046 |
+
showlegend=False,
|
1047 |
+
title="Skill Assessment Based on Resume"
|
1048 |
+
)
|
1049 |
+
|
1050 |
+
return fig
|
1051 |
+
else:
|
1052 |
+
fig = go.Figure()
|
1053 |
+
fig.add_annotation(text="Could not extract skills from resume", showarrow=False)
|
1054 |
+
return fig
|
1055 |
+
|
1056 |
+
except Exception as e:
|
1057 |
+
logger.error(f"Error creating skill radar chart: {str(e)}")
|
1058 |
+
fig = go.Figure()
|
1059 |
+
fig.add_annotation(text="Error analyzing skills", showarrow=False)
|
1060 |
+
return fig
|
1061 |
+
|
1062 |
# Gradio interface components
|
1063 |
def create_interface():
|
1064 |
"""Create the Gradio interface for Aishura MVP"""
|
|
|
1095 |
|
1096 |
# Chat function
|
1097 |
def chat(message, history):
|
1098 |
+
# Get user profile
|
1099 |
+
user_profile = get_user_profile(session_user_id)
|
|
|
1100 |
|
1101 |
+
# Convert history to the format expected by get_ai_response
|
1102 |
+
context = []
|
1103 |
+
for h in history:
|
1104 |
+
context.append({"role": "user", "message": h[0]})
|
1105 |
+
context.append({"role": "assistant", "message": h[1]})
|
1106 |
|
1107 |
# Get AI response
|
1108 |
+
response = get_ai_response(session_user_id, message, context)
|
|
|
|
|
|
|
1109 |
|
1110 |
+
# Return updated history and empty message
|
1111 |
+
history.append((message, response))
|
1112 |
return history, ""
|
1113 |
|
1114 |
# Function to search for jobs
|
|
|
1149 |
result += f"- **{task['name']}** ({task['duration']} mins, {task['points']} points)\n"
|
1150 |
result += f" *{task['description']}*\n\n"
|
1151 |
|
|
|
|
|
|
|
1152 |
return result
|
1153 |
|
1154 |
+
# Function to analyze resume
|
1155 |
+
def analyze_resume_interface(resume_text):
|
1156 |
+
if not resume_text:
|
1157 |
+
return "Please enter your resume text."
|
1158 |
+
|
1159 |
+
analysis = analyze_resume_with_ai(session_user_id, resume_text)
|
1160 |
+
|
1161 |
+
# Update skill chart
|
1162 |
+
skill_fig = create_skill_radar_chart(session_user_id)
|
1163 |
+
|
1164 |
+
return analysis, skill_fig
|
1165 |
+
|
1166 |
+
# Function to analyze portfolio
|
1167 |
+
def analyze_portfolio_interface(portfolio_url, portfolio_description):
|
1168 |
+
if not portfolio_description:
|
1169 |
+
return "Please enter a description of your portfolio."
|
1170 |
+
|
1171 |
+
analysis = analyze_portfolio_with_ai(session_user_id, portfolio_url, portfolio_description)
|
1172 |
+
return analysis
|
1173 |
+
|
1174 |
# Function to mark a task as complete
|
1175 |
def complete_task(task_name):
|
1176 |
if not task_name:
|
|
|
1211 |
emotion_fig
|
1212 |
)
|
1213 |
|
1214 |
+
# Function to display recommendations
|
1215 |
+
def display_recommendations():
|
1216 |
+
user_profile = get_user_profile(session_user_id)
|
1217 |
+
recommendations = user_profile.get('recommendations', [])
|
1218 |
+
|
1219 |
+
if not recommendations:
|
1220 |
+
return "No recommendations available yet. Continue chatting with Aishura to receive personalized suggestions."
|
1221 |
+
|
1222 |
+
# Show the most recent 5 recommendations
|
1223 |
+
recent_recs = recommendations[-5:]
|
1224 |
+
|
1225 |
+
result = "# Your Personalized Recommendations\n\n"
|
1226 |
+
|
1227 |
+
for i, rec in enumerate(recent_recs, 1):
|
1228 |
+
recommendation = rec['recommendation']
|
1229 |
+
result += f"## {i}. {recommendation['title']}\n\n"
|
1230 |
+
result += f"{recommendation['description']}\n\n"
|
1231 |
+
result += f"**Priority:** {recommendation['priority'].title()}\n"
|
1232 |
+
result += f"**Type:** {recommendation['action_type'].replace('_', ' ').title()}\n\n"
|
1233 |
+
result += "---\n\n"
|
1234 |
+
|
1235 |
+
return result
|
1236 |
+
|
1237 |
# Create the interface
|
1238 |
with gr.Blocks(theme=gr.themes.Soft()) as app:
|
1239 |
gr.Markdown("# Aishura - Your AI Career Assistant")
|
|
|
1256 |
with gr.Tabs() as tabs:
|
1257 |
# Chat tab
|
1258 |
with gr.TabItem("Chat with Aishura"):
|
1259 |
+
with gr.Row():
|
1260 |
+
with gr.Column(scale=2):
|
1261 |
+
chatbot = gr.Chatbot(height=500, avatar_images=["👤", "🤖"])
|
1262 |
+
msg = gr.Textbox(show_label=False, placeholder="Type your message here...", container=False)
|
1263 |
+
|
1264 |
+
with gr.Column(scale=1):
|
1265 |
+
gr.Markdown("## Your Recommendations")
|
1266 |
+
recommendation_output = gr.Markdown()
|
1267 |
+
refresh_recs_button = gr.Button("Refresh Recommendations")
|
1268 |
|
1269 |
msg.submit(chat, [msg, chatbot], [chatbot, msg])
|
1270 |
+
refresh_recs_button.click(display_recommendations, [], recommendation_output)
|
1271 |
+
|
1272 |
+
# Profile and Career Analysis tab
|
1273 |
+
with gr.TabItem("Profile & Analysis"):
|
1274 |
+
with gr.Tabs() as analysis_tabs:
|
1275 |
+
# Resume Analysis
|
1276 |
+
with gr.TabItem("Resume Analysis"):
|
1277 |
+
gr.Markdown("## Resume Analysis")
|
1278 |
+
resume_text = gr.Textbox(label="Paste your resume here", lines=10, placeholder="Copy and paste your entire resume here for analysis...")
|
1279 |
+
analyze_resume_button = gr.Button("Analyze Resume")
|
1280 |
+
resume_output = gr.Markdown()
|
1281 |
+
skill_chart = gr.Plot(label="Skill Assessment")
|
1282 |
+
|
1283 |
+
analyze_resume_button.click(
|
1284 |
+
analyze_resume_interface,
|
1285 |
+
[resume_text],
|
1286 |
+
[resume_output, skill_chart]
|
1287 |
+
)
|
1288 |
+
|
1289 |
+
# Portfolio Analysis
|
1290 |
+
with gr.TabItem("Portfolio Analysis"):
|
1291 |
+
gr.Markdown("## Portfolio Analysis")
|
1292 |
+
portfolio_url = gr.Textbox(label="Portfolio URL", placeholder="https://your-portfolio-website.com")
|
1293 |
+
portfolio_description = gr.Textbox(label="Describe your portfolio", lines=5, placeholder="Describe the content, structure, and purpose of your portfolio...")
|
1294 |
+
analyze_portfolio_button = gr.Button("Analyze Portfolio")
|
1295 |
+
portfolio_output = gr.Markdown()
|
1296 |
+
|
1297 |
+
analyze_portfolio_button.click(
|
1298 |
+
analyze_portfolio_interface,
|
1299 |
+
[portfolio_url, portfolio_description],
|
1300 |
+
portfolio_output
|
1301 |
+
)
|
1302 |
|
1303 |
# Job Search tab
|
1304 |
with gr.TabItem("Find Opportunities"):
|
|
|
1316 |
with gr.TabItem("Document Templates"):
|
1317 |
gr.Markdown("## Generate Document Templates")
|
1318 |
doc_type = gr.Dropdown(
|
1319 |
+
choices=["Resume", "Cover Letter", "Self-Introduction", "LinkedIn Profile", "Portfolio", "Interview Preparation"],
|
1320 |
label="Document Type"
|
1321 |
)
|
1322 |
career_field = gr.Textbox(label="Career Field/Industry")
|
|
|
1388 |
[name_input, location_input, emotion_dropdown, goal_dropdown],
|
1389 |
[welcome_output, welcome_group, main_interface]
|
1390 |
)
|
1391 |
+
|
1392 |
+
# Load initial recommendations
|
1393 |
+
app.load(
|
1394 |
+
display_recommendations,
|
1395 |
+
[],
|
1396 |
+
recommendation_output
|
1397 |
+
)
|
1398 |
|
1399 |
return app
|
1400 |
|