Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,855 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
import matplotlib.pyplot as plt
|
5 |
+
import plotly.graph_objects as go
|
6 |
+
import plotly.express as px
|
7 |
+
from datetime import datetime, timedelta
|
8 |
+
import random
|
9 |
+
import json
|
10 |
+
import os
|
11 |
+
import time
|
12 |
+
import requests
|
13 |
+
from typing import List, Dict, Any, Optional
|
14 |
+
import logging
|
15 |
+
from dotenv import load_dotenv
|
16 |
+
import pytz
|
17 |
+
import uuid
|
18 |
+
import google.generativeai as genai
|
19 |
+
from google.generativeai.types import HarmCategory, HarmBlockThreshold
|
20 |
+
from google.generativeai.types import Tool, FunctionDeclaration, GenerateContentConfig
|
21 |
+
|
22 |
+
# Load environment variables
|
23 |
+
load_dotenv()
|
24 |
+
|
25 |
+
# Set up logging
|
26 |
+
logging.basicConfig(level=logging.INFO,
|
27 |
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
28 |
+
logger = logging.getLogger(__name__)
|
29 |
+
|
30 |
+
# Configure API keys
|
31 |
+
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", "your-gemini-api-key")
|
32 |
+
SERPER_API_KEY = os.getenv("SERPER_API_KEY", "your-serper-api-key")
|
33 |
+
|
34 |
+
# Configure Google Gemini API
|
35 |
+
genai.configure(api_key=GEMINI_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 |
+
# Gemini model configuration
|
43 |
+
model = genai.GenerativeModel(
|
44 |
+
model_name="gemini-2.0-flash",
|
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 Gemini API
|
60 |
+
search_jobs = FunctionDeclaration(
|
61 |
+
name="search_jobs",
|
62 |
+
description="Search for job opportunities based on location and career goals",
|
63 |
+
parameters={
|
64 |
+
"type": "OBJECT",
|
65 |
+
"properties": {
|
66 |
+
"location": {
|
67 |
+
"type": "STRING",
|
68 |
+
"description": "The city or country where the user is located",
|
69 |
+
},
|
70 |
+
"career_goal": {
|
71 |
+
"type": "STRING",
|
72 |
+
"description": "The user's career goal or job interest",
|
73 |
+
},
|
74 |
+
"max_results": {
|
75 |
+
"type": "NUMBER",
|
76 |
+
"description": "Maximum number of job opportunities to return",
|
77 |
+
},
|
78 |
+
},
|
79 |
+
"required": ["location", "career_goal"],
|
80 |
+
},
|
81 |
+
)
|
82 |
+
|
83 |
+
generate_document = FunctionDeclaration(
|
84 |
+
name="generate_document_template",
|
85 |
+
description="Generate a document template for job applications",
|
86 |
+
parameters={
|
87 |
+
"type": "OBJECT",
|
88 |
+
"properties": {
|
89 |
+
"document_type": {
|
90 |
+
"type": "STRING",
|
91 |
+
"description": "Type of document to generate (Resume, Cover Letter, Self-introduction)",
|
92 |
+
},
|
93 |
+
"career_field": {
|
94 |
+
"type": "STRING",
|
95 |
+
"description": "The career field or industry the document is for",
|
96 |
+
},
|
97 |
+
"experience_level": {
|
98 |
+
"type": "STRING",
|
99 |
+
"description": "User's experience level (Entry, Mid, Senior)",
|
100 |
+
},
|
101 |
+
},
|
102 |
+
"required": ["document_type"],
|
103 |
+
},
|
104 |
+
)
|
105 |
+
|
106 |
+
create_routine = FunctionDeclaration(
|
107 |
+
name="create_personalized_routine",
|
108 |
+
description="Create a personalized career development routine",
|
109 |
+
parameters={
|
110 |
+
"type": "OBJECT",
|
111 |
+
"properties": {
|
112 |
+
"emotion": {
|
113 |
+
"type": "STRING",
|
114 |
+
"description": "User's current emotional state",
|
115 |
+
},
|
116 |
+
"goal": {
|
117 |
+
"type": "STRING",
|
118 |
+
"description": "User's career goal",
|
119 |
+
},
|
120 |
+
"available_time_minutes": {
|
121 |
+
"type": "NUMBER",
|
122 |
+
"description": "Available time in minutes per day",
|
123 |
+
},
|
124 |
+
"routine_length_days": {
|
125 |
+
"type": "NUMBER",
|
126 |
+
"description": "Length of routine in days",
|
127 |
+
},
|
128 |
+
},
|
129 |
+
"required": ["emotion", "goal"],
|
130 |
+
},
|
131 |
+
)
|
132 |
+
|
133 |
+
tools = [
|
134 |
+
Tool(function_declarations=[search_jobs]),
|
135 |
+
Tool(function_declarations=[generate_document]),
|
136 |
+
Tool(function_declarations=[create_routine])
|
137 |
+
]
|
138 |
+
|
139 |
+
# User database functions
|
140 |
+
def load_user_database():
|
141 |
+
"""Load user database from JSON file or create if it doesn't exist"""
|
142 |
+
try:
|
143 |
+
with open(USER_DB_PATH, 'r') as file:
|
144 |
+
return json.load(file)
|
145 |
+
except (FileNotFoundError, json.JSONDecodeError):
|
146 |
+
# Initialize empty database
|
147 |
+
db = {'users': {}}
|
148 |
+
save_user_database(db)
|
149 |
+
return db
|
150 |
+
|
151 |
+
def save_user_database(db):
|
152 |
+
"""Save user database to JSON file"""
|
153 |
+
with open(USER_DB_PATH, 'w') as file:
|
154 |
+
json.dump(db, file, indent=4)
|
155 |
+
|
156 |
+
def get_user_profile(user_id):
|
157 |
+
"""Get user profile from database or create new one"""
|
158 |
+
db = load_user_database()
|
159 |
+
if user_id not in db['users']:
|
160 |
+
db['users'][user_id] = {
|
161 |
+
"user_id": user_id,
|
162 |
+
"name": "",
|
163 |
+
"location": "",
|
164 |
+
"current_emotion": "",
|
165 |
+
"career_goal": "",
|
166 |
+
"progress_points": 0,
|
167 |
+
"completed_tasks": [],
|
168 |
+
"upcoming_events": [],
|
169 |
+
"routine_history": [],
|
170 |
+
"daily_emotions": [],
|
171 |
+
"joined_date": datetime.now().strftime("%Y-%m-%d")
|
172 |
+
}
|
173 |
+
save_user_database(db)
|
174 |
+
return db['users'][user_id]
|
175 |
+
|
176 |
+
def update_user_profile(user_id, updates):
|
177 |
+
"""Update user profile with new information"""
|
178 |
+
db = load_user_database()
|
179 |
+
if user_id in db['users']:
|
180 |
+
for key, value in updates.items():
|
181 |
+
db['users'][user_id][key] = value
|
182 |
+
save_user_database(db)
|
183 |
+
return db['users'][user_id]
|
184 |
+
|
185 |
+
def add_task_to_user(user_id, task):
|
186 |
+
"""Add a new task to user's completed tasks"""
|
187 |
+
db = load_user_database()
|
188 |
+
if user_id in db['users']:
|
189 |
+
if 'completed_tasks' not in db['users'][user_id]:
|
190 |
+
db['users'][user_id]['completed_tasks'] = []
|
191 |
+
|
192 |
+
task_with_date = {
|
193 |
+
"task": task,
|
194 |
+
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
195 |
+
}
|
196 |
+
db['users'][user_id]['completed_tasks'].append(task_with_date)
|
197 |
+
db['users'][user_id]['progress_points'] += random.randint(10, 25)
|
198 |
+
save_user_database(db)
|
199 |
+
return db['users'][user_id]
|
200 |
+
|
201 |
+
def add_emotion_record(user_id, emotion):
|
202 |
+
"""Add a new emotion record to user's daily emotions"""
|
203 |
+
db = load_user_database()
|
204 |
+
if user_id in db['users']:
|
205 |
+
if 'daily_emotions' not in db['users'][user_id]:
|
206 |
+
db['users'][user_id]['daily_emotions'] = []
|
207 |
+
|
208 |
+
emotion_record = {
|
209 |
+
"emotion": emotion,
|
210 |
+
"date": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
211 |
+
}
|
212 |
+
db['users'][user_id]['daily_emotions'].append(emotion_record)
|
213 |
+
db['users'][user_id]['current_emotion'] = emotion
|
214 |
+
save_user_database(db)
|
215 |
+
return db['users'][user_id]
|
216 |
+
|
217 |
+
def add_routine_to_user(user_id, routine):
|
218 |
+
"""Add a new routine to user's routine history"""
|
219 |
+
db = load_user_database()
|
220 |
+
if user_id in db['users']:
|
221 |
+
if 'routine_history' not in db['users'][user_id]:
|
222 |
+
db['users'][user_id]['routine_history'] = []
|
223 |
+
|
224 |
+
routine_with_date = {
|
225 |
+
"routine": routine,
|
226 |
+
"start_date": datetime.now().strftime("%Y-%m-%d"),
|
227 |
+
"end_date": (datetime.now() + timedelta(days=routine.get('days', 7))).strftime("%Y-%m-%d"),
|
228 |
+
"completion": 0
|
229 |
+
}
|
230 |
+
db['users'][user_id]['routine_history'].append(routine_with_date)
|
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"""
|
237 |
+
try:
|
238 |
+
headers = {
|
239 |
+
'X-API-KEY': SERPER_API_KEY,
|
240 |
+
'Content-Type': 'application/json'
|
241 |
+
}
|
242 |
+
|
243 |
+
params = {
|
244 |
+
'q': f"{query} jobs in {location}",
|
245 |
+
'num': max_results
|
246 |
+
}
|
247 |
+
|
248 |
+
response = requests.get(
|
249 |
+
'https://serper.dev/search',
|
250 |
+
headers=headers,
|
251 |
+
params=params
|
252 |
+
)
|
253 |
+
|
254 |
+
if response.status_code == 200:
|
255 |
+
data = response.json()
|
256 |
+
# Extract job listings from search results
|
257 |
+
job_results = []
|
258 |
+
|
259 |
+
# Process organic results
|
260 |
+
if 'organic' in data:
|
261 |
+
for item in data['organic']:
|
262 |
+
if 'title' in item and 'link' in item and 'snippet' in item:
|
263 |
+
# Check if it looks like a job listing
|
264 |
+
if any(keyword in item['title'].lower() for keyword in ['job', 'career', 'position', 'hiring', 'work']):
|
265 |
+
job_results.append({
|
266 |
+
'title': item['title'],
|
267 |
+
'company': extract_company_from_title(item['title']),
|
268 |
+
'description': item['snippet'],
|
269 |
+
'link': item['link'],
|
270 |
+
'location': location,
|
271 |
+
'date_posted': 'Recent' # Serper doesn't provide this directly
|
272 |
+
})
|
273 |
+
|
274 |
+
return job_results
|
275 |
+
else:
|
276 |
+
logger.error(f"Error from Serper API: {response.status_code} - {response.text}")
|
277 |
+
return []
|
278 |
+
except Exception as e:
|
279 |
+
logger.error(f"Exception in search_jobs_with_serper: {str(e)}")
|
280 |
+
return []
|
281 |
+
|
282 |
+
def extract_company_from_title(title):
|
283 |
+
"""Extract company name from job title if possible"""
|
284 |
+
# This is a simple heuristic and can be improved
|
285 |
+
if ' at ' in title:
|
286 |
+
return title.split(' at ')[1].strip()
|
287 |
+
if ' - ' in 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 Gemini API"""
|
293 |
+
try:
|
294 |
+
user_profile = get_user_profile(user_id)
|
295 |
+
|
296 |
+
system_instruction = """
|
297 |
+
You are Aishura, an emotionally intelligent AI career assistant. Your goal is to empathize with the user's emotions
|
298 |
+
and provide realistic information and actionable suggestions. Follow this structure:
|
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 |
+
Remember to be proactive and preemptive - suggest actions before the user asks.
|
305 |
+
"""
|
306 |
+
|
307 |
+
# Build conversation context
|
308 |
+
messages = []
|
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 |
+
messages.append({"role": "user", "parts": [profile_context]})
|
321 |
+
messages.append({"role": "user", "parts": [user_input]})
|
322 |
+
|
323 |
+
response = model.generate_content(
|
324 |
+
messages,
|
325 |
+
generation_config={"temperature": 0.7},
|
326 |
+
system_instruction=system_instruction,
|
327 |
+
tools=tools
|
328 |
+
)
|
329 |
+
|
330 |
+
return response.text
|
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.
|
341 |
+
|
342 |
+
For each day, suggest 1-3 specific tasks that will help them make progress toward their goal while considering their emotional state.
|
343 |
+
|
344 |
+
For each task provide:
|
345 |
+
1. Task name
|
346 |
+
2. Duration in minutes
|
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 |
+
response = model.generate_content(prompt)
|
354 |
+
routine_text = response.text
|
355 |
+
|
356 |
+
# Extract JSON portion from the response
|
357 |
+
try:
|
358 |
+
# Find JSON content between ```json and ``` if present
|
359 |
+
if "```json" in routine_text and "```" in routine_text.split("```json")[1]:
|
360 |
+
json_str = routine_text.split("```json")[1].split("```")[0].strip()
|
361 |
+
else:
|
362 |
+
# Otherwise try to find anything that looks like JSON
|
363 |
+
import re
|
364 |
+
json_match = re.search(r'(\{.*\})', routine_text, re.DOTALL)
|
365 |
+
if json_match:
|
366 |
+
json_str = json_match.group(1)
|
367 |
+
else:
|
368 |
+
json_str = routine_text
|
369 |
+
|
370 |
+
routine = json.loads(json_str)
|
371 |
+
|
372 |
+
# Add to user's routines
|
373 |
+
user_profile = add_routine_to_user(user_id, routine)
|
374 |
+
return routine
|
375 |
+
except json.JSONDecodeError:
|
376 |
+
logger.error(f"Failed to parse JSON from AI response: {routine_text}")
|
377 |
+
# Fallback to a basic routine
|
378 |
+
return generate_basic_routine(emotion, goal, available_time, days)
|
379 |
+
except Exception as e:
|
380 |
+
logger.error(f"Error in create_personalized_routine_with_ai: {str(e)}")
|
381 |
+
# Fallback to a basic routine
|
382 |
+
return generate_basic_routine(emotion, goal, available_time, days)
|
383 |
+
|
384 |
+
def generate_basic_routine(emotion, goal, available_time=60, days=7):
|
385 |
+
"""Generate a basic routine as fallback"""
|
386 |
+
routine_types = {
|
387 |
+
"job_search": [
|
388 |
+
{"name": "Research target companies", "points": 10, "duration": 20, "description": "Identify potential employers that align with your career goals"},
|
389 |
+
{"name": "Update LinkedIn profile", "points": 15, "duration": 30, "description": "Keep your professional presence current and compelling"},
|
390 |
+
{"name": "Practice interview questions", "points": 20, "duration": 45, "description": "Build confidence and prepare for upcoming opportunities"},
|
391 |
+
{"name": "Reach out to a contact", "points": 25, "duration": 15, "description": "Grow your network and gather industry insights"}
|
392 |
+
],
|
393 |
+
"skill_building": [
|
394 |
+
{"name": "Complete one tutorial", "points": 20, "duration": 60, "description": "Develop practical skills in your field"},
|
395 |
+
{"name": "Read industry article", "points": 10, "duration": 15, "description": "Stay current with trends and developments"},
|
396 |
+
{"name": "Work on portfolio project", "points": 30, "duration": 90, "description": "Create tangible evidence of your abilities"},
|
397 |
+
{"name": "Watch expert talk", "points": 15, "duration": 30, "description": "Learn from leaders in your field"}
|
398 |
+
],
|
399 |
+
"motivation": [
|
400 |
+
{"name": "Write in gratitude journal", "points": 10, "duration": 10, "description": "Cultivate a positive mindset to enhance motivation"},
|
401 |
+
{"name": "Set 3 goals for the day", "points": 15, "duration": 15, "description": "Focus your energy on achievable tasks"},
|
402 |
+
{"name": "Exercise break", "points": 20, "duration": 20, "description": "Boost energy and mood with physical activity"},
|
403 |
+
{"name": "Reflect on progress", "points": 15, "duration": 15, "description": "Acknowledge achievements and identify next steps"}
|
404 |
+
]
|
405 |
+
}
|
406 |
+
|
407 |
+
# Select routine type based on goal
|
408 |
+
if "job" in goal.lower() or "company" in goal.lower():
|
409 |
+
routine_type = "job_search"
|
410 |
+
elif "skill" in goal.lower() or "learn" in goal.lower():
|
411 |
+
routine_type = "skill_building"
|
412 |
+
else:
|
413 |
+
# Default to motivation if feeling negative emotions
|
414 |
+
if emotion.lower() in ["unmotivated", "anxious", "confused", "overwhelmed", "discouraged"]:
|
415 |
+
routine_type = "motivation"
|
416 |
+
else:
|
417 |
+
routine_type = random.choice(list(routine_types.keys()))
|
418 |
+
|
419 |
+
# Create daily plan
|
420 |
+
daily_tasks = []
|
421 |
+
for day in range(1, days + 1):
|
422 |
+
# Randomly select 1-3 tasks for the day that fit within available time
|
423 |
+
available_tasks = routine_types[routine_type].copy()
|
424 |
+
random.shuffle(available_tasks)
|
425 |
+
day_tasks = []
|
426 |
+
remaining_time = available_time
|
427 |
+
|
428 |
+
for task in available_tasks:
|
429 |
+
if task["duration"] <= remaining_time and len(day_tasks) < 3:
|
430 |
+
day_tasks.append(task)
|
431 |
+
remaining_time -= task["duration"]
|
432 |
+
|
433 |
+
if remaining_time < 10 or len(day_tasks) >= 3:
|
434 |
+
break
|
435 |
+
|
436 |
+
daily_tasks.append({
|
437 |
+
"day": day,
|
438 |
+
"tasks": day_tasks
|
439 |
+
})
|
440 |
+
|
441 |
+
routine = {
|
442 |
+
"name": f"{days}-Day {routine_type.replace('_', ' ').title()} Plan",
|
443 |
+
"description": f"A personalized routine to help you {goal} while managing feelings of {emotion}.",
|
444 |
+
"days": days,
|
445 |
+
"daily_tasks": daily_tasks
|
446 |
+
}
|
447 |
+
|
448 |
+
return routine
|
449 |
+
|
450 |
+
def generate_document_template_with_ai(document_type, career_field="", experience_level=""):
|
451 |
+
"""Generate document templates using AI"""
|
452 |
+
try:
|
453 |
+
prompt = f"""
|
454 |
+
Create a detailed template for a {document_type} for someone in the {career_field} field
|
455 |
+
with {experience_level} experience level.
|
456 |
+
|
457 |
+
The template should include all necessary sections and sample content that can be replaced.
|
458 |
+
Format it in markdown.
|
459 |
+
"""
|
460 |
+
|
461 |
+
response = model.generate_content(prompt)
|
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"""
|
470 |
+
user_profile = get_user_profile(user_id)
|
471 |
+
emotion_records = user_profile.get('daily_emotions', [])
|
472 |
+
|
473 |
+
if not emotion_records:
|
474 |
+
# Return empty chart if no data
|
475 |
+
fig = px.line(title="Emotion Tracking: No data available yet")
|
476 |
+
return fig
|
477 |
+
|
478 |
+
# Prepare data
|
479 |
+
emotion_values = {
|
480 |
+
"Unmotivated": 1,
|
481 |
+
"Anxious": 2,
|
482 |
+
"Confused": 3,
|
483 |
+
"Discouraged": 4,
|
484 |
+
"Overwhelmed": 5,
|
485 |
+
"Excited": 6
|
486 |
+
}
|
487 |
+
|
488 |
+
dates = []
|
489 |
+
emotion_scores = []
|
490 |
+
emotion_names = []
|
491 |
+
|
492 |
+
for record in emotion_records:
|
493 |
+
dates.append(datetime.strptime(record['date'], "%Y-%m-%d %H:%M:%S"))
|
494 |
+
emotion = record['emotion']
|
495 |
+
emotion_names.append(emotion)
|
496 |
+
emotion_scores.append(emotion_values.get(emotion, 3))
|
497 |
+
|
498 |
+
df = pd.DataFrame({
|
499 |
+
'Date': dates,
|
500 |
+
'Emotion Score': emotion_scores,
|
501 |
+
'Emotion': emotion_names
|
502 |
+
})
|
503 |
+
|
504 |
+
# Create chart
|
505 |
+
fig = px.line(df, x='Date', y='Emotion Score', markers=True,
|
506 |
+
labels={"Emotion Score": "Emotional State"},
|
507 |
+
title="Your Emotional Journey")
|
508 |
+
|
509 |
+
# Add emotion names as hover text
|
510 |
+
fig.update_traces(hovertemplate='%{x}<br>Feeling: %{text}', text=df['Emotion'])
|
511 |
+
|
512 |
+
# Customize y-axis to show emotion names instead of numbers
|
513 |
+
fig.update_yaxes(
|
514 |
+
tickvals=list(emotion_values.values()),
|
515 |
+
ticktext=list(emotion_values.keys())
|
516 |
+
)
|
517 |
+
|
518 |
+
return fig
|
519 |
+
|
520 |
+
def create_progress_chart(user_id):
|
521 |
+
"""Create a chart showing user's progress over time"""
|
522 |
+
user_profile = get_user_profile(user_id)
|
523 |
+
tasks = user_profile.get('completed_tasks', [])
|
524 |
+
|
525 |
+
if not tasks:
|
526 |
+
# Return empty chart if no data
|
527 |
+
fig = px.line(title="Progress Tracking: No data available yet")
|
528 |
+
return fig
|
529 |
+
|
530 |
+
# Prepare data
|
531 |
+
dates = []
|
532 |
+
points = []
|
533 |
+
cumulative_points = 0
|
534 |
+
task_labels = []
|
535 |
+
|
536 |
+
for task in tasks:
|
537 |
+
dates.append(datetime.strptime(task['date'], "%Y-%m-%d %H:%M:%S"))
|
538 |
+
# Increment points (assuming each task has inherent points)
|
539 |
+
cumulative_points += 20
|
540 |
+
points.append(cumulative_points)
|
541 |
+
task_labels.append(task['task'])
|
542 |
+
|
543 |
+
df = pd.DataFrame({
|
544 |
+
'Date': dates,
|
545 |
+
'Points': points,
|
546 |
+
'Task': task_labels
|
547 |
+
})
|
548 |
+
|
549 |
+
# Create chart
|
550 |
+
fig = px.line(df, x='Date', y='Points', markers=True,
|
551 |
+
title="Your Career Journey Progress")
|
552 |
+
|
553 |
+
# Add task names as hover text
|
554 |
+
fig.update_traces(hovertemplate='%{x}<br>Points: %{y}<br>Task: %{text}', text=df['Task'])
|
555 |
+
|
556 |
+
return fig
|
557 |
+
|
558 |
+
def create_routine_completion_gauge(user_id):
|
559 |
+
"""Create a gauge chart showing routine completion percentage"""
|
560 |
+
user_profile = get_user_profile(user_id)
|
561 |
+
routines = user_profile.get('routine_history', [])
|
562 |
+
|
563 |
+
if not routines:
|
564 |
+
# Return empty chart if no data
|
565 |
+
fig = go.Figure()
|
566 |
+
fig.add_annotation(text="No active routines yet", showarrow=False)
|
567 |
+
return fig
|
568 |
+
|
569 |
+
# Get the most recent routine
|
570 |
+
latest_routine = routines[-1]
|
571 |
+
completion = latest_routine.get('completion', 0)
|
572 |
+
|
573 |
+
# Create gauge chart
|
574 |
+
fig = go.Figure(go.Indicator(
|
575 |
+
mode = "gauge+number",
|
576 |
+
value = completion,
|
577 |
+
domain = {'x': [0, 1], 'y': [0, 1]},
|
578 |
+
title = {'text': "Current Routine Completion"},
|
579 |
+
gauge = {
|
580 |
+
'axis': {'range': [None, 100]},
|
581 |
+
'bar': {'color': "darkblue"},
|
582 |
+
'steps': [
|
583 |
+
{'range': [0, 30], 'color': "lightgray"},
|
584 |
+
{'range': [30, 70], 'color': "gray"},
|
585 |
+
{'range': [70, 100], 'color': "darkgray"}
|
586 |
+
],
|
587 |
+
'threshold': {
|
588 |
+
'line': {'color': "red", 'width': 4},
|
589 |
+
'thickness': 0.75,
|
590 |
+
'value': 90
|
591 |
+
}
|
592 |
+
}
|
593 |
+
))
|
594 |
+
|
595 |
+
return fig
|
596 |
+
|
597 |
+
# Gradio interface components
|
598 |
+
def create_interface():
|
599 |
+
"""Create the Gradio interface for Aishura MVP"""
|
600 |
+
|
601 |
+
# Generate a unique user ID for this session
|
602 |
+
session_user_id = str(uuid.uuid4())
|
603 |
+
|
604 |
+
# Welcome page
|
605 |
+
def welcome(name, location, emotion, goal):
|
606 |
+
if not name or not location or not emotion or not goal:
|
607 |
+
return ("Please fill out all fields to continue.",
|
608 |
+
gr.update(visible=True),
|
609 |
+
gr.update(visible=False))
|
610 |
+
|
611 |
+
# Update user profile
|
612 |
+
update_user_profile(session_user_id, {
|
613 |
+
"name": name,
|
614 |
+
"location": location,
|
615 |
+
"career_goal": goal
|
616 |
+
})
|
617 |
+
|
618 |
+
# Record emotion
|
619 |
+
add_emotion_record(session_user_id, emotion)
|
620 |
+
|
621 |
+
# Generate initial AI response
|
622 |
+
response = get_ai_response(
|
623 |
+
session_user_id,
|
624 |
+
f"I'm {name} from {location}. I'm feeling {emotion} and my career goal is to {goal}."
|
625 |
+
)
|
626 |
+
|
627 |
+
return (response,
|
628 |
+
gr.update(visible=False),
|
629 |
+
gr.update(visible=True))
|
630 |
+
|
631 |
+
# Chat function
|
632 |
+
def chat(message, history):
|
633 |
+
# Record the user message for context
|
634 |
+
if not history:
|
635 |
+
history = []
|
636 |
+
|
637 |
+
# Add user's message to history
|
638 |
+
history.append({"role": "user", "parts": [message]})
|
639 |
+
|
640 |
+
# Get AI response
|
641 |
+
response = get_ai_response(session_user_id, message, history)
|
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
|
649 |
+
def search_jobs_interface(query, location, max_results=5):
|
650 |
+
jobs = search_jobs_with_serper(query, location, int(max_results))
|
651 |
+
|
652 |
+
if not jobs:
|
653 |
+
return "No job opportunities found. Try adjusting your search terms."
|
654 |
+
|
655 |
+
result = "## Job Opportunities Found\n\n"
|
656 |
+
for i, job in enumerate(jobs, 1):
|
657 |
+
result += f"### {i}. {job['title']}\n"
|
658 |
+
result += f"**Company:** {job['company']}\n"
|
659 |
+
result += f"**Location:** {job['location']}\n"
|
660 |
+
result += f"**Description:** {job['description']}\n"
|
661 |
+
result += f"**Link:** [Apply Here]({job['link']})\n\n"
|
662 |
+
|
663 |
+
return result
|
664 |
+
|
665 |
+
# Function to generate document templates
|
666 |
+
def generate_template(document_type, career_field, experience_level):
|
667 |
+
template = generate_document_template_with_ai(document_type, career_field, experience_level)
|
668 |
+
return template
|
669 |
+
|
670 |
+
# Function to create personal routine
|
671 |
+
def create_personal_routine(emotion, goal, available_time, days):
|
672 |
+
routine = create_personalized_routine_with_ai(
|
673 |
+
session_user_id, emotion, goal, int(available_time), int(days)
|
674 |
+
)
|
675 |
+
|
676 |
+
# Format routine for display
|
677 |
+
result = f"# Your {routine['name']}\n\n"
|
678 |
+
result += f"{routine['description']}\n\n"
|
679 |
+
|
680 |
+
for day_plan in routine['daily_tasks']:
|
681 |
+
result += f"## Day {day_plan['day']}\n\n"
|
682 |
+
for task in day_plan['tasks']:
|
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:
|
694 |
+
return "Please enter a task name."
|
695 |
+
|
696 |
+
user_profile = add_task_to_user(session_user_id, task_name)
|
697 |
+
|
698 |
+
# Update completion percentage of current routine
|
699 |
+
if user_profile.get('routine_history'):
|
700 |
+
latest_routine = user_profile['routine_history'][-1]
|
701 |
+
# Simple approach: increase completion by random amount between 5-15%
|
702 |
+
new_completion = min(100, latest_routine.get('completion', 0) + random.randint(5, 15))
|
703 |
+
latest_routine['completion'] = new_completion
|
704 |
+
update_user_profile(session_user_id, {"routine_history": user_profile['routine_history']})
|
705 |
+
|
706 |
+
# Create updated charts
|
707 |
+
emotion_fig = create_emotion_chart(session_user_id)
|
708 |
+
progress_fig = create_progress_chart(session_user_id)
|
709 |
+
gauge_fig = create_routine_completion_gauge(session_user_id)
|
710 |
+
|
711 |
+
return (
|
712 |
+
f"Task '{task_name}' completed! You earned {random.randint(10, 25)} points.",
|
713 |
+
"",
|
714 |
+
emotion_fig,
|
715 |
+
progress_fig,
|
716 |
+
gauge_fig
|
717 |
+
)
|
718 |
+
|
719 |
+
# Function to update emotion
|
720 |
+
def update_emotion(emotion):
|
721 |
+
add_emotion_record(session_user_id, emotion)
|
722 |
+
|
723 |
+
# Create updated emotion chart
|
724 |
+
emotion_fig = create_emotion_chart(session_user_id)
|
725 |
+
|
726 |
+
return (
|
727 |
+
f"Your emotional state has been updated to: {emotion}",
|
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")
|
734 |
+
|
735 |
+
# Welcome page
|
736 |
+
with gr.Group(visible=True) as welcome_group:
|
737 |
+
gr.Markdown("## Welcome to Aishura")
|
738 |
+
gr.Markdown("Let's start by getting to know you a little better.")
|
739 |
+
|
740 |
+
name_input = gr.Textbox(label="Your Name")
|
741 |
+
location_input = gr.Textbox(label="Your Location (City/Country)")
|
742 |
+
emotion_dropdown = gr.Dropdown(choices=EMOTIONS, label="How are you feeling today?")
|
743 |
+
goal_dropdown = gr.Dropdown(choices=GOAL_TYPES, label="What's your career goal?")
|
744 |
+
|
745 |
+
welcome_button = gr.Button("Get Started")
|
746 |
+
welcome_output = gr.Markdown()
|
747 |
+
|
748 |
+
# Main interface
|
749 |
+
with gr.Group(visible=False) as main_interface:
|
750 |
+
with gr.Tabs() as tabs:
|
751 |
+
# Chat tab
|
752 |
+
with gr.TabItem("Chat with Aishura"):
|
753 |
+
chatbot = gr.Chatbot(height=400, avatar_images=["👤", "🤖"])
|
754 |
+
msg = gr.Textbox(show_label=False, placeholder="Type your message here...", container=False)
|
755 |
+
|
756 |
+
msg.submit(chat, [msg, chatbot], [chatbot, msg])
|
757 |
+
|
758 |
+
# Job Search tab
|
759 |
+
with gr.TabItem("Find Opportunities"):
|
760 |
+
gr.Markdown("## Search for Job Opportunities")
|
761 |
+
job_query = gr.Textbox(label="What kind of job are you looking for?")
|
762 |
+
job_location = gr.Textbox(label="Location")
|
763 |
+
job_results = gr.Slider(minimum=5, maximum=20, value=10, step=5, label="Number of Results")
|
764 |
+
|
765 |
+
search_button = gr.Button("Search")
|
766 |
+
job_output = gr.Markdown()
|
767 |
+
|
768 |
+
search_button.click(search_jobs_interface, [job_query, job_location, job_results], job_output)
|
769 |
+
|
770 |
+
# Document Templates tab
|
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")
|
778 |
+
experience = gr.Dropdown(
|
779 |
+
choices=["Entry Level", "Mid-Career", "Senior"],
|
780 |
+
label="Experience Level"
|
781 |
+
)
|
782 |
+
|
783 |
+
template_button = gr.Button("Generate Template")
|
784 |
+
template_output = gr.Markdown()
|
785 |
+
|
786 |
+
template_button.click(generate_template, [doc_type, career_field, experience], template_output)
|
787 |
+
|
788 |
+
# Personal Routine tab
|
789 |
+
with gr.TabItem("Personal Routine"):
|
790 |
+
gr.Markdown("## Create Your Personal Development Routine")
|
791 |
+
routine_emotion = gr.Dropdown(choices=EMOTIONS, label="Current Emotional State")
|
792 |
+
routine_goal = gr.Textbox(label="What specific goal are you working toward?")
|
793 |
+
time_available = gr.Slider(minimum=15, maximum=120, value=60, step=15, label="Minutes Available Per Day")
|
794 |
+
routine_days = gr.Slider(minimum=3, maximum=30, value=7, step=1, label="Length of Routine (Days)")
|
795 |
+
|
796 |
+
routine_button = gr.Button("Create Routine")
|
797 |
+
routine_output = gr.Markdown()
|
798 |
+
|
799 |
+
routine_button.click(create_personal_routine,
|
800 |
+
[routine_emotion, routine_goal, time_available, routine_days],
|
801 |
+
routine_output)
|
802 |
+
|
803 |
+
# Progress Tracking tab
|
804 |
+
with gr.TabItem("Track Progress"):
|
805 |
+
with gr.Row():
|
806 |
+
with gr.Column():
|
807 |
+
gr.Markdown("## Mark Tasks as Complete")
|
808 |
+
task_input = gr.Textbox(label="Enter Task Name")
|
809 |
+
complete_button = gr.Button("Mark as Complete")
|
810 |
+
task_output = gr.Markdown()
|
811 |
+
|
812 |
+
with gr.Column():
|
813 |
+
gr.Markdown("## Update Your Emotional State")
|
814 |
+
new_emotion = gr.Dropdown(choices=EMOTIONS, label="How are you feeling now?")
|
815 |
+
emotion_button = gr.Button("Update")
|
816 |
+
emotion_output = gr.Markdown()
|
817 |
+
|
818 |
+
with gr.Row():
|
819 |
+
with gr.Column():
|
820 |
+
emotion_chart = gr.Plot(label="Emotional Journey")
|
821 |
+
|
822 |
+
with gr.Column():
|
823 |
+
progress_chart = gr.Plot(label="Progress Journey")
|
824 |
+
|
825 |
+
with gr.Row():
|
826 |
+
gauge_chart = gr.Plot(label="Routine Completion")
|
827 |
+
|
828 |
+
complete_button.click(
|
829 |
+
complete_task,
|
830 |
+
[task_input],
|
831 |
+
[task_output, task_input, emotion_chart, progress_chart, gauge_chart]
|
832 |
+
)
|
833 |
+
|
834 |
+
emotion_button.click(
|
835 |
+
update_emotion,
|
836 |
+
[new_emotion],
|
837 |
+
[emotion_output, emotion_chart]
|
838 |
+
)
|
839 |
+
|
840 |
+
# Welcome button action
|
841 |
+
welcome_button.click(
|
842 |
+
welcome,
|
843 |
+
[name_input, location_input, emotion_dropdown, goal_dropdown],
|
844 |
+
[welcome_output, welcome_group, main_interface]
|
845 |
+
)
|
846 |
+
|
847 |
+
return app
|
848 |
+
|
849 |
+
# Main function to launch the app
|
850 |
+
def main():
|
851 |
+
app = create_interface()
|
852 |
+
app.launch(share=True)
|
853 |
+
|
854 |
+
if __name__ == "__main__":
|
855 |
+
main()
|