jdesiree commited on
Commit
a3aef29
·
verified ·
1 Parent(s): c4c901c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +197 -249
app.py CHANGED
@@ -7,32 +7,39 @@ import time
7
  import logging
8
  import re
9
 
 
10
  logging.basicConfig(level=logging.INFO)
11
  logger = logging.getLogger(__name__)
12
 
 
 
 
 
 
 
13
  llm = HuggingFaceEndpoint(
14
- repo_id="HuggingFaceH4/zephyr-7b-beta",
15
- temperature=0.7,
16
- top_p=0.9,
17
- repetition_penalty=1.1,
18
- model_kwargs={"max_length": 1024},
19
- huggingfacehub_api_token=os.getenv("HUGGINGFACEHUB_API_TOKEN")
20
  )
21
 
22
  math_template = ChatPromptTemplate.from_messages([
23
- ("system", """{system_message}
24
 
25
  You are an expert math tutor. For every math problem:
26
  1. Break it down into key concepts
27
- 2. Breifly explain concepts
28
  3. Outline the process for solving a similar problem
29
 
30
  Be comprehensive and educational. Structure your response clearly."""),
31
- ("human", "{question}")
32
  ])
33
 
34
  research_template = ChatPromptTemplate.from_messages([
35
- ("system", """{system_message}
36
 
37
  You are a research skills mentor. Help students with:
38
  - Determining the validity of sources
@@ -43,11 +50,11 @@ You are a research skills mentor. Help students with:
43
  - Database navigation and search strategies
44
 
45
  Provide detailed, actionable advice with specific examples."""),
46
- ("human", "{question}")
47
  ])
48
 
49
  study_template = ChatPromptTemplate.from_messages([
50
- ("system", """{system_message}
51
 
52
  You are a study skills coach. Help students with:
53
  - Effective study methods for different learning styles
@@ -59,260 +66,201 @@ You are a study skills coach. Help students with:
59
  - Offer short quiz sessions where you pose one to two questions at a time, then provide feedback on the students answers.
60
 
61
  Provide comprehensive, personalized advice with practical examples."""),
62
- ("human", "{question}")
63
  ])
64
 
65
  general_template = ChatPromptTemplate.from_messages([
66
- ("system", """{system_message}
67
 
68
  You are EduBot, a comprehensive AI learning assistant. You help students with:
69
- 📐 Mathematics (Concise explainations rooted in understanding the concepts and process rather than answering the math problem directly)
70
  🔍 Research skills (source guidance, research advice, evaluation, and citation)
71
  📚 Study strategies (effective learning techniques and exam preparation)
72
  🛠️ Educational tools (guidance on learning resources and technologies)
73
 
74
  Always be encouraging, patient, thorough, and comprehensive."""),
75
- ("human", "{question}")
76
  ])
77
 
 
78
  def detect_subject(message):
79
- message_lower = message.lower()
80
-
81
- math_keywords = ['math', 'solve', 'calculate', 'equation', 'formula', 'algebra', 'geometry', 'calculus', 'derivative', 'integral', 'theorem', 'proof']
82
- research_keywords = ['research', 'source', 'citation', 'bibliography', 'reference', 'academic', 'paper', 'essay', 'thesis', 'database', 'journal']
83
- study_keywords = ['study', 'memorize', 'exam', 'test', 'quiz', 'review', 'learn', 'remember', 'focus', 'motivation', 'notes']
84
-
85
- if any(keyword in message_lower for keyword in math_keywords):
86
- return math_template, "🧮 Math Mode"
87
- elif any(keyword in message_lower for keyword in research_keywords):
88
- return research_template, "🔍 Research Mode"
89
- elif any(keyword in message_lower for keyword in study_keywords):
90
- return study_template, "📚 Study Mode"
91
- else:
92
- return general_template, "🎓 General Mode"
 
93
 
94
  def smart_truncate(text, max_length=3000):
95
- if len(text) <= max_length:
96
- return text
97
-
98
- sentences = re.split(r'(?<=[.!?]) +', text[:max_length])
99
- if len(sentences) > 1:
100
- return ' '.join(sentences[:-1]) + "... [Response truncated - ask for continuation]"
101
- else:
102
- words = text[:max_length].split()
103
- return ' '.join(words[:-1]) + "... [Response truncated - ask for continuation]"
 
 
 
104
 
105
  def respond_with_enhanced_streaming(message, history):
106
- try:
107
- template, mode = detect_subject(message)
108
- chain = template | llm
109
-
110
- yield f"*{mode}*\n\nGenerating response..."
111
-
112
- logger.info(f"Processing {mode} query: {message[:50]}...")
113
-
114
- response = chain.invoke({
115
- "question": message,
116
- "system_message": "You are EduBot, an expert AI learning assistant. Provide comprehensive, educational responses that help students truly understand concepts."
117
- })
118
-
119
- response = smart_truncate(response, max_length=3000)
120
-
121
- words = response.split()
122
- partial_response = f"*{mode}*\n\n"
123
-
124
- for i, word in enumerate(words):
125
- partial_response += word + " "
126
-
127
- if i % 4 == 0:
128
- yield partial_response
129
- time.sleep(0.03)
130
-
131
- final_response = f"*{mode}*\n\n{response}"
132
- logger.info(f"Response completed. Length: {len(response)} characters")
133
- yield final_response
134
-
135
- except Exception as e:
136
- logger.exception("Error in LangChain response generation")
137
- yield f"Sorry, I encountered an error: {str(e)[:150]}"
138
-
139
- theme = gr.themes.Glass(
140
- primary_hue="amber",
141
- text_size="md",
142
- radius_size="md",
143
- ).set(
144
- shadow_drop='*shadow_drop_lg',
145
- shadow_drop_lg='*shadow_drop',
146
- shadow_spread='*shadow_drop_lg',
147
- block_info_text_weight='500',
148
- button_border_width='*block_border_width'
149
- )
150
 
151
- with gr.Blocks(theme=theme, css="""
152
- .main-container {
153
- max-width: 900px;
154
- margin: 0 auto;
155
- padding: 1rem;
156
- height: 100vh;
157
- display: flex;
158
- flex-direction: column;
159
- }
160
- .title {
161
- text-align: center;
162
- font-size: clamp(1.8rem, 4vw, 2.5rem);
163
- font-weight: bold;
164
- color: #d97706;
165
- margin-bottom: 1rem;
166
- flex-shrink: 0;
167
- }
168
- .chat-container {
169
- flex: 1;
170
- display: flex;
171
- flex-direction: column;
172
- min-height: 0;
173
- margin-bottom: 1rem;
174
- }
175
- .gradio-chatbot {
176
- flex: 1 !important;
177
- height: auto !important;
178
- min-height: 400px;
179
- max-height: calc(100vh - 250px);
180
- }
181
-
182
- .gradio-chatbot .message-wrap .avatar-container {
183
- display: none !important;
184
- }
185
-
186
- .input-row {
187
- flex-shrink: 0;
188
- margin-top: 0.5rem;
189
- display: flex;
190
- justify-content: stretch;
191
- align-items: flex-end;
192
- gap: 10px;
193
- max-width: 700px;
194
- margin-left: auto;
195
- margin-right: auto;
196
- }
197
-
198
- .custom-textarea {
199
- width: 100% !important;
200
- min-width: 600px !important;
201
- }
202
-
203
- .custom-textarea textarea {
204
- border-radius: 20px !important;
205
- border: 2px solid #64748b !important;
206
- padding: 15px 20px !important;
207
- font-size: 16px !important;
208
- background: white !important;
209
- outline: none !important;
210
- box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
211
- transition: all 0.3s ease !important;
212
- resize: vertical !important;
213
- font-family: inherit !important;
214
- width: 100% !important;
215
- min-width: 600px !important;
216
- min-height: 120px !important;
217
- max-height: 240px !important;
218
- }
219
-
220
- .custom-textarea textarea:focus {
221
- border-color: #475569 !important;
222
- box-shadow: 0 0 0 3px rgba(100, 116, 139, 0.1) !important;
223
- }
224
-
225
- .custom-textarea label {
226
- display: none !important;
227
- }
228
-
229
- .send-button {
230
- border-radius: 50% !important;
231
- width: 50px !important;
232
- height: 50px !important;
233
- display: flex !important;
234
- align-items: center !important;
235
- justify-content: center !important;
236
- flex-shrink: 0 !important;
237
- align-self: flex-end !important;
238
- }
239
-
240
- @media (max-width: 768px) {
241
- .main-container {
242
- padding: 0.5rem;
243
- }
244
- .title {
245
- font-size: 1.5rem;
246
- margin-bottom: 0.5rem;
247
- }
248
- .gradio-chatbot {
249
- min-height: 300px;
250
- max-height: calc(100vh - 200px);
251
- }
252
- .input-row {
253
- margin-top: 0.25rem;
254
- max-width: 100%;
255
- padding: 0 0.5rem;
256
- }
257
- .custom-textarea textarea {
258
- padding: 12px 15px !important;
259
- font-size: 14px !important;
260
- }
261
- }
262
-
263
- @media (min-width: 769px) and (max-width: 1024px) {
264
- .gradio-chatbot {
265
- min-height: 450px;
266
- max-height: calc(100vh - 230px);
267
- }
268
- }
269
-
270
- @media (min-width: 1025px) {
271
- .gradio-chatbot {
272
- min-height: 500px;
273
- max-height: calc(100vh - 250px);
274
- }
275
- }
276
- """) as demo:
277
-
278
- with gr.Column(elem_classes=["main-container"]):
279
- gr.HTML('<div class="title">🎓 EduBot</div>')
280
-
281
- with gr.Column(elem_classes=["chat-container"]):
282
- chatbot = gr.Chatbot(
283
- show_copy_button=True,
284
- avatar_images=[None, None],
285
- placeholder="Hi! I'm EduBot. What would you like to learn today? 📚",
286
- container=True,
287
- elem_classes=["gradio-chatbot"]
288
- )
289
-
290
- with gr.Row(elem_classes=["input-row"]):
291
- msg = gr.Textbox(
292
- placeholder="Ask me about math, research, study strategies, or any educational topic...",
293
- show_label=False,
294
- lines=4,
295
- max_lines=8,
296
- elem_classes=["custom-textarea"]
297
- )
298
- send_btn = gr.Button("📤", variant="primary", elem_classes=["send-button"])
299
-
300
- def respond_and_update(message, history):
301
- # Add the user's message to history with a placeholder for the bot's response
302
- history.append([message, None])
303
-
304
- # This first yield makes the user's message appear immediately
305
- yield history, ""
306
-
307
- full_response = ""
308
- # Stream the bot's full response
309
- for response_chunk in respond_with_enhanced_streaming(message, history):
310
- full_response = response_chunk
311
- # Continuously update the bot's message in the last row of history
312
- history[-1][1] = full_response
313
- # Yield the updated history and clear the input textbox
314
- yield history, ""
315
 
316
  if __name__ == "__main__":
317
- logger.info("Starting EduBot...")
318
- demo.launch()
 
7
  import logging
8
  import re
9
 
10
+ # --- Environment and Logging Setup ---
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger(__name__)
13
 
14
+ # Make sure to set your Hugging Face API token in your environment variables
15
+ # For example: export HUGGINGFACEHUB_API_TOKEN='your_token_here'
16
+ if "HUGGINGFACEHUB_API_TOKEN" not in os.environ:
17
+ logger.warning("HUGGINGFACEHUB_API_TOKEN not set, the application may not work.")
18
+
19
+ # --- LLM and Template Configuration ---
20
  llm = HuggingFaceEndpoint(
21
+ repo_id="HuggingFaceH4/zephyr-7b-beta",
22
+ temperature=0.7,
23
+ top_p=0.9,
24
+ repetition_penalty=1.1,
25
+ model_kwargs={"max_length": 1024},
26
+ huggingfacehub_api_token=os.getenv("HUGGINGFACEHUB_API_TOKEN")
27
  )
28
 
29
  math_template = ChatPromptTemplate.from_messages([
30
+ ("system", """{system_message}
31
 
32
  You are an expert math tutor. For every math problem:
33
  1. Break it down into key concepts
34
+ 2. Briefly explain concepts
35
  3. Outline the process for solving a similar problem
36
 
37
  Be comprehensive and educational. Structure your response clearly."""),
38
+ ("human", "{question}")
39
  ])
40
 
41
  research_template = ChatPromptTemplate.from_messages([
42
+ ("system", """{system_message}
43
 
44
  You are a research skills mentor. Help students with:
45
  - Determining the validity of sources
 
50
  - Database navigation and search strategies
51
 
52
  Provide detailed, actionable advice with specific examples."""),
53
+ ("human", "{question}")
54
  ])
55
 
56
  study_template = ChatPromptTemplate.from_messages([
57
+ ("system", """{system_message}
58
 
59
  You are a study skills coach. Help students with:
60
  - Effective study methods for different learning styles
 
66
  - Offer short quiz sessions where you pose one to two questions at a time, then provide feedback on the students answers.
67
 
68
  Provide comprehensive, personalized advice with practical examples."""),
69
+ ("human", "{question}")
70
  ])
71
 
72
  general_template = ChatPromptTemplate.from_messages([
73
+ ("system", """{system_message}
74
 
75
  You are EduBot, a comprehensive AI learning assistant. You help students with:
76
+ 📐 Mathematics (Concise explanations rooted in understanding the concepts and process rather than answering the math problem directly)
77
  🔍 Research skills (source guidance, research advice, evaluation, and citation)
78
  📚 Study strategies (effective learning techniques and exam preparation)
79
  🛠️ Educational tools (guidance on learning resources and technologies)
80
 
81
  Always be encouraging, patient, thorough, and comprehensive."""),
82
+ ("human", "{question}")
83
  ])
84
 
85
+ # --- Core Logic Functions ---
86
  def detect_subject(message):
87
+ """Detects the subject of the user's message based on keywords."""
88
+ message_lower = message.lower()
89
+
90
+ math_keywords = ['math', 'solve', 'calculate', 'equation', 'formula', 'algebra', 'geometry', 'calculus', 'derivative', 'integral', 'theorem', 'proof']
91
+ research_keywords = ['research', 'source', 'citation', 'bibliography', 'reference', 'academic', 'paper', 'essay', 'thesis', 'database', 'journal']
92
+ study_keywords = ['study', 'memorize', 'exam', 'test', 'quiz', 'review', 'learn', 'remember', 'focus', 'motivation', 'notes']
93
+
94
+ if any(keyword in message_lower for keyword in math_keywords):
95
+ return math_template, "🧮 Math Mode"
96
+ elif any(keyword in message_lower for keyword in research_keywords):
97
+ return research_template, "🔍 Research Mode"
98
+ elif any(keyword in message_lower for keyword in study_keywords):
99
+ return study_template, "📚 Study Mode"
100
+ else:
101
+ return general_template, "🎓 General Mode"
102
 
103
  def smart_truncate(text, max_length=3000):
104
+ """Truncates text intelligently to the last full sentence or word."""
105
+ if len(text) <= max_length:
106
+ return text
107
+
108
+ # Try to split by sentence
109
+ sentences = re.split(r'(?<=[.!?])\s+', text[:max_length])
110
+ if len(sentences) > 1:
111
+ return ' '.join(sentences[:-1]) + "... [Response truncated - ask for continuation]"
112
+ # Otherwise, split by word
113
+ else:
114
+ words = text[:max_length].split()
115
+ return ' '.join(words[:-1]) + "... [Response truncated - ask for continuation]"
116
 
117
  def respond_with_enhanced_streaming(message, history):
118
+ """Streams the bot's response, detecting the subject and handling errors."""
119
+ try:
120
+ template, mode = detect_subject(message)
121
+ chain = template | llm
122
+
123
+ yield f"*{mode}*\n\nGenerating response..."
124
+
125
+ logger.info(f"Processing {mode} query: {message[:50]}...")
126
+
127
+ response = chain.invoke({
128
+ "question": message,
129
+ "system_message": "You are EduBot, an expert AI learning assistant. Provide comprehensive, educational responses that help students truly understand concepts."
130
+ })
131
+
132
+ response = smart_truncate(response, max_length=3000)
133
+
134
+ # Stream the response word by word
135
+ words = response.split()
136
+ partial_response = f"*{mode}*\n\n"
137
+
138
+ for i, word in enumerate(words):
139
+ partial_response += word + " "
140
+
141
+ # Update the stream periodically
142
+ if i % 4 == 0:
143
+ yield partial_response
144
+ time.sleep(0.03)
145
+
146
+ final_response = f"*{mode}*\n\n{response}"
147
+ logger.info(f"Response completed. Length: {len(response)} characters")
148
+ yield final_response
149
+
150
+ except Exception as e:
151
+ logger.exception("Error in LangChain response generation")
152
+ yield f"Sorry, I encountered an error: {str(e)[:150]}"
 
 
 
 
 
 
 
 
 
153
 
154
+ # --- Gradio UI and CSS ---
155
+ custom_css = """
156
+ /* Main container and background */
157
+ .gradio-container {
158
+ background-color: rgb(240, 236, 230) !important;
159
+ }
160
+
161
+ /* Centered chat interface */
162
+ .chat-container {
163
+ max-width: 800px;
164
+ margin: 0 auto;
165
+ padding: 1rem;
166
+ height: calc(100vh - 40px);
167
+ display: flex;
168
+ flex-direction: column;
169
+ }
170
+
171
+ /* Model name title */
172
+ .model-name {
173
+ font-size: 1.5rem;
174
+ font-weight: bold;
175
+ color: black;
176
+ margin-bottom: 1rem;
177
+ text-align: left;
178
+ }
179
+
180
+ /* Chatbot styling */
181
+ .gradio-chatbot {
182
+ flex: 1;
183
+ overflow-y: auto;
184
+ }
185
+
186
+ /* General message bubble styling */
187
+ .gradio-chatbot .message {
188
+ border-radius: 18px !important;
189
+ padding: 12px !important;
190
+ color: black !important;
191
+ box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important;
192
+ }
193
+
194
+ /* Model's chat bubble color */
195
+ .gradio-chatbot .message.bot {
196
+ background-color: rgb(240, 185, 103) !important;
197
+ }
198
+
199
+ /* User's chat bubble color */
200
+ .gradio-chatbot .message.user {
201
+ background-color: rgb(242, 238, 233) !important;
202
+ }
203
+
204
+ /* Hide the default Gradio avatars */
205
+ .gradio-chatbot .avatar-container {
206
+ display: none !important;
207
+ }
208
+
209
+ /* Input textbox styling */
210
+ .custom-textarea textarea {
211
+ background-color: rgb(242, 238, 233) !important;
212
+ border: 2px solid rgb(28, 18, 5) !important;
213
+ border-radius: 20px !important;
214
+ color: black !important;
215
+ padding: 15px !important;
216
+ font-size: 16px !important;
217
+ box-shadow: none !important;
218
+ }
219
+
220
+ /* Remove the label from the textbox */
221
+ .custom-textarea label {
222
+ display: none !important;
223
+ }
224
+ """
225
+
226
+ with gr.Blocks(css=custom_css) as demo:
227
+ with gr.Column(elem_classes=["chat-container"]):
228
+ # Model Name in the top-left corner
229
+ gr.HTML('<div class="model-name">🎓 EduBot</div>')
230
+
231
+ # The main chatbot interface
232
+ chatbot = gr.Chatbot(
233
+ elem_classes=["gradio-chatbot"],
234
+ show_copy_button=True,
235
+ avatar_images=[None, None], # Avatars are hidden by CSS but this is good practice
236
+ placeholder="Hi! I'm EduBot. What would you like to learn today? 📚"
237
+ )
238
+
239
+ # The user input textbox
240
+ msg = gr.Textbox(
241
+ placeholder="Ask me about math, research, study strategies, or any educational topic...",
242
+ show_label=False,
243
+ lines=2,
244
+ elem_classes=["custom-textarea"]
245
+ )
246
+
247
+ def respond_and_update(message, history):
248
+ """Main function to handle user submission."""
249
+ # Add user message to history
250
+ history.append([message, None])
251
+ # Yield history to show the user message immediately, and clear the textbox
252
+ yield history, ""
253
+
254
+ # Stream the bot's response
255
+ full_response = ""
256
+ for response_chunk in respond_with_enhanced_streaming(message, history):
257
+ full_response = response_chunk
258
+ history[-1][1] = full_response
259
+ yield history, ""
260
+
261
+ # Set up the "send on Enter" event
262
+ msg.submit(respond_and_update, [msg, chatbot], [chatbot, msg])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
 
264
  if __name__ == "__main__":
265
+ logger.info("Starting EduBot...")
266
+ demo.launch(debug=True)