├── README.md ├── requirements.txt ├── images ├── wallstreatlogo2.png ├── wallstreatlogo3.png └── wallstreatlogo1.jpeg ├── .devcontainer └── devcontainer.json └── app.py /README.md: -------------------------------------------------------------------------------- 1 | # languageAI 2 | an app build for Kat 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit 2 | langchain 3 | openai 4 | langchain-community 5 | 6 | -------------------------------------------------------------------------------- /images/wallstreatlogo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubentak/languageAI/main/images/wallstreatlogo2.png -------------------------------------------------------------------------------- /images/wallstreatlogo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubentak/languageAI/main/images/wallstreatlogo3.png -------------------------------------------------------------------------------- /images/wallstreatlogo1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rubentak/languageAI/main/images/wallstreatlogo1.jpeg -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Python 3", 3 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 4 | "image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye", 5 | "customizations": { 6 | "codespaces": { 7 | "openFiles": [ 8 | "README.md", 9 | "app.py" 10 | ] 11 | }, 12 | "vscode": { 13 | "settings": {}, 14 | "extensions": [ 15 | "ms-python.python", 16 | "ms-python.vscode-pylance" 17 | ] 18 | } 19 | }, 20 | "updateContentCommand": "[ -f packages.txt ] && sudo apt update && sudo apt upgrade -y && sudo xargs apt install -y 26 | .main { 27 | background-color: #EDF2F7; 28 | } 29 | .content-container { 30 | max-width: 800px; 31 | margin: 0 auto; 32 | padding: 20px; 33 | } 34 | .interaction-container { 35 | max-width: 600px; 36 | margin: 0 auto; 37 | padding: 20px; 38 | } 39 | .wallstreet-logo { 40 | width: 150px; 41 | margin-bottom: 20px; 42 | } 43 | .feedback-container { 44 | background-color: #ffffff; 45 | padding: 20px; 46 | border-radius: 10px; 47 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); 48 | } 49 | .feedback-header { 50 | 2D3748; 51 | } 52 | .highlight-incorrect { 53 | color: red; 54 | text-decoration: line-through; 55 | } 56 | .highlight-corrected { 57 | color: green; 58 | font-weight: bold; 59 | } 60 | .stButton>button { 61 | background-color: #003359; 62 | color: white; 63 | border: none; 64 | padding: 10px 20px; 65 | font-size: 16px; 66 | cursor: pointer; 67 | border-radius: 5px; 68 | } 69 | .stButton>button:hover { 70 | background-color: #c1161d; 71 | } 72 | 73 | """, 74 | unsafe_allow_html=True 75 | ) 76 | 77 | # Header with branding 78 | st.image("images/wallstreatlogo3.png", width=150) 79 | st.markdown( 80 | """ 81 |

Wall Street English - Language Learning App

82 | 83 | """, 84 | unsafe_allow_html=True 85 | ) 86 | 87 | # Main Content Container 88 | st.markdown("
", unsafe_allow_html=True) 89 | 90 | # Function to highlight marked incorrect words with red strikethrough 91 | def highlight_incorrect(original, incorrect_words): 92 | highlighted_text = original 93 | for word in incorrect_words: 94 | highlighted_text = re.sub(rf'\b{re.escape(word)}\b', f'{word}', highlighted_text) 95 | return highlighted_text 96 | 97 | # Function to make corrected words green 98 | def highlight_corrected(corrected): 99 | highlighted_corrected = re.sub(r"\{(.*?)\}", r"\1", corrected) 100 | return highlighted_corrected 101 | 102 | # Initialize chain only once 103 | @st.cache_resource 104 | def initialize_qa_chain(): 105 | llm = ChatOpenAI(model_name=MODEL, temperature=0, openai_api_key=OPENAI_API_KEY) 106 | prompt = PromptTemplate( 107 | input_variables=["user_answer"], 108 | template=""" 109 | You are a language tutor. Please analyze the following answer given by the user: 110 | 111 | User's Answer: {user_answer} 112 | 113 | 1. Identify all incorrect words or phrases in the user's answer by surrounding them with double curly braces {{like this}}. 114 | 2. Provide a corrected answer to improve grammar, vocabulary, and sentence structure, changing only the identified incorrect parts. Highlight corrected parts by surrounding them with double curly braces {{like this}}. 115 | 3. Write a short feedback with constructive advice on how the user could improve their response. 116 | 117 | Example: 118 | User's Answer: I goed to the market yesterday. 119 | Marked Incorrect Words: I {{goed}} to the market yesterday. 120 | Corrected Answer: I {{went}} to the market yesterday. 121 | Feedback: The word "goed" is incorrect. Use the correct past tense "went". 122 | 123 | Marked Incorrect Words: 124 | Corrected Answer: 125 | Feedback:""" 126 | ) 127 | return prompt | llm 128 | 129 | # Initialize session state variables 130 | if 'chain' not in st.session_state: 131 | st.session_state['chain'] = initialize_qa_chain() 132 | if 'exercise' not in st.session_state: 133 | st.session_state['exercise'] = random.choice(EXERCISES) 134 | if 'submitted' not in st.session_state: 135 | st.session_state['submitted'] = False 136 | if 'user_response' not in st.session_state: 137 | st.session_state['user_response'] = '' 138 | 139 | # Main UI 140 | st.markdown("### Practice and improve your language skills with personalized feedback.") 141 | 142 | # Interaction Container 143 | st.markdown("
", unsafe_allow_html=True) 144 | 145 | # Show the exercise if one is set 146 | st.write("#### Your Exercise:") 147 | st.write(st.session_state['exercise']) 148 | 149 | # Text area for user's response 150 | if not st.session_state['submitted']: 151 | user_answer = st.text_area("Your response:", key='user_response', height=100) 152 | 153 | # Conditional display of buttons based on whether the answer is submitted or not 154 | if not st.session_state['submitted']: 155 | # Show "Submit Answer" button 156 | if st.button("Submit Answer"): 157 | # Run the chain to get corrected answer and feedback 158 | with st.spinner("Analyzing your answer..."): 159 | try: 160 | # Call the agent with the user’s answer 161 | response = st.session_state['chain'].invoke({"user_answer": st.session_state['user_response']}) 162 | 163 | # Extract and process the response, with error handling 164 | try: 165 | answer_text = response.content 166 | 167 | # Parse the answer text to separate marked incorrect words, corrected answer, and feedback 168 | if "Marked Incorrect Words:" in answer_text and "Corrected Answer:" in answer_text and "Feedback:" in answer_text: 169 | marked_incorrect = answer_text.split("Marked Incorrect Words:")[1].split("Corrected Answer:")[0].strip() 170 | corrected_answer = answer_text.split("Corrected Answer:")[1].split("Feedback:")[0].strip() 171 | feedback = answer_text.split("Feedback:")[1].strip() 172 | else: 173 | marked_incorrect = "Marked incorrect words data missing." 174 | corrected_answer = "Correction data missing." 175 | feedback = "Feedback data missing." 176 | 177 | # Extract and format the marked incorrect words 178 | incorrect_words = re.findall(r"\{(.*?)\}", marked_incorrect) 179 | highlighted_original = highlight_incorrect(st.session_state['user_response'], incorrect_words) 180 | 181 | # Highlight corrections in the corrected answer 182 | highlighted_corrected = highlight_corrected(corrected_answer) 183 | 184 | # Corrected version for displaying highlighted text consistently 185 | st.markdown("### Feedback") 186 | 187 | # Highlight User Answer (with corrections) 188 | st.markdown("**Your Answer (with corrections):**", unsafe_allow_html=True) 189 | st.markdown(f"", 190 | unsafe_allow_html=True) # Wrap it in a div for safe HTML handling 191 | 192 | # Highlight Corrected Answer 193 | st.markdown("**Corrected Version:**", unsafe_allow_html=True) 194 | st.markdown(f"", 195 | unsafe_allow_html=True) # Corrected parts in green and bold 196 | 197 | # Display Feedback 198 | st.markdown(f"**Feedback:** {feedback}", unsafe_allow_html=True) 199 | 200 | # Mark as submitted so that the "Next Question" button appears 201 | st.session_state['submitted'] = True 202 | 203 | except Exception as parse_error: 204 | st.error(f"Error parsing the response: {parse_error}") 205 | except Exception as e: 206 | st.error(f"Error generating feedback: {e}") 207 | 208 | if st.session_state['submitted']: 209 | # Show "Next Question" button at the bottom after the answer is submitted 210 | if st.button("Next Question", key='next_button'): 211 | # Reset the state for the next question 212 | st.session_state['exercise'] = random.choice(EXERCISES) 213 | st.session_state['submitted'] = False 214 | st.session_state['user_response'] = '' 215 | 216 | # Close Interaction Container 217 | st.markdown("
", unsafe_allow_html=True) 218 | 219 | # Close Main Content Container 220 | st.markdown("
", unsafe_allow_html=True) 221 | --------------------------------------------------------------------------------