Upload 18 files
Browse files- pages/__pycache__/ai_buddy.cpython-311.pyc +0 -0
- pages/__pycache__/chatbot.cpython-311.pyc +0 -0
- pages/__pycache__/meditation.cpython-311.pyc +0 -0
- pages/__pycache__/mind_palace.cpython-311.pyc +0 -0
- pages/__pycache__/mnemonics_generation.cpython-311.pyc +0 -0
- pages/__pycache__/notes_generation.cpython-311.pyc +0 -0
- pages/__pycache__/sherlock_observation.cpython-311.pyc +0 -0
- pages/__pycache__/study_roadmap.cpython-311.pyc +0 -0
- pages/ai_buddy.py +368 -0
- pages/chatbot.py +242 -0
- pages/exam_prepration.py +325 -0
- pages/interview_prepration.py +560 -0
- pages/meditation.py +130 -0
- pages/mind_palace.py +341 -0
- pages/mnemonics_generation.py +265 -0
- pages/notes_generation.py +213 -0
- pages/sherlock_observation.py +191 -0
- pages/study_roadmap.py +481 -0
pages/__pycache__/ai_buddy.cpython-311.pyc
ADDED
Binary file (24.7 kB). View file
|
|
pages/__pycache__/chatbot.cpython-311.pyc
ADDED
Binary file (25.4 kB). View file
|
|
pages/__pycache__/meditation.cpython-311.pyc
ADDED
Binary file (9.58 kB). View file
|
|
pages/__pycache__/mind_palace.cpython-311.pyc
ADDED
Binary file (20.5 kB). View file
|
|
pages/__pycache__/mnemonics_generation.cpython-311.pyc
ADDED
Binary file (15.6 kB). View file
|
|
pages/__pycache__/notes_generation.cpython-311.pyc
ADDED
Binary file (12.8 kB). View file
|
|
pages/__pycache__/sherlock_observation.cpython-311.pyc
ADDED
Binary file (10.5 kB). View file
|
|
pages/__pycache__/study_roadmap.cpython-311.pyc
ADDED
Binary file (28.1 kB). View file
|
|
pages/ai_buddy.py
ADDED
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import random
|
3 |
+
from langchain.chat_models import ChatOpenAI
|
4 |
+
from langchain.schema import HumanMessage, SystemMessage
|
5 |
+
import os
|
6 |
+
from dotenv import load_dotenv
|
7 |
+
import pandas as pd
|
8 |
+
from datetime import datetime
|
9 |
+
import plotly.express as px
|
10 |
+
import json
|
11 |
+
import speech_recognition as sr
|
12 |
+
from gtts import gTTS
|
13 |
+
import pygame
|
14 |
+
from io import BytesIO
|
15 |
+
|
16 |
+
# Load environment variables
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
AI71_BASE_URL = "https://api.ai71.ai/v1/"
|
20 |
+
AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
|
21 |
+
|
22 |
+
# Initialize the Falcon model
|
23 |
+
chat = ChatOpenAI(
|
24 |
+
model="tiiuae/falcon-180B-chat",
|
25 |
+
api_key=AI71_API_KEY,
|
26 |
+
base_url=AI71_BASE_URL,
|
27 |
+
streaming=True,
|
28 |
+
)
|
29 |
+
|
30 |
+
# Expanded Therapy techniques
|
31 |
+
THERAPY_TECHNIQUES = {
|
32 |
+
"CBT": "Use Cognitive Behavioral Therapy techniques to help the user identify and change negative thought patterns.",
|
33 |
+
"Mindfulness": "Guide the user through mindfulness exercises to promote present-moment awareness and reduce stress.",
|
34 |
+
"Solution-Focused": "Focus on the user's strengths and resources to help them find solutions to their problems.",
|
35 |
+
"Emotion-Focused": "Help the user identify, experience, and regulate their emotions more effectively.",
|
36 |
+
"Psychodynamic": "Explore the user's past experiences and unconscious patterns to gain insight into current issues.",
|
37 |
+
"ACT": "Use Acceptance and Commitment Therapy to help the user accept their thoughts and feelings while committing to positive changes.",
|
38 |
+
"DBT": "Apply Dialectical Behavior Therapy techniques to help the user manage intense emotions and improve relationships.",
|
39 |
+
"Gestalt": "Use Gestalt therapy techniques to focus on the present moment and increase self-awareness.",
|
40 |
+
"Existential": "Explore existential themes such as meaning, freedom, and responsibility to help the user find purpose.",
|
41 |
+
"Narrative": "Use storytelling and narrative techniques to help the user reframe their life experiences and create new meaning.",
|
42 |
+
}
|
43 |
+
|
44 |
+
def get_ai_response(user_input, buddy_config, therapy_technique=None):
|
45 |
+
system_message = f"You are {buddy_config['name']}, an AI companion with the following personality: {buddy_config['personality']}. "
|
46 |
+
system_message += f"Additional details about you: {buddy_config['details']}. "
|
47 |
+
|
48 |
+
if therapy_technique:
|
49 |
+
system_message += f"In this conversation, {THERAPY_TECHNIQUES[therapy_technique]}"
|
50 |
+
|
51 |
+
messages = [
|
52 |
+
SystemMessage(content=system_message),
|
53 |
+
HumanMessage(content=user_input)
|
54 |
+
]
|
55 |
+
response = chat.invoke(messages).content
|
56 |
+
return response
|
57 |
+
|
58 |
+
def show_progress_dashboard():
|
59 |
+
st.subheader("Your Progress Dashboard")
|
60 |
+
|
61 |
+
# Placeholder data for demonstration
|
62 |
+
mood_logs = [{"date": "2023-08-01", "mood": 7}, {"date": "2023-08-02", "mood": 8}, {"date": "2023-08-03", "mood": 6}]
|
63 |
+
|
64 |
+
# Mood trend
|
65 |
+
if mood_logs:
|
66 |
+
df = pd.DataFrame(mood_logs)
|
67 |
+
fig = px.line(df, x="date", y="mood", title="Mood Trend")
|
68 |
+
st.plotly_chart(fig)
|
69 |
+
else:
|
70 |
+
st.info("No mood data available yet. Start logging your mood!")
|
71 |
+
|
72 |
+
# Journal entries
|
73 |
+
st.subheader("Recent Journal Entries")
|
74 |
+
journal_entries = [
|
75 |
+
{"date": "2023-08-03", "entry": "Had a productive day at work..."},
|
76 |
+
{"date": "2023-08-02", "entry": "Feeling a bit stressed about..."},
|
77 |
+
{"date": "2023-08-01", "entry": "Started a new book today..."}
|
78 |
+
]
|
79 |
+
for entry in journal_entries[-5:]:
|
80 |
+
st.text(f"{entry['date']}: {entry['entry'][:50]}...")
|
81 |
+
|
82 |
+
# Goals progress
|
83 |
+
st.subheader("Goals Progress")
|
84 |
+
goals = [
|
85 |
+
{"description": "Exercise 3 times a week", "progress": 60},
|
86 |
+
{"description": "Read 2 books this month", "progress": 75},
|
87 |
+
{"description": "Learn Python programming", "progress": 40}
|
88 |
+
]
|
89 |
+
for goal in goals:
|
90 |
+
st.progress(goal['progress'])
|
91 |
+
st.text(f"{goal['description']}: {goal['progress']}% complete")
|
92 |
+
|
93 |
+
def show_goal_setting_interface():
|
94 |
+
st.subheader("Set Your Goals")
|
95 |
+
goal_description = st.text_input("Describe your goal")
|
96 |
+
goal_progress = st.slider("Initial progress", 0, 100, 0)
|
97 |
+
|
98 |
+
if st.button("Add Goal"):
|
99 |
+
st.success("Goal added successfully!")
|
100 |
+
|
101 |
+
def show_meditation_timer():
|
102 |
+
st.subheader("Meditation Timer")
|
103 |
+
duration = st.slider("Select duration (minutes)", 1, 60, 5)
|
104 |
+
if st.button("Start Meditation"):
|
105 |
+
progress_bar = st.progress(0)
|
106 |
+
for i in range(duration * 60):
|
107 |
+
progress_bar.progress((i + 1) / (duration * 60))
|
108 |
+
st.empty().text(f"Time remaining: {duration - (i // 60)}:{59 - (i % 60):02d}")
|
109 |
+
time.sleep(1)
|
110 |
+
st.success("Meditation complete!")
|
111 |
+
|
112 |
+
def show_community_forum():
|
113 |
+
st.subheader("Community Forum")
|
114 |
+
st.warning("This feature is not yet implemented. Check back soon!")
|
115 |
+
|
116 |
+
def show_personalized_recommendations():
|
117 |
+
st.subheader("Personalized Recommendations")
|
118 |
+
|
119 |
+
# Placeholder for demonstration
|
120 |
+
avg_mood = 6
|
121 |
+
|
122 |
+
if avg_mood < 4:
|
123 |
+
st.markdown("Based on your recent mood, we recommend:")
|
124 |
+
st.markdown("- Practice daily gratitude journaling")
|
125 |
+
st.markdown("- Try a guided meditation for stress relief")
|
126 |
+
st.markdown("- Read 'The Happiness Trap' by Russ Harris")
|
127 |
+
elif avg_mood < 7:
|
128 |
+
st.markdown("To maintain and improve your mood, consider:")
|
129 |
+
st.markdown("- Start a new hobby or learn a new skill")
|
130 |
+
st.markdown("- Practice mindfulness meditation")
|
131 |
+
st.markdown("- Read 'Atomic Habits' by James Clear")
|
132 |
+
else:
|
133 |
+
st.markdown("Great job maintaining a positive mood! To keep it up:")
|
134 |
+
st.markdown("- Share your positivity with others")
|
135 |
+
st.markdown("- Set ambitious goals for personal growth")
|
136 |
+
st.markdown("- Read 'Flow' by Mihaly Csikszentmihalyi")
|
137 |
+
|
138 |
+
def start_voice_interaction():
|
139 |
+
st.subheader("Voice Interaction")
|
140 |
+
if st.button("Start Voice Recognition"):
|
141 |
+
r = sr.Recognizer()
|
142 |
+
with sr.Microphone() as source:
|
143 |
+
st.info("Listening... Speak now!")
|
144 |
+
audio = r.listen(source)
|
145 |
+
try:
|
146 |
+
text = r.recognize_google(audio)
|
147 |
+
st.success(f"You said: {text}")
|
148 |
+
|
149 |
+
# Get AI response
|
150 |
+
buddy_config = {
|
151 |
+
"name": st.session_state.buddy_name,
|
152 |
+
"personality": st.session_state.buddy_personality,
|
153 |
+
"details": st.session_state.buddy_details
|
154 |
+
}
|
155 |
+
response = get_ai_response(text, buddy_config)
|
156 |
+
|
157 |
+
# Convert response to speech
|
158 |
+
tts = gTTS(text=response, lang='en')
|
159 |
+
fp = BytesIO()
|
160 |
+
tts.write_to_fp(fp)
|
161 |
+
fp.seek(0)
|
162 |
+
|
163 |
+
# Play the response
|
164 |
+
pygame.mixer.init()
|
165 |
+
pygame.mixer.music.load(fp)
|
166 |
+
pygame.mixer.music.play()
|
167 |
+
while pygame.mixer.music.get_busy():
|
168 |
+
pygame.time.Clock().tick(10)
|
169 |
+
|
170 |
+
st.success(f"AI response: {response}")
|
171 |
+
except sr.UnknownValueError:
|
172 |
+
st.error("Sorry, I couldn't understand what you said.")
|
173 |
+
except sr.RequestError as e:
|
174 |
+
st.error(f"Could not request results; {e}")
|
175 |
+
|
176 |
+
def show_daily_challenge():
|
177 |
+
st.subheader("Daily Personal Growth Challenge")
|
178 |
+
challenges = [
|
179 |
+
"Write down three things you're grateful for today.",
|
180 |
+
"Reach out to a friend or family member you haven't spoken to in a while.",
|
181 |
+
"Try a new healthy recipe for dinner tonight.",
|
182 |
+
"Take a 15-minute walk in nature and practice mindfulness.",
|
183 |
+
"Learn five new words in a language you're interested in.",
|
184 |
+
"Declutter one area of your living space.",
|
185 |
+
"Practice active listening in your next conversation.",
|
186 |
+
"Try a new form of exercise or physical activity.",
|
187 |
+
"Write a short story or poem expressing your current emotions.",
|
188 |
+
"Perform a random act of kindness for someone."
|
189 |
+
]
|
190 |
+
|
191 |
+
if 'daily_challenge' not in st.session_state:
|
192 |
+
st.session_state.daily_challenge = random.choice(challenges)
|
193 |
+
|
194 |
+
st.info(f"Your challenge for today: {st.session_state.daily_challenge}")
|
195 |
+
|
196 |
+
if st.button("Complete Challenge"):
|
197 |
+
st.success("Great job completing today's challenge! Keep up the good work.")
|
198 |
+
st.session_state.daily_challenge = random.choice(challenges)
|
199 |
+
|
200 |
+
def show_therapist_scheduling_interface():
|
201 |
+
st.subheader("Schedule a Video Therapy Session")
|
202 |
+
st.warning("This feature would integrate with a real therapist booking system. For demonstration purposes, we'll use a simplified version.")
|
203 |
+
|
204 |
+
therapists = ["Dr. Smith", "Dr. Johnson", "Dr. Williams", "Dr. Brown"]
|
205 |
+
selected_therapist = st.selectbox("Choose a therapist", therapists)
|
206 |
+
|
207 |
+
date = st.date_input("Select a date")
|
208 |
+
time = st.time_input("Select a time")
|
209 |
+
|
210 |
+
if st.button("Schedule Session"):
|
211 |
+
st.success(f"Session scheduled with {selected_therapist} on {date} at {time}. You will receive a confirmation email with further details.")
|
212 |
+
|
213 |
+
def main():
|
214 |
+
st.set_page_config(page_title="S.H.E.R.L.O.C.K. AI Buddy", page_icon="π΅οΈ", layout="wide")
|
215 |
+
|
216 |
+
# Custom CSS for improved styling
|
217 |
+
st.markdown("""
|
218 |
+
<style>
|
219 |
+
.stApp {
|
220 |
+
background-color: #f0f2f6;
|
221 |
+
}
|
222 |
+
.stButton>button {
|
223 |
+
background-color: #4CAF50;
|
224 |
+
color: white;
|
225 |
+
font-weight: bold;
|
226 |
+
}
|
227 |
+
.stTextInput>div>div>input {
|
228 |
+
background-color: #ffffff;
|
229 |
+
}
|
230 |
+
</style>
|
231 |
+
""", unsafe_allow_html=True)
|
232 |
+
|
233 |
+
st.title("π΅οΈ S.H.E.R.L.O.C.K. AI Buddy")
|
234 |
+
st.markdown("Your personalized AI companion for conversation, therapy, and personal growth.")
|
235 |
+
|
236 |
+
# Initialize session state
|
237 |
+
if 'buddy_name' not in st.session_state:
|
238 |
+
st.session_state.buddy_name = "Sherlock"
|
239 |
+
if 'buddy_personality' not in st.session_state:
|
240 |
+
st.session_state.buddy_personality = "Friendly, empathetic, and insightful"
|
241 |
+
if 'buddy_details' not in st.session_state:
|
242 |
+
st.session_state.buddy_details = "Knowledgeable about various therapy techniques and always ready to listen"
|
243 |
+
if 'messages' not in st.session_state:
|
244 |
+
st.session_state.messages = []
|
245 |
+
|
246 |
+
# Sidebar for AI Buddy configuration
|
247 |
+
with st.sidebar:
|
248 |
+
st.header("π€ Configure Your AI Buddy")
|
249 |
+
st.session_state.buddy_name = st.text_input("Name your AI Buddy", value=st.session_state.buddy_name)
|
250 |
+
st.session_state.buddy_personality = st.text_area("Describe your buddy's personality", value=st.session_state.buddy_personality)
|
251 |
+
st.session_state.buddy_details = st.text_area("Additional details about your buddy", value=st.session_state.buddy_details)
|
252 |
+
|
253 |
+
st.header("π§ Therapy Session")
|
254 |
+
therapy_mode = st.checkbox("Enable Therapy Mode")
|
255 |
+
if therapy_mode:
|
256 |
+
therapy_technique = st.selectbox("Select Therapy Technique", list(THERAPY_TECHNIQUES.keys()))
|
257 |
+
else:
|
258 |
+
therapy_technique = None
|
259 |
+
|
260 |
+
st.markdown("---")
|
261 |
+
st.markdown("Powered by Falcon-180B and Streamlit")
|
262 |
+
|
263 |
+
# Main content area
|
264 |
+
tab1, tab2, tab3, tab4, tab5 = st.tabs(["Chat", "Progress", "Goals", "Community", "Tools"])
|
265 |
+
|
266 |
+
with tab1:
|
267 |
+
# Chat interface
|
268 |
+
st.header("π¨οΈ Chat with Your AI Buddy")
|
269 |
+
chat_container = st.container()
|
270 |
+
with chat_container:
|
271 |
+
for message in st.session_state.messages[-5:]: # Display last 5 messages
|
272 |
+
with st.chat_message(message["role"]):
|
273 |
+
st.markdown(message["content"])
|
274 |
+
|
275 |
+
# User input
|
276 |
+
if prompt := st.chat_input("What's on your mind?"):
|
277 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
278 |
+
with st.chat_message("user"):
|
279 |
+
st.markdown(prompt)
|
280 |
+
|
281 |
+
buddy_config = {
|
282 |
+
"name": st.session_state.buddy_name,
|
283 |
+
"personality": st.session_state.buddy_personality,
|
284 |
+
"details": st.session_state.buddy_details
|
285 |
+
}
|
286 |
+
|
287 |
+
with st.chat_message("assistant"):
|
288 |
+
message_placeholder = st.empty()
|
289 |
+
full_response = ""
|
290 |
+
for chunk in chat.stream(get_ai_response(prompt, buddy_config, therapy_technique)):
|
291 |
+
full_response += chunk.content
|
292 |
+
message_placeholder.markdown(full_response + "β")
|
293 |
+
message_placeholder.markdown(full_response)
|
294 |
+
st.session_state.messages.append({"role": "assistant", "content": full_response})
|
295 |
+
|
296 |
+
with tab2:
|
297 |
+
show_progress_dashboard()
|
298 |
+
|
299 |
+
with tab3:
|
300 |
+
show_goal_setting_interface()
|
301 |
+
|
302 |
+
with tab4:
|
303 |
+
show_community_forum()
|
304 |
+
|
305 |
+
with tab5:
|
306 |
+
tool_choice = st.selectbox("Select a tool", ["Meditation Timer", "Recommendations", "Voice Interaction", "Daily Challenge", "Schedule Therapy"])
|
307 |
+
if tool_choice == "Meditation Timer":
|
308 |
+
show_meditation_timer()
|
309 |
+
elif tool_choice == "Recommendations":
|
310 |
+
show_personalized_recommendations()
|
311 |
+
elif tool_choice == "Voice Interaction":
|
312 |
+
start_voice_interaction()
|
313 |
+
elif tool_choice == "Daily Challenge":
|
314 |
+
show_daily_challenge()
|
315 |
+
elif tool_choice == "Schedule Therapy":
|
316 |
+
show_therapist_scheduling_interface()
|
317 |
+
|
318 |
+
# Mood tracker
|
319 |
+
st.sidebar.markdown("---")
|
320 |
+
st.sidebar.header("π Mood Tracker")
|
321 |
+
mood = st.sidebar.slider("How are you feeling today?", 1, 10, 5)
|
322 |
+
if st.sidebar.button("Log Mood"):
|
323 |
+
st.sidebar.success(f"Mood logged: {mood}/10")
|
324 |
+
st.balloons()
|
325 |
+
|
326 |
+
# Journaling feature
|
327 |
+
st.sidebar.markdown("---")
|
328 |
+
st.sidebar.header("π Daily Journal")
|
329 |
+
journal_entry = st.sidebar.text_area("Write your thoughts for today")
|
330 |
+
if st.sidebar.button("Save Journal Entry"):
|
331 |
+
st.sidebar.success("Journal entry saved!")
|
332 |
+
st.toast("Journal entry saved successfully!", icon="β
")
|
333 |
+
|
334 |
+
# Resources and Emergency Contact
|
335 |
+
st.sidebar.markdown("---")
|
336 |
+
st.sidebar.header("π Resources")
|
337 |
+
st.sidebar.info("If you're in crisis, please reach out for help:")
|
338 |
+
st.sidebar.markdown("- [Mental Health Resources](https://www.mentalhealth.gov/get-help/immediate-help)")
|
339 |
+
st.sidebar.markdown("- Emergency Contact: 911 or your local emergency number")
|
340 |
+
|
341 |
+
# Inspiration Quote
|
342 |
+
st.sidebar.markdown("---")
|
343 |
+
st.sidebar.header("π‘ Daily Inspiration")
|
344 |
+
if st.sidebar.button("Get Inspirational Quote"):
|
345 |
+
quotes = [
|
346 |
+
"The only way to do great work is to love what you do. - Steve Jobs",
|
347 |
+
"Believe you can and you're halfway there. - Theodore Roosevelt",
|
348 |
+
"The future belongs to those who believe in the beauty of their dreams. - Eleanor Roosevelt",
|
349 |
+
"Strive not to be a success, but rather to be of value. - Albert Einstein",
|
350 |
+
"The only limit to our realization of tomorrow will be our doubts of today. - Franklin D. Roosevelt"
|
351 |
+
]
|
352 |
+
random_quote = random.choice(quotes)
|
353 |
+
st.sidebar.success(random_quote)
|
354 |
+
|
355 |
+
# Chat Export
|
356 |
+
st.sidebar.markdown("---")
|
357 |
+
if st.sidebar.button("Export Chat History"):
|
358 |
+
chat_history = "\n".join([f"{msg['role']}: {msg['content']}" for msg in st.session_state.messages])
|
359 |
+
st.sidebar.download_button(
|
360 |
+
label="Download Chat History",
|
361 |
+
data=chat_history,
|
362 |
+
file_name="ai_buddy_chat_history.txt",
|
363 |
+
mime="text/plain"
|
364 |
+
)
|
365 |
+
st.sidebar.success("Chat history ready for download!")
|
366 |
+
|
367 |
+
if __name__ == "__main__":
|
368 |
+
main()
|
pages/chatbot.py
ADDED
@@ -0,0 +1,242 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import random
|
3 |
+
from langchain.chat_models import ChatOpenAI
|
4 |
+
from langchain.schema import HumanMessage, SystemMessage
|
5 |
+
from langchain.document_loaders import TextLoader
|
6 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
7 |
+
from langchain.embeddings import HuggingFaceEmbeddings
|
8 |
+
from langchain_community.vectorstores import FAISS
|
9 |
+
from langchain.chains import RetrievalQA
|
10 |
+
import os
|
11 |
+
from dotenv import load_dotenv
|
12 |
+
import requests
|
13 |
+
from bs4 import BeautifulSoup
|
14 |
+
import pandas as pd
|
15 |
+
from googleapiclient.discovery import build
|
16 |
+
from googleapiclient.errors import HttpError
|
17 |
+
import time
|
18 |
+
from langchain.schema import Document
|
19 |
+
from docx import Document as DocxDocument
|
20 |
+
from PyPDF2 import PdfReader
|
21 |
+
import io
|
22 |
+
|
23 |
+
# Load environment variables
|
24 |
+
load_dotenv()
|
25 |
+
|
26 |
+
AI71_BASE_URL = "https://api.ai71.ai/v1/"
|
27 |
+
AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
|
28 |
+
|
29 |
+
# Initialize session state variables
|
30 |
+
if "custom_personality" not in st.session_state:
|
31 |
+
st.session_state.custom_personality = ""
|
32 |
+
if "messages" not in st.session_state:
|
33 |
+
st.session_state.messages = []
|
34 |
+
|
35 |
+
# Initialize the Falcon model
|
36 |
+
@st.cache_resource
|
37 |
+
def get_llm():
|
38 |
+
return ChatOpenAI(
|
39 |
+
model="tiiuae/falcon-180B-chat",
|
40 |
+
api_key=AI71_API_KEY,
|
41 |
+
base_url=AI71_BASE_URL,
|
42 |
+
streaming=True,
|
43 |
+
)
|
44 |
+
|
45 |
+
# Initialize embeddings
|
46 |
+
@st.cache_resource
|
47 |
+
def get_embeddings():
|
48 |
+
return HuggingFaceEmbeddings()
|
49 |
+
|
50 |
+
def process_documents(uploaded_files):
|
51 |
+
documents = []
|
52 |
+
for uploaded_file in uploaded_files:
|
53 |
+
file_extension = os.path.splitext(uploaded_file.name)[1].lower()
|
54 |
+
try:
|
55 |
+
if file_extension in [".txt", ".md"]:
|
56 |
+
content = uploaded_file.getvalue().decode("utf-8")
|
57 |
+
documents.append(Document(page_content=content, metadata={"source": uploaded_file.name}))
|
58 |
+
elif file_extension == ".docx":
|
59 |
+
docx_file = io.BytesIO(uploaded_file.getvalue())
|
60 |
+
doc = DocxDocument(docx_file)
|
61 |
+
content = "\n".join([para.text for para in doc.paragraphs])
|
62 |
+
documents.append(Document(page_content=content, metadata={"source": uploaded_file.name}))
|
63 |
+
elif file_extension == ".pdf":
|
64 |
+
pdf_file = io.BytesIO(uploaded_file.getvalue())
|
65 |
+
pdf_reader = PdfReader(pdf_file)
|
66 |
+
content = ""
|
67 |
+
for page in pdf_reader.pages:
|
68 |
+
content += page.extract_text()
|
69 |
+
documents.append(Document(page_content=content, metadata={"source": uploaded_file.name}))
|
70 |
+
else:
|
71 |
+
st.warning(f"Unsupported file type: {file_extension}")
|
72 |
+
except Exception as e:
|
73 |
+
st.error(f"Error processing file {uploaded_file.name}: {str(e)}")
|
74 |
+
|
75 |
+
if not documents:
|
76 |
+
return None
|
77 |
+
|
78 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
|
79 |
+
texts = text_splitter.split_documents(documents)
|
80 |
+
|
81 |
+
vectorstore = FAISS.from_documents(texts, get_embeddings())
|
82 |
+
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
|
83 |
+
|
84 |
+
qa_chain = RetrievalQA.from_chain_type(
|
85 |
+
llm=get_llm(),
|
86 |
+
chain_type="stuff",
|
87 |
+
retriever=retriever,
|
88 |
+
return_source_documents=True,
|
89 |
+
)
|
90 |
+
|
91 |
+
return qa_chain
|
92 |
+
|
93 |
+
def get_chatbot_response(user_input, qa_chain=None, personality="default", web_search=False):
|
94 |
+
system_message = get_personality_prompt(personality)
|
95 |
+
|
96 |
+
web_info = ""
|
97 |
+
if web_search:
|
98 |
+
web_results = search_web_duckduckgo(user_input)
|
99 |
+
web_info = "\n\n".join([f"Title: {result['title']}\nLink: {result['link']}\nSnippet: {result['snippet']}" for result in web_results])
|
100 |
+
user_input += f"\n\nWeb search results:\n{web_info}"
|
101 |
+
|
102 |
+
if qa_chain:
|
103 |
+
result = qa_chain({"query": user_input})
|
104 |
+
response = result['result']
|
105 |
+
source_docs = result.get('source_documents', [])
|
106 |
+
else:
|
107 |
+
messages = [
|
108 |
+
SystemMessage(content=system_message),
|
109 |
+
HumanMessage(content=user_input)
|
110 |
+
]
|
111 |
+
response = get_llm().invoke(messages).content
|
112 |
+
source_docs = []
|
113 |
+
|
114 |
+
return response, source_docs, web_results if web_search else None
|
115 |
+
|
116 |
+
def get_personality_prompt(personality):
|
117 |
+
personalities = {
|
118 |
+
"default": "You are a helpful assistant.",
|
119 |
+
"sherlock": "You are Sherlock Holmes, the world's greatest detective. Respond with keen observation and deductive reasoning.",
|
120 |
+
"yoda": "Wise and cryptic, you are. Like Yoda from Star Wars, speak you must.",
|
121 |
+
"shakespeare": "Thou art the Bard himself. In iambic pentameter, respond with eloquence and poetic flair.",
|
122 |
+
"custom": st.session_state.custom_personality
|
123 |
+
}
|
124 |
+
return personalities.get(personality, personalities["default"])
|
125 |
+
|
126 |
+
def search_web_duckduckgo(query: str, num_results: int = 3, max_retries: int = 3):
|
127 |
+
api_key = "AIzaSyD-1OMuZ0CxGAek0PaXrzHOmcDWFvZQtm8"
|
128 |
+
cse_id = "877170db56f5c4629"
|
129 |
+
|
130 |
+
for attempt in range(max_retries):
|
131 |
+
try:
|
132 |
+
service = build("customsearch", "v1", developerKey=api_key)
|
133 |
+
res = service.cse().list(q=query, cx=cse_id, num=num_results).execute()
|
134 |
+
results = []
|
135 |
+
if "items" in res:
|
136 |
+
for item in res["items"]:
|
137 |
+
result = {
|
138 |
+
"title": item["title"],
|
139 |
+
"link": item["link"],
|
140 |
+
"snippet": item.get("snippet", "")
|
141 |
+
}
|
142 |
+
results.append(result)
|
143 |
+
return results
|
144 |
+
except HttpError as e:
|
145 |
+
print(f"HTTP error occurred: {e}. Attempt {attempt + 1} of {max_retries}")
|
146 |
+
except Exception as e:
|
147 |
+
print(f"An unexpected error occurred: {e}. Attempt {attempt + 1} of {max_retries}")
|
148 |
+
time.sleep(2 ** attempt)
|
149 |
+
print("Max retries reached. No results found.")
|
150 |
+
return []
|
151 |
+
|
152 |
+
def main():
|
153 |
+
st.set_page_config(page_title="S.H.E.R.L.O.C.K. Chatbot", page_icon="π΅οΈ", layout="wide")
|
154 |
+
|
155 |
+
st.title("S.H.E.R.L.O.C.K. Chatbot")
|
156 |
+
|
157 |
+
# Sidebar
|
158 |
+
with st.sidebar:
|
159 |
+
st.image("data:image/jpeg;base64,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", use_column_width=True)
|
160 |
+
|
161 |
+
st.subheader("π Document Upload")
|
162 |
+
uploaded_files = st.file_uploader("Upload documents", type=["txt", "md", "docx", "pdf"], accept_multiple_files=True)
|
163 |
+
|
164 |
+
st.subheader("π Chatbot Personality")
|
165 |
+
personality = st.selectbox("Choose chatbot personality", ["default", "sherlock", "yoda", "shakespeare", "custom"])
|
166 |
+
|
167 |
+
if personality == "custom":
|
168 |
+
st.session_state.custom_personality = st.text_area("Enter custom personality details:", value=st.session_state.custom_personality)
|
169 |
+
|
170 |
+
st.subheader("π Web Search")
|
171 |
+
web_search = st.checkbox("Enable web search")
|
172 |
+
|
173 |
+
st.subheader("π¬ Chat Mode")
|
174 |
+
chat_mode = st.radio("Select chat mode", ["General Chat", "Document Chat"])
|
175 |
+
|
176 |
+
if st.button("Clear Chat History"):
|
177 |
+
st.session_state.messages = []
|
178 |
+
st.rerun()
|
179 |
+
|
180 |
+
# Main content
|
181 |
+
if uploaded_files:
|
182 |
+
qa_chain = process_documents(uploaded_files)
|
183 |
+
if qa_chain:
|
184 |
+
st.success("Documents processed successfully!")
|
185 |
+
else:
|
186 |
+
st.warning("No valid documents were uploaded or processed.")
|
187 |
+
else:
|
188 |
+
qa_chain = None
|
189 |
+
|
190 |
+
# Chat interface
|
191 |
+
for message in st.session_state.messages:
|
192 |
+
with st.chat_message(message["role"]):
|
193 |
+
st.markdown(message["content"])
|
194 |
+
|
195 |
+
if prompt := st.chat_input("What is your question?"):
|
196 |
+
st.chat_message("user").markdown(prompt)
|
197 |
+
st.session_state.messages.append({"role": "user", "content": prompt})
|
198 |
+
|
199 |
+
if chat_mode == "General Chat" or not qa_chain:
|
200 |
+
response, _, web_results = get_chatbot_response(prompt, personality=personality, web_search=web_search)
|
201 |
+
else:
|
202 |
+
response, source_docs, web_results = get_chatbot_response(prompt, qa_chain, personality, web_search)
|
203 |
+
|
204 |
+
with st.chat_message("assistant"):
|
205 |
+
st.markdown(response)
|
206 |
+
if chat_mode == "Document Chat" and qa_chain and source_docs:
|
207 |
+
with st.expander("Source Documents"):
|
208 |
+
for doc in source_docs:
|
209 |
+
st.markdown(f"**Source:** {doc.metadata.get('source', 'Unknown')}")
|
210 |
+
st.markdown(doc.page_content[:200] + "...")
|
211 |
+
|
212 |
+
if web_search and web_results:
|
213 |
+
with st.expander("Web Search Results"):
|
214 |
+
for result in web_results:
|
215 |
+
st.markdown(f"**[{result['title']}]({result['link']})**")
|
216 |
+
st.markdown(result['snippet'])
|
217 |
+
|
218 |
+
st.session_state.messages.append({"role": "assistant", "content": response})
|
219 |
+
|
220 |
+
# Chat history and download
|
221 |
+
with st.sidebar:
|
222 |
+
st.subheader("π Chat History")
|
223 |
+
history_expander = st.expander("View Chat History")
|
224 |
+
with history_expander:
|
225 |
+
for message in st.session_state.messages:
|
226 |
+
st.text(f"{message['role']}: {message['content'][:50]}...")
|
227 |
+
|
228 |
+
if st.session_state.messages:
|
229 |
+
chat_history_df = pd.DataFrame(st.session_state.messages)
|
230 |
+
csv = chat_history_df.to_csv(index=False)
|
231 |
+
st.download_button(
|
232 |
+
label="π₯ Download Chat History",
|
233 |
+
data=csv,
|
234 |
+
file_name="chat_history.csv",
|
235 |
+
mime="text/csv",
|
236 |
+
)
|
237 |
+
|
238 |
+
st.sidebar.markdown("---")
|
239 |
+
st.sidebar.markdown("Powered by Falcon-180B and Streamlit")
|
240 |
+
|
241 |
+
if __name__ == "__main__":
|
242 |
+
main()
|
pages/exam_prepration.py
ADDED
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import random
|
3 |
+
import time
|
4 |
+
from typing import List, Dict
|
5 |
+
from langchain_openai import ChatOpenAI
|
6 |
+
from langchain.schema import HumanMessage, SystemMessage
|
7 |
+
from langchain_community.document_loaders import PyPDFLoader, TextLoader, UnstructuredWordDocumentLoader
|
8 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
9 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
10 |
+
from langchain_community.vectorstores import FAISS
|
11 |
+
from langchain.chains import RetrievalQA
|
12 |
+
from langchain_community.graphs import NetworkxEntityGraph
|
13 |
+
from googleapiclient.discovery import build
|
14 |
+
from googleapiclient.errors import HttpError
|
15 |
+
import os
|
16 |
+
from dotenv import load_dotenv
|
17 |
+
import requests
|
18 |
+
from bs4 import BeautifulSoup
|
19 |
+
|
20 |
+
# Load environment variables
|
21 |
+
load_dotenv()
|
22 |
+
|
23 |
+
AI71_BASE_URL = "https://api.ai71.ai/v1/"
|
24 |
+
AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
|
25 |
+
GOOGLE_API_KEY = "AIzaSyD-1OMuZ0CxGAek0PaXrzHOmcDWFvZQtm8"
|
26 |
+
GOOGLE_CSE_ID = "877170db56f5c4629"
|
27 |
+
YOUTUBE_API_KEY = "AIzaSyD-1OMuZ0CxGAek0PaXrzHOmcDWFvZQtm8"
|
28 |
+
|
29 |
+
# Initialize the Falcon model
|
30 |
+
chat = ChatOpenAI(
|
31 |
+
model="tiiuae/falcon-180B-chat",
|
32 |
+
api_key=AI71_API_KEY,
|
33 |
+
base_url=AI71_BASE_URL,
|
34 |
+
streaming=True,
|
35 |
+
)
|
36 |
+
|
37 |
+
# Initialize embeddings
|
38 |
+
embeddings = HuggingFaceEmbeddings()
|
39 |
+
|
40 |
+
FIELDS = [
|
41 |
+
"Mathematics", "Physics", "Chemistry", "Biology", "Computer Science",
|
42 |
+
"History", "Geography", "Literature", "Philosophy", "Psychology",
|
43 |
+
"Sociology", "Economics", "Business", "Finance", "Accounting",
|
44 |
+
"Law", "Political Science", "Environmental Science", "Astronomy", "Geology",
|
45 |
+
"Linguistics", "Anthropology", "Art History", "Music Theory", "Film Studies",
|
46 |
+
"Medical Science", "Nursing", "Public Health", "Nutrition", "Physical Education",
|
47 |
+
"Engineering", "Architecture", "Urban Planning", "Agriculture", "Veterinary Science",
|
48 |
+
"Oceanography", "Meteorology", "Statistics", "Data Science", "Artificial Intelligence",
|
49 |
+
"Cybersecurity", "Renewable Energy", "Quantum Physics", "Neuroscience", "Genetics",
|
50 |
+
"Biotechnology", "Nanotechnology", "Robotics", "Space Exploration", "Cryptography"
|
51 |
+
]
|
52 |
+
|
53 |
+
# List of educational resources
|
54 |
+
EDUCATIONAL_RESOURCES = [
|
55 |
+
"https://www.coursera.org",
|
56 |
+
"https://www.khanacademy.org",
|
57 |
+
"https://scholar.google.com",
|
58 |
+
"https://www.edx.org",
|
59 |
+
"https://www.udacity.com",
|
60 |
+
"https://www.udemy.com",
|
61 |
+
"https://www.futurelearn.com",
|
62 |
+
"https://www.lynda.com",
|
63 |
+
"https://www.skillshare.com",
|
64 |
+
"https://www.codecademy.com",
|
65 |
+
"https://www.brilliant.org",
|
66 |
+
"https://www.duolingo.com",
|
67 |
+
"https://www.ted.com/talks",
|
68 |
+
"https://ocw.mit.edu",
|
69 |
+
"https://www.open.edu/openlearn",
|
70 |
+
"https://www.coursebuffet.com",
|
71 |
+
"https://www.academicearth.org",
|
72 |
+
"https://www.edutopia.org",
|
73 |
+
"https://www.saylor.org",
|
74 |
+
"https://www.openculture.com",
|
75 |
+
"https://www.gutenberg.org",
|
76 |
+
"https://www.archive.org",
|
77 |
+
"https://www.wolframalpha.com",
|
78 |
+
"https://www.quizlet.com",
|
79 |
+
"https://www.mathway.com",
|
80 |
+
"https://www.symbolab.com",
|
81 |
+
"https://www.lessonplanet.com",
|
82 |
+
"https://www.teacherspayteachers.com",
|
83 |
+
"https://www.brainpop.com",
|
84 |
+
"https://www.ck12.org"
|
85 |
+
]
|
86 |
+
|
87 |
+
def search_web(query: str, num_results: int = 30, max_retries: int = 3) -> List[Dict[str, str]]:
|
88 |
+
user_agents = [
|
89 |
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
90 |
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15',
|
91 |
+
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36'
|
92 |
+
]
|
93 |
+
|
94 |
+
for attempt in range(max_retries):
|
95 |
+
try:
|
96 |
+
headers = {'User-Agent': random.choice(user_agents)}
|
97 |
+
service = build("customsearch", "v1", developerKey=GOOGLE_API_KEY)
|
98 |
+
res = service.cse().list(q=query, cx=GOOGLE_CSE_ID, num=num_results).execute()
|
99 |
+
|
100 |
+
results = []
|
101 |
+
if "items" in res:
|
102 |
+
for item in res["items"]:
|
103 |
+
result = {
|
104 |
+
"title": item["title"],
|
105 |
+
"link": item["link"],
|
106 |
+
"snippet": item.get("snippet", "")
|
107 |
+
}
|
108 |
+
results.append(result)
|
109 |
+
|
110 |
+
return results
|
111 |
+
except Exception as e:
|
112 |
+
print(f"An error occurred: {e}. Attempt {attempt + 1} of {max_retries}")
|
113 |
+
time.sleep(2 ** attempt)
|
114 |
+
|
115 |
+
print("Max retries reached. No results found.")
|
116 |
+
return []
|
117 |
+
|
118 |
+
def scrape_webpage(url: str) -> str:
|
119 |
+
try:
|
120 |
+
response = requests.get(url, timeout=10)
|
121 |
+
soup = BeautifulSoup(response.content, 'html.parser')
|
122 |
+
return soup.get_text()
|
123 |
+
except Exception as e:
|
124 |
+
print(f"Error scraping {url}: {e}")
|
125 |
+
return ""
|
126 |
+
|
127 |
+
def process_documents(uploaded_files):
|
128 |
+
documents = []
|
129 |
+
for uploaded_file in uploaded_files:
|
130 |
+
file_extension = os.path.splitext(uploaded_file.name)[1].lower()
|
131 |
+
|
132 |
+
if file_extension == '.pdf':
|
133 |
+
loader = PyPDFLoader(uploaded_file)
|
134 |
+
elif file_extension in ['.txt', '.md']:
|
135 |
+
loader = TextLoader(uploaded_file)
|
136 |
+
elif file_extension in ['.doc', '.docx']:
|
137 |
+
loader = UnstructuredWordDocumentLoader(uploaded_file)
|
138 |
+
else:
|
139 |
+
st.warning(f"Unsupported file type: {file_extension}")
|
140 |
+
continue
|
141 |
+
|
142 |
+
documents.extend(loader.load())
|
143 |
+
|
144 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
|
145 |
+
texts = text_splitter.split_documents(documents)
|
146 |
+
|
147 |
+
vectorstore = FAISS.from_documents(texts, embeddings)
|
148 |
+
graph = NetworkxEntityGraph()
|
149 |
+
graph.add_documents(texts)
|
150 |
+
|
151 |
+
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
|
152 |
+
|
153 |
+
qa_chain = RetrievalQA.from_chain_type(
|
154 |
+
llm=chat,
|
155 |
+
chain_type="stuff",
|
156 |
+
retriever=retriever,
|
157 |
+
return_source_documents=True
|
158 |
+
)
|
159 |
+
|
160 |
+
return qa_chain, graph
|
161 |
+
|
162 |
+
def generate_questions(topic, difficulty, num_questions, include_answers, qa_chain=None, graph=None):
|
163 |
+
system_prompt = f"""You are an expert exam question generator. Generate {num_questions} {difficulty}-level questions about {topic}.
|
164 |
+
{"Each question should be followed by its correct answer." if include_answers else "Do not include answers."}
|
165 |
+
Format your response as follows:
|
166 |
+
|
167 |
+
Q1. [Question]
|
168 |
+
{"A1. [Answer]" if include_answers else ""}
|
169 |
+
|
170 |
+
Q2. [Question]
|
171 |
+
{"A2. [Answer]" if include_answers else ""}
|
172 |
+
|
173 |
+
... and so on.
|
174 |
+
"""
|
175 |
+
|
176 |
+
if qa_chain and graph:
|
177 |
+
context = graph.get_relevant_documents(topic)
|
178 |
+
context_text = "\n".join([doc.page_content for doc in context])
|
179 |
+
|
180 |
+
result = qa_chain({"query": system_prompt, "context": context_text})
|
181 |
+
questions = result['result']
|
182 |
+
else:
|
183 |
+
messages = [
|
184 |
+
SystemMessage(content=system_prompt),
|
185 |
+
HumanMessage(content=f"Please generate {num_questions} {difficulty} questions about {topic}.")
|
186 |
+
]
|
187 |
+
questions = chat(messages).content
|
188 |
+
|
189 |
+
return questions
|
190 |
+
|
191 |
+
def gather_resources(field: str) -> List[Dict[str, str]]:
|
192 |
+
resources = []
|
193 |
+
for resource_url in EDUCATIONAL_RESOURCES:
|
194 |
+
search_results = search_web(f"site:{resource_url} {field}", num_results=1)
|
195 |
+
if search_results:
|
196 |
+
result = search_results[0]
|
197 |
+
content = scrape_webpage(result['link'])
|
198 |
+
resources.append({
|
199 |
+
"title": result['title'],
|
200 |
+
"link": result['link'],
|
201 |
+
"content": content[:500] + "..." if len(content) > 500 else content
|
202 |
+
})
|
203 |
+
|
204 |
+
# YouTube search
|
205 |
+
youtube = build('youtube', 'v3', developerKey=YOUTUBE_API_KEY)
|
206 |
+
youtube_results = youtube.search().list(q=field, type='video', part='id,snippet', maxResults=5).execute()
|
207 |
+
for item in youtube_results.get('items', []):
|
208 |
+
video_id = item['id']['videoId']
|
209 |
+
resources.append({
|
210 |
+
"title": item['snippet']['title'],
|
211 |
+
"link": f"https://www.youtube.com/watch?v={video_id}",
|
212 |
+
"content": item['snippet']['description'],
|
213 |
+
"thumbnail": item['snippet']['thumbnails']['medium']['url']
|
214 |
+
})
|
215 |
+
|
216 |
+
return resources
|
217 |
+
|
218 |
+
def main():
|
219 |
+
st.set_page_config(page_title="Advanced Exam Preparation System", layout="wide")
|
220 |
+
|
221 |
+
st.sidebar.title("Advanced Exam Prep")
|
222 |
+
st.sidebar.markdown("""
|
223 |
+
Welcome to our advanced exam preparation system!
|
224 |
+
Here you can generate practice questions, explore educational resources,
|
225 |
+
and interact with an AI tutor to enhance your learning experience.
|
226 |
+
""")
|
227 |
+
|
228 |
+
# Main area tabs
|
229 |
+
tab1, tab2, tab3 = st.tabs(["Question Generator", "Resource Explorer", "Academic Tutor"])
|
230 |
+
|
231 |
+
with tab1:
|
232 |
+
st.header("Question Generator")
|
233 |
+
col1, col2 = st.columns(2)
|
234 |
+
with col1:
|
235 |
+
topic = st.text_input("Enter the exam topic:")
|
236 |
+
exam_type = st.selectbox("Select exam type:", ["General", "STEM", "Humanities", "Business", "Custom"])
|
237 |
+
with col2:
|
238 |
+
difficulty = st.select_slider(
|
239 |
+
"Select difficulty level:",
|
240 |
+
options=["Super Easy", "Easy", "Beginner", "Intermediate", "Higher Intermediate", "Master", "Advanced"]
|
241 |
+
)
|
242 |
+
num_questions = st.number_input("Number of questions:", min_value=1, max_value=50, value=5)
|
243 |
+
include_answers = st.checkbox("Include answers", value=True)
|
244 |
+
|
245 |
+
if st.button("Generate Questions", key="generate_questions"):
|
246 |
+
if topic:
|
247 |
+
with st.spinner("Generating questions..."):
|
248 |
+
questions = generate_questions(topic, difficulty, num_questions, include_answers)
|
249 |
+
st.success("Questions generated successfully!")
|
250 |
+
st.markdown(questions)
|
251 |
+
else:
|
252 |
+
st.warning("Please enter a topic.")
|
253 |
+
|
254 |
+
with tab2:
|
255 |
+
st.header("Resource Explorer")
|
256 |
+
selected_field = st.selectbox("Select a field to explore:", FIELDS)
|
257 |
+
if st.button("Explore Resources", key="explore_resources"):
|
258 |
+
with st.spinner("Gathering resources..."):
|
259 |
+
resources = gather_resources(selected_field)
|
260 |
+
st.success(f"Found {len(resources)} resources!")
|
261 |
+
|
262 |
+
for i, resource in enumerate(resources):
|
263 |
+
col1, col2 = st.columns([1, 3])
|
264 |
+
with col1:
|
265 |
+
if "thumbnail" in resource:
|
266 |
+
st.image(resource["thumbnail"], use_column_width=True)
|
267 |
+
else:
|
268 |
+
st.image("https://via.placeholder.com/150", use_column_width=True)
|
269 |
+
with col2:
|
270 |
+
st.subheader(f"[{resource['title']}]({resource['link']})")
|
271 |
+
st.write(resource['content'])
|
272 |
+
st.markdown("---")
|
273 |
+
|
274 |
+
with tab3:
|
275 |
+
st.header("Academic Tutor")
|
276 |
+
uploaded_files = st.file_uploader("Upload documents (PDF, TXT, MD, DOC, DOCX)", type=["pdf", "txt", "md", "doc", "docx"], accept_multiple_files=True)
|
277 |
+
|
278 |
+
if uploaded_files:
|
279 |
+
qa_chain, graph = process_documents(uploaded_files)
|
280 |
+
st.success("Documents processed successfully!")
|
281 |
+
else:
|
282 |
+
qa_chain, graph = None, None
|
283 |
+
|
284 |
+
st.subheader("Chat with AI Tutor")
|
285 |
+
if 'chat_history' not in st.session_state:
|
286 |
+
st.session_state.chat_history = []
|
287 |
+
|
288 |
+
chat_container = st.container()
|
289 |
+
with chat_container:
|
290 |
+
for i, (role, message) in enumerate(st.session_state.chat_history):
|
291 |
+
with st.chat_message(role):
|
292 |
+
st.write(message)
|
293 |
+
|
294 |
+
user_input = st.chat_input("Ask a question or type 'search: your query' to perform a web search:")
|
295 |
+
if user_input:
|
296 |
+
st.session_state.chat_history.append(("user", user_input))
|
297 |
+
with st.chat_message("user"):
|
298 |
+
st.write(user_input)
|
299 |
+
|
300 |
+
with st.chat_message("assistant"):
|
301 |
+
if user_input.lower().startswith("search:"):
|
302 |
+
search_query = user_input[7:].strip()
|
303 |
+
search_results = search_web(search_query, num_results=3)
|
304 |
+
response = f"Here are some search results for '{search_query}':\n\n"
|
305 |
+
for result in search_results:
|
306 |
+
response += f"- [{result['title']}]({result['link']})\n {result['snippet']}\n\n"
|
307 |
+
else:
|
308 |
+
response = chat([HumanMessage(content=user_input)]).content
|
309 |
+
st.write(response)
|
310 |
+
st.session_state.chat_history.append(("assistant", response))
|
311 |
+
|
312 |
+
# Scroll to bottom of chat
|
313 |
+
js = f"""
|
314 |
+
<script>
|
315 |
+
function scroll_to_bottom() {{
|
316 |
+
var chatElement = window.parent.document.querySelector('.stChatFloatingInputContainer');
|
317 |
+
chatElement.scrollIntoView({{behavior: 'smooth'}});
|
318 |
+
}}
|
319 |
+
scroll_to_bottom();
|
320 |
+
</script>
|
321 |
+
"""
|
322 |
+
st.components.v1.html(js)
|
323 |
+
|
324 |
+
if __name__ == "__main__":
|
325 |
+
main()
|
pages/interview_prepration.py
ADDED
@@ -0,0 +1,560 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import random
|
3 |
+
from langchain_community.chat_models import ChatOpenAI
|
4 |
+
from langchain.schema import HumanMessage, SystemMessage
|
5 |
+
import os
|
6 |
+
from dotenv import load_dotenv
|
7 |
+
import base64
|
8 |
+
import cv2
|
9 |
+
import numpy as np
|
10 |
+
from PIL import Image
|
11 |
+
import io
|
12 |
+
import time
|
13 |
+
import PyPDF2
|
14 |
+
import docx
|
15 |
+
import markdown
|
16 |
+
|
17 |
+
# Load environment variables
|
18 |
+
load_dotenv()
|
19 |
+
|
20 |
+
AI71_BASE_URL = "https://api.ai71.ai/v1/"
|
21 |
+
AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
|
22 |
+
|
23 |
+
# Initialize the Falcon model
|
24 |
+
chat = ChatOpenAI(
|
25 |
+
model="tiiuae/falcon-180B-chat",
|
26 |
+
api_key=AI71_API_KEY,
|
27 |
+
base_url=AI71_BASE_URL,
|
28 |
+
streaming=True,
|
29 |
+
timeout=60,
|
30 |
+
)
|
31 |
+
|
32 |
+
# Expanded list of roles
|
33 |
+
roles = [
|
34 |
+
"Software Engineer", "Data Scientist", "Product Manager", "UX Designer", "Marketing Manager",
|
35 |
+
"Sales Representative", "Human Resources Manager", "Financial Analyst", "Project Manager",
|
36 |
+
"Business Analyst", "Content Writer", "Graphic Designer", "Customer Service Representative",
|
37 |
+
"Operations Manager", "Research Scientist", "Legal Counsel", "Network Administrator",
|
38 |
+
"Quality Assurance Tester", "Supply Chain Manager", "Public Relations Specialist"
|
39 |
+
]
|
40 |
+
|
41 |
+
def generate_interview_questions(role):
|
42 |
+
system_message = f"""You are an experienced interviewer for the role of {role}.
|
43 |
+
Generate 5 challenging and relevant interview questions for this position.
|
44 |
+
The questions should cover a range of skills and experiences required for the role."""
|
45 |
+
|
46 |
+
messages = [
|
47 |
+
SystemMessage(content=system_message),
|
48 |
+
HumanMessage(content="Please provide 5 interview questions for this role.")
|
49 |
+
]
|
50 |
+
|
51 |
+
response = chat.invoke(messages).content
|
52 |
+
questions = response.split('\n')
|
53 |
+
return [q.strip() for q in questions if q.strip()]
|
54 |
+
|
55 |
+
def get_interview_response(role, question, answer):
|
56 |
+
system_message = f"""You are an experienced interviewer for the role of {role}.
|
57 |
+
Your task is to evaluate the candidate's response to the following question: '{question}'
|
58 |
+
|
59 |
+
The candidate's answer was: '{answer}'
|
60 |
+
|
61 |
+
Please provide:
|
62 |
+
1. A brief evaluation of the answer (2-3 sentences)
|
63 |
+
2. Specific feedback on how to improve (if needed) or praise for a good answer
|
64 |
+
3. A follow-up question based on their response
|
65 |
+
4. A score out of 10 for their answer
|
66 |
+
|
67 |
+
Format your response as follows:
|
68 |
+
Evaluation: [Your evaluation here]
|
69 |
+
Feedback: [Your specific feedback or praise here]
|
70 |
+
Follow-up: [Your follow-up question here]
|
71 |
+
Score: [Score out of 10]
|
72 |
+
"""
|
73 |
+
|
74 |
+
messages = [
|
75 |
+
SystemMessage(content=system_message),
|
76 |
+
HumanMessage(content="Please provide your evaluation, feedback, follow-up question, and score.")
|
77 |
+
]
|
78 |
+
|
79 |
+
response = chat.invoke(messages).content
|
80 |
+
return response
|
81 |
+
|
82 |
+
def analyze_appearance(image):
|
83 |
+
# Convert PIL Image to OpenCV format
|
84 |
+
cv_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
|
85 |
+
|
86 |
+
# Load pre-trained face detection model
|
87 |
+
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
|
88 |
+
|
89 |
+
# Convert to grayscale for face detection
|
90 |
+
gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)
|
91 |
+
|
92 |
+
# Detect faces
|
93 |
+
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
|
94 |
+
|
95 |
+
analysis = []
|
96 |
+
|
97 |
+
if len(faces) == 0:
|
98 |
+
analysis.append("No face detected in the image. Please ensure your face is clearly visible.")
|
99 |
+
else:
|
100 |
+
analysis.append(f"Detected {len(faces)} face(s) in the image.")
|
101 |
+
|
102 |
+
# Analyze facial positioning
|
103 |
+
for (x, y, w, h) in faces:
|
104 |
+
face_center = (x + w//2, y + h//2)
|
105 |
+
image_center = (cv_image.shape[1]//2, cv_image.shape[0]//2)
|
106 |
+
|
107 |
+
if abs(face_center[0] - image_center[0]) > cv_image.shape[1]//8:
|
108 |
+
analysis.append("Your face is not centered horizontally. Try to position yourself in the middle of the frame.")
|
109 |
+
|
110 |
+
if abs(face_center[1] - image_center[1]) > cv_image.shape[0]//8:
|
111 |
+
analysis.append("Your face is not centered vertically. Adjust your camera or seating position.")
|
112 |
+
|
113 |
+
if w * h < (cv_image.shape[0] * cv_image.shape[1]) // 16:
|
114 |
+
analysis.append("Your face appears too small in the frame. Consider moving closer to the camera.")
|
115 |
+
elif w * h > (cv_image.shape[0] * cv_image.shape[1]) // 4:
|
116 |
+
analysis.append("Your face appears too large in the frame. Consider moving slightly away from the camera.")
|
117 |
+
|
118 |
+
# Analyze image brightness
|
119 |
+
brightness = np.mean(gray)
|
120 |
+
if brightness < 100:
|
121 |
+
analysis.append("The image appears too dark. Consider improving your lighting for better visibility.")
|
122 |
+
elif brightness > 200:
|
123 |
+
analysis.append("The image appears too bright. You might want to reduce harsh lighting or adjust your camera settings.")
|
124 |
+
|
125 |
+
# Analyze image contrast
|
126 |
+
contrast = np.std(gray)
|
127 |
+
if contrast < 20:
|
128 |
+
analysis.append("The image lacks contrast. This might make it difficult to see details. Consider adjusting your lighting or camera settings.")
|
129 |
+
|
130 |
+
return "\n".join(analysis)
|
131 |
+
|
132 |
+
def extract_text_from_file(file):
|
133 |
+
file_extension = file.name.split('.')[-1].lower()
|
134 |
+
|
135 |
+
if file_extension == 'pdf':
|
136 |
+
pdf_reader = PyPDF2.PdfReader(file)
|
137 |
+
text = ""
|
138 |
+
for page in pdf_reader.pages:
|
139 |
+
text += page.extract_text()
|
140 |
+
elif file_extension == 'docx':
|
141 |
+
doc = docx.Document(file)
|
142 |
+
text = "\n".join([paragraph.text for paragraph in doc.paragraphs])
|
143 |
+
elif file_extension == 'txt':
|
144 |
+
text = file.read().decode()
|
145 |
+
elif file_extension == 'md':
|
146 |
+
md_text = file.read().decode()
|
147 |
+
text = markdown.markdown(md_text)
|
148 |
+
else:
|
149 |
+
raise ValueError(f"Unsupported file format: {file_extension}")
|
150 |
+
|
151 |
+
return text
|
152 |
+
|
153 |
+
def analyze_cv(cv_text):
|
154 |
+
system_message = """You are an expert CV reviewer with extensive experience in various industries.
|
155 |
+
Analyze the given CV and provide:
|
156 |
+
1. An overall assessment of the CV's strengths
|
157 |
+
2. Areas that need improvement
|
158 |
+
3. Specific suggestions for enhancing the CV
|
159 |
+
4. Tips for tailoring the CV to specific job applications
|
160 |
+
|
161 |
+
Be thorough, constructive, and provide actionable advice."""
|
162 |
+
|
163 |
+
messages = [
|
164 |
+
SystemMessage(content=system_message),
|
165 |
+
HumanMessage(content=f"Here's the text of the CV to review:\n\n{cv_text}\n\nPlease provide your analysis and suggestions.")
|
166 |
+
]
|
167 |
+
|
168 |
+
response = chat.invoke(messages).content
|
169 |
+
return response
|
170 |
+
|
171 |
+
def resize_image(image, max_size=800):
|
172 |
+
"""Resize image while maintaining aspect ratio"""
|
173 |
+
ratio = max_size / max(image.size)
|
174 |
+
new_size = tuple([int(x*ratio) for x in image.size])
|
175 |
+
return image.resize(new_size, Image.LANCZOS)
|
176 |
+
|
177 |
+
def get_mock_interview_tips():
|
178 |
+
tips = [
|
179 |
+
"Research the company and role thoroughly before the interview.",
|
180 |
+
"Practice common interview questions with a friend or family member.",
|
181 |
+
"Prepare specific examples to illustrate your skills and experiences.",
|
182 |
+
"Dress professionally and ensure your background is tidy for video interviews.",
|
183 |
+
"Have questions prepared to ask the interviewer about the role and company.",
|
184 |
+
"Use the STAR method (Situation, Task, Action, Result) to structure your answers.",
|
185 |
+
"Be aware of your body language and maintain good eye contact.",
|
186 |
+
"Listen carefully to each question and take a moment to gather your thoughts before answering.",
|
187 |
+
"Be honest about your experiences and skills, but focus on your strengths.",
|
188 |
+
"Follow up with a thank-you note or email after the interview.",
|
189 |
+
]
|
190 |
+
return tips
|
191 |
+
|
192 |
+
def get_interview_resources():
|
193 |
+
resources = [
|
194 |
+
{"name": "Glassdoor Interview Questions & Reviews", "url": "https://www.glassdoor.com/Interview/index.htm"},
|
195 |
+
{"name": "LinkedIn Interview Preparation", "url": "https://www.linkedin.com/interview-prep/"},
|
196 |
+
{"name": "Indeed Career Guide", "url": "https://www.indeed.com/career-advice"},
|
197 |
+
{"name": "Coursera - How to Succeed in an Interview", "url": "https://www.coursera.org/learn/interview-preparation"},
|
198 |
+
{"name": "Harvard Business Review - Interview Tips", "url": "https://hbr.org/topic/interviewing"},
|
199 |
+
]
|
200 |
+
return resources
|
201 |
+
|
202 |
+
def main():
|
203 |
+
st.set_page_config(page_title="S.H.E.R.L.O.C.K. Interview Preparation", page_icon="ποΈ", layout="wide")
|
204 |
+
|
205 |
+
st.title("ποΈ S.H.E.R.L.O.C.K. Interview Preparation")
|
206 |
+
st.markdown("### Streamlined Help for Enhancing Responsive Learning and Optimizing Career Knowledge")
|
207 |
+
|
208 |
+
# Sidebar for user details and interview settings
|
209 |
+
with st.sidebar:
|
210 |
+
st.header("Interview Settings")
|
211 |
+
name = st.text_input("Your Name")
|
212 |
+
role = st.selectbox("Interview Role", roles)
|
213 |
+
experience = st.slider("Years of Experience", 0, 20, 5)
|
214 |
+
|
215 |
+
st.header("Quick Tips")
|
216 |
+
if st.button("Get Mock Interview Tips"):
|
217 |
+
tips = get_mock_interview_tips()
|
218 |
+
for tip in tips:
|
219 |
+
st.info(tip)
|
220 |
+
|
221 |
+
st.header("Useful Resources")
|
222 |
+
resources = get_interview_resources()
|
223 |
+
for resource in resources:
|
224 |
+
st.markdown(f"[{resource['name']}]({resource['url']})")
|
225 |
+
|
226 |
+
# Appearance Analysis
|
227 |
+
st.header("Appearance Analysis")
|
228 |
+
uploaded_image = st.file_uploader("Upload your interview outfit image", type=["jpg", "jpeg", "png"])
|
229 |
+
if uploaded_image is not None:
|
230 |
+
try:
|
231 |
+
image = Image.open(uploaded_image)
|
232 |
+
image = resize_image(image)
|
233 |
+
st.image(image, caption="Your uploaded image", use_column_width=True)
|
234 |
+
if st.button("Analyze Appearance"):
|
235 |
+
with st.spinner("Analyzing your appearance..."):
|
236 |
+
appearance_feedback = analyze_appearance(image)
|
237 |
+
st.write(appearance_feedback)
|
238 |
+
|
239 |
+
st.write("\nGeneral tips for professional appearance in video interviews:")
|
240 |
+
tips = [
|
241 |
+
"Dress professionally from head to toe, even if only your upper body is visible.",
|
242 |
+
"Choose solid colors over busy patterns for a less distracting appearance.",
|
243 |
+
"Ensure your background is tidy and professional.",
|
244 |
+
"Position your camera at eye level for the most flattering angle.",
|
245 |
+
"Use soft, diffused lighting to avoid harsh shadows.",
|
246 |
+
"Make eye contact by looking directly into the camera when speaking.",
|
247 |
+
]
|
248 |
+
for tip in tips:
|
249 |
+
st.write(f"- {tip}")
|
250 |
+
except Exception as e:
|
251 |
+
st.error(f"An error occurred while processing the image: {str(e)}")
|
252 |
+
st.info("Please make sure you've uploaded a valid image file.")
|
253 |
+
|
254 |
+
# CV Analysis
|
255 |
+
st.header("CV Analysis")
|
256 |
+
uploaded_cv = st.file_uploader("Upload your CV", type=["pdf", "docx", "txt", "md"])
|
257 |
+
if uploaded_cv is not None:
|
258 |
+
try:
|
259 |
+
cv_text = extract_text_from_file(uploaded_cv)
|
260 |
+
if st.button("Analyze CV"):
|
261 |
+
with st.spinner("Analyzing your CV..."):
|
262 |
+
cv_feedback = analyze_cv(cv_text)
|
263 |
+
st.write(cv_feedback)
|
264 |
+
except Exception as e:
|
265 |
+
st.error(f"An error occurred while processing the CV: {str(e)}")
|
266 |
+
|
267 |
+
# Initialize session state variables
|
268 |
+
if 'interview_started' not in st.session_state:
|
269 |
+
st.session_state.interview_started = False
|
270 |
+
if 'current_question' not in st.session_state:
|
271 |
+
st.session_state.current_question = 0
|
272 |
+
if 'questions' not in st.session_state:
|
273 |
+
st.session_state.questions = []
|
274 |
+
if 'answers' not in st.session_state:
|
275 |
+
st.session_state.answers = []
|
276 |
+
if 'feedback' not in st.session_state:
|
277 |
+
st.session_state.feedback = []
|
278 |
+
if 'scores' not in st.session_state:
|
279 |
+
st.session_state.scores = []
|
280 |
+
if 'chat_history' not in st.session_state:
|
281 |
+
st.session_state.chat_history = []
|
282 |
+
|
283 |
+
# Start Interview button
|
284 |
+
if not st.session_state.interview_started:
|
285 |
+
if st.button("Start Mock Interview"):
|
286 |
+
if name and role:
|
287 |
+
st.session_state.interview_started = True
|
288 |
+
with st.spinner("Generating interview questions..."):
|
289 |
+
st.session_state.questions = generate_interview_questions(role)
|
290 |
+
st.rerun()
|
291 |
+
else:
|
292 |
+
st.warning("Please enter your name and select a role before starting the interview.")
|
293 |
+
|
294 |
+
# Interview in progress
|
295 |
+
if st.session_state.interview_started:
|
296 |
+
st.header("Mock Interview")
|
297 |
+
if st.session_state.current_question < len(st.session_state.questions):
|
298 |
+
st.subheader(f"Question {st.session_state.current_question + 1}")
|
299 |
+
st.write(st.session_state.questions[st.session_state.current_question])
|
300 |
+
|
301 |
+
# Display chat history
|
302 |
+
for i, (q, a, f) in enumerate(st.session_state.chat_history):
|
303 |
+
with st.expander(f"Question {i+1}"):
|
304 |
+
st.write(f"Q: {q}")
|
305 |
+
st.write(f"Your Answer: {a}")
|
306 |
+
st.write(f"Feedback: {f}")
|
307 |
+
|
308 |
+
answer = st.text_area("Your Answer", key=f"answer_{st.session_state.current_question}")
|
309 |
+
|
310 |
+
col1, col2 = st.columns(2)
|
311 |
+
with col1:
|
312 |
+
if st.button("Submit Answer"):
|
313 |
+
if answer:
|
314 |
+
with st.spinner("Evaluating your answer..."):
|
315 |
+
response = get_interview_response(role, st.session_state.questions[st.session_state.current_question], answer)
|
316 |
+
st.session_state.answers.append(answer)
|
317 |
+
st.session_state.feedback.append(response)
|
318 |
+
|
319 |
+
# Extract score from response
|
320 |
+
score_lines = [line for line in response.split('\n') if line.startswith('Score:')]
|
321 |
+
if score_lines:
|
322 |
+
score_str = score_lines[0].split(':')[1].strip()
|
323 |
+
try:
|
324 |
+
score = int(score_str)
|
325 |
+
except ValueError:
|
326 |
+
# If the score is a fraction like "6/10", extract the numerator
|
327 |
+
score = int(score_str.split('/')[0])
|
328 |
+
else:
|
329 |
+
# If no score is found, use a default value
|
330 |
+
score = 5 # or any other default value you prefer
|
331 |
+
st.warning("No score was provided in the response. Using a default score of 5.")
|
332 |
+
|
333 |
+
st.session_state.scores.append(score)
|
334 |
+
|
335 |
+
# Update chat history
|
336 |
+
st.session_state.chat_history.append((
|
337 |
+
st.session_state.questions[st.session_state.current_question],
|
338 |
+
answer,
|
339 |
+
response
|
340 |
+
))
|
341 |
+
|
342 |
+
st.session_state.current_question += 1
|
343 |
+
if st.session_state.current_question < len(st.session_state.questions):
|
344 |
+
st.rerun()
|
345 |
+
else:
|
346 |
+
st.warning("Please provide an answer before submitting.")
|
347 |
+
with col2:
|
348 |
+
if st.button("Skip Question"):
|
349 |
+
st.session_state.current_question += 1
|
350 |
+
if st.session_state.current_question < len(st.session_state.questions):
|
351 |
+
st.rerun()
|
352 |
+
|
353 |
+
else:
|
354 |
+
st.success("Interview Completed!")
|
355 |
+
total_score = sum(st.session_state.scores)
|
356 |
+
average_score = total_score / len(st.session_state.scores)
|
357 |
+
|
358 |
+
st.header("Interview Summary")
|
359 |
+
st.subheader(f"Overall Score: {average_score:.2f}/10")
|
360 |
+
|
361 |
+
for i, (q, a, f) in enumerate(st.session_state.chat_history):
|
362 |
+
with st.expander(f"Question {i+1}"):
|
363 |
+
st.write(f"Q: {q}")
|
364 |
+
st.write(f"Your Answer: {a}")
|
365 |
+
st.write(f"Feedback: {f}")
|
366 |
+
|
367 |
+
# Generate overall feedback
|
368 |
+
overall_feedback_prompt = f"""
|
369 |
+
You are an experienced career coach. Based on the candidate's performance in the interview for the role of {role},
|
370 |
+
with {experience} years of experience, please provide:
|
371 |
+
1. A summary of their strengths (2-3 points)
|
372 |
+
2. Areas for improvement (2-3 points)
|
373 |
+
3. Advice for future interviews (2-3 tips)
|
374 |
+
4. Personalized tips for improving their professional appearance and body language
|
375 |
+
5. Strategies for managing interview anxiety
|
376 |
+
|
377 |
+
Their overall score was {average_score:.2f}/10.
|
378 |
+
|
379 |
+
Format your response as follows:
|
380 |
+
Strengths:
|
381 |
+
- [Strength 1]
|
382 |
+
- [Strength 2]
|
383 |
+
- [Strength 3]
|
384 |
+
|
385 |
+
Areas for Improvement:
|
386 |
+
- [Area 1]
|
387 |
+
- [Area 2]
|
388 |
+
- [Area 3]
|
389 |
+
|
390 |
+
Tips for Future Interviews:
|
391 |
+
- [Tip 1]
|
392 |
+
- [Tip 2]
|
393 |
+
- [Tip 3]
|
394 |
+
|
395 |
+
Professional Appearance and Body Language:
|
396 |
+
- [Tip 1]
|
397 |
+
- [Tip 2]
|
398 |
+
- [Tip 3]
|
399 |
+
|
400 |
+
Managing Interview Anxiety:
|
401 |
+
- [Strategy 1]
|
402 |
+
- [Strategy 2]
|
403 |
+
- [Strategy 3]
|
404 |
+
"""
|
405 |
+
|
406 |
+
messages = [
|
407 |
+
SystemMessage(content=overall_feedback_prompt),
|
408 |
+
HumanMessage(content="Please provide the overall feedback for the interview.")
|
409 |
+
]
|
410 |
+
|
411 |
+
with st.spinner("Generating overall feedback..."):
|
412 |
+
overall_feedback = chat.invoke(messages).content
|
413 |
+
|
414 |
+
st.subheader("Overall Feedback")
|
415 |
+
st.write(overall_feedback)
|
416 |
+
|
417 |
+
if st.button("Start New Interview"):
|
418 |
+
st.session_state.interview_started = False
|
419 |
+
st.session_state.current_question = 0
|
420 |
+
st.session_state.questions = []
|
421 |
+
st.session_state.answers = []
|
422 |
+
st.session_state.feedback = []
|
423 |
+
st.session_state.scores = []
|
424 |
+
st.session_state.chat_history = []
|
425 |
+
st.rerun()
|
426 |
+
|
427 |
+
# Footer
|
428 |
+
st.markdown("---")
|
429 |
+
st.markdown("Powered by Falcon-180B and Streamlit")
|
430 |
+
|
431 |
+
# Interview Preparation Checklist
|
432 |
+
st.sidebar.header("Interview Preparation Checklist")
|
433 |
+
checklist_items = [
|
434 |
+
"Research the company",
|
435 |
+
"Review the job description",
|
436 |
+
"Prepare your elevator pitch",
|
437 |
+
"Practice common interview questions",
|
438 |
+
"Prepare questions for the interviewer",
|
439 |
+
"Choose appropriate attire",
|
440 |
+
"Test your technology (for virtual interviews)",
|
441 |
+
"Gather necessary documents (resume, portfolio, etc.)",
|
442 |
+
"Plan your route or set up your interview space",
|
443 |
+
"Get a good night's sleep"
|
444 |
+
]
|
445 |
+
for item in checklist_items:
|
446 |
+
st.sidebar.checkbox(item)
|
447 |
+
|
448 |
+
# Interview Timer
|
449 |
+
if st.session_state.interview_started:
|
450 |
+
st.sidebar.header("Interview Timer")
|
451 |
+
if 'start_time' not in st.session_state:
|
452 |
+
st.session_state.start_time = time.time()
|
453 |
+
|
454 |
+
elapsed_time = int(time.time() - st.session_state.start_time)
|
455 |
+
minutes, seconds = divmod(elapsed_time, 60)
|
456 |
+
st.sidebar.write(f"Elapsed Time: {minutes:02d}:{seconds:02d}")
|
457 |
+
|
458 |
+
# Confidence Boost
|
459 |
+
st.sidebar.header("Confidence Boost")
|
460 |
+
if st.sidebar.button("Get a Confidence Boost"):
|
461 |
+
confidence_boosters = [
|
462 |
+
"You've got this! Your preparation will pay off.",
|
463 |
+
"Remember, the interviewer wants you to succeed too.",
|
464 |
+
"Take deep breaths and stay calm. You're well-prepared.",
|
465 |
+
"Your unique experiences make you a valuable candidate.",
|
466 |
+
"Every interview is a learning opportunity. Embrace it!",
|
467 |
+
"Believe in yourself. Your skills and knowledge are valuable.",
|
468 |
+
"Stay positive and confident. Your attitude shines through.",
|
469 |
+
"You've overcome challenges before. This is just another opportunity to shine.",
|
470 |
+
"Focus on your strengths and what you can bring to the role.",
|
471 |
+
"Remember your past successes. You're capable of greatness!"
|
472 |
+
]
|
473 |
+
st.sidebar.success(random.choice(confidence_boosters))
|
474 |
+
|
475 |
+
# Interview Do's and Don'ts
|
476 |
+
st.sidebar.header("Interview Do's and Don'ts")
|
477 |
+
dos_and_donts = {
|
478 |
+
"Do": [
|
479 |
+
"Arrive early or log in on time",
|
480 |
+
"Maintain good eye contact",
|
481 |
+
"Listen actively and ask thoughtful questions",
|
482 |
+
"Show enthusiasm for the role and company",
|
483 |
+
"Provide specific examples to support your answers"
|
484 |
+
],
|
485 |
+
"Don't": [
|
486 |
+
"Speak negatively about past employers",
|
487 |
+
"Interrupt the interviewer",
|
488 |
+
"Use filler words excessively (um, like, you know)",
|
489 |
+
"Check your phone or watch frequently",
|
490 |
+
"Provide vague or generic answers"
|
491 |
+
]
|
492 |
+
}
|
493 |
+
dos_tab, donts_tab = st.sidebar.tabs(["Do's", "Don'ts"])
|
494 |
+
with dos_tab:
|
495 |
+
for do_item in dos_and_donts["Do"]:
|
496 |
+
st.write(f"β
{do_item}")
|
497 |
+
with donts_tab:
|
498 |
+
for dont_item in dos_and_donts["Don't"]:
|
499 |
+
st.write(f"β {dont_item}")
|
500 |
+
|
501 |
+
# Personal Notes
|
502 |
+
st.sidebar.header("Personal Notes")
|
503 |
+
personal_notes = st.sidebar.text_area("Jot down your thoughts or reminders here:")
|
504 |
+
|
505 |
+
# Initialize session state for saved notes if it doesn't exist
|
506 |
+
if 'saved_notes' not in st.session_state:
|
507 |
+
st.session_state.saved_notes = []
|
508 |
+
|
509 |
+
# Save Notes button
|
510 |
+
if st.sidebar.button("Save Notes"):
|
511 |
+
if personal_notes.strip(): # Check if the note is not empty
|
512 |
+
st.session_state.saved_notes.append(personal_notes)
|
513 |
+
st.sidebar.success("Note saved successfully!")
|
514 |
+
# Clear the text area after saving
|
515 |
+
personal_notes = ""
|
516 |
+
else:
|
517 |
+
st.sidebar.warning("Please enter a note before saving.")
|
518 |
+
|
519 |
+
# Display saved notes as checkboxes
|
520 |
+
st.sidebar.subheader("Saved Notes")
|
521 |
+
for i, note in enumerate(st.session_state.saved_notes):
|
522 |
+
col1, col2 = st.sidebar.columns([3, 1])
|
523 |
+
with col1:
|
524 |
+
st.checkbox(note, key=f"note_{i}")
|
525 |
+
with col2:
|
526 |
+
if st.button("Delete", key=f"delete_{i}"):
|
527 |
+
del st.session_state.saved_notes[i]
|
528 |
+
st.rerun()
|
529 |
+
|
530 |
+
# Follow-up Email Template
|
531 |
+
if st.session_state.interview_started and st.session_state.current_question >= len(st.session_state.questions):
|
532 |
+
st.header("Follow-up Email Template")
|
533 |
+
interviewer_name = st.text_input("Interviewer's Name")
|
534 |
+
company_name = st.text_input("Company Name")
|
535 |
+
specific_topic = st.text_input("Specific topic discussed during the interview")
|
536 |
+
|
537 |
+
if interviewer_name and company_name and specific_topic:
|
538 |
+
email_template = f"""
|
539 |
+
Subject: Thank you for the interview - {role} position
|
540 |
+
|
541 |
+
Dear {interviewer_name},
|
542 |
+
|
543 |
+
I hope this email finds you well. I wanted to express my sincere gratitude for taking the time to interview me for the {role} position at {company_name}. I thoroughly enjoyed our conversation and learning more about the role and the company.
|
544 |
+
|
545 |
+
Our discussion about {specific_topic} was particularly interesting, and it reinforced my enthusiasm for the position. I am excited about the possibility of bringing my skills and experience to your team and contributing to {company_name}'s success.
|
546 |
+
|
547 |
+
If you need any additional information or have any further questions, please don't hesitate to contact me. I look forward to hearing about the next steps in the process.
|
548 |
+
|
549 |
+
Thank you again for your time and consideration.
|
550 |
+
|
551 |
+
Best regards,
|
552 |
+
{name}
|
553 |
+
"""
|
554 |
+
st.text_area("Follow-up Email Template", email_template, height=300)
|
555 |
+
if st.button("Copy to Clipboard"):
|
556 |
+
st.write("Email template copied to clipboard!")
|
557 |
+
# Note: In a web app, you'd use JavaScript to copy to clipboard
|
558 |
+
|
559 |
+
if __name__ == "__main__":
|
560 |
+
main()
|
pages/meditation.py
ADDED
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import time
|
3 |
+
import random
|
4 |
+
from pydub import AudioSegment
|
5 |
+
from pydub.generators import Sine
|
6 |
+
import io
|
7 |
+
import base64
|
8 |
+
|
9 |
+
# Function to generate binaural beats
|
10 |
+
def generate_binaural_beat(freq1, freq2, duration_ms):
|
11 |
+
beat1 = Sine(freq1).to_audio_segment(duration=duration_ms)
|
12 |
+
beat2 = Sine(freq2).to_audio_segment(duration=duration_ms)
|
13 |
+
|
14 |
+
stereo_beat = AudioSegment.from_mono_audiosegments(beat1, beat2)
|
15 |
+
|
16 |
+
buffer = io.BytesIO()
|
17 |
+
stereo_beat.export(buffer, format="wav")
|
18 |
+
return buffer.getvalue()
|
19 |
+
|
20 |
+
# Function to get binary audio data as base64
|
21 |
+
def get_binary_file_downloader_html(bin_file, file_label='File'):
|
22 |
+
bin_str = base64.b64encode(bin_file).decode()
|
23 |
+
href = f'<a href="data:application/octet-stream;base64,{bin_str}" download="{file_label}.wav">Download {file_label}</a>'
|
24 |
+
return href
|
25 |
+
|
26 |
+
# Streamlit app
|
27 |
+
def main():
|
28 |
+
st.set_page_config(page_title="Meditation & Mindfulness Hub", layout="wide")
|
29 |
+
|
30 |
+
st.title("π§ Meditation & Mindfulness Hub")
|
31 |
+
st.markdown("Improve your mental health and focus with our holistic learning experience.")
|
32 |
+
|
33 |
+
# Sidebar for navigation
|
34 |
+
menu = st.sidebar.selectbox("Choose a Practice", ["Meditation", "Mindfulness Exercises", "Binaural Beats", "Positive Affirmations"])
|
35 |
+
|
36 |
+
if menu == "Meditation":
|
37 |
+
meditation_page()
|
38 |
+
elif menu == "Mindfulness Exercises":
|
39 |
+
mindfulness_page()
|
40 |
+
elif menu == "Binaural Beats":
|
41 |
+
binaural_beats_page()
|
42 |
+
elif menu == "Positive Affirmations":
|
43 |
+
affirmations_page()
|
44 |
+
|
45 |
+
def meditation_page():
|
46 |
+
st.header("Guided Meditation")
|
47 |
+
|
48 |
+
meditation_types = {
|
49 |
+
"Breath Awareness": "Focus on your breath, observing each inhale and exhale...",
|
50 |
+
"Body Scan": "Starting from your toes, gradually move your attention up through your body...",
|
51 |
+
"Loving-Kindness": "Direct feelings of love and compassion towards yourself and others...",
|
52 |
+
"Visualization": "Imagine a peaceful scene, like a serene beach or a tranquil forest..."
|
53 |
+
}
|
54 |
+
|
55 |
+
selected_meditation = st.selectbox("Choose a meditation type:", list(meditation_types.keys()))
|
56 |
+
duration = st.slider("Meditation duration (minutes):", 5, 30, 10)
|
57 |
+
|
58 |
+
if st.button("Start Meditation"):
|
59 |
+
with st.spinner(f"Meditating for {duration} minutes..."):
|
60 |
+
st.text(meditation_types[selected_meditation])
|
61 |
+
meditation_progress = st.progress(0)
|
62 |
+
for i in range(duration * 60):
|
63 |
+
time.sleep(1)
|
64 |
+
meditation_progress.progress((i + 1) / (duration * 60))
|
65 |
+
st.success("Meditation complete. How do you feel?")
|
66 |
+
|
67 |
+
def mindfulness_exercises_page():
|
68 |
+
st.header("Mindfulness Exercises")
|
69 |
+
|
70 |
+
exercises = [
|
71 |
+
"Take three deep breaths, focusing on the sensation of air entering and leaving your body.",
|
72 |
+
"Notice five things you can see, four things you can touch, three things you can hear, two things you can smell, and one thing you can taste.",
|
73 |
+
"Spend one minute focusing solely on the present moment, acknowledging thoughts without judgment.",
|
74 |
+
"Practice mindful walking by paying attention to each step and the sensations in your feet.",
|
75 |
+
"Eat a small snack mindfully, noticing the taste, texture, and aroma with each bite."
|
76 |
+
]
|
77 |
+
|
78 |
+
selected_exercise = st.selectbox("Choose a mindfulness exercise:", exercises)
|
79 |
+
|
80 |
+
if st.button("Start Exercise"):
|
81 |
+
with st.spinner("Practicing mindfulness..."):
|
82 |
+
st.text(selected_exercise)
|
83 |
+
time.sleep(60) # Give the user a minute to practice
|
84 |
+
st.success("Exercise complete. Remember to carry this mindfulness with you throughout your day.")
|
85 |
+
|
86 |
+
def binaural_beats_page():
|
87 |
+
st.header("Binaural Beats Generator")
|
88 |
+
|
89 |
+
st.write("Binaural beats are created when two slightly different frequencies are played in each ear, potentially influencing brainwave activity.")
|
90 |
+
|
91 |
+
base_freq = st.slider("Base Frequency (Hz):", 100, 500, 200)
|
92 |
+
beat_freq = st.slider("Desired Beat Frequency (Hz):", 1, 40, 10)
|
93 |
+
duration = st.slider("Duration (seconds):", 30, 300, 60)
|
94 |
+
|
95 |
+
if st.button("Generate Binaural Beat"):
|
96 |
+
with st.spinner("Generating binaural beat..."):
|
97 |
+
audio_data = generate_binaural_beat(base_freq, base_freq + beat_freq, duration * 1000)
|
98 |
+
st.audio(audio_data, format='audio/wav')
|
99 |
+
st.markdown(get_binary_file_downloader_html(audio_data, f"binaural_beat_{base_freq}_{beat_freq}Hz"), unsafe_allow_html=True)
|
100 |
+
|
101 |
+
def affirmations_page():
|
102 |
+
st.header("Positive Affirmations")
|
103 |
+
|
104 |
+
predefined_affirmations = [
|
105 |
+
"I am capable of achieving great things.",
|
106 |
+
"Every day, I'm getting stronger and more confident.",
|
107 |
+
"I trust in my abilities and embrace new challenges.",
|
108 |
+
"I radiate positivity and attract positive experiences.",
|
109 |
+
"I am worthy of love, respect, and success."
|
110 |
+
]
|
111 |
+
|
112 |
+
affirmation_type = st.radio("Choose affirmation type:", ["Predefined", "Custom"])
|
113 |
+
|
114 |
+
if affirmation_type == "Predefined":
|
115 |
+
affirmation = st.selectbox("Select an affirmation:", predefined_affirmations)
|
116 |
+
else:
|
117 |
+
affirmation = st.text_input("Enter your custom affirmation:")
|
118 |
+
|
119 |
+
repetitions = st.slider("Number of repetitions:", 1, 50, 10)
|
120 |
+
interval = st.slider("Interval between repetitions (seconds):", 1, 10, 3)
|
121 |
+
|
122 |
+
if st.button("Start Affirmations"):
|
123 |
+
with st.spinner(f"Repeating affirmation {repetitions} times..."):
|
124 |
+
for i in range(repetitions):
|
125 |
+
st.write(affirmation)
|
126 |
+
time.sleep(interval)
|
127 |
+
st.success("Affirmation session complete. Carry this positive energy with you!")
|
128 |
+
|
129 |
+
if __name__ == "__main__":
|
130 |
+
main()
|
pages/mind_palace.py
ADDED
@@ -0,0 +1,341 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import random
|
3 |
+
from langchain_community.chat_models import ChatOpenAI
|
4 |
+
from langchain.schema import HumanMessage, SystemMessage
|
5 |
+
from langchain.document_loaders import TextLoader, UnstructuredFileLoader
|
6 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
7 |
+
from langchain.embeddings import HuggingFaceEmbeddings
|
8 |
+
from langchain.vectorstores import FAISS
|
9 |
+
import os
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
import json
|
12 |
+
from tenacity import retry, stop_after_attempt, wait_fixed
|
13 |
+
from streamlit_chat import message
|
14 |
+
from gtts import gTTS
|
15 |
+
import io
|
16 |
+
from PyPDF2 import PdfReader
|
17 |
+
import docx2txt
|
18 |
+
import logging
|
19 |
+
import tempfile
|
20 |
+
|
21 |
+
logging.basicConfig(level=logging.INFO)
|
22 |
+
logger = logging.getLogger(__name__)
|
23 |
+
|
24 |
+
# Load environment variables
|
25 |
+
load_dotenv()
|
26 |
+
|
27 |
+
AI71_BASE_URL = "https://api.ai71.ai/v1/"
|
28 |
+
AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
|
29 |
+
|
30 |
+
# Initialize the models
|
31 |
+
chat = ChatOpenAI(
|
32 |
+
model="tiiuae/falcon-180B-chat",
|
33 |
+
api_key=AI71_API_KEY,
|
34 |
+
base_url=AI71_BASE_URL,
|
35 |
+
streaming=True,
|
36 |
+
)
|
37 |
+
|
38 |
+
# Use SentenceTransformers for embeddings
|
39 |
+
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
|
40 |
+
|
41 |
+
def process_document(file):
|
42 |
+
content = ""
|
43 |
+
file_extension = file.name.split('.')[-1].lower()
|
44 |
+
|
45 |
+
if file_extension == 'txt':
|
46 |
+
content = file.getvalue().decode('utf-8')
|
47 |
+
elif file_extension == 'pdf':
|
48 |
+
try:
|
49 |
+
pdf_reader = PdfReader(io.BytesIO(file.getvalue()))
|
50 |
+
for page in pdf_reader.pages:
|
51 |
+
content += page.extract_text()
|
52 |
+
except Exception as e:
|
53 |
+
st.error(f"Error processing PDF: {str(e)}")
|
54 |
+
return None
|
55 |
+
elif file_extension == 'docx':
|
56 |
+
content = docx2txt.process(io.BytesIO(file.getvalue()))
|
57 |
+
else:
|
58 |
+
st.error(f"Unsupported file type: {file_extension}")
|
59 |
+
return None
|
60 |
+
|
61 |
+
if not content.strip():
|
62 |
+
st.warning("The uploaded file appears to be empty or unreadable. Please check the file and try again.")
|
63 |
+
return None
|
64 |
+
|
65 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
|
66 |
+
chunks = text_splitter.split_text(content)
|
67 |
+
|
68 |
+
if not chunks:
|
69 |
+
st.warning("Unable to extract meaningful content from the file. Please try a different file.")
|
70 |
+
return None
|
71 |
+
|
72 |
+
vectorstore = FAISS.from_texts(chunks, embeddings)
|
73 |
+
|
74 |
+
return vectorstore, content
|
75 |
+
|
76 |
+
@retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
|
77 |
+
def generate_mind_palace(topic, learning_style, user_preferences, content=None):
|
78 |
+
system_message = f"""
|
79 |
+
You are an expert in creating memorable and personalized mind palaces to aid in learning and retention.
|
80 |
+
The user wants to learn about '{topic}' and their preferred learning style is '{learning_style}'.
|
81 |
+
Their personal preferences are: {user_preferences}
|
82 |
+
Create a vivid and easy-to-remember mind palace description that incorporates the topic, caters to the user's learning style, and aligns with their preferences.
|
83 |
+
The mind palace should have 5-7 interconnected rooms or areas, each representing a key aspect of the topic.
|
84 |
+
For each room, provide:
|
85 |
+
1. A catchy and memorable name related to the topic
|
86 |
+
2. A vivid description that incorporates the user's preferences and makes use of multiple senses
|
87 |
+
3. 3-5 key elements or objects in the room that represent important information
|
88 |
+
4. How these elements relate to the topic
|
89 |
+
5. A simple and effective memory technique or association specific to the user's learning style
|
90 |
+
|
91 |
+
Ensure that the mind palace is coherent, with a logical flow between rooms. Use vivid imagery, familiar concepts, and emotional connections to make it more memorable.
|
92 |
+
|
93 |
+
Format your response as a JSON object with the following structure:
|
94 |
+
{{
|
95 |
+
"palace_name": "Catchy Name of the Mind Palace",
|
96 |
+
"rooms": [
|
97 |
+
{{
|
98 |
+
"name": "Memorable Room Name",
|
99 |
+
"description": "Vivid description of the room",
|
100 |
+
"elements": [
|
101 |
+
{{
|
102 |
+
"name": "Striking Element Name",
|
103 |
+
"description": "How this element relates to the topic",
|
104 |
+
"memory_technique": "A simple and effective memory technique or association"
|
105 |
+
}}
|
106 |
+
]
|
107 |
+
}}
|
108 |
+
]
|
109 |
+
}}
|
110 |
+
|
111 |
+
Ensure that your response is a valid JSON object. Do not include any text before or after the JSON object.
|
112 |
+
"""
|
113 |
+
|
114 |
+
messages = [
|
115 |
+
SystemMessage(content=system_message),
|
116 |
+
HumanMessage(content=f"Create a memorable mind palace for the topic: {topic}")
|
117 |
+
]
|
118 |
+
|
119 |
+
if content:
|
120 |
+
messages.append(HumanMessage(content=f"Use this additional context to enhance the mind palace, focusing on the most important and memorable aspects: {content[:2000]}"))
|
121 |
+
|
122 |
+
try:
|
123 |
+
response = chat.invoke(messages)
|
124 |
+
json_response = json.loads(response.content)
|
125 |
+
return json_response
|
126 |
+
except json.JSONDecodeError as e:
|
127 |
+
st.error(f"Error decoding JSON response: {str(e)}")
|
128 |
+
st.error("Raw response content:")
|
129 |
+
st.error(response.content)
|
130 |
+
raise
|
131 |
+
|
132 |
+
def generate_audio_description(mind_palace_data):
|
133 |
+
description = f"Welcome to your personalized and memorable mind palace: {mind_palace_data['palace_name']}. Let's take a journey through your palace, using vivid imagery and your preferred learning style to make it unforgettable. "
|
134 |
+
for room in mind_palace_data['rooms']:
|
135 |
+
description += f"We're entering the {room['name']}. {room['description']} "
|
136 |
+
for element in room['elements']:
|
137 |
+
description += f"Focus on the {element['name']}. {element['description']} To remember this, use this simple technique: {element['memory_technique']} Take a moment to really visualize and feel this connection. "
|
138 |
+
description += "Now, let's move to the next room, carrying these vivid images with us. "
|
139 |
+
description += "We've completed our tour of your mind palace. Take a deep breath and recall the journey we've just taken, visualizing each room and its striking elements."
|
140 |
+
|
141 |
+
tts = gTTS(text=description, lang='en', slow=False)
|
142 |
+
fp = io.BytesIO()
|
143 |
+
tts.write_to_fp(fp)
|
144 |
+
fp.seek(0)
|
145 |
+
|
146 |
+
return fp
|
147 |
+
|
148 |
+
def main():
|
149 |
+
st.set_page_config(page_title="S.H.E.R.L.O.C.K. Memorable Mind Palace Generator", layout="wide")
|
150 |
+
|
151 |
+
# Custom CSS for dark theme
|
152 |
+
st.markdown("""
|
153 |
+
<style>
|
154 |
+
.main {
|
155 |
+
background-color: #1E1E1E;
|
156 |
+
color: #FFFFFF;
|
157 |
+
}
|
158 |
+
.stButton>button {
|
159 |
+
background-color: #4CAF50;
|
160 |
+
color: white;
|
161 |
+
font-weight: bold;
|
162 |
+
}
|
163 |
+
.stTextInput>div>div>input, .stTextArea textarea {
|
164 |
+
background-color: #2E2E2E;
|
165 |
+
color: #FFFFFF;
|
166 |
+
}
|
167 |
+
.room-expander {
|
168 |
+
background-color: #2E2E2E;
|
169 |
+
border-radius: 5px;
|
170 |
+
padding: 10px;
|
171 |
+
margin-bottom: 10px;
|
172 |
+
}
|
173 |
+
.stSelectbox>div>div>select {
|
174 |
+
background-color: #2E2E2E;
|
175 |
+
color: #FFFFFF;
|
176 |
+
}
|
177 |
+
</style>
|
178 |
+
""", unsafe_allow_html=True)
|
179 |
+
|
180 |
+
# Sidebar
|
181 |
+
with st.sidebar:
|
182 |
+
st.title("S.H.E.R.L.O.C.K.")
|
183 |
+
st.subheader("Memorable Mind Palace Generator")
|
184 |
+
|
185 |
+
st.markdown("---")
|
186 |
+
st.markdown("How to use your Memorable Mind Palace:")
|
187 |
+
st.markdown("""
|
188 |
+
1. Choose to enter a topic or upload a document.
|
189 |
+
2. Select your learning style and enter your preferences.
|
190 |
+
3. Generate your personalized, easy-to-remember mind palace.
|
191 |
+
4. Listen to the vivid audio description and imagine each room.
|
192 |
+
5. Explore the detailed text description, focusing on the striking elements.
|
193 |
+
6. Use the chat to reinforce your understanding of the mind palace.
|
194 |
+
7. Practice recalling information by mentally walking through your palace, using the memory techniques provided.
|
195 |
+
""")
|
196 |
+
|
197 |
+
st.markdown("---")
|
198 |
+
st.markdown("Powered by Falcon-180B and SentenceTransformers")
|
199 |
+
|
200 |
+
# Main content
|
201 |
+
st.title("S.H.E.R.L.O.C.K. Memorable Mind Palace Generator")
|
202 |
+
|
203 |
+
st.write("""
|
204 |
+
Welcome to the Memorable Mind Palace Generator! This tool will help you create a vivid and easy-to-remember
|
205 |
+
mind palace to enhance your learning and memory retention. Choose to enter a topic or upload a document,
|
206 |
+
select your preferred learning style, and enter your personal preferences. We'll generate a unique,
|
207 |
+
unforgettable mind palace tailored just for you.
|
208 |
+
""")
|
209 |
+
|
210 |
+
input_method = st.radio("Choose your input method:", ["Enter a topic", "Upload a document"])
|
211 |
+
|
212 |
+
if input_method == "Enter a topic":
|
213 |
+
topic = st.text_input("Enter the topic you want to learn:")
|
214 |
+
uploaded_file = None
|
215 |
+
else:
|
216 |
+
topic = None
|
217 |
+
uploaded_file = st.file_uploader("Upload a document to memorize", type=['txt', 'md', 'pdf', 'docx'])
|
218 |
+
|
219 |
+
learning_style = st.selectbox("Choose your preferred learning style:",
|
220 |
+
["Visual", "Auditory", "Kinesthetic", "Reading/Writing"])
|
221 |
+
|
222 |
+
st.write("""
|
223 |
+
Learning Styles:
|
224 |
+
- Visual: You learn best through images, diagrams, and spatial understanding. We'll create vivid mental pictures.
|
225 |
+
- Auditory: You prefer learning through listening and speaking. We'll focus on memorable sounds and verbal associations.
|
226 |
+
- Kinesthetic: You learn by doing, moving, and touching. We'll incorporate imaginary physical sensations and movements.
|
227 |
+
- Reading/Writing: You learn best through words. We'll use powerful written descriptions and word associations.
|
228 |
+
""")
|
229 |
+
|
230 |
+
user_preferences = st.text_area("Enter your personal preferences (e.g., favorite places, hobbies, movies, or anything that resonates with you):")
|
231 |
+
|
232 |
+
if st.button("Generate Memorable Mind Palace"):
|
233 |
+
with st.spinner("Crafting your unforgettable mind palace..."):
|
234 |
+
content = None
|
235 |
+
if uploaded_file is not None:
|
236 |
+
vectorstore, content = process_document(uploaded_file)
|
237 |
+
if vectorstore is None:
|
238 |
+
st.error("Failed to process the uploaded document. Please try again with a different file.")
|
239 |
+
return
|
240 |
+
topic = "Document Content"
|
241 |
+
elif topic is None or topic.strip() == "":
|
242 |
+
st.error("Please enter a topic or upload a document.")
|
243 |
+
return
|
244 |
+
|
245 |
+
try:
|
246 |
+
mind_palace_data = generate_mind_palace(topic, learning_style, user_preferences, content)
|
247 |
+
if mind_palace_data is None:
|
248 |
+
st.error("Failed to generate the mind palace. Please try again.")
|
249 |
+
return
|
250 |
+
|
251 |
+
st.session_state.mind_palace = mind_palace_data
|
252 |
+
st.session_state.chat_history = []
|
253 |
+
|
254 |
+
# Generate audio description with selected voice
|
255 |
+
with st.spinner("Creating a vivid audio guide for your mind palace..."):
|
256 |
+
audio_fp = generate_audio_description(mind_palace_data)
|
257 |
+
st.session_state.mind_palace_audio = audio_fp
|
258 |
+
except Exception as e:
|
259 |
+
logger.error(f"An error occurred while generating the mind palace: {str(e)}")
|
260 |
+
st.error(f"An error occurred while generating the mind palace. Please try again.")
|
261 |
+
return
|
262 |
+
|
263 |
+
if 'mind_palace' in st.session_state:
|
264 |
+
mind_palace_data = st.session_state.mind_palace
|
265 |
+
|
266 |
+
st.subheader(f"Your Memorable Mind Palace: {mind_palace_data['palace_name']}")
|
267 |
+
|
268 |
+
# Audio player
|
269 |
+
if 'mind_palace_audio' in st.session_state:
|
270 |
+
try:
|
271 |
+
st.audio(st.session_state.mind_palace_audio, format='audio/wav')
|
272 |
+
st.write("π Listen to the vivid audio guide and imagine your mind palace. Close your eyes and immerse yourself in this mental journey.")
|
273 |
+
except Exception as e:
|
274 |
+
logger.error(f"Error playing audio: {str(e)}")
|
275 |
+
st.warning("There was an issue playing the audio. You can still explore the text description of your mind palace.")
|
276 |
+
|
277 |
+
# Text description
|
278 |
+
for room in mind_palace_data['rooms']:
|
279 |
+
with st.expander(f"Room: {room['name']}", expanded=True):
|
280 |
+
st.markdown(f"**Description:** {room['description']}")
|
281 |
+
st.markdown("**Key Elements:**")
|
282 |
+
for element in room['elements']:
|
283 |
+
st.markdown(f"- **{element['name']}:** {element['description']}")
|
284 |
+
st.markdown(f" *Memory Technique:* {element['memory_technique']}")
|
285 |
+
|
286 |
+
st.success("Your memorable mind palace has been generated successfully! Take some time to walk through it mentally, focusing on the vivid details and connections.")
|
287 |
+
|
288 |
+
# Chat interface
|
289 |
+
st.subheader("Reinforce Your Mind Palace")
|
290 |
+
|
291 |
+
# Initialize input key if not present
|
292 |
+
if 'input_key' not in st.session_state:
|
293 |
+
st.session_state.input_key = 0
|
294 |
+
|
295 |
+
# Display chat history
|
296 |
+
if 'chat_history' not in st.session_state:
|
297 |
+
st.session_state.chat_history = []
|
298 |
+
|
299 |
+
for i, (sender, message_text) in enumerate(st.session_state.chat_history):
|
300 |
+
if sender == "user":
|
301 |
+
message(message_text, is_user=True, key=f"{i}_user")
|
302 |
+
else:
|
303 |
+
message(message_text, key=f"{i}_assistant")
|
304 |
+
|
305 |
+
# User input text box with dynamic key
|
306 |
+
user_input = st.text_input("Ask a question or request a memory reinforcement exercise:", key=f"user_input_{st.session_state.input_key}")
|
307 |
+
|
308 |
+
# Ask button below the text input
|
309 |
+
ask_button = st.button("Ask")
|
310 |
+
|
311 |
+
if ask_button and user_input:
|
312 |
+
with st.spinner("Generating response to enhance your memory..."):
|
313 |
+
# Prepare context for the AI
|
314 |
+
context = f"Mind Palace Data: {json.dumps(mind_palace_data)}\n\n"
|
315 |
+
if 'uploaded_content' in st.session_state:
|
316 |
+
context += f"Uploaded Document Content: {st.session_state.uploaded_content}\n\n"
|
317 |
+
|
318 |
+
system_message = f"""
|
319 |
+
You are an AI assistant helping the user understand and remember their personalized mind palace.
|
320 |
+
Use the following context to provide responses that reinforce the vivid imagery and memory techniques used in the mind palace.
|
321 |
+
If asked about specific content from the uploaded document, refer to it in your response.
|
322 |
+
|
323 |
+
{context}
|
324 |
+
"""
|
325 |
+
|
326 |
+
response = chat.invoke([
|
327 |
+
SystemMessage(content=system_message),
|
328 |
+
HumanMessage(content=user_input)
|
329 |
+
])
|
330 |
+
|
331 |
+
st.session_state.chat_history.append(("user", user_input))
|
332 |
+
st.session_state.chat_history.append(("assistant", response.content))
|
333 |
+
|
334 |
+
# Increment the input key to reset the input field
|
335 |
+
st.session_state.input_key += 1
|
336 |
+
|
337 |
+
# Force a rerun to update the chat history display and reset the input
|
338 |
+
st.experimental_rerun()
|
339 |
+
|
340 |
+
if __name__ == "__main__":
|
341 |
+
main()
|
pages/mnemonics_generation.py
ADDED
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import random
|
3 |
+
from langchain_community.chat_models import ChatOpenAI
|
4 |
+
from langchain.schema import HumanMessage, SystemMessage
|
5 |
+
from langchain_community.document_loaders import PyPDFLoader, TextLoader, UnstructuredMarkdownLoader, UnstructuredWordDocumentLoader
|
6 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
7 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
8 |
+
from langchain_community.vectorstores import FAISS
|
9 |
+
from langchain.chains import RetrievalQA
|
10 |
+
import os
|
11 |
+
from dotenv import load_dotenv
|
12 |
+
import tempfile
|
13 |
+
from PIL import Image
|
14 |
+
import io
|
15 |
+
|
16 |
+
# Load environment variables
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
AI71_BASE_URL = "https://api.ai71.ai/v1/"
|
20 |
+
AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
|
21 |
+
|
22 |
+
# Initialize the Falcon model
|
23 |
+
chat = ChatOpenAI(
|
24 |
+
model="tiiuae/falcon-180B-chat",
|
25 |
+
api_key=AI71_API_KEY,
|
26 |
+
base_url=AI71_BASE_URL,
|
27 |
+
streaming=True,
|
28 |
+
)
|
29 |
+
|
30 |
+
# Initialize embeddings
|
31 |
+
embeddings = HuggingFaceEmbeddings()
|
32 |
+
|
33 |
+
def process_documents(uploaded_files):
|
34 |
+
documents = []
|
35 |
+
for uploaded_file in uploaded_files:
|
36 |
+
file_extension = os.path.splitext(uploaded_file.name)[1].lower()
|
37 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=file_extension) as temp_file:
|
38 |
+
temp_file.write(uploaded_file.getvalue())
|
39 |
+
temp_file_path = temp_file.name
|
40 |
+
|
41 |
+
if file_extension == '.pdf':
|
42 |
+
loader = PyPDFLoader(temp_file_path)
|
43 |
+
elif file_extension == '.txt':
|
44 |
+
loader = TextLoader(temp_file_path)
|
45 |
+
elif file_extension == '.md':
|
46 |
+
loader = UnstructuredMarkdownLoader(temp_file_path)
|
47 |
+
elif file_extension in ['.doc', '.docx']:
|
48 |
+
loader = UnstructuredWordDocumentLoader(temp_file_path)
|
49 |
+
else:
|
50 |
+
st.warning(f"Unsupported file type: {file_extension}")
|
51 |
+
continue
|
52 |
+
|
53 |
+
documents.extend(loader.load())
|
54 |
+
os.unlink(temp_file_path)
|
55 |
+
|
56 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
|
57 |
+
texts = text_splitter.split_documents(documents)
|
58 |
+
|
59 |
+
vectorstore = FAISS.from_documents(texts, embeddings)
|
60 |
+
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
|
61 |
+
|
62 |
+
qa_chain = RetrievalQA.from_chain_type(
|
63 |
+
llm=chat,
|
64 |
+
chain_type="stuff",
|
65 |
+
retriever=retriever,
|
66 |
+
return_source_documents=True
|
67 |
+
)
|
68 |
+
|
69 |
+
return qa_chain
|
70 |
+
|
71 |
+
def generate_mnemonic(topic, user_preferences):
|
72 |
+
prompt = f"""
|
73 |
+
Generate a memorable mnemonic for the topic: {topic}.
|
74 |
+
Consider the user's preferences: {user_preferences}.
|
75 |
+
The mnemonic should be easy to remember and relate to the topic.
|
76 |
+
Also provide a brief explanation of how the mnemonic relates to the topic.
|
77 |
+
"""
|
78 |
+
response = chat.invoke([HumanMessage(content=prompt)])
|
79 |
+
return response.content
|
80 |
+
|
81 |
+
def generate_quiz_question(mnemonic):
|
82 |
+
quiz_prompt = f"""
|
83 |
+
Create a quiz question based on the mnemonic: {mnemonic}
|
84 |
+
Format your response as follows:
|
85 |
+
Question: [Your question here]
|
86 |
+
Answer: [Your answer here]
|
87 |
+
"""
|
88 |
+
quiz_response = chat.invoke([HumanMessage(content=quiz_prompt)])
|
89 |
+
content = quiz_response.content.strip()
|
90 |
+
|
91 |
+
try:
|
92 |
+
question_part, answer_part = content.split("Answer:", 1)
|
93 |
+
question = question_part.replace("Question:", "").strip()
|
94 |
+
answer = answer_part.strip()
|
95 |
+
except ValueError:
|
96 |
+
question = content
|
97 |
+
answer = "Unable to generate a specific answer. Please refer to the mnemonic."
|
98 |
+
|
99 |
+
return question, answer
|
100 |
+
|
101 |
+
def generate_image_prompt(mnemonic):
|
102 |
+
prompt = f"""
|
103 |
+
Create a detailed image prompt for Midjourney based on the mnemonic: {mnemonic}
|
104 |
+
The image should visually represent the key elements of the mnemonic.
|
105 |
+
"""
|
106 |
+
response = chat.invoke([HumanMessage(content=prompt)])
|
107 |
+
return response.content
|
108 |
+
|
109 |
+
def main():
|
110 |
+
st.set_page_config(page_title="S.H.E.R.L.O.C.K. Mnemonic Generator", page_icon="π§ ", layout="wide")
|
111 |
+
|
112 |
+
# Custom CSS
|
113 |
+
st.markdown("""
|
114 |
+
<style>
|
115 |
+
.stApp {
|
116 |
+
background-color: #f0f2f6;
|
117 |
+
}
|
118 |
+
.main .block-container {
|
119 |
+
padding-top: 2rem;
|
120 |
+
}
|
121 |
+
.stButton>button {
|
122 |
+
background-color: #4CAF50;
|
123 |
+
color: white;
|
124 |
+
font-weight: bold;
|
125 |
+
}
|
126 |
+
.stTextInput>div>div>input {
|
127 |
+
background-color: #ffffff;
|
128 |
+
}
|
129 |
+
</style>
|
130 |
+
""", unsafe_allow_html=True)
|
131 |
+
|
132 |
+
st.title("π§ S.H.E.R.L.O.C.K. Mnemonic Generator")
|
133 |
+
|
134 |
+
# Initialize session state
|
135 |
+
if 'generated_mnemonic' not in st.session_state:
|
136 |
+
st.session_state.generated_mnemonic = None
|
137 |
+
if 'quiz_question' not in st.session_state:
|
138 |
+
st.session_state.quiz_question = None
|
139 |
+
if 'quiz_answer' not in st.session_state:
|
140 |
+
st.session_state.quiz_answer = None
|
141 |
+
if 'image_prompt' not in st.session_state:
|
142 |
+
st.session_state.image_prompt = None
|
143 |
+
|
144 |
+
# Sidebar
|
145 |
+
with st.sidebar:
|
146 |
+
st.header("π Document Upload")
|
147 |
+
uploaded_files = st.file_uploader("Upload documents (optional)", type=["pdf", "txt", "md", "doc", "docx"], accept_multiple_files=True)
|
148 |
+
if uploaded_files:
|
149 |
+
qa_chain = process_documents(uploaded_files)
|
150 |
+
st.success(f"{len(uploaded_files)} document(s) processed successfully!")
|
151 |
+
else:
|
152 |
+
qa_chain = None
|
153 |
+
|
154 |
+
st.header("π¨ User Preferences")
|
155 |
+
user_preferences = st.text_area("Enter your interests or preferences:")
|
156 |
+
|
157 |
+
# Main area
|
158 |
+
col1, col2 = st.columns([2, 1])
|
159 |
+
|
160 |
+
with col1:
|
161 |
+
st.header("π Generate Mnemonic")
|
162 |
+
topic = st.text_input("Enter the topic for your mnemonic:")
|
163 |
+
|
164 |
+
if st.button("Generate Mnemonic"):
|
165 |
+
if topic:
|
166 |
+
with st.spinner("Generating mnemonic..."):
|
167 |
+
mnemonic = generate_mnemonic(topic, user_preferences)
|
168 |
+
st.session_state.generated_mnemonic = mnemonic
|
169 |
+
|
170 |
+
with st.spinner("Generating quiz question..."):
|
171 |
+
question, answer = generate_quiz_question(mnemonic)
|
172 |
+
st.session_state.quiz_question = question
|
173 |
+
st.session_state.quiz_answer = answer
|
174 |
+
|
175 |
+
with st.spinner("Generating image prompt..."):
|
176 |
+
image_prompt = generate_image_prompt(mnemonic)
|
177 |
+
st.session_state.image_prompt = image_prompt
|
178 |
+
else:
|
179 |
+
st.warning("Please enter a topic to generate a mnemonic.")
|
180 |
+
|
181 |
+
with col2:
|
182 |
+
if st.session_state.generated_mnemonic:
|
183 |
+
st.header("π Generated Mnemonic")
|
184 |
+
st.write(st.session_state.generated_mnemonic)
|
185 |
+
|
186 |
+
# Quiz section
|
187 |
+
if st.session_state.quiz_question:
|
188 |
+
st.header("π§ Mnemonic Quiz")
|
189 |
+
st.write(st.session_state.quiz_question)
|
190 |
+
user_answer = st.text_input("Your answer:")
|
191 |
+
if st.button("Submit Answer"):
|
192 |
+
if user_answer.lower() == st.session_state.quiz_answer.lower():
|
193 |
+
st.success("π Correct! Well done.")
|
194 |
+
else:
|
195 |
+
st.error(f"β Not quite. The correct answer is: {st.session_state.quiz_answer}")
|
196 |
+
|
197 |
+
# Image prompt section
|
198 |
+
if st.session_state.image_prompt:
|
199 |
+
st.header("πΌοΈ Image Prompt")
|
200 |
+
st.write(st.session_state.image_prompt)
|
201 |
+
st.info("You can use this prompt with Midjourney or other image generation tools to create a visual representation of your mnemonic.")
|
202 |
+
|
203 |
+
# Document Q&A section
|
204 |
+
if qa_chain:
|
205 |
+
st.header("π Document Q&A")
|
206 |
+
user_question = st.text_input("Ask a question about the uploaded document(s):")
|
207 |
+
if st.button("Get Answer"):
|
208 |
+
with st.spinner("Searching for the answer..."):
|
209 |
+
result = qa_chain({"query": user_question})
|
210 |
+
st.subheader("Answer:")
|
211 |
+
st.write(result["result"])
|
212 |
+
st.subheader("Sources:")
|
213 |
+
for source in result["source_documents"]:
|
214 |
+
st.write(source.page_content)
|
215 |
+
|
216 |
+
# Mnemonic visualization
|
217 |
+
if st.session_state.generated_mnemonic:
|
218 |
+
st.header("π¨ Mnemonic Visualization")
|
219 |
+
visualization_type = st.selectbox("Choose visualization type:", ["Word Cloud", "Mind Map"])
|
220 |
+
if st.button("Generate Visualization"):
|
221 |
+
with st.spinner("Generating visualization..."):
|
222 |
+
visualization_prompt = f"""
|
223 |
+
Create a detailed description of a {visualization_type} based on the mnemonic:
|
224 |
+
{st.session_state.generated_mnemonic}
|
225 |
+
Describe the layout, key elements, and their relationships.
|
226 |
+
"""
|
227 |
+
visualization_description = chat.invoke([HumanMessage(content=visualization_prompt)]).content
|
228 |
+
st.write(visualization_description)
|
229 |
+
st.info("You can use this description to create a visual representation of your mnemonic using tools like Canva or Mindmeister.")
|
230 |
+
|
231 |
+
# Export options
|
232 |
+
if st.session_state.generated_mnemonic:
|
233 |
+
st.header("π€ Export Options")
|
234 |
+
export_format = st.selectbox("Choose export format:", ["Text", "PDF", "Markdown"])
|
235 |
+
if st.button("Export Mnemonic"):
|
236 |
+
export_content = f"""
|
237 |
+
Topic: {topic}
|
238 |
+
|
239 |
+
Mnemonic:
|
240 |
+
{st.session_state.generated_mnemonic}
|
241 |
+
|
242 |
+
Quiz Question:
|
243 |
+
{st.session_state.quiz_question}
|
244 |
+
|
245 |
+
Quiz Answer:
|
246 |
+
{st.session_state.quiz_answer}
|
247 |
+
|
248 |
+
Image Prompt:
|
249 |
+
{st.session_state.image_prompt}
|
250 |
+
"""
|
251 |
+
|
252 |
+
if export_format == "Text":
|
253 |
+
st.download_button("Download Text", export_content, file_name="mnemonic_export.txt")
|
254 |
+
elif export_format == "PDF":
|
255 |
+
# You'd need to implement PDF generation here, for example using reportlab
|
256 |
+
st.warning("PDF export not implemented in this example.")
|
257 |
+
elif export_format == "Markdown":
|
258 |
+
st.download_button("Download Markdown", export_content, file_name="mnemonic_export.md")
|
259 |
+
|
260 |
+
# Footer
|
261 |
+
st.sidebar.markdown("---")
|
262 |
+
st.sidebar.markdown("Powered by Falcon-180B and Streamlit")
|
263 |
+
|
264 |
+
if __name__ == "__main__":
|
265 |
+
main()
|
pages/notes_generation.py
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import os
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
from langchain_community.chat_models import ChatOpenAI
|
5 |
+
from langchain_community.document_loaders import PyPDFLoader, TextLoader, UnstructuredMarkdownLoader, Docx2txtLoader
|
6 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
7 |
+
from langchain_community.embeddings import HuggingFaceEmbeddings
|
8 |
+
from langchain_community.vectorstores import FAISS
|
9 |
+
from langchain.chains import RetrievalQA
|
10 |
+
from langchain.prompts import PromptTemplate
|
11 |
+
import tempfile
|
12 |
+
from typing import List, Dict
|
13 |
+
import json
|
14 |
+
from datetime import datetime
|
15 |
+
|
16 |
+
# Load environment variables
|
17 |
+
load_dotenv()
|
18 |
+
|
19 |
+
AI71_BASE_URL = "https://api.ai71.ai/v1/"
|
20 |
+
AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
|
21 |
+
|
22 |
+
# Initialize the Falcon model
|
23 |
+
@st.cache_resource
|
24 |
+
def get_llm():
|
25 |
+
return ChatOpenAI(
|
26 |
+
model="tiiuae/falcon-180B-chat",
|
27 |
+
api_key=AI71_API_KEY,
|
28 |
+
base_url=AI71_BASE_URL,
|
29 |
+
streaming=True,
|
30 |
+
)
|
31 |
+
|
32 |
+
# Initialize embeddings
|
33 |
+
@st.cache_resource
|
34 |
+
def get_embeddings():
|
35 |
+
return HuggingFaceEmbeddings()
|
36 |
+
|
37 |
+
def process_document(file_content, file_type):
|
38 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=f'.{file_type}') as tmp_file:
|
39 |
+
if isinstance(file_content, str):
|
40 |
+
tmp_file.write(file_content.encode('utf-8'))
|
41 |
+
else:
|
42 |
+
tmp_file.write(file_content)
|
43 |
+
tmp_file_path = tmp_file.name
|
44 |
+
|
45 |
+
if file_type == 'pdf':
|
46 |
+
loader = PyPDFLoader(tmp_file_path)
|
47 |
+
elif file_type == 'txt':
|
48 |
+
loader = TextLoader(tmp_file_path)
|
49 |
+
elif file_type == 'md':
|
50 |
+
loader = UnstructuredMarkdownLoader(tmp_file_path)
|
51 |
+
elif file_type == 'docx':
|
52 |
+
loader = Docx2txtLoader(tmp_file_path)
|
53 |
+
else:
|
54 |
+
raise ValueError(f"Unsupported file type: {file_type}")
|
55 |
+
|
56 |
+
documents = loader.load()
|
57 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
|
58 |
+
texts = text_splitter.split_documents(documents)
|
59 |
+
|
60 |
+
vectorstore = FAISS.from_documents(texts, get_embeddings())
|
61 |
+
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
|
62 |
+
|
63 |
+
os.unlink(tmp_file_path)
|
64 |
+
return retriever
|
65 |
+
|
66 |
+
def generate_notes(retriever, topic, style, length):
|
67 |
+
prompt_template = f"""
|
68 |
+
You are an expert note-taker and summarizer. Your task is to create {style} and {length} notes on the given topic.
|
69 |
+
Use the following guidelines:
|
70 |
+
1. Focus on key concepts and important details.
|
71 |
+
2. Use bullet points or numbered lists for clarity.
|
72 |
+
3. Include relevant examples or explanations where necessary.
|
73 |
+
4. Organize the information in a logical and easy-to-follow structure.
|
74 |
+
5. Aim for clarity without sacrificing important information.
|
75 |
+
|
76 |
+
Context: {{context}}
|
77 |
+
Topic: {{question}}
|
78 |
+
|
79 |
+
Notes:
|
80 |
+
"""
|
81 |
+
|
82 |
+
PROMPT = PromptTemplate(
|
83 |
+
template=prompt_template,
|
84 |
+
input_variables=["context", "question"]
|
85 |
+
)
|
86 |
+
|
87 |
+
chain_type_kwargs = {"prompt": PROMPT}
|
88 |
+
qa_chain = RetrievalQA.from_chain_type(
|
89 |
+
llm=get_llm(),
|
90 |
+
chain_type="stuff",
|
91 |
+
retriever=retriever,
|
92 |
+
chain_type_kwargs=chain_type_kwargs
|
93 |
+
)
|
94 |
+
|
95 |
+
result = qa_chain({"query": topic})
|
96 |
+
return result['result']
|
97 |
+
|
98 |
+
def save_notes(notes: str, topic: str):
|
99 |
+
notes_data = load_notes_data()
|
100 |
+
timestamp = datetime.now().isoformat()
|
101 |
+
notes_data.append({"topic": topic, "notes": notes, "timestamp": timestamp})
|
102 |
+
with open("saved_notes.json", "w") as f:
|
103 |
+
json.dump(notes_data, f)
|
104 |
+
|
105 |
+
def load_notes_data() -> List[Dict]:
|
106 |
+
try:
|
107 |
+
with open("saved_notes.json", "r") as f:
|
108 |
+
return json.load(f)
|
109 |
+
except FileNotFoundError:
|
110 |
+
return []
|
111 |
+
|
112 |
+
def main():
|
113 |
+
st.set_page_config(page_title="S.H.E.R.L.O.C.K. Notes Generator", layout="wide")
|
114 |
+
|
115 |
+
st.title("S.H.E.R.L.O.C.K. Notes Generator")
|
116 |
+
|
117 |
+
st.markdown("""
|
118 |
+
This tool helps you generate concise and relevant notes on specific topics.
|
119 |
+
You can upload a document or enter text directly.
|
120 |
+
""")
|
121 |
+
|
122 |
+
# Sidebar content
|
123 |
+
st.sidebar.title("About S.H.E.R.L.O.C.K.")
|
124 |
+
st.sidebar.markdown("""
|
125 |
+
S.H.E.R.L.O.C.K. (Summarizing Helper & Effective Research Liaison for Organizing Comprehensive Knowledge)
|
126 |
+
is an advanced AI-powered tool designed to assist you in generating comprehensive notes from various sources.
|
127 |
+
|
128 |
+
Key Features:
|
129 |
+
- Multi-format support (PDF, TXT, MD, DOCX)
|
130 |
+
- Customizable note generation
|
131 |
+
- Intelligent text processing
|
132 |
+
- Save and retrieve notes
|
133 |
+
|
134 |
+
How to use:
|
135 |
+
1. Choose your input method
|
136 |
+
2. Process your document or text
|
137 |
+
3. Enter a topic and customize note style
|
138 |
+
4. Generate and save your notes
|
139 |
+
|
140 |
+
Enjoy your enhanced note-taking experience!
|
141 |
+
""")
|
142 |
+
|
143 |
+
input_method = st.radio("Choose input method:", ("Upload Document", "Enter Text"))
|
144 |
+
|
145 |
+
if input_method == "Upload Document":
|
146 |
+
uploaded_file = st.file_uploader("Upload a document", type=["pdf", "txt", "md", "docx"])
|
147 |
+
if uploaded_file:
|
148 |
+
file_type = uploaded_file.name.split('.')[-1].lower()
|
149 |
+
file_content = uploaded_file.read()
|
150 |
+
st.success("Document uploaded successfully!")
|
151 |
+
|
152 |
+
with st.spinner("Processing document..."):
|
153 |
+
retriever = process_document(file_content, file_type)
|
154 |
+
st.session_state.retriever = retriever
|
155 |
+
st.success("Document processed!")
|
156 |
+
elif input_method == "Enter Text":
|
157 |
+
text_input = st.text_area("Enter your text here:", height=200)
|
158 |
+
if text_input:
|
159 |
+
with st.spinner("Processing text..."):
|
160 |
+
retriever = process_document(text_input, 'txt')
|
161 |
+
st.session_state.retriever = retriever
|
162 |
+
st.success("Text processed!")
|
163 |
+
|
164 |
+
topic = st.text_input("Enter the topic for note generation:")
|
165 |
+
|
166 |
+
col1, col2 = st.columns(2)
|
167 |
+
with col1:
|
168 |
+
style = st.selectbox("Note Style", ["Concise", "Detailed", "Academic", "Casual"])
|
169 |
+
with col2:
|
170 |
+
length = st.selectbox("Note Length", ["Short", "Medium", "Long"])
|
171 |
+
|
172 |
+
if st.button("Generate Notes"):
|
173 |
+
if topic and hasattr(st.session_state, 'retriever'):
|
174 |
+
with st.spinner("Generating notes..."):
|
175 |
+
try:
|
176 |
+
notes = generate_notes(st.session_state.retriever, topic, style, length)
|
177 |
+
st.subheader("Generated Notes:")
|
178 |
+
st.markdown(notes)
|
179 |
+
|
180 |
+
# Download button for the generated notes
|
181 |
+
st.download_button(
|
182 |
+
label="Download Notes",
|
183 |
+
data=notes,
|
184 |
+
file_name=f"{topic.replace(' ', '_')}_notes.txt",
|
185 |
+
mime="text/plain"
|
186 |
+
)
|
187 |
+
|
188 |
+
# Save notes
|
189 |
+
if st.button("Save Notes"):
|
190 |
+
save_notes(notes, topic)
|
191 |
+
st.success("Notes saved successfully!")
|
192 |
+
except Exception as e:
|
193 |
+
st.error(f"An error occurred while generating notes: {str(e)}")
|
194 |
+
else:
|
195 |
+
st.warning("Please upload a document or enter text, and specify a topic before generating notes.")
|
196 |
+
|
197 |
+
# Display saved notes
|
198 |
+
st.sidebar.subheader("Saved Notes")
|
199 |
+
saved_notes = load_notes_data()
|
200 |
+
for i, note in enumerate(saved_notes):
|
201 |
+
if st.sidebar.button(f"{note['topic']} - {note['timestamp'][:10]}", key=f"saved_note_{i}"):
|
202 |
+
st.subheader(f"Saved Notes: {note['topic']}")
|
203 |
+
st.markdown(note['notes'])
|
204 |
+
|
205 |
+
st.sidebar.markdown("---")
|
206 |
+
st.sidebar.markdown("Powered by Falcon-180B and Streamlit")
|
207 |
+
|
208 |
+
# Add a footer
|
209 |
+
st.markdown("---")
|
210 |
+
st.markdown("Created by Your Team Name | Β© 2024")
|
211 |
+
|
212 |
+
if __name__ == "__main__":
|
213 |
+
main()
|
pages/sherlock_observation.py
ADDED
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import random
|
3 |
+
from langchain_community.chat_models import ChatOpenAI
|
4 |
+
from langchain.schema import HumanMessage, SystemMessage
|
5 |
+
from langchain_community.document_loaders import PyPDFLoader, TextLoader
|
6 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
7 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
8 |
+
from langchain_community.vectorstores import FAISS
|
9 |
+
from langchain.chains import RetrievalQA
|
10 |
+
import os
|
11 |
+
from dotenv import load_dotenv
|
12 |
+
import tempfile
|
13 |
+
|
14 |
+
# Load environment variables
|
15 |
+
load_dotenv()
|
16 |
+
|
17 |
+
AI71_BASE_URL = "https://api.ai71.ai/v1/"
|
18 |
+
AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
|
19 |
+
|
20 |
+
# Initialize the Falcon model
|
21 |
+
chat = ChatOpenAI(
|
22 |
+
model="tiiuae/falcon-180B-chat",
|
23 |
+
api_key=AI71_API_KEY,
|
24 |
+
base_url=AI71_BASE_URL,
|
25 |
+
streaming=True,
|
26 |
+
)
|
27 |
+
|
28 |
+
# Initialize embeddings
|
29 |
+
embeddings = HuggingFaceEmbeddings()
|
30 |
+
|
31 |
+
# Expanded list of predefined topics
|
32 |
+
PREDEFINED_TOPICS = [
|
33 |
+
"Quantum Computing", "Artificial Intelligence Ethics", "Blockchain Technology",
|
34 |
+
"Neuroscience", "Climate Change Mitigation", "Space Exploration",
|
35 |
+
"Renewable Energy", "Genetic Engineering", "Cybersecurity",
|
36 |
+
"Machine Learning", "Nanotechnology", "Robotics",
|
37 |
+
"Virtual Reality", "Augmented Reality", "Internet of Things",
|
38 |
+
"5G Technology", "Autonomous Vehicles", "Bioinformatics",
|
39 |
+
"Cloud Computing", "Data Science", "Artificial General Intelligence",
|
40 |
+
"Quantum Cryptography", "3D Printing", "Smart Cities",
|
41 |
+
"Biotechnology", "Fusion Energy", "Sustainable Agriculture",
|
42 |
+
"Space Tourism", "Quantum Sensors", "Brain-Computer Interfaces",
|
43 |
+
"Personalized Medicine", "Synthetic Biology", "Exoplanets",
|
44 |
+
"Dark Matter", "CRISPR Technology", "Quantum Internet",
|
45 |
+
"Deep Learning", "Edge Computing", "Humanoid Robots",
|
46 |
+
"Drone Technology", "Quantum Supremacy", "Neuromorphic Computing",
|
47 |
+
"Asteroid Mining", "Bionic Implants", "Smart Materials",
|
48 |
+
"Quantum Dots", "Lab-grown Meat", "Vertical Farming",
|
49 |
+
"Hyperloop Transportation", "Molecular Nanotechnology", "Quantum Metrology",
|
50 |
+
"Artificial Photosynthesis", "Cognitive Computing", "Swarm Robotics",
|
51 |
+
"Metamaterials", "Neuroplasticity", "Quantum Machine Learning",
|
52 |
+
"Green Hydrogen", "Organ-on-a-Chip", "Bioprinting",
|
53 |
+
"Plasma Physics", "Quantum Simulation", "Soft Robotics",
|
54 |
+
"Geoengineering", "Exoskeletons", "Programmable Matter",
|
55 |
+
"Graphene Applications", "Quantum Sensing", "Neuralink",
|
56 |
+
"Holographic Displays", "Quantum Error Correction", "Synthetic Genomes",
|
57 |
+
"Carbon Capture and Storage", "Quantum Memory", "Organoids",
|
58 |
+
"Artificial Synapses", "Quantum Imaging", "Biosensors",
|
59 |
+
"Memristors", "Quantum Annealing", "DNA Data Storage",
|
60 |
+
"Cultured Meat", "Quantum Radar", "Neuromorphic Hardware",
|
61 |
+
"Quantum Entanglement", "Phytomining", "Biohacking",
|
62 |
+
"Topological Quantum Computing", "Neuroprosthetics", "Optogenetics",
|
63 |
+
"Quantum Gravity", "Molecular Machines", "Biomimicry",
|
64 |
+
"Quantum Teleportation", "Neurogenesis", "Bioelectronics",
|
65 |
+
"Quantum Tunneling", "Tissue Engineering", "Bioremediation",
|
66 |
+
"Quantum Photonics", "Synthetic Neurobiology", "Nanomedicine",
|
67 |
+
"Quantum Biology", "Biogeochemistry", "Molecular Gastronomy",
|
68 |
+
"Quantum Thermodynamics", "Nutrigenomics", "Biomechatronics",
|
69 |
+
"Quantum Chemistry", "Psychoneuroimmunology", "Nanophotonics",
|
70 |
+
"Quantum Optics", "Neuroeconomics", "Bionanotechnology"
|
71 |
+
]
|
72 |
+
|
73 |
+
def process_document(file):
|
74 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file.name)[1]) as temp_file:
|
75 |
+
temp_file.write(file.getvalue())
|
76 |
+
temp_file_path = temp_file.name
|
77 |
+
|
78 |
+
if file.name.endswith('.pdf'):
|
79 |
+
loader = PyPDFLoader(temp_file_path)
|
80 |
+
else:
|
81 |
+
loader = TextLoader(temp_file_path)
|
82 |
+
|
83 |
+
documents = loader.load()
|
84 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
|
85 |
+
texts = text_splitter.split_documents(documents)
|
86 |
+
|
87 |
+
vectorstore = FAISS.from_documents(texts, embeddings)
|
88 |
+
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
|
89 |
+
|
90 |
+
qa_chain = RetrievalQA.from_chain_type(
|
91 |
+
llm=chat,
|
92 |
+
chain_type="stuff",
|
93 |
+
retriever=retriever,
|
94 |
+
return_source_documents=True
|
95 |
+
)
|
96 |
+
|
97 |
+
os.unlink(temp_file_path)
|
98 |
+
return qa_chain
|
99 |
+
|
100 |
+
def get_sherlock_analysis(topic, qa_chain=None):
|
101 |
+
system_prompt = """
|
102 |
+
You are Sherlock Holmes, the world's greatest detective and master of observation and deduction.
|
103 |
+
Your task is to provide an in-depth analysis of the given topic, offering unique insights on how to approach learning it from the ground up.
|
104 |
+
Your analysis should:
|
105 |
+
1. Break down the topic into its fundamental components.
|
106 |
+
2. Identify key concepts and their relationships.
|
107 |
+
3. Suggest a structured approach to learning, starting from first principles.
|
108 |
+
4. Highlight potential challenges and how to overcome them.
|
109 |
+
5. Provide a unique point of view that encourages critical thinking.
|
110 |
+
Your response should be detailed, insightful, and encourage a deep understanding of the subject.
|
111 |
+
"""
|
112 |
+
|
113 |
+
if qa_chain:
|
114 |
+
result = qa_chain({"query": f"Provide a Sherlock Holmes style analysis of the topic: {topic}"})
|
115 |
+
response = result['result']
|
116 |
+
else:
|
117 |
+
messages = [
|
118 |
+
SystemMessage(content=system_prompt),
|
119 |
+
HumanMessage(content=f"Analyze the following topic: {topic}")
|
120 |
+
]
|
121 |
+
response = chat.invoke(messages).content
|
122 |
+
|
123 |
+
return response
|
124 |
+
|
125 |
+
def chunk_text(text, max_chunk_size=4000):
|
126 |
+
chunks = []
|
127 |
+
current_chunk = ""
|
128 |
+
for sentence in text.split(". "):
|
129 |
+
if len(current_chunk) + len(sentence) < max_chunk_size:
|
130 |
+
current_chunk += sentence + ". "
|
131 |
+
else:
|
132 |
+
chunks.append(current_chunk)
|
133 |
+
current_chunk = sentence + ". "
|
134 |
+
if current_chunk:
|
135 |
+
chunks.append(current_chunk)
|
136 |
+
return chunks
|
137 |
+
|
138 |
+
def main():
|
139 |
+
st.set_page_config(page_title="S.H.E.R.L.O.C.K. Observation", page_icon="π", layout="wide")
|
140 |
+
|
141 |
+
st.title("π΅οΈ S.H.E.R.L.O.C.K. Observation")
|
142 |
+
st.markdown("*Uncover the depths of any subject with the keen insight of Sherlock Holmes*")
|
143 |
+
|
144 |
+
col1, col2 = st.columns([2, 1])
|
145 |
+
|
146 |
+
with col2:
|
147 |
+
st.subheader("Choose Your Method")
|
148 |
+
method = st.radio("Select input method:", ["Enter Topic", "Upload Document", "Choose from List"])
|
149 |
+
|
150 |
+
if method == "Enter Topic":
|
151 |
+
topic = st.text_input("Enter your topic of interest:")
|
152 |
+
elif method == "Upload Document":
|
153 |
+
uploaded_file = st.file_uploader("Upload a document (PDF or TXT)", type=["pdf", "txt"])
|
154 |
+
if uploaded_file:
|
155 |
+
topic = uploaded_file.name
|
156 |
+
else:
|
157 |
+
topic = st.selectbox("Choose a topic:", PREDEFINED_TOPICS)
|
158 |
+
|
159 |
+
if st.button("Analyze", key="analyze_button"):
|
160 |
+
if method == "Upload Document" and uploaded_file:
|
161 |
+
qa_chain = process_document(uploaded_file)
|
162 |
+
analysis = get_sherlock_analysis(topic, qa_chain)
|
163 |
+
elif topic:
|
164 |
+
analysis = get_sherlock_analysis(topic)
|
165 |
+
else:
|
166 |
+
st.warning("Please provide a topic or upload a document.")
|
167 |
+
return
|
168 |
+
|
169 |
+
col1.markdown("## Sherlock's Analysis")
|
170 |
+
chunks = chunk_text(analysis)
|
171 |
+
for chunk in chunks:
|
172 |
+
col1.markdown(chunk)
|
173 |
+
|
174 |
+
st.sidebar.image("https://upload.wikimedia.org/wikipedia/commons/c/cd/Sherlock_Holmes_Portrait_Paget.jpg", use_column_width=True)
|
175 |
+
st.sidebar.title("About S.H.E.R.L.O.C.K. Observation")
|
176 |
+
st.sidebar.markdown("""
|
177 |
+
S.H.E.R.L.O.C.K. Observation is your personal detective for any subject.
|
178 |
+
It provides:
|
179 |
+
- In-depth analysis of topics
|
180 |
+
- Unique perspectives on learning approaches
|
181 |
+
- First principles breakdown of subjects
|
182 |
+
- Critical thinking encouragement
|
183 |
+
|
184 |
+
Let Sherlock guide you through the intricacies of any field of study!
|
185 |
+
""")
|
186 |
+
|
187 |
+
st.sidebar.markdown("---")
|
188 |
+
st.sidebar.markdown("Powered by Falcon-180B and Streamlit")
|
189 |
+
|
190 |
+
if __name__ == "__main__":
|
191 |
+
main()
|
pages/study_roadmap.py
ADDED
@@ -0,0 +1,481 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import networkx as nx
|
3 |
+
import plotly.graph_objects as go
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
from langchain.chat_models import ChatOpenAI
|
6 |
+
from langchain.prompts import ChatPromptTemplate
|
7 |
+
from langchain.output_parsers import PydanticOutputParser
|
8 |
+
from pydantic import BaseModel, Field
|
9 |
+
from typing import List, Dict, Optional
|
10 |
+
import json
|
11 |
+
import pandas as pd
|
12 |
+
import time
|
13 |
+
from datetime import datetime
|
14 |
+
import random
|
15 |
+
import re
|
16 |
+
from PIL import Image
|
17 |
+
import logging
|
18 |
+
|
19 |
+
# Set up logging
|
20 |
+
logging.basicConfig(level=logging.INFO)
|
21 |
+
logger = logging.getLogger(__name__)
|
22 |
+
|
23 |
+
# Load environment variables
|
24 |
+
load_dotenv()
|
25 |
+
|
26 |
+
AI71_BASE_URL = "https://api.ai71.ai/v1/"
|
27 |
+
AI71_API_KEY = "api71-api-92fc2ef9-9f3c-47e5-a019-18e257b04af2"
|
28 |
+
|
29 |
+
# Initialize the Falcon model
|
30 |
+
chat = ChatOpenAI(
|
31 |
+
model="tiiuae/falcon-180B-chat",
|
32 |
+
api_key=AI71_API_KEY,
|
33 |
+
base_url=AI71_BASE_URL,
|
34 |
+
temperature=0.7,
|
35 |
+
)
|
36 |
+
|
37 |
+
class RoadmapStep(BaseModel):
|
38 |
+
title: str
|
39 |
+
description: str
|
40 |
+
resources: List[Dict[str, str]] = Field(default_factory=list)
|
41 |
+
estimated_time: str
|
42 |
+
how_to_use: Optional[str] = None
|
43 |
+
|
44 |
+
class Roadmap(BaseModel):
|
45 |
+
steps: Dict[str, RoadmapStep] = Field(default_factory=dict)
|
46 |
+
|
47 |
+
def clean_json(content):
|
48 |
+
# Remove any leading or trailing whitespace
|
49 |
+
content = content.strip()
|
50 |
+
|
51 |
+
# Ensure the content starts and ends with curly braces
|
52 |
+
if not content.startswith('{'):
|
53 |
+
content = '{' + content
|
54 |
+
if not content.endswith('}'):
|
55 |
+
content = content + '}'
|
56 |
+
|
57 |
+
# Remove any newline characters and extra spaces
|
58 |
+
content = ' '.join(content.split())
|
59 |
+
|
60 |
+
# Escape any unescaped double quotes within string values
|
61 |
+
content = re.sub(r'(?<!\\)"(?=(?:(?:[^"]*"){2})*[^"]*$)', r'\"', content)
|
62 |
+
|
63 |
+
return content
|
64 |
+
|
65 |
+
def ensure_valid_json(content):
|
66 |
+
# First, apply our existing cleaning function
|
67 |
+
content = clean_json(content)
|
68 |
+
|
69 |
+
# Use regex to find and fix unquoted property names
|
70 |
+
pattern = r'(\{|\,)\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*:'
|
71 |
+
content = re.sub(pattern, r'\1 "\2":', content)
|
72 |
+
|
73 |
+
# Replace single quotes with double quotes
|
74 |
+
content = content.replace("'", '"')
|
75 |
+
|
76 |
+
# Attempt to parse the JSON to catch any remaining issues
|
77 |
+
try:
|
78 |
+
json_obj = json.loads(content)
|
79 |
+
return json.dumps(json_obj) # Return a properly formatted JSON string
|
80 |
+
except json.JSONDecodeError as e:
|
81 |
+
# If we still can't parse it, log the error and return None
|
82 |
+
logger.error(f"Failed to parse JSON after cleaning: {str(e)}")
|
83 |
+
logger.debug(f"Problematic JSON: {content}")
|
84 |
+
return None
|
85 |
+
|
86 |
+
def generate_roadmap(topic):
|
87 |
+
levels = [
|
88 |
+
"knowledge",
|
89 |
+
"comprehension",
|
90 |
+
"application",
|
91 |
+
"analysis",
|
92 |
+
"synthesis",
|
93 |
+
"evaluation"
|
94 |
+
]
|
95 |
+
|
96 |
+
roadmap = Roadmap()
|
97 |
+
|
98 |
+
for level in levels:
|
99 |
+
try:
|
100 |
+
logger.info(f"Generating roadmap step for topic: {topic} at {level} level")
|
101 |
+
step = generate_simplified_step(topic, level, chat)
|
102 |
+
roadmap.steps[level] = step
|
103 |
+
logger.info(f"Added step for {level} level")
|
104 |
+
|
105 |
+
except Exception as e:
|
106 |
+
logger.error(f"Error in generate_roadmap for {level}: {str(e)}")
|
107 |
+
step = create_fallback_step(topic, level, chat)
|
108 |
+
roadmap.steps[level] = step
|
109 |
+
|
110 |
+
logger.info("Roadmap generation complete")
|
111 |
+
return roadmap
|
112 |
+
|
113 |
+
def generate_diverse_resources(topic, level):
|
114 |
+
encoded_topic = topic.replace(' ', '+')
|
115 |
+
encoded_level = level.replace(' ', '+')
|
116 |
+
|
117 |
+
resource_templates = [
|
118 |
+
{"title": "Wikipedia", "url": f"https://en.wikipedia.org/wiki/{topic.replace(' ', '_')}"},
|
119 |
+
{"title": "YouTube Overview", "url": f"https://www.youtube.com/results?search_query={encoded_topic}+{encoded_level}"},
|
120 |
+
{"title": "Coursera Courses", "url": f"https://www.coursera.org/search?query={encoded_topic}"},
|
121 |
+
{"title": "edX Courses", "url": f"https://www.edx.org/search?q={encoded_topic}"},
|
122 |
+
{"title": "Brilliant", "url": f"https://brilliant.org/search/?q={encoded_topic}"},
|
123 |
+
{"title": "Google Scholar", "url": f"https://scholar.google.com/scholar?q={encoded_topic}"},
|
124 |
+
{"title": "MIT OpenCourseWare", "url": f"https://ocw.mit.edu/search/?q={encoded_topic}"},
|
125 |
+
{"title": "Khan Academy", "url": f"https://www.khanacademy.org/search?query={encoded_topic}"},
|
126 |
+
{"title": "TED Talks", "url": f"https://www.ted.com/search?q={encoded_topic}"},
|
127 |
+
{"title": "arXiv Papers", "url": f"https://arxiv.org/search/?query={encoded_topic}&searchtype=all"},
|
128 |
+
{"title": "ResearchGate", "url": f"https://www.researchgate.net/search/publication?q={encoded_topic}"},
|
129 |
+
{"title": "Academic Earth", "url": f"https://academicearth.org/search/?q={encoded_topic}"},
|
130 |
+
]
|
131 |
+
|
132 |
+
# Randomly select 5-7 resources
|
133 |
+
num_resources = random.randint(5, 7)
|
134 |
+
selected_resources = random.sample(resource_templates, num_resources)
|
135 |
+
|
136 |
+
return selected_resources
|
137 |
+
|
138 |
+
def create_fallback_step(topic, level, chat):
|
139 |
+
def generate_component(prompt, default_value):
|
140 |
+
try:
|
141 |
+
response = chat.invoke([{"role": "system", "content": prompt}])
|
142 |
+
return response.content.strip() or default_value
|
143 |
+
except Exception as e:
|
144 |
+
logger.error(f"Error generating component: {str(e)}")
|
145 |
+
return default_value
|
146 |
+
|
147 |
+
# Generate title
|
148 |
+
title_prompt = f"Create a concise title (max 10 words) for a study step about {topic} at the {level} level of Bloom's Taxonomy."
|
149 |
+
default_title = f"{level.capitalize()} Step for {topic}"
|
150 |
+
title = generate_component(title_prompt, default_title)
|
151 |
+
|
152 |
+
# Generate description
|
153 |
+
description_prompt = f"""Write a detailed description (500-700 words) for a study step about {topic} at the {level} level of Bloom's Taxonomy.
|
154 |
+
Explain what this step entails, how the user should approach it, and why it's important for mastering the topic at this level.
|
155 |
+
The description should be specific to {topic} and not a generic explanation of the Bloom's Taxonomy level."""
|
156 |
+
default_description = f"In this step, you will focus on {topic} at the {level} level. This involves understanding key concepts and theories related to {topic}. Engage with the provided resources to build a strong foundation."
|
157 |
+
description = generate_component(description_prompt, default_description)
|
158 |
+
|
159 |
+
# Generate estimated time
|
160 |
+
time_prompt = f"Estimate the time needed to complete a study step about {topic} at the {level} level of Bloom's Taxonomy. Provide the answer in a format like '3-4 days' or '1-2 weeks'."
|
161 |
+
default_time = "3-4 days"
|
162 |
+
estimated_time = generate_component(time_prompt, default_time)
|
163 |
+
|
164 |
+
# Generate how to use
|
165 |
+
how_to_use_prompt = f"""Write a paragraph (100-150 words) on how to effectively use the {level} level of Bloom's Taxonomy when studying {topic}.
|
166 |
+
Include tips and strategies specific to {topic} at this {level} level."""
|
167 |
+
default_how_to_use = f"Explore the provided resources and take notes on key concepts related to {topic}. Practice explaining these concepts in your own words to reinforce your understanding at the {level} level."
|
168 |
+
how_to_use = generate_component(how_to_use_prompt, default_how_to_use)
|
169 |
+
|
170 |
+
return RoadmapStep(
|
171 |
+
title=title,
|
172 |
+
description=description,
|
173 |
+
resources=generate_diverse_resources(topic, level),
|
174 |
+
estimated_time=estimated_time,
|
175 |
+
how_to_use=how_to_use
|
176 |
+
)
|
177 |
+
|
178 |
+
def create_interactive_graph(roadmap):
|
179 |
+
G = nx.DiGraph()
|
180 |
+
color_map = {
|
181 |
+
'Knowledge': '#FF6B6B',
|
182 |
+
'Comprehension': '#4ECDC4',
|
183 |
+
'Application': '#45B7D1',
|
184 |
+
'Analysis': '#FFA07A',
|
185 |
+
'Synthesis': '#98D8C8',
|
186 |
+
'Evaluation': '#F9D56E'
|
187 |
+
}
|
188 |
+
|
189 |
+
for i, (level, step) in enumerate(roadmap.steps.items()):
|
190 |
+
G.add_node(step.title, level=level.capitalize(), pos=(i, -i))
|
191 |
+
|
192 |
+
pos = nx.get_node_attributes(G, 'pos')
|
193 |
+
|
194 |
+
edge_trace = go.Scatter(
|
195 |
+
x=[], y=[],
|
196 |
+
line=dict(width=2, color='#888'),
|
197 |
+
hoverinfo='none',
|
198 |
+
mode='lines')
|
199 |
+
|
200 |
+
node_trace = go.Scatter(
|
201 |
+
x=[], y=[],
|
202 |
+
mode='markers+text',
|
203 |
+
hoverinfo='text',
|
204 |
+
marker=dict(
|
205 |
+
showscale=False,
|
206 |
+
color=[],
|
207 |
+
size=30,
|
208 |
+
line_width=2
|
209 |
+
),
|
210 |
+
text=[],
|
211 |
+
textposition="top center"
|
212 |
+
)
|
213 |
+
|
214 |
+
for node in G.nodes():
|
215 |
+
x, y = pos[node]
|
216 |
+
node_trace['x'] += (x,)
|
217 |
+
node_trace['y'] += (y,)
|
218 |
+
node_info = f"{node}<br>Level: {G.nodes[node]['level']}"
|
219 |
+
node_trace['text'] += (node_info,)
|
220 |
+
node_trace['marker']['color'] += (color_map.get(G.nodes[node]['level'], '#CCCCCC'),)
|
221 |
+
|
222 |
+
fig = go.Figure(data=[edge_trace, node_trace],
|
223 |
+
layout=go.Layout(
|
224 |
+
title='Interactive Study Roadmap',
|
225 |
+
titlefont_size=16,
|
226 |
+
showlegend=False,
|
227 |
+
hovermode='closest',
|
228 |
+
margin=dict(b=20,l=5,r=5,t=40),
|
229 |
+
annotations=[dict(
|
230 |
+
text="",
|
231 |
+
showarrow=False,
|
232 |
+
xref="paper", yref="paper",
|
233 |
+
x=0.005, y=-0.002
|
234 |
+
)],
|
235 |
+
xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
|
236 |
+
yaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
|
237 |
+
plot_bgcolor='rgba(0,0,0,0)',
|
238 |
+
paper_bgcolor='rgba(0,0,0,0)'
|
239 |
+
))
|
240 |
+
|
241 |
+
# Add a color legend
|
242 |
+
for level, color in color_map.items():
|
243 |
+
fig.add_trace(go.Scatter(
|
244 |
+
x=[None], y=[None],
|
245 |
+
mode='markers',
|
246 |
+
marker=dict(size=10, color=color),
|
247 |
+
showlegend=True,
|
248 |
+
name=level
|
249 |
+
))
|
250 |
+
|
251 |
+
fig.update_layout(legend=dict(
|
252 |
+
orientation="h",
|
253 |
+
yanchor="bottom",
|
254 |
+
y=1.02,
|
255 |
+
xanchor="right",
|
256 |
+
x=1
|
257 |
+
))
|
258 |
+
|
259 |
+
return fig
|
260 |
+
|
261 |
+
def get_user_progress(roadmap):
|
262 |
+
if 'user_progress' not in st.session_state:
|
263 |
+
st.session_state.user_progress = {}
|
264 |
+
|
265 |
+
for level, step in roadmap.steps.items():
|
266 |
+
if step.title not in st.session_state.user_progress:
|
267 |
+
st.session_state.user_progress[step.title] = 0
|
268 |
+
|
269 |
+
return st.session_state.user_progress
|
270 |
+
|
271 |
+
def update_user_progress(step_title, progress):
|
272 |
+
st.session_state.user_progress[step_title] = progress
|
273 |
+
|
274 |
+
def calculate_overall_progress(progress_dict):
|
275 |
+
if not progress_dict:
|
276 |
+
return 0
|
277 |
+
total_steps = len(progress_dict)
|
278 |
+
completed_steps = sum(1 for progress in progress_dict.values() if progress == 100)
|
279 |
+
return (completed_steps / total_steps) * 100
|
280 |
+
|
281 |
+
def generate_simplified_step(topic, level, chat):
|
282 |
+
prompt = f"""Create a detailed study step for the topic: {topic} at the {level} level of Bloom's Taxonomy.
|
283 |
+
|
284 |
+
Provide:
|
285 |
+
1. A descriptive title (max 10 words)
|
286 |
+
2. A detailed description (500-700 words) explaining what this step entails, how the user should approach it, and why it's important for mastering the topic at this level. The description should be specific to {topic} and not a generic explanation of the Bloom's Taxonomy level.
|
287 |
+
3. Estimated time for completion (e.g., 3-4 days, 1-2 weeks, etc.)
|
288 |
+
4. A paragraph (100-150 words) on how to use this level effectively, including tips and strategies specific to {topic} at this {level} level
|
289 |
+
|
290 |
+
Format your response as a valid JSON object with the following structure:
|
291 |
+
{{
|
292 |
+
"title": "Step title",
|
293 |
+
"description": "Step description",
|
294 |
+
"estimated_time": "Estimated time",
|
295 |
+
"how_to_use": "Paragraph on how to use this level effectively"
|
296 |
+
}}
|
297 |
+
"""
|
298 |
+
|
299 |
+
try:
|
300 |
+
response = chat.invoke([{"role": "system", "content": prompt}])
|
301 |
+
valid_json = ensure_valid_json(response.content)
|
302 |
+
if valid_json is None:
|
303 |
+
raise ValueError("Failed to create valid JSON")
|
304 |
+
|
305 |
+
step_dict = json.loads(valid_json)
|
306 |
+
|
307 |
+
# Generate diverse resources
|
308 |
+
resources = generate_diverse_resources(topic, level)
|
309 |
+
|
310 |
+
return RoadmapStep(
|
311 |
+
title=step_dict["title"],
|
312 |
+
description=step_dict["description"],
|
313 |
+
resources=resources,
|
314 |
+
estimated_time=step_dict["estimated_time"],
|
315 |
+
how_to_use=step_dict["how_to_use"]
|
316 |
+
)
|
317 |
+
except Exception as e:
|
318 |
+
logger.error(f"Error in generate_simplified_step for {level}: {str(e)}")
|
319 |
+
return create_fallback_step(topic, level, chat)
|
320 |
+
|
321 |
+
|
322 |
+
|
323 |
+
def display_step(step, level, user_progress):
|
324 |
+
with st.expander(f"{level.capitalize()}: {step.title}"):
|
325 |
+
st.write(f"**Description:** {step.description}")
|
326 |
+
st.write(f"**Estimated Time:** {step.estimated_time}")
|
327 |
+
st.write("**Resources:**")
|
328 |
+
for resource in step.resources:
|
329 |
+
st.markdown(f"- [{resource['title']}]({resource['url']})")
|
330 |
+
if 'contribution' in resource:
|
331 |
+
st.write(f" *{resource['contribution']}*")
|
332 |
+
|
333 |
+
# Check if how_to_use exists before displaying it
|
334 |
+
if step.how_to_use:
|
335 |
+
st.write("**How to use this level effectively:**")
|
336 |
+
st.write(step.how_to_use)
|
337 |
+
|
338 |
+
progress = st.slider(f"Progress for {step.title}", 0, 100, user_progress.get(step.title, 0), key=f"progress_{level}")
|
339 |
+
update_user_progress(step.title, progress)
|
340 |
+
|
341 |
+
def main():
|
342 |
+
st.set_page_config(page_title="S.H.E.R.L.O.C.K. Study Roadmap Generator", layout="wide")
|
343 |
+
|
344 |
+
# Custom CSS for dark theme
|
345 |
+
st.markdown("""
|
346 |
+
<style>
|
347 |
+
.stApp {
|
348 |
+
background-color: #1E1E1E;
|
349 |
+
color: #FFFFFF;
|
350 |
+
}
|
351 |
+
.stButton>button {
|
352 |
+
background-color: #4CAF50;
|
353 |
+
color: white;
|
354 |
+
border-radius: 5px;
|
355 |
+
}
|
356 |
+
.stProgress > div > div > div > div {
|
357 |
+
background-color: #4CAF50;
|
358 |
+
}
|
359 |
+
.streamlit-expanderHeader {
|
360 |
+
background-color: #2E2E2E;
|
361 |
+
color: #FFFFFF;
|
362 |
+
}
|
363 |
+
.streamlit-expanderContent {
|
364 |
+
background-color: #2E2E2E;
|
365 |
+
color: #FFFFFF;
|
366 |
+
}
|
367 |
+
</style>
|
368 |
+
""", unsafe_allow_html=True)
|
369 |
+
|
370 |
+
st.title("π§ S.H.E.R.L.O.C.K. Study Roadmap Generator")
|
371 |
+
st.write("Generate a comprehensive study roadmap based on first principles for any topic.")
|
372 |
+
|
373 |
+
# Sidebar
|
374 |
+
with st.sidebar:
|
375 |
+
st.image("https://placekitten.com/300/200", caption="S.H.E.R.L.O.C.K.", use_column_width=True)
|
376 |
+
st.markdown("""
|
377 |
+
## About S.H.E.R.L.O.C.K.
|
378 |
+
**S**tudy **H**elper for **E**fficient **R**oadmaps and **L**earning **O**ptimization using **C**omprehensive **K**nowledge
|
379 |
+
|
380 |
+
S.H.E.R.L.O.C.K. is your AI-powered study companion, designed to create personalized learning roadmaps for any topic. It breaks down complex subjects into manageable steps, ensuring a comprehensive understanding from fundamentals to advanced concepts.
|
381 |
+
""")
|
382 |
+
|
383 |
+
st.subheader("π Todo List")
|
384 |
+
if 'todos' not in st.session_state:
|
385 |
+
st.session_state.todos = []
|
386 |
+
|
387 |
+
new_todo = st.text_input("Add a new todo:")
|
388 |
+
if st.button("Add Todo", key="add_todo"):
|
389 |
+
if new_todo:
|
390 |
+
st.session_state.todos.append({"task": new_todo, "completed": False})
|
391 |
+
st.success("Todo added successfully!")
|
392 |
+
else:
|
393 |
+
st.warning("Please enter a todo item.")
|
394 |
+
|
395 |
+
for i, todo in enumerate(st.session_state.todos):
|
396 |
+
col1, col2, col3 = st.columns([0.05, 0.8, 0.15])
|
397 |
+
with col1:
|
398 |
+
todo['completed'] = st.checkbox("", todo['completed'], key=f"todo_{i}")
|
399 |
+
with col2:
|
400 |
+
st.write(todo['task'], key=f"todo_text_{i}")
|
401 |
+
with col3:
|
402 |
+
if st.button("ποΈ", key=f"delete_{i}", help="Delete todo"):
|
403 |
+
st.session_state.todos.pop(i)
|
404 |
+
st.experimental_rerun()
|
405 |
+
|
406 |
+
st.subheader("β±οΈ Pomodoro Timer")
|
407 |
+
pomodoro_duration = st.slider("Pomodoro Duration (minutes)", 1, 60, 25)
|
408 |
+
if st.button("Start Pomodoro"):
|
409 |
+
progress_bar = st.progress(0)
|
410 |
+
for i in range(pomodoro_duration * 60):
|
411 |
+
time.sleep(1)
|
412 |
+
progress_bar.progress((i + 1) / (pomodoro_duration * 60))
|
413 |
+
st.success("Pomodoro completed!")
|
414 |
+
if 'achievements' not in st.session_state:
|
415 |
+
st.session_state.achievements = set()
|
416 |
+
st.session_state.achievements.add("Consistent Learner")
|
417 |
+
|
418 |
+
topic = st.text_input("π Enter the topic you want to master:")
|
419 |
+
|
420 |
+
if st.button("π Generate Roadmap"):
|
421 |
+
if topic:
|
422 |
+
with st.spinner("π§ Generating your personalized study roadmap..."):
|
423 |
+
try:
|
424 |
+
logger.info(f"Starting roadmap generation for topic: {topic}")
|
425 |
+
roadmap = generate_roadmap(topic)
|
426 |
+
if roadmap and roadmap.steps:
|
427 |
+
logger.info("Roadmap generated successfully")
|
428 |
+
st.session_state.current_roadmap = roadmap
|
429 |
+
st.session_state.current_topic = topic
|
430 |
+
st.success("Roadmap generated successfully!")
|
431 |
+
else:
|
432 |
+
logger.warning("Generated roadmap is empty or invalid")
|
433 |
+
st.error("Failed to generate a valid roadmap. Please try again with a different topic.")
|
434 |
+
except Exception as e:
|
435 |
+
logger.error(f"Error during roadmap generation: {str(e)}", exc_info=True)
|
436 |
+
st.error(f"An error occurred while generating the roadmap: {str(e)}")
|
437 |
+
|
438 |
+
if 'current_roadmap' in st.session_state:
|
439 |
+
st.subheader(f"π Study Roadmap for: {st.session_state.current_topic}")
|
440 |
+
|
441 |
+
roadmap = st.session_state.current_roadmap
|
442 |
+
fig = create_interactive_graph(roadmap)
|
443 |
+
fig.update_layout(
|
444 |
+
plot_bgcolor='rgba(0,0,0,0)',
|
445 |
+
paper_bgcolor='rgba(0,0,0,0)',
|
446 |
+
font_color='#FFFFFF'
|
447 |
+
)
|
448 |
+
st.plotly_chart(fig, use_container_width=True)
|
449 |
+
|
450 |
+
user_progress = get_user_progress(roadmap)
|
451 |
+
|
452 |
+
levels_description = {
|
453 |
+
"knowledge": "Understanding and remembering basic facts and concepts",
|
454 |
+
"comprehension": "Grasping the meaning and interpreting information",
|
455 |
+
"application": "Using knowledge in new situations",
|
456 |
+
"analysis": "Breaking information into parts and examining relationships",
|
457 |
+
"synthesis": "Combining elements to form a new whole",
|
458 |
+
"evaluation": "Making judgments based on criteria and standards"
|
459 |
+
}
|
460 |
+
|
461 |
+
for level, step in roadmap.steps.items():
|
462 |
+
st.header(f"{level.capitalize()} Level")
|
463 |
+
st.write(f"**Description:** {levels_description[level]}")
|
464 |
+
st.write("**How to master this level:**")
|
465 |
+
st.write(f"To master the {level} level, focus on {levels_description[level].lower()}. Engage with the resources provided, practice applying the concepts, and gradually build your understanding. Remember that mastery at this level is crucial before moving to the next.")
|
466 |
+
display_step(step, level, user_progress)
|
467 |
+
|
468 |
+
overall_progress = calculate_overall_progress(user_progress)
|
469 |
+
st.progress(overall_progress / 100)
|
470 |
+
st.write(f"Overall progress: {overall_progress:.2f}%")
|
471 |
+
|
472 |
+
roadmap_json = json.dumps(roadmap.dict(), indent=2)
|
473 |
+
st.download_button(
|
474 |
+
label="π₯ Download Roadmap as JSON",
|
475 |
+
data=roadmap_json,
|
476 |
+
file_name="study_roadmap.json",
|
477 |
+
mime="application/json"
|
478 |
+
)
|
479 |
+
|
480 |
+
if __name__ == "__main__":
|
481 |
+
main()
|