Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,28 +1,119 @@
|
|
|
|
1 |
import streamlit as st
|
2 |
import pytesseract
|
3 |
from PIL import Image
|
4 |
import fitz # PyMuPDF
|
5 |
import io
|
6 |
import requests
|
|
|
7 |
|
8 |
# --- Config ---
|
9 |
API_KEY = "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"
|
10 |
API_URL = "https://openrouter.ai/api/v1/chat/completions"
|
11 |
MODEL = "mistralai/mistral-7b-instruct"
|
12 |
|
13 |
-
|
14 |
-
st.
|
|
|
|
|
|
|
|
|
15 |
|
|
|
16 |
st.markdown("""
|
17 |
-
|
18 |
-
|
19 |
-
-
|
20 |
-
|
21 |
-
-
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
-
#
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
|
27 |
lab_text = ""
|
28 |
if uploaded_file:
|
@@ -37,21 +128,48 @@ if uploaded_file:
|
|
37 |
image = Image.open(io.BytesIO(file_bytes))
|
38 |
lab_text = pytesseract.image_to_string(image)
|
39 |
|
40 |
-
|
41 |
-
st.
|
|
|
|
|
42 |
|
|
|
|
|
43 |
# -- AI Evaluation Prompt --
|
44 |
-
full_prompt = f"""
|
45 |
-
{lab_text}
|
46 |
|
47 |
-
|
48 |
-
|
49 |
-
- Point out any missing or incomplete sections.
|
50 |
-
- Give feedback or improvement suggestions like: "Try writing a more detailed observation."
|
51 |
-
- Grade it roughly on a scale from 1 to 10 for completeness.
|
52 |
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
|
56 |
def query_ai(prompt):
|
57 |
headers = {
|
@@ -61,30 +179,152 @@ Respond clearly:
|
|
61 |
payload = {
|
62 |
"model": MODEL,
|
63 |
"messages": [
|
64 |
-
{"role": "system", "content": "You are a helpful science teacher."},
|
65 |
{"role": "user", "content": prompt}
|
66 |
]
|
67 |
}
|
68 |
-
|
69 |
-
|
|
|
|
|
|
|
|
|
|
|
70 |
|
71 |
-
if st.button("
|
72 |
-
with st.spinner("Analyzing report with AI..."):
|
73 |
result = query_ai(full_prompt)
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
with st.spinner("Thinking..."):
|
83 |
-
followup_prompt = f"
|
84 |
-
{lab_text}
|
85 |
-
|
86 |
-
|
87 |
-
|
|
|
|
|
|
|
88 |
followup_response = query_ai(followup_prompt)
|
89 |
-
|
90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# app.py
|
2 |
import streamlit as st
|
3 |
import pytesseract
|
4 |
from PIL import Image
|
5 |
import fitz # PyMuPDF
|
6 |
import io
|
7 |
import requests
|
8 |
+
import re
|
9 |
|
10 |
# --- Config ---
|
11 |
API_KEY = "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"
|
12 |
API_URL = "https://openrouter.ai/api/v1/chat/completions"
|
13 |
MODEL = "mistralai/mistral-7b-instruct"
|
14 |
|
15 |
+
# Set page config
|
16 |
+
st.set_page_config(
|
17 |
+
page_title="π¬ AI Science Lab Assistant",
|
18 |
+
layout="centered",
|
19 |
+
page_icon="π¬"
|
20 |
+
)
|
21 |
|
22 |
+
# Custom CSS for styling
|
23 |
st.markdown("""
|
24 |
+
<style>
|
25 |
+
.header {
|
26 |
+
font-size: 36px;
|
27 |
+
color: #2e86c1;
|
28 |
+
text-align: center;
|
29 |
+
padding: 20px;
|
30 |
+
}
|
31 |
+
.subheader {
|
32 |
+
font-size: 24px;
|
33 |
+
color: #28b463;
|
34 |
+
border-bottom: 2px solid #f4d03f;
|
35 |
+
padding-bottom: 10px;
|
36 |
+
margin-top: 30px;
|
37 |
+
}
|
38 |
+
.stButton>button {
|
39 |
+
background-color: #28b463 !important;
|
40 |
+
color: white !important;
|
41 |
+
border-radius: 8px;
|
42 |
+
padding: 8px 20px;
|
43 |
+
transition: all 0.3s;
|
44 |
+
}
|
45 |
+
.stButton>button:hover {
|
46 |
+
background-color: #239b56 !important;
|
47 |
+
transform: scale(1.05);
|
48 |
+
}
|
49 |
+
.score-card {
|
50 |
+
background: linear-gradient(135deg, #e8f8f5, #d1f2eb);
|
51 |
+
border-radius: 15px;
|
52 |
+
padding: 20px;
|
53 |
+
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
54 |
+
margin-bottom: 20px;
|
55 |
+
}
|
56 |
+
.highlight {
|
57 |
+
background-color: #f9e79f;
|
58 |
+
padding: 5px;
|
59 |
+
border-radius: 5px;
|
60 |
+
font-weight: bold;
|
61 |
+
}
|
62 |
+
.tip-box {
|
63 |
+
background-color: #eafaf1;
|
64 |
+
border-left: 5px solid #28b463;
|
65 |
+
padding: 15px;
|
66 |
+
margin: 15px 0;
|
67 |
+
border-radius: 0 8px 8px 0;
|
68 |
+
}
|
69 |
+
</style>
|
70 |
+
""", unsafe_allow_html=True)
|
71 |
|
72 |
+
# Header
|
73 |
+
st.markdown('<p class="header">π¬ AI Science Lab Assistant</p>', unsafe_allow_html=True)
|
74 |
+
|
75 |
+
# Introduction
|
76 |
+
st.markdown("""
|
77 |
+
<div style="text-align: center; margin-bottom: 30px;">
|
78 |
+
<p style="font-size: 18px;">Transform your lab reports with AI-powered analysis! Get instant feedback on completeness,
|
79 |
+
receive improvement suggestions, and ask questions about your scientific work.</p>
|
80 |
+
</div>
|
81 |
+
""", unsafe_allow_html=True)
|
82 |
+
|
83 |
+
# Features in columns
|
84 |
+
col1, col2, col3 = st.columns(3)
|
85 |
+
with col1:
|
86 |
+
st.markdown("""
|
87 |
+
<div style="text-align: center;">
|
88 |
+
<h4>π Comprehensive Analysis</h4>
|
89 |
+
<p>Checks for all essential lab report sections</p>
|
90 |
+
</div>
|
91 |
+
""", unsafe_allow_html=True)
|
92 |
+
|
93 |
+
with col2:
|
94 |
+
st.markdown("""
|
95 |
+
<div style="text-align: center;">
|
96 |
+
<h4>π― Smart Scoring</h4>
|
97 |
+
<p>Grades your report on completeness and structure</p>
|
98 |
+
</div>
|
99 |
+
""", unsafe_allow_html=True)
|
100 |
+
|
101 |
+
with col3:
|
102 |
+
st.markdown("""
|
103 |
+
<div style="text-align: center;">
|
104 |
+
<h4>π Improvement Tips</h4>
|
105 |
+
<p>Personalized suggestions to enhance your report</p>
|
106 |
+
</div>
|
107 |
+
""", unsafe_allow_html=True)
|
108 |
+
|
109 |
+
# Divider
|
110 |
+
st.markdown("---")
|
111 |
+
|
112 |
+
# --- File Upload ---
|
113 |
+
st.markdown('<p class="subheader">π€ Upload Your Lab Report</p>', unsafe_allow_html=True)
|
114 |
+
uploaded_file = st.file_uploader("Upload image (JPG, PNG) or PDF",
|
115 |
+
type=["jpg", "jpeg", "png", "pdf"],
|
116 |
+
label_visibility="collapsed")
|
117 |
|
118 |
lab_text = ""
|
119 |
if uploaded_file:
|
|
|
128 |
image = Image.open(io.BytesIO(file_bytes))
|
129 |
lab_text = pytesseract.image_to_string(image)
|
130 |
|
131 |
+
# Allow text editing
|
132 |
+
st.markdown('<p class="subheader">βοΈ Extracted Text</p>', unsafe_allow_html=True)
|
133 |
+
st.caption("Review and edit the extracted text if needed before analysis")
|
134 |
+
lab_text = st.text_area("", lab_text, height=300, label_visibility="collapsed")
|
135 |
|
136 |
+
# --- AI Evaluation ---
|
137 |
+
if lab_text.strip():
|
138 |
# -- AI Evaluation Prompt --
|
139 |
+
full_prompt = f"""You are a science teacher evaluating a student's lab report. Please provide a comprehensive analysis:
|
|
|
140 |
|
141 |
+
Lab Report:
|
142 |
+
{lab_text}
|
|
|
|
|
|
|
143 |
|
144 |
+
Evaluation Guidelines:
|
145 |
+
1. **Section Check**: Identify which of these sections are present and which are missing:
|
146 |
+
- Title
|
147 |
+
- Objective
|
148 |
+
- Hypothesis
|
149 |
+
- Materials
|
150 |
+
- Procedure
|
151 |
+
- Observations
|
152 |
+
- Results
|
153 |
+
- Conclusion
|
154 |
+
- References
|
155 |
+
|
156 |
+
2. **Completeness Score**:
|
157 |
+
- Assign a numerical score from 1-10 based on completeness
|
158 |
+
- Justify the score based on missing sections and content quality
|
159 |
+
|
160 |
+
3. **Improvement Tips**:
|
161 |
+
- For each missing section, explain why it's important
|
162 |
+
- Provide specific suggestions for improvement (e.g., "Try writing a more detailed observation section by including quantitative data")
|
163 |
+
- Highlight any sections that need more detail or clarity
|
164 |
+
|
165 |
+
4. **Structure Response**:
|
166 |
+
- Start with: "### Missing Sections:"
|
167 |
+
- Then: "### Completeness Score: X/10"
|
168 |
+
- Then: "### Improvement Tips:"
|
169 |
+
- Finally: "### Detailed Feedback:"
|
170 |
+
|
171 |
+
Be concise but thorough in your analysis.
|
172 |
+
"""
|
173 |
|
174 |
def query_ai(prompt):
|
175 |
headers = {
|
|
|
179 |
payload = {
|
180 |
"model": MODEL,
|
181 |
"messages": [
|
182 |
+
{"role": "system", "content": "You are a helpful science teacher providing detailed lab report feedback."},
|
183 |
{"role": "user", "content": prompt}
|
184 |
]
|
185 |
}
|
186 |
+
try:
|
187 |
+
response = requests.post(API_URL, headers=headers, json=payload, timeout=120)
|
188 |
+
response.raise_for_status()
|
189 |
+
return response.json()['choices'][0]['message']['content']
|
190 |
+
except Exception as e:
|
191 |
+
st.error(f"Error connecting to AI service: {str(e)}")
|
192 |
+
return None
|
193 |
|
194 |
+
if st.button("π§ͺ Analyze Report", use_container_width=True):
|
195 |
+
with st.spinner("π Analyzing report with AI. This may take 20-30 seconds..."):
|
196 |
result = query_ai(full_prompt)
|
197 |
+
|
198 |
+
if result:
|
199 |
+
st.success("β
Analysis Complete!")
|
200 |
+
st.balloons()
|
201 |
+
|
202 |
+
# Extract score using regex
|
203 |
+
score_match = re.search(r"Completeness Score:\s*(\d+)/10", result)
|
204 |
+
score = int(score_match.group(1)) if score_match else None
|
205 |
+
|
206 |
+
# Display score in a card
|
207 |
+
if score is not None:
|
208 |
+
with st.container():
|
209 |
+
st.markdown('<div class="score-card">', unsafe_allow_html=True)
|
210 |
+
|
211 |
+
# Create columns for score visualization
|
212 |
+
col1, col2 = st.columns([1, 3])
|
213 |
+
|
214 |
+
with col1:
|
215 |
+
st.markdown(f"<h2 style='text-align: center; color: #28b463;'>{score}/10</h2>",
|
216 |
+
unsafe_allow_html=True)
|
217 |
+
st.markdown("<h4 style='text-align: center;'>Completeness Score</h4>",
|
218 |
+
unsafe_allow_html=True)
|
219 |
+
|
220 |
+
with col2:
|
221 |
+
# Create a color gradient based on score
|
222 |
+
if score >= 8:
|
223 |
+
color = "#28b463" # Green
|
224 |
+
elif score >= 5:
|
225 |
+
color = "#f39c12" # Orange
|
226 |
+
else:
|
227 |
+
color = "#e74c3c" # Red
|
228 |
+
|
229 |
+
# Display progress bar with styling
|
230 |
+
st.progress(score/10, text=f"{score*10}% complete")
|
231 |
+
st.markdown(
|
232 |
+
f"<style>"
|
233 |
+
f".stProgress > div > div > div {{"
|
234 |
+
f" background-color: {color} !important;"
|
235 |
+
f" border-radius: 10px;"
|
236 |
+
f"}}"
|
237 |
+
f"</style>",
|
238 |
+
unsafe_allow_html=True
|
239 |
+
)
|
240 |
+
|
241 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
242 |
+
|
243 |
+
# Display AI analysis with formatting
|
244 |
+
st.markdown("## π Analysis Results")
|
245 |
+
|
246 |
+
# Split sections for better display
|
247 |
+
sections = {
|
248 |
+
"Missing Sections": None,
|
249 |
+
"Improvement Tips": None,
|
250 |
+
"Detailed Feedback": None
|
251 |
+
}
|
252 |
+
|
253 |
+
current_section = None
|
254 |
+
for line in result.split('\n'):
|
255 |
+
if "### Missing Sections:" in line:
|
256 |
+
current_section = "Missing Sections"
|
257 |
+
sections[current_section] = []
|
258 |
+
elif "### Improvement Tips:" in line:
|
259 |
+
current_section = "Improvement Tips"
|
260 |
+
sections[current_section] = []
|
261 |
+
elif "### Detailed Feedback:" in line:
|
262 |
+
current_section = "Detailed Feedback"
|
263 |
+
sections[current_section] = []
|
264 |
+
elif current_section and line.strip():
|
265 |
+
sections[current_section].append(line)
|
266 |
+
|
267 |
+
# Display each section
|
268 |
+
if sections["Missing Sections"]:
|
269 |
+
st.markdown("### π Missing Sections")
|
270 |
+
missing_text = '\n'.join(sections["Missing Sections"])
|
271 |
+
st.markdown(f'<div class="highlight">{missing_text}</div>', unsafe_allow_html=True)
|
272 |
+
|
273 |
+
if sections["Improvement Tips"]:
|
274 |
+
st.markdown("### π‘ Improvement Tips")
|
275 |
+
tips_text = '\n'.join(sections["Improvement Tips"])
|
276 |
+
st.markdown(f'<div class="tip-box">{tips_text}</div>', unsafe_allow_html=True)
|
277 |
+
|
278 |
+
if sections["Detailed Feedback"]:
|
279 |
+
st.markdown("### π Detailed Feedback")
|
280 |
+
st.write('\n'.join(sections["Detailed Feedback"]))
|
281 |
+
|
282 |
+
# Show full AI response in expander
|
283 |
+
with st.expander("View Full AI Analysis"):
|
284 |
+
st.markdown(result)
|
285 |
+
|
286 |
+
# --- Question Answering Section ---
|
287 |
+
st.markdown("---")
|
288 |
+
st.markdown('<p class="subheader">β Ask About Your Report</p>', unsafe_allow_html=True)
|
289 |
+
|
290 |
+
col1, col2 = st.columns([3, 1])
|
291 |
+
with col1:
|
292 |
+
user_question = st.text_input("Ask a question about your lab report",
|
293 |
+
placeholder="e.g., How can I improve my hypothesis?")
|
294 |
+
with col2:
|
295 |
+
st.markdown("<div style='height: 28px;'></div>", unsafe_allow_html=True)
|
296 |
+
ask_button = st.button("π Ask Question", use_container_width=True)
|
297 |
+
|
298 |
+
if (ask_button or user_question) and user_question.strip():
|
299 |
with st.spinner("Thinking..."):
|
300 |
+
followup_prompt = f"""Lab Report:
|
301 |
+
{lab_text}
|
302 |
+
|
303 |
+
Question: {user_question}
|
304 |
+
|
305 |
+
Answer the question based on the lab report. If the question can't be answered from the report,
|
306 |
+
suggest what information the student should add to answer it.
|
307 |
+
"""
|
308 |
followup_response = query_ai(followup_prompt)
|
309 |
+
|
310 |
+
if followup_response:
|
311 |
+
st.markdown("### π¬ AI Response")
|
312 |
+
st.markdown(f'<div class="tip-box">{followup_response}</div>', unsafe_allow_html=True)
|
313 |
+
else:
|
314 |
+
# Show sample report if no file uploaded
|
315 |
+
st.markdown("---")
|
316 |
+
st.markdown('<p class="subheader">π Sample Lab Report</p>', unsafe_allow_html=True)
|
317 |
+
st.markdown("""
|
318 |
+
**Title:** Effect of Temperature on Enzyme Activity
|
319 |
+
**Objective:** To investigate how temperature affects catalase enzyme activity
|
320 |
+
**Hypothesis:** Enzyme activity will increase with temperature up to 37Β°C, then decrease
|
321 |
+
**Materials:** Test tubes, hydrogen peroxide, liver extract, thermometer
|
322 |
+
**Procedure:**
|
323 |
+
1. Prepare test tubes at 5 different temperatures
|
324 |
+
2. Add equal amounts of hydrogen peroxide and liver extract
|
325 |
+
3. Measure oxygen production
|
326 |
+
**Observations:** More bubbles at 37Β°C compared to lower or higher temperatures
|
327 |
+
**Conclusion:** Enzyme activity peaks at body temperature
|
328 |
+
""")
|
329 |
+
|
330 |
+
st.info("π Upload your own lab report to get a personalized analysis!")
|