Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
from graph_tool import generate_plot
|
| 3 |
from metrics import EduBotMetrics
|
| 4 |
-
from together import Together
|
| 5 |
import os
|
| 6 |
import time
|
| 7 |
import logging
|
| 8 |
import json
|
| 9 |
import re
|
|
|
|
| 10 |
|
| 11 |
# --- Environment and Logging Setup ---
|
| 12 |
logging.basicConfig(level=logging.INFO)
|
|
@@ -17,13 +17,13 @@ hf_token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACEHUB_API_TOKE
|
|
| 17 |
if not hf_token:
|
| 18 |
logger.warning("Neither HF_TOKEN nor HUGGINGFACEHUB_API_TOKEN is set, the application may not work.")
|
| 19 |
|
| 20 |
-
# ---
|
| 21 |
-
|
|
|
|
| 22 |
|
| 23 |
metrics_tracker = EduBotMetrics(save_file="edu_metrics.json")
|
| 24 |
|
| 25 |
# --- Tools ---
|
| 26 |
-
|
| 27 |
tools = [
|
| 28 |
{
|
| 29 |
"type": "function",
|
|
@@ -47,7 +47,6 @@ tools = [
|
|
| 47 |
]
|
| 48 |
|
| 49 |
# --- LLM Templates ---
|
| 50 |
-
# Enhanced base system message
|
| 51 |
SYSTEM_MESSAGE = """You are EduBot, an expert multi-concept tutor designed to facilitate genuine learning and understanding. Your primary mission is to guide students through the learning process rather than providing direct answers to academic work.
|
| 52 |
|
| 53 |
## Core Educational Principles
|
|
@@ -75,20 +74,23 @@ You recognize that students may seek direct answers to homework, assignments, or
|
|
| 75 |
- **Encourage original thinking**: Help students develop their own reasoning and analytical skills
|
| 76 |
- **Suggest study strategies**: Recommend effective learning approaches for the subject matter
|
| 77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
## Response Guidelines
|
| 79 |
- **For math problems**: Explain concepts, provide formula derivations, and guide through problem-solving steps without computing final numerical answers
|
| 80 |
- **For multiple-choice questions**: Discuss the concepts being tested and help students understand how to analyze options rather than identifying the correct choice
|
| 81 |
- **For essays or written work**: Discuss research strategies, organizational techniques, and critical thinking approaches rather than providing content or thesis statements
|
| 82 |
- **For factual questions**: Provide educational context and encourage students to synthesize information rather than stating direct answers
|
| 83 |
|
| 84 |
-
## Handling Limitations
|
| 85 |
-
**Web Search Requests**: You do not have access to the internet and cannot perform web searches. When asked to search the web, respond honestly about this limitation and offer alternative assistance:
|
| 86 |
-
- "I'm unable to perform web searches, but I can help you plan a research strategy for this topic"
|
| 87 |
-
- "I can't browse the internet, but I'd be happy to teach you effective Google search syntax to find what you need"
|
| 88 |
-
- "While I can't search online, I can help you evaluate whether sources you find are reliable and appropriate for your research"
|
| 89 |
-
|
| 90 |
-
**Other Limitations**: When encountering other technical limitations, acknowledge them directly and offer constructive alternatives that support learning. You are also unable to create images or attach images to your response. Never pretend to say that an image is in a response.
|
| 91 |
-
|
| 92 |
## Communication Guidelines
|
| 93 |
- Maintain a supportive, non-judgmental tone in all interactions
|
| 94 |
- Assume positive intent while redirecting toward genuine learning
|
|
@@ -97,59 +99,6 @@ You recognize that students may seek direct answers to homework, assignments, or
|
|
| 97 |
- Encourage students to explain their thinking and reasoning
|
| 98 |
- Provide honest, accurate feedback even when it may not be what the student wants to hear
|
| 99 |
|
| 100 |
-
## Modes
|
| 101 |
-
**Select the mode that best matches the user's needs.**
|
| 102 |
-
|
| 103 |
-
**Math Mode**
|
| 104 |
-
LaTeX formatting is enabled for math. You must provide LaTeX formatting for all math, either as inline LaTeX or centered display LaTeX.
|
| 105 |
-
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.
|
| 106 |
-
LaTeX should always be used for math.
|
| 107 |
-
LaTeX Examples:
|
| 108 |
-
- Inline: "The slope is $m = \\frac{{y_2 - y_1}}{{x_2 - x_1}}$ in this case."
|
| 109 |
-
- Display: "The quadratic formula is: $x = \\frac{{-b \\pm \\sqrt{{b^2-4ac}}}}{{2a}}$"
|
| 110 |
-
Always use double backslashes (\\\\) for LaTeX commands like \\\\frac, \\\\sqrt, \\\\int, etc.
|
| 111 |
-
|
| 112 |
-
**Research Mode**
|
| 113 |
-
Your main goal is to help the user learn to research topics, a critical skill. Function as a partner rather than a search engine.
|
| 114 |
-
Over the course of the conversation, guide the user through a seven-step research process:
|
| 115 |
-
1) **Identifying a topic**
|
| 116 |
-
2) **Finding background information**
|
| 117 |
-
3) **Developing a research design**
|
| 118 |
-
4) **Collecting data**
|
| 119 |
-
5) **Analyzing data**
|
| 120 |
-
6) **Drawing conclusions**
|
| 121 |
-
7) **Disseminating findings**
|
| 122 |
-
You may provide formatted citations if the user asks for them and provides the needed information. If not all information is provided but citations are requested, follow up with guidance on how to obtain the information to generate a citation. By default, you will not provide citations.
|
| 123 |
-
Example citations:
|
| 124 |
-
APA Style
|
| 125 |
-
In-text: (Smith, 2023, p. 45)
|
| 126 |
-
Reference: Smith, J. A. (2023). Book title. Publisher.
|
| 127 |
-
MLA Style
|
| 128 |
-
In-text: (Smith 45)
|
| 129 |
-
Works Cited: Smith, John A. Book Title. Publisher, 2023.
|
| 130 |
-
Chicago Style
|
| 131 |
-
Footnote: ¹John A. Smith, Book Title (Publisher, 2023), 45.
|
| 132 |
-
Bibliography: Smith, John A. Book Title. Publisher, 2023.
|
| 133 |
-
Harvard Style
|
| 134 |
-
In-text: (Smith 2023, p. 45)
|
| 135 |
-
Reference: Smith, J.A. (2023) Book title. Publisher.
|
| 136 |
-
IEEE Style
|
| 137 |
-
In-text: [1]
|
| 138 |
-
Reference: [1] J. A. Smith, Book Title. Publisher, 2023.
|
| 139 |
-
In this mode you may not use LaTeX formatting.
|
| 140 |
-
|
| 141 |
-
**Study Mode**
|
| 142 |
-
Engage the user in a mix of two teaching styles: student-centered and inquiry-based learning.
|
| 143 |
-
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.
|
| 144 |
-
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.
|
| 145 |
-
Over the course of the conversation, prompt the user with a question to gauge their growing knowledge or progress on the topic.
|
| 146 |
-
For example:
|
| 147 |
-
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.
|
| 148 |
-
In this mode you may not use LaTeX formatting.
|
| 149 |
-
|
| 150 |
-
**General/Other Mode**
|
| 151 |
-
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**.
|
| 152 |
-
|
| 153 |
Your goal is to be an educational partner who empowers students to succeed through understanding, not a service that completes their work for them."""
|
| 154 |
|
| 155 |
# --- Core Logic Functions ---
|
|
@@ -168,97 +117,180 @@ def smart_truncate(text, max_length=3000):
|
|
| 168 |
words = text[:max_length].split()
|
| 169 |
return ' '.join(words[:-1]) + "... [Response truncated - ask for continuation]"
|
| 170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
def respond_with_enhanced_streaming(message, history):
|
| 172 |
-
"""
|
| 173 |
timing_context = metrics_tracker.start_timing()
|
| 174 |
error_occurred = False
|
| 175 |
error_message = None
|
| 176 |
full_response = ""
|
| 177 |
|
| 178 |
try:
|
|
|
|
| 179 |
api_messages = [{"role": "system", "content": SYSTEM_MESSAGE}]
|
|
|
|
| 180 |
if history:
|
| 181 |
-
# Handle
|
| 182 |
-
for exchange in history[-5:]: #
|
| 183 |
if isinstance(exchange, dict):
|
| 184 |
-
# New format: {"role": "user", "content": "..."}
|
| 185 |
api_messages.append(exchange)
|
| 186 |
else:
|
| 187 |
-
#
|
| 188 |
-
api_messages.append({"role": "user", "content": exchange[0]})
|
| 189 |
-
api_messages.append({"role": "assistant", "content": exchange[1]})
|
| 190 |
|
| 191 |
api_messages.append({"role": "user", "content": message})
|
| 192 |
|
| 193 |
metrics_tracker.mark_provider_start(timing_context)
|
| 194 |
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
top_p=0.9,
|
| 201 |
-
stream=True,
|
| 202 |
-
tools=tools,
|
| 203 |
-
)
|
| 204 |
|
| 205 |
-
# Buffers to handle multi-chunk tool calls
|
| 206 |
-
tool_call_name = ""
|
| 207 |
-
tool_call_args_str = ""
|
| 208 |
-
|
| 209 |
-
for chunk in stream:
|
| 210 |
-
# Check if chunk has choices and handle accordingly
|
| 211 |
-
if hasattr(chunk, 'choices') and chunk.choices and len(chunk.choices) > 0:
|
| 212 |
-
choice = chunk.choices[0]
|
| 213 |
-
|
| 214 |
-
# Handle text chunks
|
| 215 |
-
if hasattr(choice, 'delta') and hasattr(choice.delta, 'content') and choice.delta.content:
|
| 216 |
-
text_chunk = choice.delta.content
|
| 217 |
-
full_response += text_chunk
|
| 218 |
-
yield full_response
|
| 219 |
-
|
| 220 |
-
# Handle tool call chunks
|
| 221 |
-
if hasattr(choice, 'delta') and hasattr(choice.delta, 'tool_calls') and choice.delta.tool_calls:
|
| 222 |
-
tool_call_delta = choice.delta.tool_calls[0]
|
| 223 |
-
|
| 224 |
-
# Accumulate name and arguments from stream
|
| 225 |
-
if hasattr(tool_call_delta, 'function'):
|
| 226 |
-
if hasattr(tool_call_delta.function, 'name') and tool_call_delta.function.name:
|
| 227 |
-
tool_call_name = tool_call_delta.function.name
|
| 228 |
-
if hasattr(tool_call_delta.function, 'arguments') and tool_call_delta.function.arguments:
|
| 229 |
-
tool_call_args_str += tool_call_delta.function.arguments
|
| 230 |
-
|
| 231 |
-
# Check if we have received the full tool call
|
| 232 |
-
if tool_call_name and '}' in tool_call_args_str:
|
| 233 |
-
try:
|
| 234 |
-
tool_args = json.loads(tool_call_args_str)
|
| 235 |
-
if tool_call_name == "create_graph":
|
| 236 |
-
logger.info(f"Executing tool: {tool_call_name} with args: {tool_args}")
|
| 237 |
-
graph_html = generate_plot(**tool_args)
|
| 238 |
-
full_response += graph_html
|
| 239 |
-
yield full_response
|
| 240 |
-
|
| 241 |
-
# Reset buffers
|
| 242 |
-
tool_call_name = ""
|
| 243 |
-
tool_call_args_str = ""
|
| 244 |
-
|
| 245 |
-
except json.JSONDecodeError:
|
| 246 |
-
logger.error("JSON parsing failed for tool arguments.")
|
| 247 |
-
full_response += f"<p style='color:red;'>Error parsing graph data.</p>"
|
| 248 |
-
yield full_response
|
| 249 |
-
except Exception as e:
|
| 250 |
-
logger.exception("Error executing tool")
|
| 251 |
-
full_response += f"<p style='color:red;'>Error executing tool: {e}</p>"
|
| 252 |
-
yield full_response
|
| 253 |
-
|
| 254 |
metrics_tracker.mark_provider_end(timing_context)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
logger.info(f"Response completed. Length: {len(full_response)} characters")
|
| 256 |
|
| 257 |
except Exception as e:
|
| 258 |
error_occurred = True
|
| 259 |
error_message = str(e)
|
| 260 |
logger.exception("Error in response generation")
|
| 261 |
-
|
|
|
|
| 262 |
|
| 263 |
finally:
|
| 264 |
metrics_tracker.log_interaction(
|
|
@@ -327,7 +359,7 @@ def respond_and_update(message, history):
|
|
| 327 |
# Add user message to history
|
| 328 |
history.append({"role": "user", "content": message})
|
| 329 |
# Yield history to show the user message immediately, and clear the textbox
|
| 330 |
-
yield history, ""
|
| 331 |
|
| 332 |
# Stream the bot's response
|
| 333 |
full_response = ""
|
|
@@ -372,9 +404,9 @@ def create_interface():
|
|
| 372 |
show_share_button=False,
|
| 373 |
avatar_images=None,
|
| 374 |
elem_id="main-chatbot",
|
| 375 |
-
container=False,
|
| 376 |
scale=1,
|
| 377 |
-
height="70vh"
|
| 378 |
)
|
| 379 |
|
| 380 |
# Input Section - fixed height
|
|
@@ -406,5 +438,4 @@ def create_interface():
|
|
| 406 |
if __name__ == "__main__":
|
| 407 |
logger.info("Starting EduBot...")
|
| 408 |
demo = create_interface()
|
| 409 |
-
demo.launch(debug=True, share=True)
|
| 410 |
-
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
from graph_tool import generate_plot
|
| 3 |
from metrics import EduBotMetrics
|
|
|
|
| 4 |
import os
|
| 5 |
import time
|
| 6 |
import logging
|
| 7 |
import json
|
| 8 |
import re
|
| 9 |
+
import requests
|
| 10 |
|
| 11 |
# --- Environment and Logging Setup ---
|
| 12 |
logging.basicConfig(level=logging.INFO)
|
|
|
|
| 17 |
if not hf_token:
|
| 18 |
logger.warning("Neither HF_TOKEN nor HUGGINGFACEHUB_API_TOKEN is set, the application may not work.")
|
| 19 |
|
| 20 |
+
# --- HF API Configuration ---
|
| 21 |
+
HF_API_URL = "https://api-inference.huggingface.co/models/Qwen/Qwen2.5-7B-Instruct"
|
| 22 |
+
HF_HEADERS = {"Authorization": f"Bearer {hf_token}"}
|
| 23 |
|
| 24 |
metrics_tracker = EduBotMetrics(save_file="edu_metrics.json")
|
| 25 |
|
| 26 |
# --- Tools ---
|
|
|
|
| 27 |
tools = [
|
| 28 |
{
|
| 29 |
"type": "function",
|
|
|
|
| 47 |
]
|
| 48 |
|
| 49 |
# --- LLM Templates ---
|
|
|
|
| 50 |
SYSTEM_MESSAGE = """You are EduBot, an expert multi-concept tutor designed to facilitate genuine learning and understanding. Your primary mission is to guide students through the learning process rather than providing direct answers to academic work.
|
| 51 |
|
| 52 |
## Core Educational Principles
|
|
|
|
| 74 |
- **Encourage original thinking**: Help students develop their own reasoning and analytical skills
|
| 75 |
- **Suggest study strategies**: Recommend effective learning approaches for the subject matter
|
| 76 |
|
| 77 |
+
## Tool Usage
|
| 78 |
+
You have access to a create_graph tool that can generate bar charts, line graphs, and pie charts. Use this tool when:
|
| 79 |
+
- A visual representation would help explain a concept
|
| 80 |
+
- The student asks for data visualization
|
| 81 |
+
- Creating practice problems that involve interpreting charts
|
| 82 |
+
- Demonstrating mathematical relationships visually
|
| 83 |
+
|
| 84 |
+
When using the create_graph tool, provide JSON-formatted data and labels. For example:
|
| 85 |
+
- data_json: '{"Math": 85, "Science": 92, "English": 78}'
|
| 86 |
+
- labels_json: '["Math", "Science", "English"]'
|
| 87 |
+
|
| 88 |
## Response Guidelines
|
| 89 |
- **For math problems**: Explain concepts, provide formula derivations, and guide through problem-solving steps without computing final numerical answers
|
| 90 |
- **For multiple-choice questions**: Discuss the concepts being tested and help students understand how to analyze options rather than identifying the correct choice
|
| 91 |
- **For essays or written work**: Discuss research strategies, organizational techniques, and critical thinking approaches rather than providing content or thesis statements
|
| 92 |
- **For factual questions**: Provide educational context and encourage students to synthesize information rather than stating direct answers
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
## Communication Guidelines
|
| 95 |
- Maintain a supportive, non-judgmental tone in all interactions
|
| 96 |
- Assume positive intent while redirecting toward genuine learning
|
|
|
|
| 99 |
- Encourage students to explain their thinking and reasoning
|
| 100 |
- Provide honest, accurate feedback even when it may not be what the student wants to hear
|
| 101 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
Your goal is to be an educational partner who empowers students to succeed through understanding, not a service that completes their work for them."""
|
| 103 |
|
| 104 |
# --- Core Logic Functions ---
|
|
|
|
| 117 |
words = text[:max_length].split()
|
| 118 |
return ' '.join(words[:-1]) + "... [Response truncated - ask for continuation]"
|
| 119 |
|
| 120 |
+
def detect_tool_request(text):
|
| 121 |
+
"""Simple heuristic to detect when a graph might be helpful."""
|
| 122 |
+
graph_keywords = [
|
| 123 |
+
"chart", "graph", "plot", "visualize", "show data", "bar chart",
|
| 124 |
+
"line graph", "pie chart", "diagram", "compare", "data visualization"
|
| 125 |
+
]
|
| 126 |
+
text_lower = text.lower()
|
| 127 |
+
return any(keyword in text_lower for keyword in graph_keywords)
|
| 128 |
+
|
| 129 |
+
def call_hf_api(messages, max_retries=3):
|
| 130 |
+
"""Call Hugging Face API with retry logic."""
|
| 131 |
+
payload = {
|
| 132 |
+
"inputs": format_messages_for_hf(messages),
|
| 133 |
+
"parameters": {
|
| 134 |
+
"max_new_tokens": 1024,
|
| 135 |
+
"temperature": 0.7,
|
| 136 |
+
"top_p": 0.9,
|
| 137 |
+
"return_full_text": False
|
| 138 |
+
}
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
for attempt in range(max_retries):
|
| 142 |
+
try:
|
| 143 |
+
response = requests.post(HF_API_URL, headers=HF_HEADERS, json=payload, timeout=30)
|
| 144 |
+
|
| 145 |
+
if response.status_code == 503:
|
| 146 |
+
# Model is loading
|
| 147 |
+
if attempt < max_retries - 1:
|
| 148 |
+
wait_time = 10 + (attempt * 5)
|
| 149 |
+
logger.info(f"Model loading, waiting {wait_time} seconds...")
|
| 150 |
+
time.sleep(wait_time)
|
| 151 |
+
continue
|
| 152 |
+
else:
|
| 153 |
+
return "The model is currently loading. Please try again in a few moments."
|
| 154 |
+
|
| 155 |
+
response.raise_for_status()
|
| 156 |
+
result = response.json()
|
| 157 |
+
|
| 158 |
+
if isinstance(result, list) and len(result) > 0:
|
| 159 |
+
return result[0].get('generated_text', '').strip()
|
| 160 |
+
else:
|
| 161 |
+
return "I apologize, but I received an unexpected response format. Please try again."
|
| 162 |
+
|
| 163 |
+
except requests.exceptions.Timeout:
|
| 164 |
+
if attempt < max_retries - 1:
|
| 165 |
+
logger.warning(f"Request timeout, retrying... (attempt {attempt + 1})")
|
| 166 |
+
time.sleep(2)
|
| 167 |
+
continue
|
| 168 |
+
else:
|
| 169 |
+
return "I'm sorry, the request timed out. Please try again."
|
| 170 |
+
except requests.exceptions.RequestException as e:
|
| 171 |
+
if attempt < max_retries - 1:
|
| 172 |
+
logger.warning(f"Request failed: {e}, retrying... (attempt {attempt + 1})")
|
| 173 |
+
time.sleep(2)
|
| 174 |
+
continue
|
| 175 |
+
else:
|
| 176 |
+
return f"I'm sorry, there was an error connecting to the service: {str(e)}"
|
| 177 |
+
|
| 178 |
+
return "I'm sorry, I encountered an error and couldn't generate a response."
|
| 179 |
+
|
| 180 |
+
def format_messages_for_hf(messages):
|
| 181 |
+
"""Format messages for HF API."""
|
| 182 |
+
formatted = ""
|
| 183 |
+
for msg in messages:
|
| 184 |
+
role = msg["role"]
|
| 185 |
+
content = msg["content"]
|
| 186 |
+
if role == "system":
|
| 187 |
+
formatted += f"System: {content}\n\n"
|
| 188 |
+
elif role == "user":
|
| 189 |
+
formatted += f"Human: {content}\n\n"
|
| 190 |
+
elif role == "assistant":
|
| 191 |
+
formatted += f"Assistant: {content}\n\n"
|
| 192 |
+
|
| 193 |
+
formatted += "Assistant: "
|
| 194 |
+
return formatted
|
| 195 |
+
|
| 196 |
+
def process_response_for_tools(response_text, original_query):
|
| 197 |
+
"""Check if we should generate a graph based on the response and query."""
|
| 198 |
+
# Simple heuristic - if the response mentions creating a chart/graph or the query requested one
|
| 199 |
+
should_create_graph = (
|
| 200 |
+
detect_tool_request(original_query) or
|
| 201 |
+
any(phrase in response_text.lower() for phrase in [
|
| 202 |
+
"let me create a", "i'll make a", "here's a chart", "here's a graph"
|
| 203 |
+
])
|
| 204 |
+
)
|
| 205 |
+
|
| 206 |
+
if should_create_graph:
|
| 207 |
+
# Try to extract data from context or create a simple example
|
| 208 |
+
if "grade" in original_query.lower() or "score" in original_query.lower():
|
| 209 |
+
data_json = '{"Math": 85, "Science": 92, "English": 78, "History": 88}'
|
| 210 |
+
labels_json = '["Math", "Science", "English", "History"]'
|
| 211 |
+
title = "Sample Grade Distribution"
|
| 212 |
+
plot_type = "bar"
|
| 213 |
+
elif "population" in original_query.lower():
|
| 214 |
+
data_json = '{"City A": 1200000, "City B": 950000, "City C": 800000}'
|
| 215 |
+
labels_json = '["City A", "City B", "City C"]'
|
| 216 |
+
title = "Population Comparison"
|
| 217 |
+
plot_type = "bar"
|
| 218 |
+
elif "time" in original_query.lower() or "trend" in original_query.lower():
|
| 219 |
+
data_json = '{"Jan": 20, "Feb": 25, "Mar": 30, "Apr": 28, "May": 35}'
|
| 220 |
+
labels_json = '["Jan", "Feb", "Mar", "Apr", "May"]'
|
| 221 |
+
title = "Monthly Trends"
|
| 222 |
+
plot_type = "line"
|
| 223 |
+
else:
|
| 224 |
+
# Default example
|
| 225 |
+
data_json = '{"Category A": 30, "Category B": 25, "Category C": 20, "Category D": 25}'
|
| 226 |
+
labels_json = '["Category A", "Category B", "Category C", "Category D"]'
|
| 227 |
+
title = "Sample Data Distribution"
|
| 228 |
+
plot_type = "pie"
|
| 229 |
+
|
| 230 |
+
try:
|
| 231 |
+
graph_html = generate_plot(data_json, labels_json, plot_type, title)
|
| 232 |
+
response_text += f"\n\n{graph_html}"
|
| 233 |
+
except Exception as e:
|
| 234 |
+
logger.error(f"Error generating graph: {e}")
|
| 235 |
+
response_text += f"\n\n<p style='color:orange;'>I tried to create a visualization but encountered an error. The concept explanation above should still be helpful!</p>"
|
| 236 |
+
|
| 237 |
+
return response_text
|
| 238 |
+
|
| 239 |
def respond_with_enhanced_streaming(message, history):
|
| 240 |
+
"""Generate response using HF API with tool support."""
|
| 241 |
timing_context = metrics_tracker.start_timing()
|
| 242 |
error_occurred = False
|
| 243 |
error_message = None
|
| 244 |
full_response = ""
|
| 245 |
|
| 246 |
try:
|
| 247 |
+
# Prepare messages for API
|
| 248 |
api_messages = [{"role": "system", "content": SYSTEM_MESSAGE}]
|
| 249 |
+
|
| 250 |
if history:
|
| 251 |
+
# Handle message history
|
| 252 |
+
for exchange in history[-5:]: # Use last 5 exchanges
|
| 253 |
if isinstance(exchange, dict):
|
|
|
|
| 254 |
api_messages.append(exchange)
|
| 255 |
else:
|
| 256 |
+
# Fallback for other formats
|
| 257 |
+
api_messages.append({"role": "user", "content": str(exchange[0])})
|
| 258 |
+
api_messages.append({"role": "assistant", "content": str(exchange[1])})
|
| 259 |
|
| 260 |
api_messages.append({"role": "user", "content": message})
|
| 261 |
|
| 262 |
metrics_tracker.mark_provider_start(timing_context)
|
| 263 |
|
| 264 |
+
# Get response from HF API
|
| 265 |
+
response_text = call_hf_api(api_messages)
|
| 266 |
+
|
| 267 |
+
# Process response for potential tool usage
|
| 268 |
+
response_text = process_response_for_tools(response_text, message)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
metrics_tracker.mark_provider_end(timing_context)
|
| 271 |
+
|
| 272 |
+
# Simulate streaming by yielding chunks
|
| 273 |
+
words = response_text.split()
|
| 274 |
+
current_response = ""
|
| 275 |
+
|
| 276 |
+
for i, word in enumerate(words):
|
| 277 |
+
current_response += word + " "
|
| 278 |
+
if i % 3 == 0: # Yield every 3 words to simulate streaming
|
| 279 |
+
yield current_response.strip()
|
| 280 |
+
time.sleep(0.01) # Small delay to simulate streaming
|
| 281 |
+
|
| 282 |
+
# Final yield with complete response
|
| 283 |
+
full_response = current_response.strip()
|
| 284 |
+
yield full_response
|
| 285 |
+
|
| 286 |
logger.info(f"Response completed. Length: {len(full_response)} characters")
|
| 287 |
|
| 288 |
except Exception as e:
|
| 289 |
error_occurred = True
|
| 290 |
error_message = str(e)
|
| 291 |
logger.exception("Error in response generation")
|
| 292 |
+
full_response = "Sorry, an error occurred while generating the response."
|
| 293 |
+
yield full_response
|
| 294 |
|
| 295 |
finally:
|
| 296 |
metrics_tracker.log_interaction(
|
|
|
|
| 359 |
# Add user message to history
|
| 360 |
history.append({"role": "user", "content": message})
|
| 361 |
# Yield history to show the user message immediately, and clear the textbox
|
| 362 |
+
yield history, ""
|
| 363 |
|
| 364 |
# Stream the bot's response
|
| 365 |
full_response = ""
|
|
|
|
| 404 |
show_share_button=False,
|
| 405 |
avatar_images=None,
|
| 406 |
elem_id="main-chatbot",
|
| 407 |
+
container=False,
|
| 408 |
scale=1,
|
| 409 |
+
height="70vh"
|
| 410 |
)
|
| 411 |
|
| 412 |
# Input Section - fixed height
|
|
|
|
| 438 |
if __name__ == "__main__":
|
| 439 |
logger.info("Starting EduBot...")
|
| 440 |
demo = create_interface()
|
| 441 |
+
demo.launch(debug=True, share=True)
|
|
|