Spaces:
Sleeping
Sleeping
| 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.") |