JoyStroy / app.py
Rathapoom's picture
Update app.py
c38ea88 verified
raw
history blame
17.1 kB
import streamlit as st
import json
import datetime
from openai import OpenAI
from typing import Dict, List, Set
# Set up Streamlit page configuration
st.set_page_config(
page_title="JoyStory - Interactive Story Adventure",
page_icon="📖",
layout="wide",
initial_sidebar_state="collapsed",
)
# Initialize OpenAI client
client = OpenAI()
# Custom CSS for Thai-English bilingual display
st.markdown("""
<style>
.thai-eng {
font-size: 1.1em;
padding: 10px;
background-color: #f8f9fa;
border-radius: 8px;
margin: 10px 0;
}
.thai {
color: #1e88e5;
font-family: 'Sarabun', sans-serif;
}
.eng {
color: #333;
}
.highlight {
background-color: #e3f2fd;
padding: 2px 5px;
border-radius: 4px;
}
</style>
""", unsafe_allow_html=True)
# Initialize session state variables
def init_session_state():
if 'story' not in st.session_state:
st.session_state.story = []
if 'feedback' not in st.session_state:
st.session_state.feedback = None
if 'level' not in st.session_state:
st.session_state.level = 'Beginner'
if 'unique_words' not in st.session_state:
st.session_state.unique_words = set()
if 'total_words' not in st.session_state:
st.session_state.total_words = 0
if 'badges' not in st.session_state:
st.session_state.badges = []
if 'should_reset' not in st.session_state:
st.session_state.should_reset = False
init_session_state()
def show_welcome_section():
st.markdown("""
<div class="thai-eng">
<div class="thai">
🌟 ยินดีต้อนรับสู่ JoyStory - แอพฝึกเขียนภาษาอังกฤษแสนสนุก!
<br>
เรียนรู้ภาษาอังกฤษผ่านการเขียนเรื่องราวด้วยตัวเอง พร้อมผู้ช่วย AI ที่จะช่วยแนะนำและให้กำลังใจ
</div>
<div class="eng">
Welcome to JoyStory - Fun English Writing Adventure!
</div>
</div>
""", unsafe_allow_html=True)
st.markdown("""
<div class="thai-eng">
<div class="thai">
📝 วิธีการใช้งาน:
<br>• เลือกระดับภาษาของน้องๆ ที่เมนูด้านข้าง
<br>• เริ่มเขียนเรื่องราวที่น้องๆ อยากเล่า
<br>• AI จะช่วยต่อเรื่องและให้คำแนะนำ
<br>• สะสมคะแนนและรางวัลจากการเขียน
</div>
</div>
""", unsafe_allow_html=True)
def generate_story_continuation(user_input: str, level: str) -> str:
"""Generate AI story continuation using ChatGPT."""
try:
story_context = '\n'.join([entry['content'] for entry in st.session_state.story])
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": f"""You are a creative storytelling assistant for {level} English learners.
Keep the language simple and engaging. Respond with only 1-2 short sentences to continue the story.
Avoid long descriptions and let the user contribute to the story development."""},
{"role": "user", "content": f"Story so far:\n{story_context}\nUser's addition:\n{user_input}\nContinue the story with 1-2 short sentences:"}
],
max_tokens=50,
temperature=0.7
)
return response.choices[0].message.content
except Exception as e:
st.error(f"Error generating story continuation: {str(e)}")
return "I'm having trouble continuing the story. Please try again."
def get_vocabulary_suggestions() -> List[str]:
"""Get contextual vocabulary suggestions with Thai translations."""
try:
recent_story = '\n'.join([entry['content'] for entry in st.session_state.story[-3:]] if st.session_state.story else "Story just started")
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": f"""You are a Thai-English bilingual teacher.
Suggest 5 English words with their Thai translations and examples.
Format each suggestion as:
word (type) - คำแปล | example sentence
Make sure words match {st.session_state.level} level."""},
{"role": "user", "content": f"Story context:\n{recent_story}\n\nSuggest 5 relevant words with Thai translations:"}
],
max_tokens=200,
temperature=0.8
)
return response.choices[0].message.content.split('\n')
except Exception as e:
st.error(f"Error getting vocabulary suggestions: {str(e)}")
return ["happy (adj) - มีความสุข | I am happy today",
"run (verb) - วิ่ง | The dog runs fast",
"tree (noun) - ต้นไม้ | A tall tree"]
# In the main UI section, update how vocabulary suggestions are displayed:
if st.button("Get Vocabulary Ideas"):
vocab_suggestions = get_vocabulary_suggestions()
st.markdown("#### 📚 Suggested Words")
for word in vocab_suggestions:
st.markdown(f"• *{word}*")
# And update how feedback is displayed to be more concise:
if st.session_state.feedback:
st.markdown("""
<div style='background-color: #f0f2f6; padding: 8px; border-radius: 4px; margin-bottom: 10px;'>
📝 <i>{}</i>
</div>
""".format(st.session_state.feedback), unsafe_allow_html=True)
# Update the creative prompt function
def get_creative_prompt() -> Dict[str, str]:
"""Generate a short, simple bilingual creative prompt."""
try:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": """Create very short story prompts in both English and Thai.
Keep it simple and under 6 words each.
Example formats:
- "What did the cat find?"
- "Where did they go next?"
- "How does the story end?"
"""},
{"role": "user", "content": "Generate a simple, short story prompt:"}
],
max_tokens=50,
temperature=0.7
)
prompt_eng = response.choices[0].message.content
# Get Thai translation
response_thai = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Translate to short Thai prompt, keep it simple and natural:"},
{"role": "user", "content": prompt_eng}
],
max_tokens=50,
temperature=0.7
)
prompt_thai = response_thai.choices[0].message.content
return {"eng": prompt_eng, "thai": prompt_thai}
except Exception as e:
st.error(f"Error generating creative prompt: {str(e)}")
return {
"eng": "What happens next?",
"thai": "แล้วอะไรจะเกิดขึ้นต่อ?"
}
def provide_feedback(text: str, level: str) -> str:
"""Provide educational feedback in Thai with English examples."""
try:
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": f"""You are a Thai English teacher helping {level} students.
Provide feedback in Thai language that:
1. Points out ONE main grammar or vocabulary point
2. Explains the correct usage in simple Thai
3. Shows example sentences
4. Gives encouragement
Format example:
"🎯 คำว่า 'go' เมื่อพูดถึงเหตุการณ์ในอดีต ต้องเปลี่ยนเป็น 'went' นะคะ
ตัวอย่าง:
- ถูก: Yesterday, I went to school.
- ผิด: Yesterday, I go to school.
เก่งมากๆ เลยค่ะ ที่พยายามเขียนเป็นภาษาอังกฤษ! ✨"
"""},
{"role": "user", "content": f"Review this sentence and provide feedback in Thai: {text}"}
],
max_tokens=200,
temperature=0.7
)
return response.choices[0].message.content
except Exception as e:
st.error(f"Error generating feedback: {str(e)}")
return "🌟 พยายามได้ดีมากค่ะ/ครับ ลองเขียนต่อไปนะ!"
# Update the feedback display in the UI
if st.session_state.feedback:
st.markdown("""
<div class="thai-eng">
<div class="thai">📝 คำแนะนำจากคุณครู</div>
<div class="eng">Teacher's Feedback</div>
</div>
""", unsafe_allow_html=True)
feedback_container = st.container()
with feedback_container:
st.markdown(f"""
<div style='background-color: #f0f2f6;
padding: 10px;
border-radius: 8px;
border-left: 4px solid #4CAF50;
margin: 5px 0;
font-family: "Sarabun", sans-serif;'>
{st.session_state.feedback}
</div>
""", unsafe_allow_html=True)
# Example feedback output will look like:
"""
🎯 คำว่า 'tree' เมื่อพูดถึงหลายต้น ต้องเติม s เป็น 'trees' นะคะ
ตัวอย่าง:
- ถูก: There are many trees in the forest.
- ผิด: There are many tree in the forest.
เขียนได้ดีมากเลยค่ะ ประโยคสื่อความหมายได้ชัดเจน! ✨
"""
# Example creative prompt will be simpler like:
"""
💭 ต่อไปตัวละครจะไปไหน?
Where will they go next?
"""
def update_achievements(text: str):
"""Update user achievements based on their writing."""
words = set(text.lower().split())
st.session_state.unique_words.update(words)
st.session_state.total_words += len(words)
achievements = {
'50 Words': len(st.session_state.unique_words) >= 50,
'100 Words': len(st.session_state.unique_words) >= 100,
'Story Master': len(st.session_state.story) >= 10,
}
for badge, condition in achievements.items():
if condition and badge not in st.session_state.badges:
st.session_state.badges.append(badge)
st.success(f"🏆 Achievement Unlocked: {badge}!")
def reset_story():
"""Reset the story and related state variables."""
st.session_state.story = []
st.session_state.feedback = None
st.session_state.unique_words = set()
st.session_state.total_words = 0
st.session_state.badges = []
st.session_state.should_reset = False
# Handle story reset if needed
if st.session_state.should_reset:
reset_story()
# Main UI Layout
st.markdown("# 📖 JoyStory")
show_welcome_section()
# Sidebar for settings
with st.sidebar:
st.markdown("""
<div class="thai">
🎯 เลือกระดับของน้องๆ
</div>
""", unsafe_allow_html=True)
level = st.radio(
"Select your English level:",
('Beginner', 'Intermediate', 'Advanced'),
index=['Beginner', 'Intermediate', 'Advanced'].index(st.session_state.level)
)
st.session_state.level = level
if st.button("Start New Story"):
st.session_state.should_reset = True
st.rerun()
# Main content area
col1, col2 = st.columns([3, 1])
with col1:
# Story Display Box
st.markdown("""
<div class="thai-eng">
<div class="thai">📖 เรื่องราวของคุณ</div>
<div class="eng">Your Story</div>
</div>
""", unsafe_allow_html=True)
story_display = st.container()
with story_display:
if not st.session_state.story:
st.info("เริ่มต้นผจญภัยด้วยการเขียนประโยคแรกกันเลย! | Start your adventure by writing the first sentence!")
else:
for entry in st.session_state.story:
if entry['role'] == 'You':
st.write("👤 You:", entry['content'])
elif entry['role'] == 'AI':
st.write("🤖 AI:", entry['content'])
# User Input Box
st.markdown("""
<div class="thai-eng">
<div class="thai">✏️ ถึงตาคุณแล้ว</div>
<div class="eng">Your Turn</div>
</div>
""", unsafe_allow_html=True)
user_input = st.text_area("เขียนต่อจากเรื่องราว | Continue the story:", height=100)
if st.button("ส่งคำตอบ | Submit"):
if user_input.strip():
st.session_state.story.append({"role": "You", "content": user_input})
update_achievements(user_input)
st.session_state.feedback = provide_feedback(user_input, st.session_state.level)
ai_response = generate_story_continuation(user_input, st.session_state.level)
st.session_state.story.append({"role": "AI", "content": ai_response})
st.rerun()
else:
st.warning("กรุณาเขียนเนื้อเรื่องก่อนกดส่ง | Please write something to continue the story.")
with col2:
# Feedback Display
if st.session_state.feedback:
st.markdown("""
<div class="thai-eng">
<div class="thai">📝 คำแนะนำ</div>
<div class="eng">Writing Feedback</div>
</div>
""", unsafe_allow_html=True)
st.markdown(f"*{st.session_state.feedback}*")
# Help and Suggestions Box
st.markdown("""
<div class="thai-eng">
<div class="thai">✨ เครื่องมือช่วยเขียน</div>
<div class="eng">Writing Tools</div>
</div>
""", unsafe_allow_html=True)
if st.button("ดูคำศัพท์แนะนำ | Get Vocabulary Ideas"):
vocab_suggestions = get_vocabulary_suggestions()
st.markdown("#### 📚 คำศัพท์น่ารู้ | Useful Words")
for word in vocab_suggestions:
st.markdown(f"• {word}")
if st.button("ขอคำใบ้ | Get Creative Prompt"):
prompt = get_creative_prompt()
st.markdown(f"""
<div class="thai-eng">
<div class="thai">💭 {prompt['thai']}</div>
<div class="eng">💭 {prompt['eng']}</div>
</div>
""", unsafe_allow_html=True)
# Achievements Box
st.markdown("""
<div class="thai-eng">
<div class="thai">🏆 ความสำเร็จ</div>
<div class="eng">Achievements</div>
</div>
""", unsafe_allow_html=True)
if st.session_state.badges:
for badge in st.session_state.badges:
st.success(f"🏆 {badge}")
else:
st.write("เขียนต่อไปเพื่อรับรางวัล | Keep writing to earn badges!")
# Save Story Button
if st.session_state.story:
if st.button("บันทึกเรื่องราว | Save Story"):
story_data = {
'level': st.session_state.level,
'date': datetime.datetime.now().isoformat(),
'story': st.session_state.story,
'achievements': st.session_state.badges,
'total_words': st.session_state.total_words,
'unique_words': list(st.session_state.unique_words)
}
st.download_button(
label="ดาวน์โหลดเรื่องราว | Download Story",
data=json.dumps(story_data, indent=2),
file_name='joystory.json',
mime='application/json'
)