Ashgen12 commited on
Commit
284f495
ยท
verified ยท
1 Parent(s): bea379e
Files changed (1) hide show
  1. app.py +625 -0
app.py ADDED
@@ -0,0 +1,625 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import sqlite3
3
+ from datetime import datetime
4
+ from langchain.chat_models import ChatOpenAI
5
+ from langchain.schema import HumanMessage, AIMessage
6
+ from langchain.memory import ConversationBufferMemory
7
+ from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
8
+ from langchain.chains import LLMChain
9
+ import pandas as pd
10
+ import matplotlib.pyplot as plt
11
+ import os
12
+ from typing import List, Dict, Tuple, Optional
13
+
14
+ # Initialize SQLite database
15
+ def init_db():
16
+ # conn = sqlite3.connect('language_learning.db')
17
+ # c = conn.cursor()
18
+ # Create a thread-local storage for the connection
19
+ if not hasattr(init_db, "conn"):
20
+ init_db.conn = sqlite3.connect('language_learning.db', check_same_thread=False)
21
+ c = init_db.conn.cursor()
22
+ # Conversations table
23
+ c.execute('''
24
+ CREATE TABLE IF NOT EXISTS conversations (
25
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
26
+ learning_language TEXT,
27
+ known_language TEXT,
28
+ proficiency_level TEXT,
29
+ scenario TEXT,
30
+ start_time DATETIME DEFAULT CURRENT_TIMESTAMP,
31
+ end_time DATETIME
32
+ )
33
+ ''')
34
+
35
+ # Messages table
36
+ c.execute('''
37
+ CREATE TABLE IF NOT EXISTS messages (
38
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
39
+ conversation_id INTEGER,
40
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
41
+ sender TEXT,
42
+ message TEXT,
43
+ is_correction BOOLEAN DEFAULT 0,
44
+ corrected_text TEXT,
45
+ mistake_type TEXT,
46
+ FOREIGN KEY (conversation_id) REFERENCES conversations (id)
47
+ )
48
+ ''')
49
+
50
+ # Mistakes table
51
+ c.execute('''
52
+ CREATE TABLE IF NOT EXISTS mistakes (
53
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
54
+ conversation_id INTEGER,
55
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
56
+ original_text TEXT,
57
+ corrected_text TEXT,
58
+ mistake_type TEXT,
59
+ explanation TEXT,
60
+ FOREIGN KEY (conversation_id) REFERENCES conversations (id)
61
+ )
62
+ ''')
63
+
64
+ # Vocabulary table
65
+ c.execute('''
66
+ CREATE TABLE IF NOT EXISTS vocabulary (
67
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
68
+ conversation_id INTEGER,
69
+ word TEXT,
70
+ translation TEXT,
71
+ example_sentence TEXT,
72
+ added_date DATETIME DEFAULT CURRENT_TIMESTAMP,
73
+ FOREIGN KEY (conversation_id) REFERENCES conversations (id)
74
+ )
75
+ ''')
76
+
77
+ init_db.conn.commit()
78
+ # conn.commit()
79
+ # return conn
80
+ return init_db.conn
81
+
82
+ # Initialize database connection
83
+ conn = init_db()
84
+
85
+ class LanguageLearningChatbot:
86
+ def __init__(self):
87
+ self.current_conversation: Optional[int] = None
88
+ self.conversation_chain: Optional[LLMChain] = None
89
+ self.messages: List[Dict[str, str]] = [] # Changed to use dict format
90
+
91
+ # Define available languages including Indian languages
92
+ self.languages = [
93
+ "Hindi", "Bengali", "Tamil", "Telugu", "Marathi", "Gujarati", "Urdu", "Punjabi",
94
+ "Spanish", "French", "German", "Italian", "Japanese", "Chinese", "Russian", "Portuguese", "English"
95
+ ]
96
+
97
+ # Indian language scripts mapping (for display purposes)
98
+ self.scripts = {
99
+ "Hindi": "Devanagari",
100
+ "Bengali": "Bengali",
101
+ "Tamil": "Tamil",
102
+ "Telugu": "Telugu",
103
+ "Marathi": "Devanagari",
104
+ "Gujarati": "Gujarati",
105
+ "Urdu": "Perso-Arabic",
106
+ "Punjabi": "Gurmukhi"
107
+ }
108
+
109
+ self.proficiency_levels = [
110
+ "A1 Beginner", "A2 Elementary", "B1 Intermediate",
111
+ "B2 Upper Intermediate", "C1 Advanced", "C2 Proficient"
112
+ ]
113
+
114
+ # Scenarios with Indian context
115
+ self.scenarios = [
116
+ "At a restaurant", "Asking for directions", "Market shopping",
117
+ "Train station", "Doctor's visit", "Family gathering",
118
+ "Festival celebration", "Hotel check-in", "Job interview", "Custom"
119
+ ]
120
+
121
+ # Create Gradio interface
122
+ self.create_interface()
123
+
124
+ def create_interface(self):
125
+ with gr.Blocks(title="Language Learning Chatbot", theme=gr.themes.Soft()) as self.demo:
126
+ gr.Markdown("# ๐ŸŒ Language Learning Chatbot (with Indian Languages)")
127
+
128
+ with gr.Tab("New Conversation"):
129
+ with gr.Row():
130
+ with gr.Column():
131
+ self.learning_lang = gr.Dropdown(
132
+ label="Language you want to learn",
133
+ choices=self.languages,
134
+ value="Hindi"
135
+ )
136
+ self.proficiency = gr.Dropdown(
137
+ label="Your current proficiency level",
138
+ choices=self.proficiency_levels,
139
+ value="A1 Beginner"
140
+ )
141
+ self.script_info = gr.Markdown("")
142
+ with gr.Column():
143
+ self.known_lang = gr.Dropdown(
144
+ label="Language you know well",
145
+ choices=self.languages,
146
+ value="English"
147
+ )
148
+ self.scenario = gr.Dropdown(
149
+ label="Choose a practice scenario",
150
+ choices=self.scenarios,
151
+ value="Market shopping"
152
+ )
153
+ self.custom_scenario = gr.Textbox(
154
+ label="Custom scenario (if selected)",
155
+ visible=False
156
+ )
157
+
158
+ self.start_btn = gr.Button("Start Conversation", variant="primary")
159
+ self.status = gr.Markdown("Select options and start a new conversation.")
160
+
161
+ # Update script info when language changes
162
+ self.learning_lang.change(
163
+ self.update_script_info,
164
+ inputs=[self.learning_lang],
165
+ outputs=[self.script_info]
166
+ )
167
+
168
+ # Show/hide custom scenario
169
+ self.scenario.change(
170
+ lambda x: gr.update(visible=x == "Custom"),
171
+ inputs=[self.scenario],
172
+ outputs=[self.custom_scenario]
173
+ )
174
+
175
+ with gr.Tab("Chat"):
176
+ # Updated to use the new messages format
177
+ self.chat_display = gr.Chatbot(label="Conversation", type="messages")
178
+ self.user_input = gr.Textbox(label="Type your message...", placeholder="Type in the language you're learning")
179
+ self.send_btn = gr.Button("Send", variant="primary")
180
+ self.end_btn = gr.Button("End Conversation")
181
+ self.conversation_info = gr.Markdown("No active conversation.")
182
+
183
+ with gr.Tab("Analysis"):
184
+ with gr.Row():
185
+ with gr.Column():
186
+ self.mistakes_df = gr.Dataframe(
187
+ label="Mistakes",
188
+ headers=["What you said", "Correction", "Mistake Type", "Explanation"],
189
+ interactive=False
190
+ )
191
+ with gr.Column():
192
+ self.mistakes_plot = gr.Plot(label="Mistake Distribution")
193
+
194
+ with gr.Row():
195
+ with gr.Column():
196
+ self.vocab_df = gr.Dataframe(
197
+ label="New Vocabulary",
198
+ headers=["Word", "Translation", "Example"],
199
+ interactive=False
200
+ )
201
+ with gr.Column():
202
+ self.recommendations = gr.Markdown("## Areas to Focus On\nStart a conversation to get recommendations.")
203
+
204
+ with gr.Tab("History"):
205
+ self.conversation_history = gr.DataFrame(
206
+ label="Past Conversations",
207
+ headers=["ID", "Learning", "Known", "Level", "Scenario", "Date"],
208
+ interactive=False
209
+ )
210
+ self.load_history_btn = gr.Button("Refresh History")
211
+ self.delete_conversation_id = gr.Dropdown(
212
+ label="Select conversation to delete",
213
+ choices=[]
214
+ )
215
+ self.delete_btn = gr.Button("Delete Conversation", variant="stop")
216
+
217
+ # Event handlers
218
+ self.start_btn.click(
219
+ self.start_conversation,
220
+ inputs=[self.learning_lang, self.known_lang, self.proficiency, self.scenario, self.custom_scenario],
221
+ outputs=[self.status, self.conversation_info, self.chat_display]
222
+ )
223
+
224
+ self.send_btn.click(
225
+ self.send_message,
226
+ inputs=[self.user_input],
227
+ outputs=[self.user_input, self.chat_display, self.mistakes_df, self.vocab_df, self.mistakes_plot, self.recommendations]
228
+ )
229
+
230
+ self.user_input.submit(
231
+ self.send_message,
232
+ inputs=[self.user_input],
233
+ outputs=[self.user_input, self.chat_display, self.mistakes_df, self.vocab_df, self.mistakes_plot, self.recommendations]
234
+ )
235
+
236
+ self.end_btn.click(
237
+ self.end_conversation,
238
+ outputs=[self.conversation_info, self.chat_display]
239
+ )
240
+
241
+ self.load_history_btn.click(
242
+ self.load_history,
243
+ outputs=[self.conversation_history, self.delete_conversation_id]
244
+ )
245
+
246
+ self.delete_btn.click(
247
+ self.delete_conversation_handler,
248
+ inputs=[self.delete_conversation_id],
249
+ outputs=[self.conversation_history, self.delete_conversation_id]
250
+ )
251
+
252
+ # Initialize
253
+ self.load_history()
254
+ self.update_script_info(self.learning_lang.value)
255
+
256
+ # def update_script_info(self, language: str) -> Dict:
257
+ # """Update the script information display based on selected language"""
258
+ # if language in self.scripts:
259
+ # return gr.Markdown.update(value=f"**Script:** {self.scripts[language]}")
260
+ # return gr.Markdown.update(value="")
261
+
262
+ def update_script_info(self, language: str) -> Dict:
263
+ """Update the script information display based on selected language"""
264
+ if language in self.scripts:
265
+ return {"value": f"**Script:** {self.scripts[language]}", "__type__": "update"}
266
+ return {"value": "", "__type__": "update"}
267
+
268
+
269
+ def init_conversation(self, learning_lang: str, known_lang: str, proficiency: str, scenario: str) -> LLMChain:
270
+ """Initialize the LangChain conversation chain with Azure o3-mini model"""
271
+ # Set your OpenAI API key as environment variable
272
+
273
+ # openai_api_key = "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" # Replace with your actual API key
274
+ # llm = ChatOpenAI(
275
+ # model="Provider-5/gpt-4o",
276
+ # api_key="ddc-beta-v7bjela50v-lI9ep55oPFJz7N06MjSh2Asj2AVGaubLqIC",
277
+ # base_url="https://beta.sree.shop/v1",
278
+ # temperature=0.7,
279
+ # streaming=False
280
+ # )
281
+ API_KEY_ENV_VAR = "API_TOKEN"
282
+ api_key_value = os.getenv(API_KEY_ENV_VAR)
283
+
284
+ # DEEPSEEK_API_KEY = "" # Replace with your Deepseek Openrouter API key
285
+ LLama_API_BASE = "https://openrouter.ai/api/v1"
286
+
287
+ llm = ChatOpenAI(
288
+ model_name="meta-llama/llama-4-scout:free",
289
+ temperature=1,
290
+ api_key=api_key_value,
291
+ base_url=LLama_API_BASE,
292
+ streaming=False
293
+ )
294
+
295
+ # Enhanced prompt with Indian language considerations
296
+ prompt_template = ChatPromptTemplate.from_messages([
297
+ ("system", f"""
298
+ You are a friendly {learning_lang} language teacher. The student knows {known_lang} and their
299
+ proficiency level in {learning_lang} is {proficiency}. You are currently practicing a
300
+ scenario about: {scenario}.
301
+
302
+ Rules:
303
+ 1. Conduct the conversation primarily in {learning_lang}.
304
+ 2. For Indian languages, provide transliterations in Latin script for beginners.
305
+ 3. For beginner levels (A1-A2), use simple vocabulary and short sentences.
306
+ 4. For intermediate levels (B1-B2), use more complex structures but still keep it understandable.
307
+ 5. For advanced levels (C1-C2), speak naturally with complex structures.
308
+ 6. Correct mistakes gently by first repeating the corrected version, then briefly explaining.
309
+ 7. Keep track of mistakes in a structured way.
310
+ 8. Occasionally introduce relevant vocabulary with translations.
311
+ 9. For Indian contexts, use culturally appropriate examples.
312
+ 10. Be encouraging and positive.
313
+
314
+ Additional Guidelines for Indian Languages:
315
+ - For Hindi: Use Devanagari script but provide Roman transliteration when needed
316
+ - For South Indian languages: Pay attention to proper noun endings
317
+ - For Bengali: Note the different verb conjugations
318
+ - For Urdu: Include both Perso-Arabic script and Roman transliteration
319
+ """),
320
+ MessagesPlaceholder(variable_name="history"),
321
+ ("human", "{input}")
322
+ ])
323
+
324
+ memory = ConversationBufferMemory(return_messages=True)
325
+ return LLMChain(
326
+ llm=llm,
327
+ prompt=prompt_template,
328
+ memory=memory,
329
+ verbose=True
330
+ )
331
+
332
+ # def save_conversation(self, learning_lang: str, known_lang: str, proficiency: str, scenario: str) -> int:
333
+ # """Save new conversation to database"""
334
+ # c = conn.cursor()
335
+ # c.execute('''
336
+ # INSERT INTO conversations (learning_language, known_language, proficiency_level, scenario)
337
+ # VALUES (?, ?, ?, ?)
338
+ # ''', (learning_lang, known_lang, proficiency, scenario))
339
+ # conn.commit()
340
+ # return c.lastrowid
341
+
342
+ def save_conversation(self, learning_lang: str, known_lang: str, proficiency: str, scenario: str) -> int:
343
+ """Save new conversation to database"""
344
+ conn = init_db() # Get connection from thread-safe storage
345
+ c = conn.cursor()
346
+ c.execute('''
347
+ INSERT INTO conversations (learning_language, known_language, proficiency_level, scenario)
348
+ VALUES (?, ?, ?, ?)
349
+ ''', (learning_lang, known_lang, proficiency, scenario))
350
+ conn.commit()
351
+ return c.lastrowid
352
+
353
+ def start_conversation(self, learning_lang: str, known_lang: str, proficiency: str, scenario: str, custom_scenario: str) -> Tuple[Dict, Dict, List]:
354
+ """Start a new conversation"""
355
+ if scenario == "Custom" and custom_scenario:
356
+ scenario = custom_scenario
357
+
358
+ # Initialize conversation
359
+ self.current_conversation = self.save_conversation(learning_lang, known_lang, proficiency, scenario)
360
+ self.conversation_chain = self.init_conversation(learning_lang, known_lang, proficiency, scenario)
361
+ self.messages = []
362
+
363
+ # Add welcome message with script info if Indian language
364
+ welcome_msg = f"Let's practice {learning_lang}!"
365
+ if learning_lang in self.scripts:
366
+ welcome_msg += f" (Script: {self.scripts[learning_lang]})"
367
+ welcome_msg += f"\nWe'll simulate: {scenario}. I'll help correct your mistakes."
368
+
369
+ # Updated to use the new message format
370
+ self.messages.append({"role": "assistant", "content": welcome_msg})
371
+ self.save_message(self.current_conversation, "assistant", welcome_msg)
372
+
373
+ # Update UI
374
+ status = f"Started new conversation: Learning {learning_lang} (know {known_lang}, level {proficiency}), scenario: {scenario}"
375
+ info = f"""### Current Conversation
376
+ - **Learning**: {learning_lang} {f"({self.scripts.get(learning_lang, '')})" if learning_lang in self.scripts else ""}
377
+ - **From**: {known_lang}
378
+ - **Level**: {proficiency}
379
+ - **Scenario**: {scenario}"""
380
+
381
+ return status, info, self.messages
382
+
383
+ def send_message(self, user_input: str) -> Tuple[str, List, Optional[pd.DataFrame], Optional[pd.DataFrame], Optional[plt.Figure], str]:
384
+ """Process and respond to user message"""
385
+ if not self.current_conversation:
386
+ return "", self.messages, None, None, None, "No active conversation. Please start one first."
387
+
388
+ # Add user message (updated format)
389
+ self.messages.append({"role": "user", "content": user_input})
390
+ self.save_message(self.current_conversation, "user", user_input)
391
+
392
+ # Get AI response
393
+ response = self.conversation_chain.run(input=user_input)
394
+
395
+ # Process response for mistakes and vocabulary
396
+ if "Correction:" in response:
397
+ parts = response.split("Correction:")
398
+ main_response = parts[0]
399
+ correction_part = parts[1]
400
+
401
+ if "Explanation:" in correction_part:
402
+ correction, explanation = correction_part.split("Explanation:")
403
+ original_text = user_input
404
+ corrected_text = correction.strip()
405
+ explanation = explanation.strip()
406
+
407
+ # Determine mistake type
408
+ mistake_type = "grammar"
409
+ if "vocabulary" in explanation.lower():
410
+ mistake_type = "vocabulary"
411
+ elif "pronunciation" in explanation.lower():
412
+ mistake_type = "pronunciation"
413
+ elif "word order" in explanation.lower():
414
+ mistake_type = "word order"
415
+ elif "script" in explanation.lower():
416
+ mistake_type = "script"
417
+
418
+ # Save mistake
419
+ self.save_mistake(
420
+ self.current_conversation,
421
+ original_text,
422
+ corrected_text,
423
+ mistake_type,
424
+ explanation
425
+ )
426
+
427
+ # Save correction message
428
+ self.save_message(
429
+ self.current_conversation,
430
+ "assistant",
431
+ response,
432
+ True,
433
+ corrected_text,
434
+ mistake_type
435
+ )
436
+ else:
437
+ self.save_message(
438
+ self.current_conversation,
439
+ "assistant",
440
+ response
441
+ )
442
+
443
+ # Check for vocabulary introduction
444
+ if "Vocabulary:" in response:
445
+ vocab_part = response.split("Vocabulary:")[1].split("\n")[0]
446
+ if "-" in vocab_part:
447
+ word, translation = vocab_part.split("-", 1)
448
+ example = response.split("Example:")[1].split("\n")[0] if "Example:" in response else ""
449
+ self.save_vocabulary(
450
+ self.current_conversation,
451
+ word.strip(),
452
+ translation.strip(),
453
+ example.strip()
454
+ )
455
+
456
+ # Add AI response to chat (updated format)
457
+ self.messages.append({"role": "assistant", "content": response})
458
+
459
+ # Get updated analysis data
460
+ mistakes_df, vocab_df, plot, recommendations = self.get_analysis_data()
461
+
462
+ return "", self.messages, mistakes_df, vocab_df, plot, recommendations
463
+
464
+ def save_message(self, conversation_id: int, sender: str, message: str,
465
+ is_correction: bool = False, corrected_text: Optional[str] = None,
466
+ mistake_type: Optional[str] = None) -> None:
467
+ """Save message to database"""
468
+ c = conn.cursor()
469
+ c.execute('''
470
+ INSERT INTO messages (conversation_id, sender, message, is_correction, corrected_text, mistake_type)
471
+ VALUES (?, ?, ?, ?, ?, ?)
472
+ ''', (conversation_id, sender, message, is_correction, corrected_text, mistake_type))
473
+ conn.commit()
474
+
475
+ def save_mistake(self, conversation_id: int, original_text: str, corrected_text: str,
476
+ mistake_type: str, explanation: str) -> None:
477
+ """Save mistake to database"""
478
+ c = conn.cursor()
479
+ c.execute('''
480
+ INSERT INTO mistakes (conversation_id, original_text, corrected_text, mistake_type, explanation)
481
+ VALUES (?, ?, ?, ?, ?)
482
+ ''', (conversation_id, original_text, corrected_text, mistake_type, explanation))
483
+ conn.commit()
484
+
485
+ def save_vocabulary(self, conversation_id: int, word: str, translation: str,
486
+ example_sentence: str) -> None:
487
+ """Save vocabulary to database"""
488
+ c = conn.cursor()
489
+ c.execute('''
490
+ INSERT INTO vocabulary (conversation_id, word, translation, example_sentence)
491
+ VALUES (?, ?, ?, ?)
492
+ ''', (conversation_id, word, translation, example_sentence))
493
+ conn.commit()
494
+
495
+ def get_analysis_data(self) -> Tuple[Optional[pd.DataFrame], Optional[pd.DataFrame], Optional[plt.Figure], str]:
496
+ """Get analysis data for current conversation"""
497
+ if not self.current_conversation:
498
+ return None, None, None, "No active conversation"
499
+
500
+ # Get mistakes
501
+ mistakes = self.get_mistakes(self.current_conversation)
502
+ if mistakes:
503
+ mistakes_df = pd.DataFrame(mistakes, columns=["What you said", "Correction", "Mistake Type", "Explanation"])
504
+
505
+ # Create plot
506
+ mistake_counts = mistakes_df['Mistake Type'].value_counts()
507
+ fig, ax = plt.subplots()
508
+ ax.pie(mistake_counts, labels=mistake_counts.index, autopct='%1.1f%%')
509
+ ax.set_title("Mistake Type Distribution")
510
+
511
+ # Create recommendations
512
+ recommendations = "## Areas to Focus On\n"
513
+ if "grammar" in mistake_counts:
514
+ recommendations += "- ๐Ÿ“ **Grammar**: Practice verb conjugations and sentence structure.\n"
515
+ if "vocabulary" in mistake_counts:
516
+ recommendations += "- ๐Ÿ“– **Vocabulary**: Review flashcards and try to use new words in sentences.\n"
517
+ if "pronunciation" in mistake_counts:
518
+ recommendations += "- ๐ŸŽค **Pronunciation**: Listen to native speakers and repeat after them.\n"
519
+ if "word order" in mistake_counts:
520
+ recommendations += "- ๐Ÿ”  **Word Order**: Practice constructing sentences with different structures.\n"
521
+ if "script" in mistake_counts:
522
+ recommendations += "- โœ๏ธ **Script**: Practice writing characters/letters of the alphabet.\n"
523
+ else:
524
+ mistakes_df = pd.DataFrame(columns=["What you said", "Correction", "Mistake Type", "Explanation"])
525
+ fig = plt.figure()
526
+ plt.text(0.5, 0.5, "No mistakes yet!", ha='center', va='center')
527
+ recommendations = "## Areas to Focus On\nNo mistakes recorded yet. Keep practicing!"
528
+
529
+ # Get vocabulary
530
+ vocab = self.get_vocabulary(self.current_conversation)
531
+ if vocab:
532
+ vocab_df = pd.DataFrame(vocab, columns=["Word", "Translation", "Example"])
533
+ else:
534
+ vocab_df = pd.DataFrame(columns=["Word", "Translation", "Example"])
535
+
536
+ return mistakes_df, vocab_df, fig, recommendations
537
+
538
+ def get_conversations(self) -> List[Tuple]:
539
+ """Get all conversations from database"""
540
+ c = conn.cursor()
541
+ return c.execute('''
542
+ SELECT id, learning_language, known_language, proficiency_level, scenario, start_time
543
+ FROM conversations
544
+ ORDER BY start_time DESC
545
+ ''').fetchall()
546
+
547
+ def get_mistakes(self, conversation_id: int) -> List[Tuple]:
548
+ """Get mistakes for a conversation"""
549
+ c = conn.cursor()
550
+ return c.execute('''
551
+ SELECT original_text, corrected_text, mistake_type, explanation
552
+ FROM mistakes
553
+ WHERE conversation_id = ?
554
+ ORDER BY timestamp
555
+ ''', (conversation_id,)).fetchall()
556
+
557
+ def get_vocabulary(self, conversation_id: int) -> List[Tuple]:
558
+ """Get vocabulary for a conversation"""
559
+ c = conn.cursor()
560
+ return c.execute('''
561
+ SELECT word, translation, example_sentence
562
+ FROM vocabulary
563
+ WHERE conversation_id = ?
564
+ ORDER BY added_date
565
+ ''', (conversation_id,)).fetchall()
566
+
567
+ def end_conversation(self) -> Tuple[Dict, List]:
568
+ """End current conversation"""
569
+ if self.current_conversation:
570
+ # Update end time in database
571
+ c = conn.cursor()
572
+ c.execute('''
573
+ UPDATE conversations
574
+ SET end_time = CURRENT_TIMESTAMP
575
+ WHERE id = ?
576
+ ''', (self.current_conversation,))
577
+ conn.commit()
578
+
579
+ # Reset state
580
+ self.current_conversation = None
581
+ self.conversation_chain = None
582
+ self.messages = []
583
+
584
+ return "Conversation ended. Start a new one to continue learning.", []
585
+ return "No active conversation to end.", []
586
+
587
+ def load_history(self) -> Tuple[List[Tuple], Dict]:
588
+ """Load conversation history"""
589
+ conversations = self.get_conversations()
590
+ if conversations:
591
+ # Format for display
592
+ display_data = [
593
+ (conv[0], conv[1], conv[2], conv[3], conv[4], conv[5].split()[0])
594
+ for conv in conversations
595
+ ]
596
+
597
+ # Update delete dropdown
598
+ delete_options = [str(conv[0]) for conv in conversations]
599
+
600
+ return display_data, {"choices": delete_options, "__type__": "update"}
601
+ return [], {"choices": [], "__type__": "update"}
602
+
603
+ def delete_conversation_handler(self, conversation_id: str) -> Tuple[List[Tuple], Dict]:
604
+ """Handle conversation deletion"""
605
+ if conversation_id:
606
+ self.delete_conversation(int(conversation_id))
607
+ return self.load_history()
608
+ return self.load_history()
609
+
610
+ def delete_conversation(self, conversation_id: int) -> None:
611
+ """Delete a conversation from database"""
612
+ c = conn.cursor()
613
+ c.execute('DELETE FROM messages WHERE conversation_id = ?', (conversation_id,))
614
+ c.execute('DELETE FROM mistakes WHERE conversation_id = ?', (conversation_id,))
615
+ c.execute('DELETE FROM vocabulary WHERE conversation_id = ?', (conversation_id,))
616
+ c.execute('DELETE FROM conversations WHERE id = ?', (conversation_id,))
617
+ conn.commit()
618
+
619
+ # Run the application
620
+ if __name__ == "__main__":
621
+ # Set your GitHub Token as environment variable
622
+ # export GITHUB_TOKEN='your-token-here'
623
+
624
+ chatbot = LanguageLearningChatbot()
625
+ chatbot.demo.launch()