GuestUser33 commited on
Commit
e560942
·
verified ·
1 Parent(s): 341dfb9

Added documentation testing

Browse files
Files changed (1) hide show
  1. app.py +853 -44
app.py CHANGED
@@ -32,6 +32,7 @@ class LearningSession:
32
  end_time: Optional[datetime] = None
33
  words_learned: int = 0
34
  idioms_learned: int = 0
 
35
  grammar_learned: int = 0
36
  questions_asked: int = 0
37
 
@@ -64,6 +65,7 @@ class PersonalizedLearningTracker:
64
  end_time TEXT,
65
  words_learned INTEGER DEFAULT 0,
66
  idioms_learned INTEGER DEFAULT 0,
 
67
  grammar_learned INTEGER DEFAULT 0,
68
  questions_asked INTEGER DEFAULT 0
69
  )
@@ -233,6 +235,25 @@ class PersonalizedLearningTracker:
233
  WHERE user_id = ? AND word = ? AND category = ?
234
  ''', (mastery_level, user_id, normalized_word, category))
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  conn.commit()
237
  conn.close()
238
 
@@ -374,6 +395,9 @@ class PersonalizedLearningTracker:
374
  if 'idiom' not in progress['category_stats'] or progress['category_stats'].get('idiom', {}).get('count', 0) < 5:
375
  recommendations.append("Explore Kazakh idioms to improve your cultural understanding!")
376
 
 
 
 
377
  words_to_review = self.get_words_to_review(user_id, 5)
378
  if words_to_review:
379
  recommendations.append(f"Review these words: {', '.join([w['word'] for w in words_to_review[:3]])}")
@@ -456,7 +480,7 @@ class PersonalizedKazakhAssistant:
456
 
457
  if term:
458
  self.known_terms.add(term)
459
- if definition == "тыныш отыру":
460
  print(f"Loaded 'тыныш отыру' idiom: '{term}' from {doc_type} folder")
461
 
462
  print(f"Loaded {len(self.known_terms)} known terms: {list(self.known_terms)[:10]}")
@@ -476,25 +500,25 @@ class PersonalizedKazakhAssistant:
476
 
477
  def setup_llm(self, target_language: str = "English"):
478
  self.system_prompt = f"""
479
- You are a personalized Kazakh language learning assistant with access to a comprehensive knowledge base and user learning history. Your role is to help users learn Kazakh words and idioms while tracking their progress and providing personalized recommendations. Respond in {target_language}.
480
 
481
  Key capabilities:
482
- 1. *Answer Queries*: Provide accurate definitions and examples for Kazakh words and idioms.
483
- 2. *Track Learning Progress*: Identify and track when users learn new words or idioms.
484
  3. *Personalized Responses*: Adapt responses based on user's learning history.
485
  4. *Progress Reporting*: Provide detailed progress reports when asked.
486
- 5. *Learning Recommendations*: Suggest words/idioms to review or learn next.
487
 
488
  Response Guidelines:
489
- - For word/idiom queries: Provide definition, usage examples, and related information in {target_language}.
490
- - When explaining a Kazakh word or idiom retrieved from the knowledge base, **bold** the term (e.g., **күләпара**) in the response to highlight it.
491
- - Only bold the main term or idiom being explained, not other Kazakh words.
492
- - Always identify the main Kazakh word/idiom for progress tracking.
493
  - *RAG Usage*:
494
- - Use Retrieval-Augmented Generation (RAG) only when the query explicitly asks for explanations of specific Kazakh terms or idioms (e.g., "What does сәлем mean?") or when the context strongly suggests a need for knowledge base information (e.g., queries about specific words or idioms).
495
- - When using RAG, limit the response to explaining 1-2 distinct terms at most, unless the user explicitly asks for multiple terms (e.g., "List several idioms"). For each term, provide 3-4 relevant examples. Do not list all or many terms or matches from the knowledge base.
496
  - For general queries (e.g., greetings, procedural questions, or commands like /progress) or grammar-related queries (e.g., "explain me nouns"), rely on your general knowledge and do not use RAG unless the knowledge base contains relevant information.
497
- - Since the knowledge base contains only words and idioms, grammar explanations (e.g., about nouns, verbs) should be provided using your own knowledge, without relying on RAG, unless the query specifically involves terms in the knowledge base.
498
  - Be encouraging and supportive.
499
  - Use simple, clear explanations.
500
  - When discussing progress, be specific and motivating.
@@ -512,14 +536,14 @@ class PersonalizedKazakhAssistant:
512
  )
513
 
514
  def normalize_term(self, term: str) -> str:
515
- return ' '.join(term.lower().strip().split())
516
 
517
  def extract_kazakh_terms(self, message: str, response: str) -> List[Tuple[str, str, str]]:
518
  terms = []
519
  seen_terms = set()
520
 
521
  try:
522
- retrieved_docs = self.vectorstore.similarity_search(message, k=5)
523
  bold_pattern = r'(?:\*\*|__)([А-Яа-яӘәҒғҚқҢңӨөҰұҮүҺһІі\s,-]+)(?:\*\*|__)(?=\s|$|[:.,!?)])'
524
  bold_matches = re.findall(bold_pattern, response, re.UNICODE)
525
 
@@ -543,30 +567,40 @@ class PersonalizedKazakhAssistant:
543
  for doc in retrieved_docs:
544
  doc_type = doc.metadata.get('doc_type', '').lower()
545
  if normalized_term in self.normalize_term(doc.page_content):
546
- if 'idioms' in doc_type or 'тіркес' in doc_type:
 
 
547
  category = "idiom"
548
- elif 'words' in doc_type:
549
  category = "word"
550
- elif 'grammar' in doc_type:
551
- category = "grammar"
552
  definition = self.extract_clean_definition(normalized_term, doc.page_content, response)
553
  break
554
  if not definition:
555
  if len(known_term.split()) > 1:
556
- category = "idiom"
557
  definition = self.extract_clean_definition(normalized_term, "", response)
558
  break
559
 
560
  if not term_matched and len(term.split()) > 1:
561
- category = "idiom"
562
- definition = self.extract_clean_definition(normalized_term, "", response)
563
- term_matched = True
564
- elif not term_matched:
 
 
 
 
 
 
 
 
 
 
565
  category = "word"
566
  definition = self.extract_clean_definition(normalized_term, "", response)
567
  term_matched = True
568
 
569
- if term_matched and definition:
570
  terms.append((original_term, category, definition))
571
  seen_terms.add(normalized_term)
572
  print(f"Added bolded term: {original_term}, category: {category}, definition: {definition}")
@@ -582,7 +616,7 @@ class PersonalizedKazakhAssistant:
582
  def extract_clean_definition(self, term: str, doc_content: str, response: str) -> str:
583
  normalized_term = self.normalize_term(term)
584
 
585
- retrieved_docs = self.vectorstore.similarity_search(term, k=5)
586
  for doc in retrieved_docs:
587
  lines = doc.page_content.replace('\r\n', '\n').replace('\r', '\n').split('\n')
588
  for line in lines:
@@ -592,6 +626,16 @@ class PersonalizedKazakhAssistant:
592
  if self.normalize_term(doc_term) == normalized_term:
593
  return doc_definition
594
 
 
 
 
 
 
 
 
 
 
 
595
  return f"Definition for {term}"
596
 
597
  def get_user_memory(self, user_id: str):
@@ -653,7 +697,7 @@ class PersonalizedKazakhAssistant:
653
  elif message.lower().startswith('/help'):
654
  return self.get_help_message()
655
 
656
- retrieved_docs = self.vectorstore.similarity_search(message, k=5)
657
  context = "\n".join([doc.page_content for doc in retrieved_docs])
658
 
659
  memory = self.get_user_memory(user_id)
@@ -703,14 +747,14 @@ class PersonalizedKazakhAssistant:
703
 
704
  User question: {message}
705
 
706
- Respond in {target_language}. If explaining a Kazakh word or idiom retrieved from the context, **bold** the term (e.g., **күләпара**) in your response to highlight it using double asterisks (**). Only bold the main term being explained.
707
  """
708
 
709
  try:
710
  if not hasattr(self.llm, 'generate_content'):
711
  raise AttributeError("LLM does not have generate_content method")
712
  response = self.llm.generate_content(full_prompt).text
713
- # print(f"Full LLM response:\n{response}\n{'-'*50}")
714
  except Exception as e:
715
  print(f"Error generating LLM response: {e}")
716
  return f"Error generating response: {str(e)}. Please try again."
@@ -729,10 +773,10 @@ class PersonalizedKazakhAssistant:
729
  for term, category, definition in unique_terms.values():
730
  self.tracker.track_word_encounter(user_id, term, definition, category)
731
 
732
- # if unique_terms:
733
- # response += "\n\n📚 **Tracked Bolded Terms**:\n"
734
- # for term, category, definition in sorted(unique_terms.values()):
735
- # response += f"- **{term}** ({category}): {definition}\n"
736
 
737
  return response
738
 
@@ -740,15 +784,15 @@ class PersonalizedKazakhAssistant:
740
  progress = self.tracker.get_user_progress(user_id)
741
 
742
  if progress['total_words'] == 0:
743
- return "Сіз әлі үйренуді бастамадыңыз! Маған кез келген қазақ сөзі немесе тіркес туралы сұраңыз. 🌟\n\nYou haven't started learning yet! Ask me about any Kazakh word or idiom to begin your journey. 🌟"
744
 
745
  report = "📊 **Сіздің үйрену прогресіңіз / Your Learning Progress Report**\n\n"
746
 
747
  report += f"🎯 **Үйренген терминдер саны / Total Terms Learned**: {progress['total_words']}\n"
748
 
749
  for category, stats in progress['category_stats'].items():
750
- emoji = "📝" if category == "word" else "🎭"
751
- category_name = "Сөздер / Words" if category == "word" else "Тіркестер / Idioms"
752
  report += f"{emoji} **{category_name}**: {stats['count']} (Орташа меңгеру / Average mastery: {stats['average_mastery']}/5)\n"
753
 
754
  report += f"\n⚡ **Соңғы белсенділік / Recent Activity**: {progress['recent_activity']} терминдер соңғы 7 күнде қаралды / terms reviewed in the last 7 days\n"
@@ -785,7 +829,7 @@ class PersonalizedKazakhAssistant:
785
 
786
  response = "📚 **Қайталауға арналған сөздер / Words to Review**:\n\n"
787
  for word_info in words_to_review:
788
- emoji = "📝" if word_info['category'] == "word" else "🎭"
789
  mastery_stars = "⭐" * min(word_info['encounter_count'], 5) + "☆" * (5 - min(word_info['encounter_count'], 5))
790
  response += f"{emoji} **{word_info['word']}** - {mastery_stars} (Кездесу саны / Encounters: {word_info['encounter_count']})\n"
791
 
@@ -793,7 +837,7 @@ class PersonalizedKazakhAssistant:
793
  response += f" {definition_preview}\n\n"
794
 
795
  return response
796
-
797
  def get_mastered_words(self, user_id: str, page: int = 1, page_size: int = 10) -> str:
798
  mastered_words = self.tracker.get_mastered_words(user_id, page, page_size)
799
 
@@ -802,7 +846,7 @@ class PersonalizedKazakhAssistant:
802
 
803
  response = f"🏆 **Меңгерілген сөздер / Mastered Words** (Бет / Page: {page}):\n\n"
804
  for word_info in mastered_words:
805
- emoji = "📝" if word_info['category'] == "word" else "🎭"
806
  mastery_stars = "🟊" * int(word_info['mastery_level'] * 2) + "⬜" * (10 - int(word_info['mastery_level'] * 2))
807
  response += f"{emoji} **{word_info['word']}** - {mastery_stars} (Кездесу саны / Encounters: {word_info['encounter_count']})\n"
808
 
@@ -810,17 +854,16 @@ class PersonalizedKazakhAssistant:
810
  response += f" {definition_preview}\n\n"
811
 
812
  return response
813
-
814
  def get_learning_words(self, user_id: str, page: int = 1, page_size: int = 10) -> str:
815
  learning_words = self.tracker.get_learning_words(user_id, page, page_size)
816
 
817
  if not learning_words:
818
- return "Сізде қазір үйрену кезеңінде сөздер жоқ. Жаңа сөздерді немесе тіркестерді сұраңыз! 🌟\n\nYou don't have any words in the learning phase right now. Ask about new words or idioms! 🌟"
819
 
820
  response = f"📖 **Үйрену кезеңіндегі сөздер / Words in Learning** (Бет / Page: {page}):\n\n"
821
  for word_info in learning_words:
822
- emoji = "📝" if word_info['category'] == "word" else "🎭"
823
-
824
  mastery_stars = "⭐" * min(word_info['encounter_count'], 5) + "☆" * (5 - min(word_info['encounter_count'], 5))
825
  response += f"{emoji} **{word_info['word']}** - {mastery_stars} (Кездесу саны / Encounters: {word_info['encounter_count']})\n"
826
 
@@ -843,7 +886,7 @@ class PersonalizedKazakhAssistant:
843
  for term in sorted(self.known_terms):
844
  normalized_term = self.normalize_term(term)
845
  if normalized_term not in shown_words and len(term.split()) == 1:
846
- retrieved_docs = self.vectorstore.similarity_search(term, k=5)
847
  for doc in retrieved_docs:
848
  lines = doc.page_content.replace('\r\n', '\n').replace('\r', '\n').split('\n')
849
  for line in lines:
@@ -873,7 +916,7 @@ class PersonalizedKazakhAssistant:
873
  for term in sorted(self.known_terms):
874
  normalized_term = self.normalize_term(term)
875
  if normalized_term not in shown_idioms and len(term.split()) > 1:
876
- retrieved_docs = self.vectorstore.similarity_search(term, k=5)
877
  for doc in retrieved_docs:
878
  lines = doc.page_content.replace('\r\n', '\n').replace('\r', '\n').split('\n')
879
  for line in lines:
@@ -1158,6 +1201,772 @@ with gr.Blocks(title="🇰🇿 Kazakh Learning API") as demo:
1158
  ["Teach me Kazakh verb conjugation in English", "English"]
1159
  ]
1160
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1161
 
1162
  if __name__ == "__main__":
1163
  demo.launch(
 
32
  end_time: Optional[datetime] = None
33
  words_learned: int = 0
34
  idioms_learned: int = 0
35
+ proverbs_learned: int = 0
36
  grammar_learned: int = 0
37
  questions_asked: int = 0
38
 
 
65
  end_time TEXT,
66
  words_learned INTEGER DEFAULT 0,
67
  idioms_learned INTEGER DEFAULT 0,
68
+ proverbs_learned INTEGER DEFAULT 0,
69
  grammar_learned INTEGER DEFAULT 0,
70
  questions_asked INTEGER DEFAULT 0
71
  )
 
235
  WHERE user_id = ? AND word = ? AND category = ?
236
  ''', (mastery_level, user_id, normalized_word, category))
237
 
238
+ if category == "word":
239
+ cursor.execute('''
240
+ UPDATE learning_sessions
241
+ SET words_learned = words_learned + 1
242
+ WHERE user_id = ? AND end_time IS NULL
243
+ ''', (user_id,))
244
+ elif category == "idiom":
245
+ cursor.execute('''
246
+ UPDATE learning_sessions
247
+ SET idioms_learned = idioms_learned + 1
248
+ WHERE user_id = ? AND end_time IS NULL
249
+ ''', (user_id,))
250
+ elif category == "proverb":
251
+ cursor.execute('''
252
+ UPDATE learning_sessions
253
+ SET proverbs_learned = proverbs_learned + 1
254
+ WHERE user_id = ? AND end_time IS NULL
255
+ ''', (user_id,))
256
+
257
  conn.commit()
258
  conn.close()
259
 
 
395
  if 'idiom' not in progress['category_stats'] or progress['category_stats'].get('idiom', {}).get('count', 0) < 5:
396
  recommendations.append("Explore Kazakh idioms to improve your cultural understanding!")
397
 
398
+ if 'proverb' not in progress['category_stats'] or progress['category_stats'].get('proverb', {}).get('count', 0) < 5:
399
+ recommendations.append("Learn Kazakh proverbs to deepen your cultural knowledge!")
400
+
401
  words_to_review = self.get_words_to_review(user_id, 5)
402
  if words_to_review:
403
  recommendations.append(f"Review these words: {', '.join([w['word'] for w in words_to_review[:3]])}")
 
480
 
481
  if term:
482
  self.known_terms.add(term)
483
+ if definition == "құстар туралы (мақал-мәтел)":
484
  print(f"Loaded 'тыныш отыру' idiom: '{term}' from {doc_type} folder")
485
 
486
  print(f"Loaded {len(self.known_terms)} known terms: {list(self.known_terms)[:10]}")
 
500
 
501
  def setup_llm(self, target_language: str = "English"):
502
  self.system_prompt = f"""
503
+ You are a personalized Kazakh language learning assistant with access to a comprehensive knowledge base and user learning history. Your role is to help users learn Kazakh words, idioms, and proverbs while tracking their progress and providing personalized recommendations. Respond in {target_language}.
504
 
505
  Key capabilities:
506
+ 1. *Answer Queries*: Provide accurate definitions and examples for Kazakh words, idioms, and proverbs.
507
+ 2. *Track Learning Progress*: Identify and track when users learn new words, idioms, or proverbs.
508
  3. *Personalized Responses*: Adapt responses based on user's learning history.
509
  4. *Progress Reporting*: Provide detailed progress reports when asked.
510
+ 5. *Learning Recommendations*: Suggest words, idioms, or proverbs to review or learn next.
511
 
512
  Response Guidelines:
513
+ - For word, idiom, or proverb queries: Provide definition, usage examples, and related information in {target_language}.
514
+ - When explaining a Kazakh word, idiom, or proverb retrieved from the knowledge base, **bold** the entire term (e.g., **күләпара**, **дәм-ауыз тигізу**, **Құс қанатымен ұшады, құйрығымен қонады**) in the response to highlight it using double asterisks (**).
515
+ - Only bold the main term, idiom, or proverb being explained, not other Kazakh words or partial phrases, if multiple idioms or proverbs were requested **bold** each of them.
516
+ - Always identify the main Kazakh word, idiom, or proverb for progress tracking.
517
  - *RAG Usage*:
518
+ - Use Retrieval-Augmented Generation (RAG) only when the query explicitly asks for explanations of specific Kazakh terms, idioms, or proverbs (e.g., "What does сәлем mean?", "Tell me about a Kazakh proverb") or when the context strongly suggests a need for knowledge base information.
519
+ - When using RAG, limit the response to explaining 1-2 distinct terms, idioms, or proverbs at most, unless the user explicitly asks for multiple terms (e.g., "List several proverbs"). For each term, provide 3-4 relevant examples. Do not list all or many terms or matches from the knowledge base.
520
  - For general queries (e.g., greetings, procedural questions, or commands like /progress) or grammar-related queries (e.g., "explain me nouns"), rely on your general knowledge and do not use RAG unless the knowledge base contains relevant information.
521
+ - Since the knowledge base contains only words, idioms, and proverbs, grammar explanations (e.g., about nouns, verbs) should be provided using your own knowledge, without relying on RAG, unless the query specifically involves terms in the knowledge base.
522
  - Be encouraging and supportive.
523
  - Use simple, clear explanations.
524
  - When discussing progress, be specific and motivating.
 
536
  )
537
 
538
  def normalize_term(self, term: str) -> str:
539
+ return ' '.join(term.replace(',', '').lower().strip().split())
540
 
541
  def extract_kazakh_terms(self, message: str, response: str) -> List[Tuple[str, str, str]]:
542
  terms = []
543
  seen_terms = set()
544
 
545
  try:
546
+ retrieved_docs = self.vectorstore.similarity_search(message, k=15)
547
  bold_pattern = r'(?:\*\*|__)([А-Яа-яӘәҒғҚқҢңӨөҰұҮүҺһІі\s,-]+)(?:\*\*|__)(?=\s|$|[:.,!?)])'
548
  bold_matches = re.findall(bold_pattern, response, re.UNICODE)
549
 
 
567
  for doc in retrieved_docs:
568
  doc_type = doc.metadata.get('doc_type', '').lower()
569
  if normalized_term in self.normalize_term(doc.page_content):
570
+ if 'proverbs' in doc_type:
571
+ category = "proverb"
572
+ elif 'idioms' in doc_type or 'тіркес' in doc_type:
573
  category = "idiom"
574
+ else:
575
  category = "word"
 
 
576
  definition = self.extract_clean_definition(normalized_term, doc.page_content, response)
577
  break
578
  if not definition:
579
  if len(known_term.split()) > 1:
580
+ category = "proverb" if any('proverbs' in doc.metadata.get('doc_type', '').lower() for doc in retrieved_docs) else "idiom"
581
  definition = self.extract_clean_definition(normalized_term, "", response)
582
  break
583
 
584
  if not term_matched and len(term.split()) > 1:
585
+ for doc in retrieved_docs:
586
+ doc_type = doc.metadata.get('doc_type', '').lower()
587
+ if 'proverbs' in doc_type and normalized_term in self.normalize_term(doc.page_content):
588
+ category = "proverb"
589
+ definition = self.extract_clean_definition(normalized_term, doc.page_content, response)
590
+ term_matched = True
591
+ break
592
+ elif 'idioms' in doc_type and normalized_term in self.normalize_term(doc.page_content):
593
+ category = "idiom"
594
+ definition = self.extract_clean_definition(normalized_term, doc.page_content, response)
595
+ term_matched = True
596
+ break
597
+
598
+ if not term_matched:
599
  category = "word"
600
  definition = self.extract_clean_definition(normalized_term, "", response)
601
  term_matched = True
602
 
603
+ if term_matched and definition and not definition.startswith("Definition for"):
604
  terms.append((original_term, category, definition))
605
  seen_terms.add(normalized_term)
606
  print(f"Added bolded term: {original_term}, category: {category}, definition: {definition}")
 
616
  def extract_clean_definition(self, term: str, doc_content: str, response: str) -> str:
617
  normalized_term = self.normalize_term(term)
618
 
619
+ retrieved_docs = self.vectorstore.similarity_search(term, k=15)
620
  for doc in retrieved_docs:
621
  lines = doc.page_content.replace('\r\n', '\n').replace('\r', '\n').split('\n')
622
  for line in lines:
 
626
  if self.normalize_term(doc_term) == normalized_term:
627
  return doc_definition
628
 
629
+ lines = response.split('\n')
630
+ for line in lines:
631
+ line = line.strip()
632
+ if f'**{term}**' in line and ':' in line:
633
+ parts = line.split(':', 1)
634
+ if len(parts) > 1:
635
+ definition = parts[1].strip()
636
+ if definition:
637
+ return definition
638
+
639
  return f"Definition for {term}"
640
 
641
  def get_user_memory(self, user_id: str):
 
697
  elif message.lower().startswith('/help'):
698
  return self.get_help_message()
699
 
700
+ retrieved_docs = self.vectorstore.similarity_search(message, k=15)
701
  context = "\n".join([doc.page_content for doc in retrieved_docs])
702
 
703
  memory = self.get_user_memory(user_id)
 
747
 
748
  User question: {message}
749
 
750
+ Respond in {target_language}. If explaining a Kazakh word, idiom or proverb retrieved from the context, **bold** the term (e.g., **күләпара**) in your response to highlight it using double asterisks (**). Only bold the main term being explained.
751
  """
752
 
753
  try:
754
  if not hasattr(self.llm, 'generate_content'):
755
  raise AttributeError("LLM does not have generate_content method")
756
  response = self.llm.generate_content(full_prompt).text
757
+ print(f"Full LLM response:\n{response}\n{'-'*50}")
758
  except Exception as e:
759
  print(f"Error generating LLM response: {e}")
760
  return f"Error generating response: {str(e)}. Please try again."
 
773
  for term, category, definition in unique_terms.values():
774
  self.tracker.track_word_encounter(user_id, term, definition, category)
775
 
776
+ if unique_terms:
777
+ response += "\n\n📚 **Tracked Bolded Terms**:\n"
778
+ for term, category, definition in sorted(unique_terms.values()):
779
+ response += f"- **{term}** ({category}): {definition}\n"
780
 
781
  return response
782
 
 
784
  progress = self.tracker.get_user_progress(user_id)
785
 
786
  if progress['total_words'] == 0:
787
+ return "Сіз әлі үйренуді бастамадыңыз! Маған кез келген қазақ сөзі, тіркес немесе мақал-мәтел туралы сұраңыз. 🌟\n\nYou haven't started learning yet! Ask me about any Kazakh word, idiom, or proverb to begin your journey. 🌟"
788
 
789
  report = "📊 **Сіздің үйрену прогресіңіз / Your Learning Progress Report**\n\n"
790
 
791
  report += f"🎯 **Үйренген терминдер саны / Total Terms Learned**: {progress['total_words']}\n"
792
 
793
  for category, stats in progress['category_stats'].items():
794
+ emoji = "📝" if category == "word" else "🎭" if category == "idiom" else "📜" if category == "proverb" else "📚"
795
+ category_name = "Сөздер / Words" if category == "word" else "Тіркестер / Idioms" if category == "idiom" else "Мақал-мәтелдер / Proverbs"
796
  report += f"{emoji} **{category_name}**: {stats['count']} (Орташа меңгеру / Average mastery: {stats['average_mastery']}/5)\n"
797
 
798
  report += f"\n⚡ **Соңғы белсенділік / Recent Activity**: {progress['recent_activity']} терминдер соңғы 7 күнде қаралды / terms reviewed in the last 7 days\n"
 
829
 
830
  response = "📚 **Қайталауға арналған сөздер / Words to Review**:\n\n"
831
  for word_info in words_to_review:
832
+ emoji = "📝" if word_info['category'] == "word" else "🎭" if word_info['category'] == "idiom" else "📜" if word_info['category'] == "proverb" else "📚"
833
  mastery_stars = "⭐" * min(word_info['encounter_count'], 5) + "☆" * (5 - min(word_info['encounter_count'], 5))
834
  response += f"{emoji} **{word_info['word']}** - {mastery_stars} (Кездесу саны / Encounters: {word_info['encounter_count']})\n"
835
 
 
837
  response += f" {definition_preview}\n\n"
838
 
839
  return response
840
+
841
  def get_mastered_words(self, user_id: str, page: int = 1, page_size: int = 10) -> str:
842
  mastered_words = self.tracker.get_mastered_words(user_id, page, page_size)
843
 
 
846
 
847
  response = f"🏆 **Меңгерілген сөздер / Mastered Words** (Бет / Page: {page}):\n\n"
848
  for word_info in mastered_words:
849
+ emoji = "📝" if word_info['category'] == "word" else "🎭" if word_info['category'] == "idiom" else "📜" if word_info['category'] == "proverb" else "📚"
850
  mastery_stars = "🟊" * int(word_info['mastery_level'] * 2) + "⬜" * (10 - int(word_info['mastery_level'] * 2))
851
  response += f"{emoji} **{word_info['word']}** - {mastery_stars} (Кездесу саны / Encounters: {word_info['encounter_count']})\n"
852
 
 
854
  response += f" {definition_preview}\n\n"
855
 
856
  return response
857
+
858
  def get_learning_words(self, user_id: str, page: int = 1, page_size: int = 10) -> str:
859
  learning_words = self.tracker.get_learning_words(user_id, page, page_size)
860
 
861
  if not learning_words:
862
+ return "Сізде қазір үйрену кезеңінде сөздер жоқ. Жаңа сөздерді, тіркестерді немесе мақал-мәтелдерді сұраңыз! 🌟\n\nYou don't have any words in the learning phase right now. Ask about new words, idioms, or proverbs! 🌟"
863
 
864
  response = f"📖 **Үйрену кезеңіндегі сөздер / Words in Learning** (Бет / Page: {page}):\n\n"
865
  for word_info in learning_words:
866
+ emoji = "📝" if word_info['category'] == "word" else "🎭" if word_info['category'] == "idiom" else "📜" if word_info['category'] == "proverb" else "📚"
 
867
  mastery_stars = "⭐" * min(word_info['encounter_count'], 5) + "☆" * (5 - min(word_info['encounter_count'], 5))
868
  response += f"{emoji} **{word_info['word']}** - {mastery_stars} (Кездесу саны / Encounters: {word_info['encounter_count']})\n"
869
 
 
886
  for term in sorted(self.known_terms):
887
  normalized_term = self.normalize_term(term)
888
  if normalized_term not in shown_words and len(term.split()) == 1:
889
+ retrieved_docs = self.vectorstore.similarity_search(term, k=15)
890
  for doc in retrieved_docs:
891
  lines = doc.page_content.replace('\r\n', '\n').replace('\r', '\n').split('\n')
892
  for line in lines:
 
916
  for term in sorted(self.known_terms):
917
  normalized_term = self.normalize_term(term)
918
  if normalized_term not in shown_idioms and len(term.split()) > 1:
919
+ retrieved_docs = self.vectorstore.similarity_search(term, k=15)
920
  for doc in retrieved_docs:
921
  lines = doc.page_content.replace('\r\n', '\n').replace('\r', '\n').split('\n')
922
  for line in lines:
 
1201
  ["Teach me Kazakh verb conjugation in English", "English"]
1202
  ]
1203
  )
1204
+
1205
+ with gr.Tab("📖 API Documentation"):
1206
+ gr.Markdown("""
1207
+ ## API Endpoints for Flutter Integration
1208
+ ### Base URL: `https://huggingface.co/spaces/GuestUser33/kazakh-learning-api`
1209
+
1210
+ ### Authentication Flow:
1211
+ 1. **Login** to get a session token
1212
+ 2. **Use session token** for subsequent API calls
1213
+ 3. **Session tokens expire** after inactivity
1214
+
1215
+ ### Available Endpoints:
1216
+
1217
+ #### 1. Login API
1218
+ ```
1219
+ POST /api/predict
1220
+ Content-Type: application/json
1221
+
1222
+ {
1223
+ "data": ["user_id"],
1224
+ "fn_index": 0
1225
+ }
1226
+ ```
1227
+ **Response**:
1228
+ ```json
1229
+ {
1230
+ "data": [
1231
+ {
1232
+ "success": true,
1233
+ "session_token": "uuid-string",
1234
+ "user_id": "user_id",
1235
+ "message": "Login successful"
1236
+ }
1237
+ ]
1238
+ }
1239
+ ```
1240
+
1241
+ #### 2. Chat API
1242
+ ```
1243
+ POST /api/predict
1244
+ Content-Type: application/json
1245
+
1246
+ {
1247
+ "data": ["message", "user_id", "session_token", "English"],
1248
+ "fn_index": 1
1249
+ }
1250
+ ```
1251
+ **Parameters**:
1252
+ - `message`: The user's query (e.g., "сәлем деген не?" or "/progress")
1253
+ - `user_id`: Unique identifier for the user
1254
+ - `session_token`: Session token from login (use empty string "" if no token)
1255
+ - `target_language`: Language for responses ("English", "Kazakh", or "Russian")
1256
+
1257
+ **Response**:
1258
+ ```json
1259
+ {
1260
+ "data": [
1261
+ {
1262
+ "success": true,
1263
+ "response": "response_text",
1264
+ "user_id": "user_id"
1265
+ }
1266
+ ]
1267
+ }
1268
+ ```
1269
+
1270
+ #### 3. Progress API
1271
+ ```
1272
+ POST /api/predict
1273
+ Content-Type: application/json
1274
+
1275
+ {
1276
+ "data": ["user_id", "session_token"],
1277
+ "fn_index": 2
1278
+ }
1279
+ ```
1280
+ **Response**:
1281
+ ```json
1282
+ {
1283
+ "data": [
1284
+ {
1285
+ "success": true,
1286
+ "progress_text": "progress_report",
1287
+ "progress_data": {
1288
+ "category_stats": {
1289
+ "word": {"count": number, "average_mastery": number},
1290
+ "idiom": {"count": number, "average_mastery": number}
1291
+ },
1292
+ "recent_activity": number,
1293
+ "daily_activity": [{"date": "YYYY-MM-DD", "daily_count": number}, ...],
1294
+ "total_words": number
1295
+ },
1296
+ "user_id": "user_id"
1297
+ }
1298
+ ]
1299
+ }
1300
+ ```
1301
+
1302
+ #### 4. Recommendations API
1303
+ ```
1304
+ POST /api/predict
1305
+ Content-Type: application/json
1306
+
1307
+ {
1308
+ "data": ["user_id", "session_token"],
1309
+ "fn_index": 3
1310
+ }
1311
+ ```
1312
+ **Response**:
1313
+ ```json
1314
+ {
1315
+ "data": [
1316
+ {
1317
+ "success": true,
1318
+ "recommendations_text": "recommendations",
1319
+ "recommendations_list": ["recommendation1", "recommendation2", ...],
1320
+ "user_id": "user_id"
1321
+ }
1322
+ ]
1323
+ }
1324
+ ```
1325
+
1326
+ #### 5. Review Words API
1327
+ ```
1328
+ POST /api/predict
1329
+ Content-Type: application/json
1330
+
1331
+ {
1332
+ "data": ["user_id", "session_token"],
1333
+ "fn_index": 4
1334
+ }
1335
+ ```
1336
+ **Response**:
1337
+ ```json
1338
+ {
1339
+ "data": [
1340
+ {
1341
+ "success": true,
1342
+ "review_text": "review_words",
1343
+ "review_data": [
1344
+ {
1345
+ "word": "word",
1346
+ "definition": "definition",
1347
+ "category": "word|idiom",
1348
+ "mastery_level": number,
1349
+ "last_reviewed": "YYYY-MM-DDTHH:MM:SS",
1350
+ "encounter_count": number
1351
+ },
1352
+ ...
1353
+ ],
1354
+ "user_id": "user_id"
1355
+ }
1356
+ ]
1357
+ }
1358
+ ```
1359
+
1360
+ #### 6. Mastered Words API
1361
+ ```
1362
+ POST /api/predict
1363
+ Content-Type: application/json
1364
+
1365
+ {
1366
+ "data": ["user_id", "session_token"],
1367
+ "fn_index": 5
1368
+ }
1369
+ ```
1370
+ **Response**:
1371
+ ```json
1372
+ {
1373
+ "data": [
1374
+ {
1375
+ "success": true,
1376
+ "mastered_text": "mastered_words",
1377
+ "mastered_data": [
1378
+ {
1379
+ "word": "word",
1380
+ "definition": "definition",
1381
+ "category": "word|idiom",
1382
+ "mastery_level": number,
1383
+ "encounter_count": number
1384
+ },
1385
+ ...
1386
+ ],
1387
+ "user_id": "user_id"
1388
+ }
1389
+ ]
1390
+ }
1391
+ ```
1392
+
1393
+ #### 7. New Word API
1394
+ ```
1395
+ POST /api/predict
1396
+ Content-Type: application/json
1397
+
1398
+ {
1399
+ "data": ["user_id", "session_token"],
1400
+ "fn_index": 6
1401
+ }
1402
+ ```
1403
+ **Response**:
1404
+ ```json
1405
+ {
1406
+ "data": [
1407
+ {
1408
+ "success": true,
1409
+ "word": "new_word",
1410
+ "definition": "definition",
1411
+ "category": "word",
1412
+ "user_id": "user_id"
1413
+ }
1414
+ ]
1415
+ }
1416
+ ```
1417
+
1418
+ #### 8. New Idiom API
1419
+ ```
1420
+ POST /api/predict
1421
+ Content-Type: application/json
1422
+
1423
+ {
1424
+ "data": ["user_id", "session_token"],
1425
+ "fn_index": 7
1426
+ }
1427
+ ```
1428
+ **Response**:
1429
+ ```json
1430
+ {
1431
+ "data": [
1432
+ {
1433
+ "success": true,
1434
+ "word": "new_idiom",
1435
+ "definition": "definition",
1436
+ "category": "idiom",
1437
+ "user_id": "user_id"
1438
+ }
1439
+ ]
1440
+ }
1441
+ ```
1442
+
1443
+ #### 9. Learning Words API
1444
+ ```
1445
+ POST /api/predict
1446
+ Content-Type: application/json
1447
+
1448
+ {
1449
+ "data": ["user_id", "session_token", page, page_size],
1450
+ "fn_index": 8
1451
+ }
1452
+ ```
1453
+ **Parameters**:
1454
+ - `user_id`: Unique identifier for the user
1455
+ - `session_token`: Session token from login (use empty string "" if no token)
1456
+ - `page`: Page number for pagination (default: 1)
1457
+ - `page_size`: Number of items per page (default: 10)
1458
+
1459
+ **Response**:
1460
+ ```json
1461
+ {
1462
+ "data": [
1463
+ {
1464
+ "success": true,
1465
+ "learning_text": "learning_words",
1466
+ "learning_data": [
1467
+ {
1468
+ "word": "word",
1469
+ "definition": "definition",
1470
+ "category": "word|idiom",
1471
+ "mastery_level": number,
1472
+ "encounter_count": number
1473
+ },
1474
+ ...
1475
+ ],
1476
+ "user_id": "user_id",
1477
+ "page": number,
1478
+ "page_size": number
1479
+ }
1480
+ ]
1481
+ }
1482
+ ```
1483
+
1484
+ ### Flutter Integration Example:
1485
+ ```dart
1486
+ import 'dart:convert';
1487
+ import 'package:http/http.dart' as http;
1488
+
1489
+ class KazakhLearningAPI {
1490
+ static const String baseUrl = 'https://huggingface.co/spaces/GuestUser33/kazakh-learning-api';
1491
+ String? sessionToken;
1492
+ String? userId;
1493
+
1494
+ // Login and get session token
1495
+ Future<bool> login(String userId) async {
1496
+ try {
1497
+ final response = await http.post(
1498
+ Uri.parse('$baseUrl/api/predict'),
1499
+ headers: {'Content-Type': 'application/json'},
1500
+ body: jsonEncode({
1501
+ 'data': [userId],
1502
+ 'fn_index': 0
1503
+ }),
1504
+ );
1505
+
1506
+ if (response.statusCode == 200) {
1507
+ final result = jsonDecode(response.body);
1508
+ if (result['data'] != null && result['data'][0]['success'] == true) {
1509
+ this.userId = userId;
1510
+ this.sessionToken = result['data'][0]['session_token'];
1511
+ return true;
1512
+ }
1513
+ }
1514
+ } catch (e) {
1515
+ print('Login error: $e');
1516
+ }
1517
+ return false;
1518
+ }
1519
+
1520
+ // Send chat message
1521
+ Future<String?> sendMessage(String message, {String targetLanguage = 'English'}) async {
1522
+ if (userId == null) return null;
1523
+
1524
+ try {
1525
+ final response = await http.post(
1526
+ Uri.parse('$baseUrl/api/predict'),
1527
+ headers: {'Content-Type': 'application/json'},
1528
+ body: jsonEncode({
1529
+ 'data': [message, userId, sessionToken ?? "", targetLanguage],
1530
+ 'fn_index': 1
1531
+ }),
1532
+ );
1533
+
1534
+ if (response.statusCode == 200) {
1535
+ final result = jsonDecode(response.body);
1536
+ if (result['data'] != null && result['data'][0]['success'] == true) {
1537
+ return result['data'][0]['response'];
1538
+ }
1539
+ }
1540
+ } catch (e) {
1541
+ print('Send message error: $e');
1542
+ }
1543
+ return null;
1544
+ }
1545
+
1546
+ // Get user progress
1547
+ Future<Map?> getProgress() async {
1548
+ if (userId == null) return null;
1549
+
1550
+ try {
1551
+ final response = await http.post(
1552
+ Uri.parse('$baseUrl/api/predict'),
1553
+ headers: {'Content-Type': 'application/json'},
1554
+ body: jsonEncode({
1555
+ 'data': [userId, sessionToken ?? ""],
1556
+ 'fn_index': 2
1557
+ }),
1558
+ );
1559
+
1560
+ if (response.statusCode == 200) {
1561
+ final result = jsonDecode(response.body);
1562
+ if (result['data'] != null && result['data'][0]['success'] == true) {
1563
+ return result['data'][0]['progress_data'];
1564
+ }
1565
+ }
1566
+ } catch (e) {
1567
+ print('Get progress error: $e');
1568
+ }
1569
+ return null;
1570
+ }
1571
+
1572
+ // Get recommendations
1573
+ Future<List?> getRecommendations() async {
1574
+ if (userId == null) return null;
1575
+
1576
+ try {
1577
+ final response = await http.post(
1578
+ Uri.parse('$baseUrl/api/predict'),
1579
+ headers: {'Content-Type': 'application/json'},
1580
+ body: jsonEncode({
1581
+ 'data': [userId, sessionToken ?? ""],
1582
+ 'fn_index': 3
1583
+ }),
1584
+ );
1585
+
1586
+ if (response.statusCode == 200) {
1587
+ final result = jsonDecode(response.body);
1588
+ if (result['data'] != null && result['data'][0]['success'] == true) {
1589
+ return List.from(result['data'][0]['recommendations_list'] ?? []);
1590
+ }
1591
+ }
1592
+ } catch (e) {
1593
+ print('Get recommendations error: $e');
1594
+ }
1595
+ return null;
1596
+ }
1597
+
1598
+ // Get words to review
1599
+ Future<List?> getReviewWords() async {
1600
+ if (userId == null) return null;
1601
+
1602
+ try {
1603
+ final response = await http.post(
1604
+ Uri.parse('$baseUrl/api/predict'),
1605
+ headers: {'Content-Type': 'application/json'},
1606
+ body: jsonEncode({
1607
+ 'data': [userId, sessionToken ?? ""],
1608
+ 'fn_index': 4
1609
+ }),
1610
+ );
1611
+
1612
+ if (response.statusCode == 200) {
1613
+ final result = jsonDecode(response.body);
1614
+ if (result['data'] != null && result['data'][0]['success'] == true) {
1615
+ return result['data'][0]['review_data'];
1616
+ }
1617
+ }
1618
+ } catch (e) {
1619
+ print('Get review words error: $e');
1620
+ }
1621
+ return null;
1622
+ }
1623
+
1624
+ // Get mastered words
1625
+ Future<List?> getMasteredWords() async {
1626
+ if (userId == null) return null;
1627
+
1628
+ try {
1629
+ final response = await http.post(
1630
+ Uri.parse('$baseUrl/api/predict'),
1631
+ headers: {'Content-Type': 'application/json'},
1632
+ body: jsonEncode({
1633
+ 'data': [userId, sessionToken ?? ""],
1634
+ 'fn_index': 5
1635
+ }),
1636
+ );
1637
+
1638
+ if (response.statusCode == 200) {
1639
+ final result = jsonDecode(response.body);
1640
+ if (result['data'] != null && result['data'][0]['success'] == true) {
1641
+ return result['data'][0]['mastered_data'];
1642
+ }
1643
+ }
1644
+ } catch (e) {
1645
+ print('Get mastered words error: $e');
1646
+ }
1647
+ return null;
1648
+ }
1649
+
1650
+ // Get new word
1651
+ Future<Map?> getNewWord() async {
1652
+ if (userId == null) return null;
1653
+
1654
+ try {
1655
+ final response = await http.post(
1656
+ Uri.parse('$baseUrl/api/predict'),
1657
+ headers: {'Content-Type': 'application/json'},
1658
+ body: jsonEncode({
1659
+ 'data': [userId, sessionToken ?? ""],
1660
+ 'fn_index': 6
1661
+ }),
1662
+ );
1663
+
1664
+ if (response.statusCode == 200) {
1665
+ final result = jsonDecode(response.body);
1666
+ if (result['data'] != null && result['data'][0]['success'] == true) {
1667
+ return result['data'][0];
1668
+ }
1669
+ }
1670
+ } catch (e) {
1671
+ print('Get new word error: $e');
1672
+ }
1673
+ return null;
1674
+ }
1675
+
1676
+ // Get new idiom
1677
+ Future<Map?> getNewIdiom() async {
1678
+ if (userId == null) return null;
1679
+
1680
+ try {
1681
+ final response = await http.post(
1682
+ Uri.parse('$baseUrl/api/predict'),
1683
+ headers: {'Content-Type': 'application/json'},
1684
+ body: jsonEncode({
1685
+ 'data': [userId, sessionToken ?? ""],
1686
+ 'fn_index': 7
1687
+ }),
1688
+ );
1689
+
1690
+ if (response.statusCode == 200) {
1691
+ final result = jsonDecode(response.body);
1692
+ if (result['data'] != null && result['data'][0]['success'] == true) {
1693
+ return result['data'][0];
1694
+ }
1695
+ }
1696
+ } catch (e) {
1697
+ print('Get new idiom error: $e');
1698
+ }
1699
+ return null;
1700
+ }
1701
+
1702
+ // Get learning words
1703
+ Future<Map?> getLearningWords({int page = 1, int pageSize = 10}) async {
1704
+ if (userId == null) return null;
1705
+
1706
+ try {
1707
+ final response = await http.post(
1708
+ Uri.parse('$baseUrl/api/predict'),
1709
+ headers: {'Content-Type': 'application/json'},
1710
+ body: jsonEncode({
1711
+ 'data': [userId, sessionToken ?? "", page, pageSize],
1712
+ 'fn_index': 8
1713
+ }),
1714
+ );
1715
+
1716
+ if (response.statusCode == 200) {
1717
+ final result = jsonDecode(response.body);
1718
+ if (result['data'] != null && result['data'][0]['success'] == true) {
1719
+ return result['data'][0];
1720
+ }
1721
+ }
1722
+ } catch (e) {
1723
+ print('Get learning words error: $e');
1724
+ }
1725
+ return null;
1726
+ }
1727
+
1728
+ // Helper method to check if session is valid
1729
+ bool get isLoggedIn => userId != null;
1730
+
1731
+ // Logout method
1732
+ void logout() {
1733
+ userId = null;
1734
+ sessionToken = null;
1735
+ }
1736
+ }
1737
+ ```
1738
+
1739
+ ### Key Features:
1740
+ - ✅ **Multi-User Support**: Each user has separate learning progress
1741
+ - ✅ **Session Management**: Secure session tokens for authentication
1742
+ - ✅ **Personalized Tracking**: Individual progress tracking per user using RAG model
1743
+ - ✅ **Multi-Language Support**: Responses in English, Kazakh, or Russian
1744
+ - ✅ **API Ready**: All endpoints ready for mobile app integration
1745
+ - ✅ **Session Validation**: Automatic session validation and expiry
1746
+
1747
+ ### Usage Notes:
1748
+ - Always call **login** first to get a session token
1749
+ - Use **empty string ""** for session_token if no token is available
1750
+ - Specify `target_language` ("English", "Kazakh", "Russian") for responses
1751
+ - Handle **session expiry** by re-logging in
1752
+ - Use **unique user_id** for each user (e.g., email, username)
1753
+ - Commands like `/progress`, `/recommendations`, `/review`, `/mastered`, `/newword`, `/newidiom`, `/learning`, `/help` are supported
1754
+ - **Error handling** is crucial - always check for success field and handle exceptions
1755
+
1756
+ ### Error Handling:
1757
+ All API responses include a `success` field. If `success: false`, check the `error` field for details:
1758
+ ```json
1759
+ {
1760
+ "data": [
1761
+ {
1762
+ "success": false,
1763
+ "error": "Error message here"
1764
+ }
1765
+ ]
1766
+ }
1767
+ """
1768
+ )
1769
+
1770
+ with gr.Tab("🔌 API Testing"):
1771
+ gr.Markdown("## Test API Endpoints")
1772
+ gr.Markdown("### Use these endpoints programmatically:")
1773
+ gr.Markdown("""
1774
+ **API Endpoints:**
1775
+ - **Login:** `/api/predict` with `fn_index=0`
1776
+ - **Chat:** `/api/predict` with `fn_index=1`
1777
+ - **Progress:** `/api/predict` with `fn_index=2`
1778
+ - **Recommendations:** `/api/predict` with `fn_index=3`
1779
+ - **Review Words:** `/api/predict` with `fn_index=4`
1780
+ - **Mastered Words:** `/api/predict` with `fn_index=5`
1781
+ - **New Word:** `/api/predict` with `fn_index=6`
1782
+ - **New Idiom:** `/api/predict` with `fn_index=7`
1783
+ - **Learning Words:** `/api/predict` with `fn_index=8`
1784
+ """)
1785
+
1786
+ with gr.Row():
1787
+ with gr.Column():
1788
+ user_id_input = gr.Textbox(label="User ID", value="test_user", placeholder="Enter unique user ID")
1789
+ session_token_input = gr.Textbox(label="Session Token", placeholder="Session token (get from login)")
1790
+ message_input = gr.Textbox(label="Message", placeholder="Enter your message in Kazakh or English")
1791
+ target_language_api = gr.Dropdown(label="Explanation Language", choices=["English", "Kazakh", "Russian"], value="English")
1792
+ page_input = gr.Number(label="Page Number", value=1, minimum=1, precision=0)
1793
+ page_size_input = gr.Number(label="Page Size", value=10, minimum=1, precision=0)
1794
+
1795
+ with gr.Row():
1796
+ login_btn = gr.Button("🔑 Test Login API")
1797
+ chat_btn = gr.Button("💬 Test Chat API")
1798
+ progress_btn = gr.Button("📊 Test Progress API")
1799
+ recommendations_btn = gr.Button("💡 Test Recommendations API")
1800
+ review_btn = gr.Button("📚 Test Review Words API")
1801
+ mastered_btn = gr.Button("🏆 Test Mastered Words API")
1802
+ new_word_btn = gr.Button("📝 Test New Word API")
1803
+ new_idiom_btn = gr.Button("🎭 Test New Idiom API")
1804
+ learning_btn = gr.Button("📖 Test Learning Words API")
1805
+
1806
+ api_output = gr.JSON(label="API Response")
1807
+
1808
+ login_interface = gr.Interface(
1809
+ fn=api_login,
1810
+ inputs=gr.Textbox(label="User ID"),
1811
+ outputs=gr.JSON(label="Response"),
1812
+ title="Login API",
1813
+ description="Login endpoint",
1814
+ allow_flagging="never"
1815
+ )
1816
+
1817
+ chat_api_interface = gr.Interface(
1818
+ fn=api_chat,
1819
+ inputs=[
1820
+ gr.Textbox(label="Message"),
1821
+ gr.Textbox(label="User ID"),
1822
+ gr.Textbox(label="Session Token"),
1823
+ gr.Dropdown(label="Target Language", choices=["English", "Kazakh", "Russian"])
1824
+ ],
1825
+ outputs=gr.JSON(label="Response"),
1826
+ title="Chat API",
1827
+ description="Chat endpoint",
1828
+ allow_flagging="never"
1829
+ )
1830
+
1831
+ progress_interface = gr.Interface(
1832
+ fn=api_progress,
1833
+ inputs=[
1834
+ gr.Textbox(label="User ID"),
1835
+ gr.Textbox(label="Session Token")
1836
+ ],
1837
+ outputs=gr.JSON(label="Response"),
1838
+ title="Progress API",
1839
+ description="Progress endpoint",
1840
+ allow_flagging="never"
1841
+ )
1842
+
1843
+ recommendations_interface = gr.Interface(
1844
+ fn=api_recommendations,
1845
+ inputs=[
1846
+ gr.Textbox(label="User ID"),
1847
+ gr.Textbox(label="Session Token")
1848
+ ],
1849
+ outputs=gr.JSON(label="Response"),
1850
+ title="Recommendations API",
1851
+ description="Recommendations endpoint",
1852
+ allow_flagging="never"
1853
+ )
1854
+
1855
+ review_interface = gr.Interface(
1856
+ fn=api_review_words,
1857
+ inputs=[
1858
+ gr.Textbox(label="User ID"),
1859
+ gr.Textbox(label="Session Token")
1860
+ ],
1861
+ outputs=gr.JSON(label="Response"),
1862
+ title="Review Words API",
1863
+ description="Review words endpoint",
1864
+ allow_flagging="never"
1865
+ )
1866
+
1867
+ mastered_interface = gr.Interface(
1868
+ fn=api_mastered_words,
1869
+ inputs=[
1870
+ gr.Textbox(label="User ID"),
1871
+ gr.Textbox(label="Session Token")
1872
+ ],
1873
+ outputs=gr.JSON(label="Response"),
1874
+ title="Mastered Words API",
1875
+ description="Mastered words endpoint",
1876
+ allow_flagging="never"
1877
+ )
1878
+
1879
+ new_word_interface = gr.Interface(
1880
+ fn=api_new_word,
1881
+ inputs=[
1882
+ gr.Textbox(label="User ID"),
1883
+ gr.Textbox(label="Session Token")
1884
+ ],
1885
+ outputs=gr.JSON(label="Response"),
1886
+ title="New Word API",
1887
+ description="New word endpoint",
1888
+ allow_flagging="never"
1889
+ )
1890
+
1891
+ new_idiom_interface = gr.Interface(
1892
+ fn=api_new_idiom,
1893
+ inputs=[
1894
+ gr.Textbox(label="User ID"),
1895
+ gr.Textbox(label="Session Token")
1896
+ ],
1897
+ outputs=gr.JSON(label="Response"),
1898
+ title="New Idiom API",
1899
+ description="New idiom endpoint",
1900
+ allow_flagging="never"
1901
+ )
1902
+
1903
+ learning_interface = gr.Interface(
1904
+ fn=api_learning_words,
1905
+ inputs=[
1906
+ gr.Textbox(label="User ID"),
1907
+ gr.Textbox(label="Session Token"),
1908
+ gr.Number(label="Page Number"),
1909
+ gr.Number(label="Page Size")
1910
+ ],
1911
+ outputs=gr.JSON(label="Response"),
1912
+ title="Learning Words API",
1913
+ description="Learning words endpoint",
1914
+ allow_flagging="never"
1915
+ )
1916
+
1917
+ login_btn.click(
1918
+ fn=api_login,
1919
+ inputs=user_id_input,
1920
+ outputs=api_output
1921
+ )
1922
+
1923
+ chat_btn.click(
1924
+ fn=api_chat,
1925
+ inputs=[message_input, user_id_input, session_token_input, target_language_api],
1926
+ outputs=api_output
1927
+ )
1928
+
1929
+ progress_btn.click(
1930
+ fn=api_progress,
1931
+ inputs=[user_id_input, session_token_input],
1932
+ outputs=api_output
1933
+ )
1934
+
1935
+ recommendations_btn.click(
1936
+ fn=api_recommendations,
1937
+ inputs=[user_id_input, session_token_input],
1938
+ outputs=api_output
1939
+ )
1940
+
1941
+ review_btn.click(
1942
+ fn=api_review_words,
1943
+ inputs=[user_id_input, session_token_input],
1944
+ outputs=api_output
1945
+ )
1946
+
1947
+ mastered_btn.click(
1948
+ fn=api_mastered_words,
1949
+ inputs=[user_id_input, session_token_input],
1950
+ outputs=api_output
1951
+ )
1952
+
1953
+ new_word_btn.click(
1954
+ fn=api_new_word,
1955
+ inputs=[user_id_input, session_token_input],
1956
+ outputs=api_output
1957
+ )
1958
+
1959
+ new_idiom_btn.click(
1960
+ fn=api_new_idiom,
1961
+ inputs=[user_id_input, session_token_input],
1962
+ outputs=api_output
1963
+ )
1964
+
1965
+ learning_btn.click(
1966
+ fn=api_learning_words,
1967
+ inputs=[user_id_input, session_token_input, page_input, page_size_input],
1968
+ outputs=api_output
1969
+ )
1970
 
1971
  if __name__ == "__main__":
1972
  demo.launch(