shakespear / app.py
farquasar's picture
Update app.py
515f909 verified
import streamlit as st
import openai
from dotenv import load_dotenv
import os
import json
from openai import Client
load_dotenv()
# Set up the OpenAI API key
openai.api_key = os.getenv("OPENAI_API_KEY")
MODEL = 'gpt-3.5-turbo'
client = Client()
# Define theme and style suggestions
THEMES = [
"Ambition",
"Good vs. Evil",
"Lady Macbeth's Influence",
"Guilt and Regret",
"Regicide and Natural Order",
"The Supernatural"
]
STYLES = [
"Formal and Analytical",
"Casual and Conversational",
"Humorous and Engaging",
"Historical Context Focused"
]
# Common weaknesses to track and address
WEAKNESSES = {
"thesis_formation": "Struggles with forming clear, arguable thesis statements",
"evidence_selection": "Has difficulty selecting appropriate textual evidence",
"analysis_depth": "Needs help deepening analysis of quotations",
"structure": "Challenges with organizing essay structure",
"context": "Difficulty connecting to historical/social context",
"conventions": "Issues with grammar, citations, or academic conventions"
}
class StudentProfile:
def __init__(self):
self.weaknesses = {key: 0 for key in WEAKNESSES.keys()}
self.identified_weaknesses = []
self.strengths = []
def update_weaknesses(self, weakness_types):
for weakness in weakness_types:
self.weaknesses[weakness] += 1
self._evaluate_weaknesses()
def _evaluate_weaknesses(self):
self.identified_weaknesses = []
total_interactions = sum(self.weaknesses.values())
if total_interactions == 0:
return
for weakness, count in self.weaknesses.items():
if count / total_interactions > 0.3:
self.identified_weaknesses.append(weakness)
def add_strength(self, strength):
if strength not in self.strengths:
self.strengths.append(strength)
def get_personalized_prompt(self):
if not self.identified_weaknesses:
return "How would you like to develop your analysis further?"
weakness_descriptions = [WEAKNESSES[w] for w in self.identified_weaknesses]
if "thesis_formation" in self.identified_weaknesses:
return ("Let's work on crafting a stronger thesis. Can you state your main argument in one clear sentence? "
"Remember, a good thesis is specific, debatable, and sets up your entire essay.")
elif "evidence_selection" in self.identified_weaknesses:
return ("I notice you could use help selecting the best quotations. What moment in the play best illustrates "
"your point? Look for passages that are rich in language and thematic significance.")
elif "analysis_depth" in self.identified_weaknesses:
return ("Let's dig deeper into your analysis. For your chosen quotation, can you explain: "
"1) What the words literally mean, 2) How the language techniques work, "
"3) Why this matters to your overall argument?")
elif len(self.identified_weaknesses) > 1:
return (f"I notice we should focus on: {', '.join(weakness_descriptions)}. "
"Let's tackle these one at a time. Which area would you like to work on first?")
else:
return (f"Let's focus on improving your {weakness_descriptions[0]}. "
"Can you show me how you would approach this in your next point?")
class EssayTutor:
def __init__(self, selected_theme, selected_style):
self.conversation = []
self.selected_theme = selected_theme
self.selected_style = selected_style
self.student_profile = StudentProfile()
system_message = {
"role": "system",
"content": (
"You are a friendly, humorous, and intelligent personalized tutor for Shakespeare's 'Macbeth'. "
f"Today, the students (which are 14 to 17 years old) will focus on the theme of '{selected_theme}' using a '{selected_style}' style - they are not experts"
"Your goal is to help the student develop a well-argued essay by guiding them to craft a clear thesis, "
"integrate key quotations, and link ideas to historical context (including references to King James I). "
"Be engaging and offer continuous assessment of the student's progress. "
"Additionally, identify and address the student's specific weaknesses in essay writing by: "
"1) Noticing patterns in their responses, "
"2) Providing targeted exercises to improve those areas, "
"3) Adjusting your feedback to their needs"
)
}
self.conversation.append(system_message)
init_message = {
"role": "assistant",
"content": (
f"Welcome, future Shakespeare scholar! Today, we'll build your essay on *Macbeth* focusing on '{selected_theme}'. "
"First, I'd like to understand your current approach. Could you share:\n"
"1) Your initial thoughts about this theme in Macbeth\n"
"2) Any specific challenges you face when writing about Shakespeare\n"
"This will help me personalize our session to your needs."
)
}
self.conversation.append(init_message)
def get_response(self):
response = client.chat.completions.create(
model=MODEL,
messages=self.conversation
)
return response.choices[0].message.content
def add_message(self, role, message):
self.conversation.append({"role": role, "content": message})
def analyze_student_response(self, student_message):
analysis_prompt = {
"role": "user",
"content": (
f"Analyze this student response for essay writing strengths and weaknesses:\n\n"
f"'{student_message}'\n\n"
"Consider these potential weaknesses:\n"
f"{WEAKNESSES}\n\n"
"Return a JSON format analysis with:\n"
"- 'weaknesses': list of weakness keys present (from the list above)\n"
"- 'strengths': list of demonstrated strengths\n"
"- 'feedback': brief constructive feedback\n"
"Only return valid JSON, no additional text."
)
}
try:
analysis_convo = self.conversation.copy()
analysis_convo.append(analysis_prompt)
response = client.chat.completions.create(
model=MODEL,
messages=analysis_convo,
response_format={"type": "json_object"}
)
analysis = json.loads(response.choices[0].message.content)
return analysis
except Exception as e:
print(f"Analysis error: {e}")
return {"weaknesses": [], "strengths": [], "feedback": ""}
def assess_progress(self):
if self.student_profile.identified_weaknesses:
prompt = self.student_profile.get_personalized_prompt()
else:
prompt = (
"Let's assess the student progress. Can you explain how user inputs, questions and ideas connect to the theme? "
"Try to be specific about quotations or examples from *Macbeth* that support your argument."
"check for typos, style and creativity of the student's input so far."
)
self.add_message("assistant", prompt)
return self.get_response()
if "tutor" not in st.session_state:
st.session_state.tutor = None
if "chat_history" not in st.session_state:
st.session_state.chat_history = []
st.sidebar.title("Settings")
selected_theme = st.sidebar.selectbox("Select a Theme", THEMES)
selected_style = st.sidebar.selectbox("Select a Tutor Style", STYLES)
st.title("Macbeth Essay Tutor")
if st.sidebar.button("Start New Session"):
st.session_state.tutor = EssayTutor(selected_theme, selected_style)
st.session_state.chat_history = [{"role": "assistant", "content": st.session_state.tutor.conversation[1]["content"]}]
st.success("Session started! The tutor is ready to help you.")
if st.session_state.tutor is not None:
for chat in st.session_state.chat_history:
if chat["role"] == "assistant":
st.markdown(f"**Tutor:** {chat['content']}")
else:
st.markdown(f"**You:** {chat['content']}")
with st.form(key="input_form", clear_on_submit=True):
user_input = st.text_area("Your response:", key="user_input", height=150)
submitted = st.form_submit_button("Send")
if submitted and user_input:
analysis = st.session_state.tutor.analyze_student_response(user_input)
st.session_state.tutor.student_profile.update_weaknesses(analysis.get("weaknesses", []))
for strength in analysis.get("strengths", []):
st.session_state.tutor.student_profile.add_strength(strength)
st.session_state.tutor.add_message("user", user_input)
st.session_state.chat_history.append({"role": "user", "content": user_input})
tutor_reply = st.session_state.tutor.get_response()
st.session_state.tutor.add_message("assistant", tutor_reply)
st.session_state.chat_history.append({"role": "assistant", "content": tutor_reply})
#replace st.experimental_rerun() with this.
st.rerun()
if len(st.session_state.chat_history) > 2:
if st.button("Get Personalized Feedback"):
assessment_feedback = st.session_state.tutor.assess_progress()
st.session_state.chat_history.append({"role": "assistant", "content": assessment_feedback})
#replace st.experimental_rerun() with this.
st.rerun()
st.sidebar.subheader("Your Writing Profile")
if st.session_state.tutor.student_profile.identified_weaknesses:
st.sidebar.warning("Areas to Improve:")
for weakness in st.session_state.tutor.student_profile.identified_weaknesses:
st.sidebar.write(f"- {WEAKNESSES[weakness]}")
else:
st.sidebar.success("No major weaknesses identified yet - keep going!")
if st.session_state.tutor.student_profile.strengths:
st.sidebar.info("Your Strengths:")
for strength in st.session_state.tutor.student_profile.strengths:
st.sidebar.write(f"- {strength}")
else:
st.info("Please start a new session using the sidebar.")