jdesiree commited on
Commit
5b7751c
·
verified ·
1 Parent(s): 151d7b5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +168 -137
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
- # --- LLM Configuration ---
21
- client = Together(api_key=hf_token)
 
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
- """Streams the bot's response, handling tool calls and errors with metrics tracking."""
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 the new messages format from Gradio
182
- for exchange in history[-5:]: # Only use last 5 exchanges for context
183
  if isinstance(exchange, dict):
184
- # New format: {"role": "user", "content": "..."}
185
  api_messages.append(exchange)
186
  else:
187
- # Old format: [user_message, assistant_message]
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
- stream = client.chat.completions.create(
196
- model="Qwen/Qwen2.5-7B-Instruct",
197
- messages=api_messages,
198
- max_tokens=4096,
199
- temperature=0.7,
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
- yield "Sorry, an error occurred while generating the response."
 
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, # Remove wrapper
376
  scale=1,
377
- height="70vh" # Explicit height instead of min_height
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)