Update app.py
Browse files
app.py
CHANGED
@@ -104,43 +104,105 @@ def get_ai_story_continuation(story_context, level):
|
|
104 |
response = openai.ChatCompletion.create(
|
105 |
model="gpt-4o-mini",
|
106 |
messages=[
|
107 |
-
{"role": "system", "content": f"""You are helping a {level} level English student write a story.
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
],
|
123 |
temperature=0.7
|
124 |
)
|
125 |
|
126 |
-
# Validate the response
|
127 |
result = json.loads(response.choices[0].message.content)
|
128 |
|
129 |
-
#
|
130 |
-
if
|
131 |
-
# Try to get a new response
|
132 |
return get_ai_story_continuation(story_context, level)
|
133 |
|
134 |
return result
|
135 |
|
136 |
except Exception as e:
|
137 |
st.error(f"Error with AI response: {e}")
|
138 |
-
return
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
|
145 |
def validate_story_quality(story_context, new_continuation):
|
146 |
"""Validate that the story maintains quality and coherence"""
|
@@ -254,26 +316,46 @@ def main():
|
|
254 |
with st.container():
|
255 |
st.markdown('<div class="box-container story-box">', unsafe_allow_html=True)
|
256 |
st.write("### 📖 Our Story So Far:")
|
|
|
257 |
for entry in st.session_state.story:
|
|
|
258 |
css_class = "ai-text" if entry['author'] == 'AI' else "player-text"
|
259 |
-
|
260 |
-
# แสดงข้อความหลัก
|
261 |
st.markdown(f'<div class="story-text {css_class}">{entry["text"]}</div>',
|
262 |
unsafe_allow_html=True)
|
263 |
|
264 |
-
# ถ้าเป็น AI response
|
265 |
if entry['author'] == 'AI':
|
|
|
266 |
if 'translation' in entry:
|
267 |
-
st.markdown(f'
|
268 |
-
|
|
|
|
|
|
|
|
|
|
|
269 |
if 'prompt' in entry:
|
270 |
-
st.markdown(f'
|
271 |
-
|
|
|
|
|
|
|
|
|
|
|
272 |
if 'vocabulary' in entry:
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
|
278 |
# Box 2: Writing Area
|
279 |
if st.session_state.current_turn == 'Player':
|
|
|
104 |
response = openai.ChatCompletion.create(
|
105 |
model="gpt-4o-mini",
|
106 |
messages=[
|
107 |
+
{"role": "system", "content": f"""You are a creative storyteller helping a {level} level English student write a story.
|
108 |
+
|
109 |
+
Rules:
|
110 |
+
1. READ the story context carefully
|
111 |
+
2. ADD meaningful story elements (new events, character development, dialogue)
|
112 |
+
3. AVOID generic transitions like 'suddenly' or 'then'
|
113 |
+
4. ENSURE story progression and character development
|
114 |
+
5. USE vocabulary appropriate for {level} level
|
115 |
+
|
116 |
+
Current story elements:
|
117 |
+
- Characters introduced: {extract_characters(story_context)}
|
118 |
+
- Current setting: {extract_setting(story_context)}
|
119 |
+
- Recent events: {extract_recent_events(story_context)}
|
120 |
+
|
121 |
+
Provide JSON response with:
|
122 |
+
1. continuation: Create an interesting next story event (1-2 sentences)
|
123 |
+
2. creative_prompt: Ask about specific details/choices for the next part
|
124 |
+
3. vocabulary_suggestions: 3 contextual words for the next part
|
125 |
+
4. translation: Thai translation of continuation
|
126 |
+
5. story_elements: New elements introduced (characters/places/objects)"""},
|
127 |
+
{"role": "user", "content": f"Story context: {story_context}\nContinue the story creatively and maintain narrative flow."}
|
128 |
],
|
129 |
temperature=0.7
|
130 |
)
|
131 |
|
|
|
132 |
result = json.loads(response.choices[0].message.content)
|
133 |
|
134 |
+
# Validate story continuation
|
135 |
+
if is_generic_response(result['continuation']):
|
|
|
136 |
return get_ai_story_continuation(story_context, level)
|
137 |
|
138 |
return result
|
139 |
|
140 |
except Exception as e:
|
141 |
st.error(f"Error with AI response: {e}")
|
142 |
+
return create_fallback_response(story_context)
|
143 |
+
|
144 |
+
def extract_characters(story_context):
|
145 |
+
"""Extract character information from story"""
|
146 |
+
try:
|
147 |
+
char_response = openai.ChatCompletion.create(
|
148 |
+
model="gpt-4o-mini",
|
149 |
+
messages=[
|
150 |
+
{"role": "system", "content": "List all characters mentioned in the story."},
|
151 |
+
{"role": "user", "content": story_context}
|
152 |
+
]
|
153 |
+
)
|
154 |
+
return char_response.choices[0].message.content
|
155 |
+
except:
|
156 |
+
return "Unknown characters"
|
157 |
+
|
158 |
+
def extract_setting(story_context):
|
159 |
+
"""Extract setting information from story"""
|
160 |
+
try:
|
161 |
+
setting_response = openai.ChatCompletion.create(
|
162 |
+
model="gpt-4o-mini",
|
163 |
+
messages=[
|
164 |
+
{"role": "system", "content": "Describe the current setting of the story."},
|
165 |
+
{"role": "user", "content": story_context}
|
166 |
+
]
|
167 |
+
)
|
168 |
+
return setting_response.choices[0].message.content
|
169 |
+
except:
|
170 |
+
return "Unknown setting"
|
171 |
+
|
172 |
+
def extract_recent_events(story_context):
|
173 |
+
"""Extract recent events from story"""
|
174 |
+
try:
|
175 |
+
events_response = openai.ChatCompletion.create(
|
176 |
+
model="gpt-4o-mini",
|
177 |
+
messages=[
|
178 |
+
{"role": "system", "content": "Summarize the most recent events in the story."},
|
179 |
+
{"role": "user", "content": story_context}
|
180 |
+
]
|
181 |
+
)
|
182 |
+
return events_response.choices[0].message.content
|
183 |
+
except:
|
184 |
+
return "Unknown events"
|
185 |
+
|
186 |
+
def is_generic_response(continuation):
|
187 |
+
"""Check if the response is too generic"""
|
188 |
+
generic_phrases = [
|
189 |
+
"suddenly", "then", "something happened",
|
190 |
+
"unexpectedly", "the story continues"
|
191 |
+
]
|
192 |
+
return any(phrase in continuation.lower() for phrase in generic_phrases)
|
193 |
+
|
194 |
+
def create_fallback_response(story_context):
|
195 |
+
"""Create contextual fallback response"""
|
196 |
+
# Extract last event or setting from story_context
|
197 |
+
last_event = story_context.split('.')[-2] if len(story_context.split('.')) > 1 else story_context
|
198 |
+
|
199 |
+
return {
|
200 |
+
"continuation": f"The characters decided to explore further into the story.",
|
201 |
+
"creative_prompt": "What interesting detail should we add to the scene?",
|
202 |
+
"vocabulary_suggestions": ["explore", "adventure", "discover"],
|
203 |
+
"translation": "ตัวละครตัดสินใจสำรวจเรื่องราวต่อไป",
|
204 |
+
"story_elements": {"new_elements": ["exploration"]}
|
205 |
+
}
|
206 |
|
207 |
def validate_story_quality(story_context, new_continuation):
|
208 |
"""Validate that the story maintains quality and coherence"""
|
|
|
316 |
with st.container():
|
317 |
st.markdown('<div class="box-container story-box">', unsafe_allow_html=True)
|
318 |
st.write("### 📖 Our Story So Far:")
|
319 |
+
|
320 |
for entry in st.session_state.story:
|
321 |
+
# แสดงเนื้อเรื่อง
|
322 |
css_class = "ai-text" if entry['author'] == 'AI' else "player-text"
|
|
|
|
|
323 |
st.markdown(f'<div class="story-text {css_class}">{entry["text"]}</div>',
|
324 |
unsafe_allow_html=True)
|
325 |
|
326 |
+
# ถ้าเป็น AI response
|
327 |
if entry['author'] == 'AI':
|
328 |
+
# แสดงคำแปล
|
329 |
if 'translation' in entry:
|
330 |
+
st.markdown(f'''
|
331 |
+
<div class="translation-box">
|
332 |
+
🇹🇭 {entry["translation"]}
|
333 |
+
</div>
|
334 |
+
''', unsafe_allow_html=True)
|
335 |
+
|
336 |
+
# แสดงคำถามกระตุ้นความคิด
|
337 |
if 'prompt' in entry:
|
338 |
+
st.markdown(f'''
|
339 |
+
<div class="prompt-box">
|
340 |
+
💭 {entry["prompt"]}
|
341 |
+
</div>
|
342 |
+
''', unsafe_allow_html=True)
|
343 |
+
|
344 |
+
# แสดงคำศัพท์แนะนำ
|
345 |
if 'vocabulary' in entry:
|
346 |
+
st.markdown(f'''
|
347 |
+
<div class="vocabulary-box">
|
348 |
+
📚 Try using: {', '.join(entry["vocabulary"])}
|
349 |
+
</div>
|
350 |
+
''', unsafe_allow_html=True)
|
351 |
+
|
352 |
+
# แสดงองค์ประกอบใหม่ในเรื่อง
|
353 |
+
if 'story_elements' in entry:
|
354 |
+
st.markdown(f'''
|
355 |
+
<div class="elements-box">
|
356 |
+
🎭 New elements: {', '.join(entry["story_elements"]["new_elements"])}
|
357 |
+
</div>
|
358 |
+
''', unsafe_allow_html=True)
|
359 |
|
360 |
# Box 2: Writing Area
|
361 |
if st.session_state.current_turn == 'Player':
|