File size: 10,607 Bytes
c255744
 
67804a9
c255744
515f909
c255744
 
515f909
c255744
 
 
 
b8b792b
c255744
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515f909
 
c255744
515f909
c255744
 
 
 
515f909
c255744
 
 
 
 
 
 
 
515f909
c255744
 
 
515f909
c255744
 
 
 
 
 
515f909
c255744
 
515f909
c255744
 
515f909
 
c255744
 
515f909
c255744
 
515f909
c255744
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515f909
c255744
 
 
 
 
 
 
 
 
515f909
c255744
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515f909
c255744
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515f909
c255744
 
 
515f909
c255744
 
 
 
 
 
 
 
 
 
515f909
 
c255744
515f909
c255744
 
 
515f909
 
c255744
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
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.")