Spaces:
Running
Running
Added documentation testing
Browse files
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
|
| 480 |
|
| 481 |
Key capabilities:
|
| 482 |
-
1. *Answer Queries*: Provide accurate definitions and examples for Kazakh words and
|
| 483 |
-
2. *Track Learning Progress*: Identify and track when users learn new words or
|
| 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
|
| 487 |
|
| 488 |
Response Guidelines:
|
| 489 |
-
- For word
|
| 490 |
-
- When explaining a Kazakh word or
|
| 491 |
-
- Only bold the main term or
|
| 492 |
-
- Always identify the main Kazakh word
|
| 493 |
- *RAG Usage*:
|
| 494 |
-
- Use Retrieval-Augmented Generation (RAG) only when the query explicitly asks for explanations of specific Kazakh terms or
|
| 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
|
| 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
|
| 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=
|
| 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 '
|
|
|
|
|
|
|
| 547 |
category = "idiom"
|
| 548 |
-
|
| 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 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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=
|
| 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=
|
| 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
|
| 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 |
-
|
| 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 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 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 "Сіз әлі үйренуді бастамадыңыз! Маған кез келген қазақ сөзі немесе т
|
| 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 "Сізде қазір үйрену кезеңінде сөздер жоқ. Жаңа сөздерді немесе т
|
| 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=
|
| 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=
|
| 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(
|