Spaces:
Sleeping
Sleeping
# app.py | |
import streamlit as st | |
import pytesseract | |
from PIL import Image | |
import fitz # PyMuPDF | |
import io | |
import requests | |
import re | |
# --- Config --- | |
API_KEY = "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" | |
API_URL = "https://openrouter.ai/api/v1/chat/completions" | |
MODEL = "mistralai/mistral-7b-instruct" | |
# Set page config | |
st.set_page_config( | |
page_title="π¬ AI Science Lab Assistant", | |
layout="centered", | |
page_icon="π¬" | |
) | |
# Custom CSS for styling | |
st.markdown(""" | |
<style> | |
.header { | |
font-size: 36px; | |
color: #2e86c1; | |
text-align: center; | |
padding: 20px; | |
} | |
.subheader { | |
font-size: 24px; | |
color: #28b463; | |
border-bottom: 2px solid #f4d03f; | |
padding-bottom: 10px; | |
margin-top: 30px; | |
} | |
.stButton>button { | |
background-color: #28b463 !important; | |
color: white !important; | |
border-radius: 8px; | |
padding: 8px 20px; | |
transition: all 0.3s; | |
} | |
.stButton>button:hover { | |
background-color: #239b56 !important; | |
transform: scale(1.05); | |
} | |
.score-card { | |
background: linear-gradient(135deg, #e8f8f5, #d1f2eb); | |
border-radius: 15px; | |
padding: 20px; | |
box-shadow: 0 4px 8px rgba(0,0,0,0.1); | |
margin-bottom: 20px; | |
} | |
.highlight { | |
background-color: #f9e79f; | |
padding: 5px; | |
border-radius: 5px; | |
font-weight: bold; | |
} | |
.tip-box { | |
background-color: #eafaf1; | |
border-left: 5px solid #28b463; | |
padding: 15px; | |
margin: 15px 0; | |
border-radius: 0 8px 8px 0; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# Header | |
st.markdown('<p class="header">π¬ AI Science Lab Assistant</p>', unsafe_allow_html=True) | |
# Introduction | |
st.markdown(""" | |
<div style="text-align: center; margin-bottom: 30px;"> | |
<p style="font-size: 18px;">Transform your lab reports with AI-powered analysis! Get instant feedback on completeness, | |
receive improvement suggestions, and ask questions about your scientific work.</p> | |
</div> | |
""", unsafe_allow_html=True) | |
# Features in columns | |
col1, col2, col3 = st.columns(3) | |
with col1: | |
st.markdown(""" | |
<div style="text-align: center;"> | |
<h4>π Comprehensive Analysis</h4> | |
<p>Checks for all essential lab report sections</p> | |
</div> | |
""", unsafe_allow_html=True) | |
with col2: | |
st.markdown(""" | |
<div style="text-align: center;"> | |
<h4>π― Smart Scoring</h4> | |
<p>Grades your report on completeness and structure</p> | |
</div> | |
""", unsafe_allow_html=True) | |
with col3: | |
st.markdown(""" | |
<div style="text-align: center;"> | |
<h4>π Improvement Tips</h4> | |
<p>Personalized suggestions to enhance your report</p> | |
</div> | |
""", unsafe_allow_html=True) | |
# Divider | |
st.markdown("---") | |
# --- File Upload --- | |
st.markdown('<p class="subheader">π€ Upload Your Lab Report</p>', unsafe_allow_html=True) | |
uploaded_file = st.file_uploader("Upload image (JPG, PNG) or PDF", | |
type=["jpg", "jpeg", "png", "pdf"], | |
label_visibility="collapsed") | |
lab_text = "" | |
if uploaded_file: | |
file_bytes = uploaded_file.read() | |
file_ext = uploaded_file.name.split(".")[-1].lower() | |
if file_ext == "pdf": | |
doc = fitz.open(stream=file_bytes, filetype="pdf") | |
for page in doc: | |
lab_text += page.get_text() | |
else: | |
image = Image.open(io.BytesIO(file_bytes)) | |
lab_text = pytesseract.image_to_string(image) | |
# Allow text editing | |
st.markdown('<p class="subheader">βοΈ Extracted Text</p>', unsafe_allow_html=True) | |
st.caption("Review and edit the extracted text if needed before analysis") | |
lab_text = st.text_area("", lab_text, height=300, label_visibility="collapsed") | |
# --- AI Evaluation --- | |
if lab_text.strip(): | |
# -- AI Evaluation Prompt -- | |
full_prompt = f"""You are a science teacher evaluating a student's lab report. Please provide a comprehensive analysis: | |
Lab Report: | |
{lab_text} | |
Evaluation Guidelines: | |
1. **Section Check**: Identify which of these sections are present and which are missing: | |
- Title | |
- Objective | |
- Hypothesis | |
- Materials | |
- Procedure | |
- Observations | |
- Results | |
- Conclusion | |
- References | |
2. **Completeness Score**: | |
- Assign a numerical score from 1-10 based on completeness | |
- Justify the score based on missing sections and content quality | |
3. **Improvement Tips**: | |
- For each missing section, explain why it's important | |
- Provide specific suggestions for improvement (e.g., "Try writing a more detailed observation section by including quantitative data") | |
- Highlight any sections that need more detail or clarity | |
4. **Structure Response**: | |
- Start with: "### Missing Sections:" | |
- Then: "### Completeness Score: X/10" | |
- Then: "### Improvement Tips:" | |
- Finally: "### Detailed Feedback:" | |
Be concise but thorough in your analysis. | |
""" | |
def query_ai(prompt): | |
headers = { | |
"Authorization": f"Bearer {API_KEY}", | |
"Content-Type": "application/json" | |
} | |
payload = { | |
"model": MODEL, | |
"messages": [ | |
{"role": "system", "content": "You are a helpful science teacher providing detailed lab report feedback."}, | |
{"role": "user", "content": prompt} | |
] | |
} | |
try: | |
response = requests.post(API_URL, headers=headers, json=payload, timeout=120) | |
response.raise_for_status() | |
return response.json()['choices'][0]['message']['content'] | |
except Exception as e: | |
st.error(f"Error connecting to AI service: {str(e)}") | |
return None | |
if st.button("π§ͺ Analyze Report", use_container_width=True): | |
with st.spinner("π Analyzing report with AI. This may take 20-30 seconds..."): | |
result = query_ai(full_prompt) | |
if result: | |
st.success("β Analysis Complete!") | |
st.balloons() | |
# Extract score using regex | |
score_match = re.search(r"Completeness Score:\s*(\d+)/10", result) | |
score = int(score_match.group(1)) if score_match else None | |
# Display score in a card | |
if score is not None: | |
with st.container(): | |
st.markdown('<div class="score-card">', unsafe_allow_html=True) | |
# Create columns for score visualization | |
col1, col2 = st.columns([1, 3]) | |
with col1: | |
st.markdown(f"<h2 style='text-align: center; color: #28b463;'>{score}/10</h2>", | |
unsafe_allow_html=True) | |
st.markdown("<h4 style='text-align: center;'>Completeness Score</h4>", | |
unsafe_allow_html=True) | |
with col2: | |
# Create a color gradient based on score | |
if score >= 8: | |
color = "#28b463" # Green | |
elif score >= 5: | |
color = "#f39c12" # Orange | |
else: | |
color = "#e74c3c" # Red | |
# Display progress bar with styling | |
st.progress(score/10, text=f"{score*10}% complete") | |
st.markdown( | |
f"<style>" | |
f".stProgress > div > div > div {{" | |
f" background-color: {color} !important;" | |
f" border-radius: 10px;" | |
f"}}" | |
f"</style>", | |
unsafe_allow_html=True | |
) | |
st.markdown('</div>', unsafe_allow_html=True) | |
# Display AI analysis with formatting | |
st.markdown("## π Analysis Results") | |
# Split sections for better display | |
sections = { | |
"Missing Sections": None, | |
"Improvement Tips": None, | |
"Detailed Feedback": None | |
} | |
current_section = None | |
for line in result.split('\n'): | |
if "### Missing Sections:" in line: | |
current_section = "Missing Sections" | |
sections[current_section] = [] | |
elif "### Improvement Tips:" in line: | |
current_section = "Improvement Tips" | |
sections[current_section] = [] | |
elif "### Detailed Feedback:" in line: | |
current_section = "Detailed Feedback" | |
sections[current_section] = [] | |
elif current_section and line.strip(): | |
sections[current_section].append(line) | |
# Display each section | |
if sections["Missing Sections"]: | |
st.markdown("### π Missing Sections") | |
missing_text = '\n'.join(sections["Missing Sections"]) | |
st.markdown(f'<div class="highlight">{missing_text}</div>', unsafe_allow_html=True) | |
if sections["Improvement Tips"]: | |
st.markdown("### π‘ Improvement Tips") | |
tips_text = '\n'.join(sections["Improvement Tips"]) | |
st.markdown(f'<div class="tip-box">{tips_text}</div>', unsafe_allow_html=True) | |
if sections["Detailed Feedback"]: | |
st.markdown("### π Detailed Feedback") | |
st.write('\n'.join(sections["Detailed Feedback"])) | |
# Show full AI response in expander | |
with st.expander("View Full AI Analysis"): | |
st.markdown(result) | |
# --- Question Answering Section --- | |
st.markdown("---") | |
st.markdown('<p class="subheader">β Ask About Your Report</p>', unsafe_allow_html=True) | |
col1, col2 = st.columns([3, 1]) | |
with col1: | |
user_question = st.text_input("Ask a question about your lab report", | |
placeholder="e.g., How can I improve my hypothesis?") | |
with col2: | |
st.markdown("<div style='height: 28px;'></div>", unsafe_allow_html=True) | |
ask_button = st.button("π Ask Question", use_container_width=True) | |
if (ask_button or user_question) and user_question.strip(): | |
with st.spinner("Thinking..."): | |
followup_prompt = f"""Lab Report: | |
{lab_text} | |
Question: {user_question} | |
Answer the question based on the lab report. If the question can't be answered from the report, | |
suggest what information the student should add to answer it. | |
""" | |
followup_response = query_ai(followup_prompt) | |
if followup_response: | |
st.markdown("### π¬ AI Response") | |
st.markdown(f'<div class="tip-box">{followup_response}</div>', unsafe_allow_html=True) | |
else: | |
# Show sample report if no file uploaded | |
st.markdown("---") | |
st.markdown('<p class="subheader">π Sample Lab Report</p>', unsafe_allow_html=True) | |
st.markdown(""" | |
**Title:** Effect of Temperature on Enzyme Activity | |
**Objective:** To investigate how temperature affects catalase enzyme activity | |
**Hypothesis:** Enzyme activity will increase with temperature up to 37Β°C, then decrease | |
**Materials:** Test tubes, hydrogen peroxide, liver extract, thermometer | |
**Procedure:** | |
1. Prepare test tubes at 5 different temperatures | |
2. Add equal amounts of hydrogen peroxide and liver extract | |
3. Measure oxygen production | |
**Observations:** More bubbles at 37Β°C compared to lower or higher temperatures | |
**Conclusion:** Enzyme activity peaks at body temperature | |
""") | |
st.info("π Upload your own lab report to get a personalized analysis!") |