Update app.py
Browse files
app.py
CHANGED
|
@@ -9,12 +9,10 @@ import requests
|
|
| 9 |
from PIL import Image
|
| 10 |
from gtts import gTTS
|
| 11 |
from duckduckgo_search import DDGS
|
| 12 |
-
import
|
| 13 |
-
|
| 14 |
|
| 15 |
-
#
|
| 16 |
-
# 1. PAGE CONFIGURATION
|
| 17 |
-
# -----------------------------------------------------------------------------
|
| 18 |
st.set_page_config(
|
| 19 |
page_title="H2 Physics Feynman Bot",
|
| 20 |
page_icon="⚛️",
|
|
@@ -22,18 +20,13 @@ st.set_page_config(
|
|
| 22 |
initial_sidebar_state="expanded"
|
| 23 |
)
|
| 24 |
|
| 25 |
-
#
|
| 26 |
-
# 2. HELPER FUNCTIONS (Optimized for Docker)
|
| 27 |
-
# -----------------------------------------------------------------------------
|
| 28 |
-
|
| 29 |
@st.cache_data(show_spinner=False, ttl=3600)
|
| 30 |
def generate_audio(text):
|
| 31 |
"""Generates MP3 audio from text, skipping code/image tags."""
|
| 32 |
-
# Clean text so the bot doesn't read code or tags out loud
|
| 33 |
clean_text = re.sub(r'```.*?```', 'I have generated a graph.', text, flags=re.DOTALL)
|
| 34 |
clean_text = re.sub(r'\[IMAGE:.*?\]', 'Here is a diagram.', clean_text)
|
| 35 |
|
| 36 |
-
# Limit text length for audio generation
|
| 37 |
if len(clean_text) > 1000:
|
| 38 |
clean_text = clean_text[:1000] + "..."
|
| 39 |
|
|
@@ -47,6 +40,7 @@ def generate_audio(text):
|
|
| 47 |
st.error(f"Audio generation error: {e}")
|
| 48 |
return None
|
| 49 |
|
|
|
|
| 50 |
def google_search_api(query, api_key, cx):
|
| 51 |
"""Helper: Performs a single Google Search."""
|
| 52 |
try:
|
|
@@ -95,44 +89,102 @@ def search_image(query):
|
|
| 95 |
"""MASTER FUNCTION: Google Search -> DuckDuckGo"""
|
| 96 |
cx = os.environ.get("GOOGLE_CX", st.secrets.get("GOOGLE_CX", ""))
|
| 97 |
|
| 98 |
-
# 1. Try Google Key 1
|
| 99 |
key1 = os.environ.get("GOOGLE_SEARCH_KEY", st.secrets.get("GOOGLE_SEARCH_KEY", ""))
|
| 100 |
if key1 and cx:
|
| 101 |
url = google_search_api(query, key1, cx)
|
| 102 |
if url:
|
| 103 |
return url
|
| 104 |
|
| 105 |
-
# 2. Try Google Key 2
|
| 106 |
key2 = os.environ.get("GOOGLE_SEARCH_KEY_2", st.secrets.get("GOOGLE_SEARCH_KEY_2", ""))
|
| 107 |
if key2 and cx:
|
| 108 |
url = google_search_api(query, key2, cx)
|
| 109 |
if url:
|
| 110 |
return url
|
| 111 |
|
| 112 |
-
# 3. Fallback to DuckDuckGo
|
| 113 |
return duckduckgo_search_api(query)
|
| 114 |
|
|
|
|
| 115 |
def execute_plotting_code(code_snippet):
|
| 116 |
"""Execute plotting code in a safe environment."""
|
| 117 |
try:
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
st.pyplot(fig)
|
|
|
|
|
|
|
| 122 |
plt.close(fig)
|
|
|
|
| 123 |
except Exception as e:
|
| 124 |
st.error(f"Graph Error: {str(e)[:200]}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
|
|
|
| 126 |
def display_message(role, content, enable_voice=False):
|
| 127 |
"""Display chat message with all features."""
|
| 128 |
with st.chat_message(role):
|
| 129 |
|
| 130 |
text_to_display = content
|
| 131 |
|
| 132 |
-
# 1.
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
# 2. Check for [IMAGE: query] Tags
|
| 138 |
image_match = re.search(r'\[IMAGE:\s*(.*?)\]', text_to_display, re.IGNORECASE)
|
|
@@ -147,11 +199,15 @@ def display_message(role, content, enable_voice=False):
|
|
| 147 |
# --- DISPLAY TEXT ---
|
| 148 |
st.markdown(text_to_display)
|
| 149 |
|
| 150 |
-
# ---
|
| 151 |
-
if
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
|
| 156 |
# --- DISPLAY IMAGE ---
|
| 157 |
if image_match and role == "assistant":
|
|
@@ -169,7 +225,7 @@ def display_message(role, content, enable_voice=False):
|
|
| 169 |
audio_bytes = generate_audio(text_to_display)
|
| 170 |
if audio_bytes:
|
| 171 |
st.audio(audio_bytes, format='audio/mp3')
|
| 172 |
-
|
| 173 |
def call_groq_api(api_key, messages, max_tokens=2000):
|
| 174 |
"""Call Groq API - FREE tier available (10,000 requests/month)."""
|
| 175 |
url = "https://api.groq.com/openai/v1/chat/completions"
|
|
@@ -187,22 +243,17 @@ def call_groq_api(api_key, messages, max_tokens=2000):
|
|
| 187 |
"role": "system",
|
| 188 |
"content": msg["content"]
|
| 189 |
})
|
| 190 |
-
elif msg["role"]
|
| 191 |
formatted_messages.append({
|
| 192 |
-
"role": "
|
| 193 |
-
"content": msg["content"]
|
| 194 |
-
})
|
| 195 |
-
elif msg["role"] == "assistant":
|
| 196 |
-
formatted_messages.append({
|
| 197 |
-
"role": "assistant",
|
| 198 |
"content": msg["content"]
|
| 199 |
})
|
| 200 |
|
| 201 |
-
# Try multiple models
|
| 202 |
models_to_try = [
|
| 203 |
-
"llama-3.1-8b-instant",
|
| 204 |
-
"llama-3.2-3b-preview",
|
| 205 |
-
"mixtral-8x7b-32768",
|
| 206 |
]
|
| 207 |
|
| 208 |
for model in models_to_try:
|
|
@@ -221,7 +272,6 @@ def call_groq_api(api_key, messages, max_tokens=2000):
|
|
| 221 |
result = response.json()
|
| 222 |
return result["choices"][0]["message"]["content"]
|
| 223 |
elif response.status_code == 429:
|
| 224 |
-
# Rate limit, try next model
|
| 225 |
continue
|
| 226 |
elif response.status_code == 401:
|
| 227 |
return "❌ Invalid API Key. Please check your Groq API key."
|
|
@@ -229,16 +279,13 @@ def call_groq_api(api_key, messages, max_tokens=2000):
|
|
| 229 |
return "❌ Payment required. The free model might be unavailable. Try again later."
|
| 230 |
|
| 231 |
except requests.exceptions.Timeout:
|
| 232 |
-
continue
|
| 233 |
except Exception as e:
|
| 234 |
-
continue
|
| 235 |
|
| 236 |
return "⚠️ All models are currently unavailable. Please try again in a few moments."
|
| 237 |
|
| 238 |
-
#
|
| 239 |
-
# 3. SYSTEM INSTRUCTIONS
|
| 240 |
-
# -----------------------------------------------------------------------------
|
| 241 |
-
|
| 242 |
SYSTEM_INSTRUCTIONS = """You are Richard Feynman, the Nobel Prize-winning physicist, now serving as a tutor for Singapore H2 Physics (syllabus 9478).
|
| 243 |
|
| 244 |
**CORE DIRECTIVE:**
|
|
@@ -246,26 +293,46 @@ SYSTEM_INSTRUCTIONS = """You are Richard Feynman, the Nobel Prize-winning physic
|
|
| 246 |
- Reject topics not in the syllabus
|
| 247 |
- Teach with clarity, enthusiasm, and the Feynman method
|
| 248 |
|
| 249 |
-
**
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
|
| 255 |
-
**
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
**FORMATTING:**
|
| 264 |
-
- Use LaTeX for equations: $F = ma$
|
| 265 |
- Use **bold** for key terms
|
| 266 |
- Keep responses concise but thorough
|
| 267 |
- Be enthusiastic and encouraging
|
| 268 |
-
- Use emojis sparingly for emphasis ⚛️📚🔬
|
| 269 |
|
| 270 |
**TOPICS COVERED (9478 Syllabus):**
|
| 271 |
1. Measurement & Uncertainty
|
|
@@ -279,11 +346,10 @@ SYSTEM_INSTRUCTIONS = """You are Richard Feynman, the Nobel Prize-winning physic
|
|
| 279 |
9. Electricity & DC Circuits
|
| 280 |
10. Electromagnetism
|
| 281 |
11. Modern Physics (Quantum/Nuclear)
|
| 282 |
-
|
| 283 |
|
| 284 |
-
|
| 285 |
-
#
|
| 286 |
-
# -----------------------------------------------------------------------------
|
| 287 |
with st.sidebar:
|
| 288 |
# Header with Feynman image
|
| 289 |
col1, col2 = st.columns([1, 3])
|
|
@@ -417,11 +483,7 @@ with st.sidebar:
|
|
| 417 |
st.divider()
|
| 418 |
st.caption("Made with ❤️ for H2 Physics students | Powered by Groq AI")
|
| 419 |
|
| 420 |
-
#
|
| 421 |
-
# 5. MAIN CHAT INTERFACE
|
| 422 |
-
# -----------------------------------------------------------------------------
|
| 423 |
-
|
| 424 |
-
# Initialize session state
|
| 425 |
if "messages" not in st.session_state:
|
| 426 |
st.session_state.messages = [
|
| 427 |
{"role": "system", "content": SYSTEM_INSTRUCTIONS},
|
|
@@ -429,14 +491,14 @@ if "messages" not in st.session_state:
|
|
| 429 |
**Hello JPJC Physics students! I'm Richard Feynman, ready to help you master H2 Physics!** ⚛️
|
| 430 |
|
| 431 |
I can:
|
| 432 |
-
- 📊 **Plot graphs** using Python
|
| 433 |
- 🖼️ **Find diagrams** through web search
|
| 434 |
- 💬 **Explain concepts** with analogies
|
| 435 |
- ❓ **Ask questions** to test your understanding
|
| 436 |
|
| 437 |
**What would you like to learn today?**
|
| 438 |
-
*You can ask me anything from the H2 Physics syllabus, upload images of problems, or ask me to explain a specific topic.*
|
| 439 |
"""}
|
|
|
|
| 440 |
]
|
| 441 |
|
| 442 |
# Title and mode indicator
|
|
@@ -565,9 +627,7 @@ st.divider()
|
|
| 565 |
st.markdown("""
|
| 566 |
<div style="text-align: center; color: #888; font-size: 0.9em;">
|
| 567 |
<p><strong>H2 Physics Feynman Tutor</strong> | Singapore H2 Physics (9478) Syllabus</p>
|
| 568 |
-
<p>Powered by <a href="https://groq.com" target="_blank">Groq AI</a>
|
| 569 |
-
<a href="https://www.seab.gov.sg/docs/default-source/national-examinations/syllabus/alevel/2024syllabus/9478_y24_sy.pdf" target="_blank">Official Syllabus</a> |
|
| 570 |
-
<a href="https://github.com" target="_blank">GitHub</a></p>
|
| 571 |
<p style="font-size: 0.8em;">Disclaimer: This is an AI tutoring assistant. Always verify with official syllabus documents.</p>
|
| 572 |
</div>
|
| 573 |
-
""", unsafe_allow_html=True)
|
|
|
|
| 9 |
from PIL import Image
|
| 10 |
from gtts import gTTS
|
| 11 |
from duckduckgo_search import DDGS
|
| 12 |
+
import warnings
|
| 13 |
+
warnings.filterwarnings('ignore')
|
| 14 |
|
| 15 |
+
# Page Configuration
|
|
|
|
|
|
|
| 16 |
st.set_page_config(
|
| 17 |
page_title="H2 Physics Feynman Bot",
|
| 18 |
page_icon="⚛️",
|
|
|
|
| 20 |
initial_sidebar_state="expanded"
|
| 21 |
)
|
| 22 |
|
| 23 |
+
# Audio generation
|
|
|
|
|
|
|
|
|
|
| 24 |
@st.cache_data(show_spinner=False, ttl=3600)
|
| 25 |
def generate_audio(text):
|
| 26 |
"""Generates MP3 audio from text, skipping code/image tags."""
|
|
|
|
| 27 |
clean_text = re.sub(r'```.*?```', 'I have generated a graph.', text, flags=re.DOTALL)
|
| 28 |
clean_text = re.sub(r'\[IMAGE:.*?\]', 'Here is a diagram.', clean_text)
|
| 29 |
|
|
|
|
| 30 |
if len(clean_text) > 1000:
|
| 31 |
clean_text = clean_text[:1000] + "..."
|
| 32 |
|
|
|
|
| 40 |
st.error(f"Audio generation error: {e}")
|
| 41 |
return None
|
| 42 |
|
| 43 |
+
# Image search functions
|
| 44 |
def google_search_api(query, api_key, cx):
|
| 45 |
"""Helper: Performs a single Google Search."""
|
| 46 |
try:
|
|
|
|
| 89 |
"""MASTER FUNCTION: Google Search -> DuckDuckGo"""
|
| 90 |
cx = os.environ.get("GOOGLE_CX", st.secrets.get("GOOGLE_CX", ""))
|
| 91 |
|
|
|
|
| 92 |
key1 = os.environ.get("GOOGLE_SEARCH_KEY", st.secrets.get("GOOGLE_SEARCH_KEY", ""))
|
| 93 |
if key1 and cx:
|
| 94 |
url = google_search_api(query, key1, cx)
|
| 95 |
if url:
|
| 96 |
return url
|
| 97 |
|
|
|
|
| 98 |
key2 = os.environ.get("GOOGLE_SEARCH_KEY_2", st.secrets.get("GOOGLE_SEARCH_KEY_2", ""))
|
| 99 |
if key2 and cx:
|
| 100 |
url = google_search_api(query, key2, cx)
|
| 101 |
if url:
|
| 102 |
return url
|
| 103 |
|
|
|
|
| 104 |
return duckduckgo_search_api(query)
|
| 105 |
|
| 106 |
+
# Graph plotting function - FIXED VERSION
|
| 107 |
def execute_plotting_code(code_snippet):
|
| 108 |
"""Execute plotting code in a safe environment."""
|
| 109 |
try:
|
| 110 |
+
# Clear any existing plots
|
| 111 |
+
plt.close('all')
|
| 112 |
+
|
| 113 |
+
# Create a new figure
|
| 114 |
+
fig, ax = plt.subplots(figsize=(10, 6))
|
| 115 |
+
|
| 116 |
+
# Prepare execution environment
|
| 117 |
+
namespace = {
|
| 118 |
+
'plt': plt,
|
| 119 |
+
'np': np,
|
| 120 |
+
'ax': ax,
|
| 121 |
+
'fig': fig,
|
| 122 |
+
'math': __import__('math')
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
# Clean the code
|
| 126 |
+
cleaned_code = code_snippet.strip()
|
| 127 |
+
|
| 128 |
+
# Execute the code
|
| 129 |
+
exec(cleaned_code, namespace)
|
| 130 |
+
|
| 131 |
+
# Get current axis
|
| 132 |
+
ax = plt.gca()
|
| 133 |
+
|
| 134 |
+
# Ensure proper formatting
|
| 135 |
+
if not ax.get_xlabel():
|
| 136 |
+
ax.set_xlabel('X-axis', fontsize=12)
|
| 137 |
+
if not ax.get_ylabel():
|
| 138 |
+
ax.set_ylabel('Y-axis', fontsize=12)
|
| 139 |
+
if not ax.get_title():
|
| 140 |
+
ax.set_title('Physics Graph', fontsize=14)
|
| 141 |
+
|
| 142 |
+
# Ensure grid is visible
|
| 143 |
+
ax.grid(True, linestyle='--', alpha=0.6)
|
| 144 |
+
|
| 145 |
+
# Ensure legend if labels exist
|
| 146 |
+
if ax.get_legend_handles_labels()[1]:
|
| 147 |
+
ax.legend()
|
| 148 |
+
|
| 149 |
+
# Display the plot
|
| 150 |
st.pyplot(fig)
|
| 151 |
+
|
| 152 |
+
# Close the figure
|
| 153 |
plt.close(fig)
|
| 154 |
+
|
| 155 |
except Exception as e:
|
| 156 |
st.error(f"Graph Error: {str(e)[:200]}")
|
| 157 |
+
|
| 158 |
+
# Fallback: Create a simple sine wave
|
| 159 |
+
try:
|
| 160 |
+
fig, ax = plt.subplots(figsize=(10, 6))
|
| 161 |
+
x = np.linspace(0, 2*np.pi, 100)
|
| 162 |
+
y = np.sin(x)
|
| 163 |
+
ax.plot(x, y, 'b-', linewidth=2, label='sin(x)')
|
| 164 |
+
ax.set_xlabel('Angle (radians)', fontsize=12)
|
| 165 |
+
ax.set_ylabel('Value', fontsize=12)
|
| 166 |
+
ax.set_title('Sample Graph (Fallback)', fontsize=14)
|
| 167 |
+
ax.grid(True, linestyle='--', alpha=0.6)
|
| 168 |
+
ax.legend()
|
| 169 |
+
st.pyplot(fig)
|
| 170 |
+
plt.close(fig)
|
| 171 |
+
except:
|
| 172 |
+
st.warning("Could not generate graph. Please check the Python code.")
|
| 173 |
|
| 174 |
+
# Display message function
|
| 175 |
def display_message(role, content, enable_voice=False):
|
| 176 |
"""Display chat message with all features."""
|
| 177 |
with st.chat_message(role):
|
| 178 |
|
| 179 |
text_to_display = content
|
| 180 |
|
| 181 |
+
# 1. Extract and handle Python code blocks
|
| 182 |
+
code_pattern = r'```python\s*(.*?)```'
|
| 183 |
+
code_matches = list(re.finditer(code_pattern, content, re.DOTALL))
|
| 184 |
+
|
| 185 |
+
# Remove code blocks from displayed text
|
| 186 |
+
for match in reversed(code_matches):
|
| 187 |
+
text_to_display = text_to_display.replace(match.group(0), "")
|
| 188 |
|
| 189 |
# 2. Check for [IMAGE: query] Tags
|
| 190 |
image_match = re.search(r'\[IMAGE:\s*(.*?)\]', text_to_display, re.IGNORECASE)
|
|
|
|
| 199 |
# --- DISPLAY TEXT ---
|
| 200 |
st.markdown(text_to_display)
|
| 201 |
|
| 202 |
+
# --- EXECUTE AND DISPLAY GRAPHS ---
|
| 203 |
+
if code_matches and role == "assistant":
|
| 204 |
+
for match in code_matches:
|
| 205 |
+
code_content = match.group(1).strip()
|
| 206 |
+
if code_content:
|
| 207 |
+
with st.expander("📊 Generated Graph", expanded=True):
|
| 208 |
+
execute_plotting_code(code_content)
|
| 209 |
+
with st.expander("📝 View Python Code"):
|
| 210 |
+
st.code(code_content, language='python')
|
| 211 |
|
| 212 |
# --- DISPLAY IMAGE ---
|
| 213 |
if image_match and role == "assistant":
|
|
|
|
| 225 |
audio_bytes = generate_audio(text_to_display)
|
| 226 |
if audio_bytes:
|
| 227 |
st.audio(audio_bytes, format='audio/mp3')
|
| 228 |
+
# Groq API function
|
| 229 |
def call_groq_api(api_key, messages, max_tokens=2000):
|
| 230 |
"""Call Groq API - FREE tier available (10,000 requests/month)."""
|
| 231 |
url = "https://api.groq.com/openai/v1/chat/completions"
|
|
|
|
| 243 |
"role": "system",
|
| 244 |
"content": msg["content"]
|
| 245 |
})
|
| 246 |
+
elif msg["role"] in ["user", "assistant"]:
|
| 247 |
formatted_messages.append({
|
| 248 |
+
"role": msg["role"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
"content": msg["content"]
|
| 250 |
})
|
| 251 |
|
| 252 |
+
# Try multiple models
|
| 253 |
models_to_try = [
|
| 254 |
+
"llama-3.1-8b-instant",
|
| 255 |
+
"llama-3.2-3b-preview",
|
| 256 |
+
"mixtral-8x7b-32768",
|
| 257 |
]
|
| 258 |
|
| 259 |
for model in models_to_try:
|
|
|
|
| 272 |
result = response.json()
|
| 273 |
return result["choices"][0]["message"]["content"]
|
| 274 |
elif response.status_code == 429:
|
|
|
|
| 275 |
continue
|
| 276 |
elif response.status_code == 401:
|
| 277 |
return "❌ Invalid API Key. Please check your Groq API key."
|
|
|
|
| 279 |
return "❌ Payment required. The free model might be unavailable. Try again later."
|
| 280 |
|
| 281 |
except requests.exceptions.Timeout:
|
| 282 |
+
continue
|
| 283 |
except Exception as e:
|
| 284 |
+
continue
|
| 285 |
|
| 286 |
return "⚠️ All models are currently unavailable. Please try again in a few moments."
|
| 287 |
|
| 288 |
+
# System Instructions for the AI - Part 5a (Start)
|
|
|
|
|
|
|
|
|
|
| 289 |
SYSTEM_INSTRUCTIONS = """You are Richard Feynman, the Nobel Prize-winning physicist, now serving as a tutor for Singapore H2 Physics (syllabus 9478).
|
| 290 |
|
| 291 |
**CORE DIRECTIVE:**
|
|
|
|
| 293 |
- Reject topics not in the syllabus
|
| 294 |
- Teach with clarity, enthusiasm, and the Feynman method
|
| 295 |
|
| 296 |
+
**GRAPH GENERATION RULES (CRITICAL):**
|
| 297 |
+
When asked to create a graph, you MUST write COMPLETE, EXECUTABLE Python code that:
|
| 298 |
+
1. Starts with: import matplotlib.pyplot as plt, import numpy as np
|
| 299 |
+
2. Creates a figure: plt.figure(figsize=(10, 6)) or fig, ax = plt.subplots(figsize=(10, 6))
|
| 300 |
+
3. Generates or uses appropriate physics data
|
| 301 |
+
4. Plots the data with plt.plot(), plt.scatter(), etc.
|
| 302 |
+
5. Adds proper labels: plt.xlabel(), plt.ylabel(), plt.title()
|
| 303 |
+
6. Adds grid: plt.grid(True, linestyle='--', alpha=0.6)
|
| 304 |
+
7. Adds legend if needed: plt.legend()
|
| 305 |
|
| 306 |
+
**EXAMPLE OF GOOD GRAPH CODE:**
|
| 307 |
+
import matplotlib.pyplot as plt
|
| 308 |
+
import numpy as np
|
| 309 |
+
|
| 310 |
+
plt.figure(figsize=(10, 6))
|
| 311 |
+
x = np.linspace(0, 10, 100)
|
| 312 |
+
y = np.sin(x)
|
| 313 |
+
plt.plot(x, y, 'b-', linewidth=2, label='sin(x)')
|
| 314 |
+
plt.xlabel('X Variable', fontsize=12)
|
| 315 |
+
plt.ylabel('Y Variable', fontsize=12)
|
| 316 |
+
plt.title('Physics Graph', fontsize=14)
|
| 317 |
+
plt.grid(True, linestyle='--', alpha=0.6)
|
| 318 |
+
plt.legend()
|
| 319 |
+
|
| 320 |
+
**DIAGRAMS:**
|
| 321 |
+
When you need to show a diagram, use: [IMAGE: search query]
|
| 322 |
+
Example: "Here's the setup: [IMAGE: double slit experiment diagram]"
|
| 323 |
|
| 324 |
+
**TEACHING METHOD:**
|
| 325 |
+
1. Ask ONE question at a time
|
| 326 |
+
2. Use analogies to explain complex concepts
|
| 327 |
+
3. Guide, don't give answers immediately
|
| 328 |
+
4. Validate understanding frequently
|
| 329 |
+
5. Only give full solutions when student says "I give up"
|
| 330 |
+
6. Summarize each concept with a clear summary in > blockquote
|
| 331 |
**FORMATTING:**
|
| 332 |
+
- Use LaTeX for equations: $F = ma$, $E = mc^2$
|
| 333 |
- Use **bold** for key terms
|
| 334 |
- Keep responses concise but thorough
|
| 335 |
- Be enthusiastic and encouraging
|
|
|
|
| 336 |
|
| 337 |
**TOPICS COVERED (9478 Syllabus):**
|
| 338 |
1. Measurement & Uncertainty
|
|
|
|
| 346 |
9. Electricity & DC Circuits
|
| 347 |
10. Electromagnetism
|
| 348 |
11. Modern Physics (Quantum/Nuclear)
|
| 349 |
+
'''
|
| 350 |
|
| 351 |
+
## **PART 6: SIDEBAR CONFIGURATION**
|
| 352 |
+
# Sidebar Configuration
|
|
|
|
| 353 |
with st.sidebar:
|
| 354 |
# Header with Feynman image
|
| 355 |
col1, col2 = st.columns([1, 3])
|
|
|
|
| 483 |
st.divider()
|
| 484 |
st.caption("Made with ❤️ for H2 Physics students | Powered by Groq AI")
|
| 485 |
|
| 486 |
+
# Main Chat Interface
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
if "messages" not in st.session_state:
|
| 488 |
st.session_state.messages = [
|
| 489 |
{"role": "system", "content": SYSTEM_INSTRUCTIONS},
|
|
|
|
| 491 |
**Hello JPJC Physics students! I'm Richard Feynman, ready to help you master H2 Physics!** ⚛️
|
| 492 |
|
| 493 |
I can:
|
| 494 |
+
- 📊 **Plot graphs** using Python (ask me to plot any physics equation!)
|
| 495 |
- 🖼️ **Find diagrams** through web search
|
| 496 |
- 💬 **Explain concepts** with analogies
|
| 497 |
- ❓ **Ask questions** to test your understanding
|
| 498 |
|
| 499 |
**What would you like to learn today?**
|
|
|
|
| 500 |
"""}
|
| 501 |
+
|
| 502 |
]
|
| 503 |
|
| 504 |
# Title and mode indicator
|
|
|
|
| 627 |
st.markdown("""
|
| 628 |
<div style="text-align: center; color: #888; font-size: 0.9em;">
|
| 629 |
<p><strong>H2 Physics Feynman Tutor</strong> | Singapore H2 Physics (9478) Syllabus</p>
|
| 630 |
+
<p>Powered by <a href="https://groq.com" target="_blank">Groq AI</a></p>
|
|
|
|
|
|
|
| 631 |
<p style="font-size: 0.8em;">Disclaimer: This is an AI tutoring assistant. Always verify with official syllabus documents.</p>
|
| 632 |
</div>
|
| 633 |
+
""", unsafe_allow_html=True)
|