Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -76,24 +76,19 @@ You recognize that students may seek direct answers to homework, assignments, or
|
|
| 76 |
- Encourage students to explain their thinking and reasoning
|
| 77 |
- Provide honest, accurate feedback even when it may not be what the student wants to hear
|
| 78 |
|
| 79 |
-
|
|
|
|
| 80 |
|
| 81 |
-
|
| 82 |
-
("system", """{system_message}
|
| 83 |
-
Math Mode
|
| 84 |
LaTeX formatting is enabled for math. You must provide LaTeX formatting for all math, either as inline LaTeX or centered display LaTeX.
|
| 85 |
You will address requests to solve, aid in understanding, or explore mathematical context. Use logical ordering for content, providing necessary terms and definitions as well as concept explanations along with math to foster understanding of core concepts. Rather than specifically answering the math problem provided, begin with solving a similar problem that requires the same steps and foundational mathematical knowledge, then prompt the user to work through the problem themselves. If the user insists you solve the problem, engage in a two-way conversation where you provide the steps but request the user solve for the answer one step at a time.
|
| 86 |
LaTeX should always be used for math.
|
| 87 |
LaTeX Examples:
|
| 88 |
- Inline: "The slope is $m = \\frac{{y_2 - y_1}}{{x_2 - x_1}}$ in this case."
|
| 89 |
- Display: "The quadratic formula is: $x = \\frac{{-b \\pm \\sqrt{{b^2-4ac}}}}{{2a}}$"
|
| 90 |
-
Always use double backslashes (\\\\) for LaTeX commands like \\\\frac, \\\\sqrt, \\\\int, etc.
|
| 91 |
-
("human", "{question}")
|
| 92 |
-
])
|
| 93 |
|
| 94 |
-
|
| 95 |
-
("system", """{system_message}
|
| 96 |
-
Research Mode
|
| 97 |
Your main goal is to help the user learn to research topics, a critical skill. Function as a partner rather than a search engine.
|
| 98 |
Over the course of the conversation, guide the user through a seven-step research process:
|
| 99 |
1) **Identifying a topic**
|
|
@@ -120,127 +115,23 @@ Reference: Smith, J.A. (2023) Book title. Publisher.
|
|
| 120 |
IEEE Style
|
| 121 |
In-text: [1]
|
| 122 |
Reference: [1] J. A. Smith, Book Title. Publisher, 2023.
|
| 123 |
-
In this mode you may not use LaTeX formatting.
|
| 124 |
-
("human", "{question}")
|
| 125 |
-
])
|
| 126 |
|
| 127 |
-
|
| 128 |
-
("system", """{system_message}
|
| 129 |
-
Study Mode
|
| 130 |
Engage the user in a mix of two teaching styles: student-centered and inquiry-based learning.
|
| 131 |
Student Centered: Adjust to reflect the student's reading level and level of understanding of a topic as the conversation progresses. Do not assume the user is an expert but instead assume they may have familiarity but desire to learn more about the topic they are studying. Provide definitions for terms you use in a conversational way, gradually shifting to using just the terms without definitions as the user becomes more familiar with them.
|
| 132 |
Inquiry-based learning: Engage the user through questions that compel them to consider what they want to know and then explore the topics through guided conversation.
|
| 133 |
Over the course of the conversation, prompt the user with a question to gauge their growing knowledge or progress on the topic.
|
| 134 |
For example:
|
| 135 |
After two to three turns of conversation discussing a topic, pick a specific term or concept from the conversation history to craft either a multiple-choice or written answer question for the user with no other comments along with it. If the student is correct, congratulate them on their progress and inquire about their next learning goal on the topic. If the user fails the question, return with a short response that explains the correct answer in a kind tone.
|
| 136 |
-
In this mode you may not use LaTeX formatting.
|
| 137 |
-
|
| 138 |
-
|
|
|
|
| 139 |
|
| 140 |
-
|
| 141 |
-
("system", """{system_message}
|
| 142 |
-
General Mode
|
| 143 |
-
You are EduBot, a comprehensive AI learning assistant. Help users leverage educational tools and resources to enrich their education. Offer yourself as a resource for the student, prompting them to request help with **math topics**, **research strategy**, or **studying a topic**."""),
|
| 144 |
-
("human", "{question}")
|
| 145 |
-
])
|
| 146 |
|
| 147 |
# --- Core Logic Functions ---
|
| 148 |
-
def detect_subject(message, history=None):
|
| 149 |
-
"""
|
| 150 |
-
Detects the subject of the user's message based on keywords and conversation context.
|
| 151 |
-
|
| 152 |
-
Args:
|
| 153 |
-
message (str): Current user message
|
| 154 |
-
history (list): Chat history with role/content structure
|
| 155 |
-
|
| 156 |
-
Returns:
|
| 157 |
-
tuple: (template, mode_string)
|
| 158 |
-
"""
|
| 159 |
-
message_lower = message.lower()
|
| 160 |
-
|
| 161 |
-
math_keywords = ['math', 'mathematics', 'solve', 'calculate', 'equation', 'formula', 'algebra', 'geometry', 'calculus', 'derivative', 'integral', 'theorem', 'proof', 'trigonometry', 'statistics', 'probability', 'arithmetic', 'fraction', 'decimal', 'percentage', 'graph', 'function', 'polynomial', 'logarithm', 'exponential', 'matrix', 'vector', 'limit', 'differential', 'optimization', 'summation']
|
| 162 |
-
|
| 163 |
-
research_keywords = ['research', 'source', 'sources', 'citation', 'cite', 'bibliography', 'reference', 'references', 'academic', 'scholarly', 'paper', 'essay', 'thesis', 'dissertation', 'database', 'journal', 'article', 'peer review', 'literature review', 'methodology', 'analysis', 'findings', 'conclusion', 'abstract', 'hypothesis', 'data collection', 'survey', 'interview', 'experiment']
|
| 164 |
-
|
| 165 |
-
study_keywords = ['study', 'studying', 'memorize', 'memory', 'exam', 'test', 'testing', 'quiz', 'quizzing', 'review', 'reviewing', 'learn', 'learning', 'remember', 'recall', 'focus', 'concentration', 'motivation', 'notes', 'note-taking', 'flashcard', 'flashcards', 'comprehension', 'understanding', 'retention', 'practice', 'drill', 'preparation', 'revision', 'cramming']
|
| 166 |
-
|
| 167 |
-
# Score the current message
|
| 168 |
-
current_scores = {
|
| 169 |
-
'math': sum(1 for keyword in math_keywords if keyword in message_lower),
|
| 170 |
-
'research': sum(1 for keyword in research_keywords if keyword in message_lower),
|
| 171 |
-
'study': sum(1 for keyword in study_keywords if keyword in message_lower)
|
| 172 |
-
}
|
| 173 |
-
|
| 174 |
-
# Analyze recent conversation history for context
|
| 175 |
-
context_scores = {'math': 0, 'research': 0, 'study': 0}
|
| 176 |
-
last_mode = None
|
| 177 |
-
|
| 178 |
-
if history:
|
| 179 |
-
# Look at last 3-5 exchanges for context
|
| 180 |
-
recent_history = history[-6:] # Last 3 user-bot exchanges
|
| 181 |
-
|
| 182 |
-
for i, exchange in enumerate(recent_history):
|
| 183 |
-
if exchange.get("role") == "user":
|
| 184 |
-
content_lower = exchange.get("content", "").lower()
|
| 185 |
-
|
| 186 |
-
# Weight recent messages more heavily
|
| 187 |
-
weight = 0.5 - (i * 0.1) # More recent = higher weight
|
| 188 |
-
if weight < 0.1:
|
| 189 |
-
weight = 0.1
|
| 190 |
-
|
| 191 |
-
context_scores['math'] += weight * sum(1 for keyword in math_keywords if keyword in content_lower)
|
| 192 |
-
context_scores['research'] += weight * sum(1 for keyword in research_keywords if keyword in content_lower)
|
| 193 |
-
context_scores['study'] += weight * sum(1 for keyword in study_keywords if keyword in content_lower)
|
| 194 |
-
|
| 195 |
-
# Check if bot responses indicate a specific mode
|
| 196 |
-
elif exchange.get("role") == "assistant":
|
| 197 |
-
content = exchange.get("content", "")
|
| 198 |
-
if "*Math Mode*" in content:
|
| 199 |
-
last_mode = 'math'
|
| 200 |
-
elif "*Research Mode*" in content:
|
| 201 |
-
last_mode = 'research'
|
| 202 |
-
elif "*Study Mode*" in content:
|
| 203 |
-
last_mode = 'study'
|
| 204 |
-
|
| 205 |
-
# Combine current message scores with context
|
| 206 |
-
final_scores = {
|
| 207 |
-
'math': current_scores['math'] * 2.0 + context_scores['math'], # Current message weighted more
|
| 208 |
-
'research': current_scores['research'] * 2.0 + context_scores['research'],
|
| 209 |
-
'study': current_scores['study'] * 2.0 + context_scores['study']
|
| 210 |
-
}
|
| 211 |
-
|
| 212 |
-
# Add bonus for continuing in the same mode (conversation persistence)
|
| 213 |
-
if last_mode and last_mode in final_scores:
|
| 214 |
-
final_scores[last_mode] += 0.5
|
| 215 |
-
|
| 216 |
-
# Handle ambiguous/continuation messages
|
| 217 |
-
ambiguous_phrases = ['next', 'continue', 'more', 'explain', 'help', 'what about', 'can you', 'how do', 'show me']
|
| 218 |
-
is_ambiguous = any(phrase in message_lower for phrase in ambiguous_phrases) and len(message.split()) < 6
|
| 219 |
-
|
| 220 |
-
if is_ambiguous and last_mode:
|
| 221 |
-
# For short, ambiguous messages, stick with the last mode
|
| 222 |
-
if last_mode == 'math':
|
| 223 |
-
return math_template, "Math Mode"
|
| 224 |
-
elif last_mode == 'research':
|
| 225 |
-
return research_template, "Research Mode"
|
| 226 |
-
elif last_mode == 'study':
|
| 227 |
-
return study_template, "Study Mode"
|
| 228 |
-
|
| 229 |
-
# Determine the best mode based on scores
|
| 230 |
-
max_score = max(final_scores.values())
|
| 231 |
-
|
| 232 |
-
if max_score == 0:
|
| 233 |
-
return general_template, "General Mode"
|
| 234 |
-
|
| 235 |
-
# Return the mode with the highest score
|
| 236 |
-
if final_scores['math'] == max_score:
|
| 237 |
-
return math_template, "Math Mode"
|
| 238 |
-
elif final_scores['research'] == max_score:
|
| 239 |
-
return research_template, "Research Mode"
|
| 240 |
-
elif final_scores['study'] == max_score:
|
| 241 |
-
return study_template, "Study Mode"
|
| 242 |
-
else:
|
| 243 |
-
return general_template, "General Mode"
|
| 244 |
|
| 245 |
def smart_truncate(text, max_length=3000):
|
| 246 |
"""Truncates text intelligently to the last full sentence or word."""
|
|
@@ -257,52 +148,26 @@ def smart_truncate(text, max_length=3000):
|
|
| 257 |
return ' '.join(words[:-1]) + "... [Response truncated - ask for continuation]"
|
| 258 |
|
| 259 |
def respond_with_enhanced_streaming(message, history):
|
| 260 |
-
"""Streams the bot's response,
|
| 261 |
|
| 262 |
# Start metrics timing
|
| 263 |
timing_context = metrics_tracker.start_timing()
|
| 264 |
error_occurred = False
|
| 265 |
error_message = None
|
| 266 |
response = ""
|
| 267 |
-
mode = ""
|
| 268 |
|
| 269 |
try:
|
| 270 |
-
#
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
# Build conversation history with proper LangChain message objects
|
| 274 |
-
messages = []
|
| 275 |
-
|
| 276 |
-
# Add system message
|
| 277 |
-
system_msg = SystemMessage(content=ENHANCED_SYSTEM_MESSAGE)
|
| 278 |
-
messages.append(system_msg)
|
| 279 |
-
|
| 280 |
-
# Add conversation history if available
|
| 281 |
if history:
|
| 282 |
-
for exchange in history[-5:]:
|
| 283 |
if exchange.get("role") == "user":
|
| 284 |
-
|
| 285 |
elif exchange.get("role") == "assistant":
|
| 286 |
-
|
| 287 |
|
| 288 |
# Add current user message
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
yield f"*{mode}*\n\nGenerating response..."
|
| 292 |
-
|
| 293 |
-
logger.info(f"Processing {mode} query: {message[:50]}...")
|
| 294 |
-
|
| 295 |
-
# Use LangChain template to format the prompt
|
| 296 |
-
formatted_prompt = template.format(
|
| 297 |
-
question=message,
|
| 298 |
-
system_message=ENHANCED_SYSTEM_MESSAGE
|
| 299 |
-
)
|
| 300 |
-
|
| 301 |
-
# Use chat completions instead of text_generation (more reliable)
|
| 302 |
-
api_messages = [
|
| 303 |
-
{"role": "system", "content": "You are EduBot, an expert AI learning assistant."},
|
| 304 |
-
{"role": "user", "content": formatted_prompt}
|
| 305 |
-
]
|
| 306 |
|
| 307 |
# Mark provider API start
|
| 308 |
metrics_tracker.mark_provider_start(timing_context)
|
|
@@ -318,46 +183,41 @@ def respond_with_enhanced_streaming(message, history):
|
|
| 318 |
# Mark provider API end
|
| 319 |
metrics_tracker.mark_provider_end(timing_context)
|
| 320 |
|
| 321 |
-
response = completion.choices[0].message.content
|
| 322 |
-
response = smart_truncate(response, max_length=3000)
|
| 323 |
|
| 324 |
-
# Stream
|
| 325 |
words = response.split()
|
| 326 |
-
partial_response =
|
| 327 |
|
| 328 |
for i, word in enumerate(words):
|
| 329 |
partial_response += word + " "
|
| 330 |
-
|
| 331 |
-
# Update the stream periodically and record chunks
|
| 332 |
if i % 4 == 0:
|
| 333 |
metrics_tracker.record_chunk(timing_context)
|
| 334 |
yield partial_response
|
| 335 |
time.sleep(0.03)
|
| 336 |
|
| 337 |
-
final_response = f"*{mode}*\n\n{response}"
|
| 338 |
logger.info(f"Response completed. Length: {len(response)} characters")
|
| 339 |
|
| 340 |
-
# Record final chunk
|
| 341 |
metrics_tracker.record_chunk(timing_context)
|
| 342 |
-
yield
|
| 343 |
-
|
| 344 |
except Exception as e:
|
| 345 |
error_occurred = True
|
| 346 |
error_message = str(e)
|
| 347 |
logger.exception("Error in response generation")
|
| 348 |
-
yield
|
| 349 |
|
| 350 |
finally:
|
| 351 |
-
# Log the complete interaction with metrics
|
| 352 |
metrics_tracker.log_interaction(
|
| 353 |
-
mode=mode or "Unknown",
|
| 354 |
query=message,
|
| 355 |
response=response,
|
| 356 |
timing_context=timing_context,
|
| 357 |
error_occurred=error_occurred,
|
| 358 |
-
error_message=error_message
|
| 359 |
)
|
| 360 |
|
|
|
|
|
|
|
| 361 |
# ===============================================================================
|
| 362 |
# UI CONFIGURATION SECTION - ALL UI RELATED CODE CENTRALIZED HERE
|
| 363 |
# ===============================================================================
|
|
|
|
| 76 |
- Encourage students to explain their thinking and reasoning
|
| 77 |
- Provide honest, accurate feedback even when it may not be what the student wants to hear
|
| 78 |
|
| 79 |
+
## Modes
|
| 80 |
+
**Select the mode that best matches the user's needs.**
|
| 81 |
|
| 82 |
+
**Math Mode**
|
|
|
|
|
|
|
| 83 |
LaTeX formatting is enabled for math. You must provide LaTeX formatting for all math, either as inline LaTeX or centered display LaTeX.
|
| 84 |
You will address requests to solve, aid in understanding, or explore mathematical context. Use logical ordering for content, providing necessary terms and definitions as well as concept explanations along with math to foster understanding of core concepts. Rather than specifically answering the math problem provided, begin with solving a similar problem that requires the same steps and foundational mathematical knowledge, then prompt the user to work through the problem themselves. If the user insists you solve the problem, engage in a two-way conversation where you provide the steps but request the user solve for the answer one step at a time.
|
| 85 |
LaTeX should always be used for math.
|
| 86 |
LaTeX Examples:
|
| 87 |
- Inline: "The slope is $m = \\frac{{y_2 - y_1}}{{x_2 - x_1}}$ in this case."
|
| 88 |
- Display: "The quadratic formula is: $x = \\frac{{-b \\pm \\sqrt{{b^2-4ac}}}}{{2a}}$"
|
| 89 |
+
Always use double backslashes (\\\\) for LaTeX commands like \\\\frac, \\\\sqrt, \\\\int, etc.
|
|
|
|
|
|
|
| 90 |
|
| 91 |
+
**Research Mode**
|
|
|
|
|
|
|
| 92 |
Your main goal is to help the user learn to research topics, a critical skill. Function as a partner rather than a search engine.
|
| 93 |
Over the course of the conversation, guide the user through a seven-step research process:
|
| 94 |
1) **Identifying a topic**
|
|
|
|
| 115 |
IEEE Style
|
| 116 |
In-text: [1]
|
| 117 |
Reference: [1] J. A. Smith, Book Title. Publisher, 2023.
|
| 118 |
+
In this mode you may not use LaTeX formatting.
|
|
|
|
|
|
|
| 119 |
|
| 120 |
+
**Study Mode**
|
|
|
|
|
|
|
| 121 |
Engage the user in a mix of two teaching styles: student-centered and inquiry-based learning.
|
| 122 |
Student Centered: Adjust to reflect the student's reading level and level of understanding of a topic as the conversation progresses. Do not assume the user is an expert but instead assume they may have familiarity but desire to learn more about the topic they are studying. Provide definitions for terms you use in a conversational way, gradually shifting to using just the terms without definitions as the user becomes more familiar with them.
|
| 123 |
Inquiry-based learning: Engage the user through questions that compel them to consider what they want to know and then explore the topics through guided conversation.
|
| 124 |
Over the course of the conversation, prompt the user with a question to gauge their growing knowledge or progress on the topic.
|
| 125 |
For example:
|
| 126 |
After two to three turns of conversation discussing a topic, pick a specific term or concept from the conversation history to craft either a multiple-choice or written answer question for the user with no other comments along with it. If the student is correct, congratulate them on their progress and inquire about their next learning goal on the topic. If the user fails the question, return with a short response that explains the correct answer in a kind tone.
|
| 127 |
+
In this mode you may not use LaTeX formatting.
|
| 128 |
+
|
| 129 |
+
**General/Other Mode**
|
| 130 |
+
You are EduBot, a comprehensive AI learning assistant. Help users leverage educational tools and resources to enrich their education. Offer yourself as a resource for the student, prompting them to request help with **math topics**, **research strategy**, or **studying a topic**.
|
| 131 |
|
| 132 |
+
Your goal is to be an educational partner who empowers students to succeed through understanding, not a service that completes their work for them."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
|
| 134 |
# --- Core Logic Functions ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
|
| 136 |
def smart_truncate(text, max_length=3000):
|
| 137 |
"""Truncates text intelligently to the last full sentence or word."""
|
|
|
|
| 148 |
return ' '.join(words[:-1]) + "... [Response truncated - ask for continuation]"
|
| 149 |
|
| 150 |
def respond_with_enhanced_streaming(message, history):
|
| 151 |
+
"""Streams the bot's response, handling errors with metrics tracking."""
|
| 152 |
|
| 153 |
# Start metrics timing
|
| 154 |
timing_context = metrics_tracker.start_timing()
|
| 155 |
error_occurred = False
|
| 156 |
error_message = None
|
| 157 |
response = ""
|
|
|
|
| 158 |
|
| 159 |
try:
|
| 160 |
+
# Build conversation history (last 5 exchanges)
|
| 161 |
+
api_messages = [{"role": "system", "content": ENHANCED_SYSTEM_MESSAGE}]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
if history:
|
| 163 |
+
for exchange in history[-5:]:
|
| 164 |
if exchange.get("role") == "user":
|
| 165 |
+
api_messages.append({"role": "user", "content": exchange["content"]})
|
| 166 |
elif exchange.get("role") == "assistant":
|
| 167 |
+
api_messages.append({"role": "assistant", "content": exchange["content"]})
|
| 168 |
|
| 169 |
# Add current user message
|
| 170 |
+
api_messages.append({"role": "user", "content": message})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
|
| 172 |
# Mark provider API start
|
| 173 |
metrics_tracker.mark_provider_start(timing_context)
|
|
|
|
| 183 |
# Mark provider API end
|
| 184 |
metrics_tracker.mark_provider_end(timing_context)
|
| 185 |
|
| 186 |
+
response = smart_truncate(completion.choices[0].message.content, max_length=3000)
|
|
|
|
| 187 |
|
| 188 |
+
# Stream fake-chunks word by word
|
| 189 |
words = response.split()
|
| 190 |
+
partial_response = ""
|
| 191 |
|
| 192 |
for i, word in enumerate(words):
|
| 193 |
partial_response += word + " "
|
|
|
|
|
|
|
| 194 |
if i % 4 == 0:
|
| 195 |
metrics_tracker.record_chunk(timing_context)
|
| 196 |
yield partial_response
|
| 197 |
time.sleep(0.03)
|
| 198 |
|
|
|
|
| 199 |
logger.info(f"Response completed. Length: {len(response)} characters")
|
| 200 |
|
|
|
|
| 201 |
metrics_tracker.record_chunk(timing_context)
|
| 202 |
+
yield response
|
| 203 |
+
|
| 204 |
except Exception as e:
|
| 205 |
error_occurred = True
|
| 206 |
error_message = str(e)
|
| 207 |
logger.exception("Error in response generation")
|
| 208 |
+
yield "Sorry, I encountered an error while generating the response."
|
| 209 |
|
| 210 |
finally:
|
|
|
|
| 211 |
metrics_tracker.log_interaction(
|
|
|
|
| 212 |
query=message,
|
| 213 |
response=response,
|
| 214 |
timing_context=timing_context,
|
| 215 |
error_occurred=error_occurred,
|
| 216 |
+
error_message=error_message,
|
| 217 |
)
|
| 218 |
|
| 219 |
+
|
| 220 |
+
|
| 221 |
# ===============================================================================
|
| 222 |
# UI CONFIGURATION SECTION - ALL UI RELATED CODE CENTRALIZED HERE
|
| 223 |
# ===============================================================================
|