Spaces:
Sleeping
Sleeping
Jakaria commited on
Commit Β·
7aa41d1
1
Parent(s): f2f3f1e
commit initial code
Browse files- Dockerfile +16 -0
- main.py +384 -0
- requirements.txt +7 -0
Dockerfile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
|
| 2 |
+
# you will also find guides on how best to write your Dockerfile
|
| 3 |
+
|
| 4 |
+
FROM python:3.9
|
| 5 |
+
|
| 6 |
+
RUN useradd -m -u 1000 user
|
| 7 |
+
USER user
|
| 8 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 9 |
+
|
| 10 |
+
WORKDIR /app
|
| 11 |
+
|
| 12 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 13 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
| 14 |
+
|
| 15 |
+
COPY --chown=user . /app
|
| 16 |
+
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
|
main.py
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
LangGraph Japanese Lesson Generator with Structured Output
|
| 3 |
+
Two-Agent System with Native Structure Output from LLM
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from fastapi import FastAPI, HTTPException
|
| 7 |
+
from fastapi.responses import JSONResponse
|
| 8 |
+
from pydantic import BaseModel, Field
|
| 9 |
+
from typing import Optional, List
|
| 10 |
+
import os
|
| 11 |
+
from langchain_groq import ChatGroq
|
| 12 |
+
from langgraph.graph import StateGraph, END
|
| 13 |
+
from typing import TypedDict
|
| 14 |
+
from dotenv import load_dotenv
|
| 15 |
+
|
| 16 |
+
# ============ LOAD ENVIRONMENT ============
|
| 17 |
+
load_dotenv()
|
| 18 |
+
|
| 19 |
+
# ============ PYDANTIC BASE MODELS ============
|
| 20 |
+
|
| 21 |
+
class VocabularyItem(BaseModel):
|
| 22 |
+
"""Vocabulary item with all required fields"""
|
| 23 |
+
japanese: str = Field(..., description="Japanese word in Kanji/Hiragana/Katakana")
|
| 24 |
+
romaji: str = Field(..., description="Romanized version of the word")
|
| 25 |
+
meaning: str = Field(..., description="English meaning of the word")
|
| 26 |
+
example: str = Field(..., description="Example sentence using the word")
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class GrammarPoint(BaseModel):
|
| 30 |
+
"""Grammar point with all required fields"""
|
| 31 |
+
pattern: str = Field(..., description="The grammar pattern (e.g., N1 γ― N2 γ§γ)")
|
| 32 |
+
explanation: str = Field(..., description="Detailed explanation of the pattern")
|
| 33 |
+
example: str = Field(..., description="Example sentence showing the pattern")
|
| 34 |
+
translation: str = Field(..., description="English translation of the example")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class Question(BaseModel):
|
| 38 |
+
"""Multiple choice question with all required fields"""
|
| 39 |
+
question: str = Field(..., description="The question text")
|
| 40 |
+
options: List[str] = Field(..., min_items=4, max_items=4, description="Exactly 4 answer options")
|
| 41 |
+
correctIndex: int = Field(..., ge=0, le=3, description="Index of correct answer (0-3)")
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class SubSection(BaseModel):
|
| 45 |
+
"""Subsection with conversation, vocabulary, grammar, and questions"""
|
| 46 |
+
title: str = Field(..., description="Title of the subsection")
|
| 47 |
+
conversation: str = Field(..., description="Dialog conversation in format 'A: text\\nB: text'")
|
| 48 |
+
vocabularies: List[VocabularyItem] = Field(..., min_items=5, description="At least 5 vocabulary items")
|
| 49 |
+
grammarPoints: List[GrammarPoint] = Field(..., min_items=3, description="At least 3 grammar points")
|
| 50 |
+
questions: List[Question] = Field(..., min_items=4, max_items=5, description="4-5 questions")
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class Lesson(BaseModel):
|
| 54 |
+
"""Complete lesson with all required sections"""
|
| 55 |
+
title: str = Field(..., description="Lesson title (English and Japanese)")
|
| 56 |
+
description: str = Field(..., description="Comprehensive lesson description")
|
| 57 |
+
color: str = Field(..., pattern="^#[0-9a-fA-F]{6}$", description="Hex color code")
|
| 58 |
+
subSections: List[SubSection] = Field(..., min_items=2, max_items=3, description="2-3 subsections")
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class OrganizedData(BaseModel):
|
| 62 |
+
"""Output from organizer agent"""
|
| 63 |
+
lessonTitle: str = Field(..., description="Title of the lesson")
|
| 64 |
+
lessonDescription: str = Field(..., description="Description of the lesson")
|
| 65 |
+
subSections: List[dict] = Field(..., description="List of subsections to create")
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class LessonRequest(BaseModel):
|
| 69 |
+
"""API request model"""
|
| 70 |
+
lesson_topic: str = Field(..., description="The lesson topic to generate")
|
| 71 |
+
additional_context: Optional[str] = Field(None, description="Additional context or requirements")
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
# ============ STATE DEFINITIONS ============
|
| 75 |
+
class State(TypedDict):
|
| 76 |
+
raw_input: str
|
| 77 |
+
organized_data: Optional[OrganizedData]
|
| 78 |
+
final_lesson: Optional[Lesson]
|
| 79 |
+
error: Optional[str]
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
# ============ INITIALIZE LLM WITH STRUCTURE OUTPUT ============
|
| 83 |
+
app = FastAPI()
|
| 84 |
+
|
| 85 |
+
base_llm = ChatGroq(
|
| 86 |
+
model="llama-3.3-70b-versatile",
|
| 87 |
+
temperature=0.7,
|
| 88 |
+
api_key=os.getenv("GROQ_API_KEY")
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
# Create structured output versions
|
| 92 |
+
organizer_llm = base_llm.with_structured_output(OrganizedData)
|
| 93 |
+
generator_llm = base_llm.with_structured_output(Lesson)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
# ============ AGENT 1: ORGANIZER AGENT ============
|
| 97 |
+
def organizer_agent(state: State) -> State:
|
| 98 |
+
"""
|
| 99 |
+
First Agent: Organizes unstructured input data
|
| 100 |
+
Uses structured output to guarantee proper format
|
| 101 |
+
"""
|
| 102 |
+
|
| 103 |
+
organize_prompt = f"""You are a Japanese lesson content organizer.
|
| 104 |
+
Your job is to take unstructured user input about a Japanese lesson topic and organize it logically.
|
| 105 |
+
|
| 106 |
+
User Input: {state['raw_input']}
|
| 107 |
+
|
| 108 |
+
Analyze this and organize into a structured format:
|
| 109 |
+
|
| 110 |
+
Requirements:
|
| 111 |
+
1. Extract or create a clear Lesson Title
|
| 112 |
+
2. Write a comprehensive Lesson Description (2-3 sentences)
|
| 113 |
+
3. Determine 2-3 SubSections (typically progression from basic to complex)
|
| 114 |
+
4. For each subsection, identify:
|
| 115 |
+
- Clear title
|
| 116 |
+
- Main topics/themes to cover
|
| 117 |
+
- Vocabulary themes (e.g., greetings, family, food)
|
| 118 |
+
- Relevant grammar patterns
|
| 119 |
+
- Number of questions (4-5)
|
| 120 |
+
|
| 121 |
+
Think pedagogically about lesson progression. Be specific and detailed."""
|
| 122 |
+
|
| 123 |
+
try:
|
| 124 |
+
print("π Organizer Agent processing...")
|
| 125 |
+
organized_result = organizer_llm.invoke(organize_prompt)
|
| 126 |
+
state["organized_data"] = organized_result
|
| 127 |
+
print("β
Organizer Agent completed successfully")
|
| 128 |
+
return state
|
| 129 |
+
except Exception as e:
|
| 130 |
+
state["error"] = f"Organizer Agent Error: {str(e)}"
|
| 131 |
+
print(f"β Organizer Error: {e}")
|
| 132 |
+
return state
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
# ============ AGENT 2: GENERATOR AGENT ============
|
| 136 |
+
def generator_agent(state: State) -> State:
|
| 137 |
+
"""
|
| 138 |
+
Second Agent: Generates complete lesson content
|
| 139 |
+
Uses structured output to guarantee exact schema compliance
|
| 140 |
+
"""
|
| 141 |
+
|
| 142 |
+
if state["error"]:
|
| 143 |
+
return state
|
| 144 |
+
|
| 145 |
+
organized_data = state["organized_data"]
|
| 146 |
+
|
| 147 |
+
generate_prompt = f"""You are an expert Japanese language teacher creating authentic, complete lesson content.
|
| 148 |
+
|
| 149 |
+
Based on this lesson structure:
|
| 150 |
+
Title: {organized_data.lessonTitle}
|
| 151 |
+
Description: {organized_data.lessonDescription}
|
| 152 |
+
|
| 153 |
+
SubSections needed:
|
| 154 |
+
{chr(10).join([f"- {sub['title']}: {sub.get('topics', [])} | Vocabulary: {sub.get('vocabularyThemes', [])} | Grammar: {sub.get('grammarPatterns', [])} | Questions: {sub.get('questionCount', 4)}" for sub in organized_data.subSections])}
|
| 155 |
+
|
| 156 |
+
Generate a COMPLETE Japanese lesson with:
|
| 157 |
+
|
| 158 |
+
1. Title: Clear lesson name in English and Japanese
|
| 159 |
+
2. Description: 2-3 sentence comprehensive overview
|
| 160 |
+
3. Color: Choose one from: #3498db, #e74c3c, #2ecc71, #f39c12, #9b59b6, #1abc9c, #e91e63, #00bcd4
|
| 161 |
+
4. Create 2-3 subsections as specified
|
| 162 |
+
|
| 163 |
+
FOR EACH SUBSECTION:
|
| 164 |
+
- Title: Clear subsection name
|
| 165 |
+
- Conversation: MUST be natural Japanese dialogue. Format: "A: [Japanese]\\nB: [Japanese]\\nA: [Japanese]\\nB: [Japanese]"
|
| 166 |
+
(Include at least 4 lines. Use REAL Japanese sentences.)
|
| 167 |
+
- Vocabularies: Minimum 5-8 items per subsection
|
| 168 |
+
* japanese: ONLY Kanji/Hiragana/Katakana (e.g., "γγγ«γ‘γ―")
|
| 169 |
+
* romaji: Romanized (e.g., "Konnichiwa")
|
| 170 |
+
* meaning: English translation
|
| 171 |
+
* example: Example sentence using the word
|
| 172 |
+
- grammarPoints: EXACTLY 3-4 grammar patterns
|
| 173 |
+
* pattern: Grammar rule (e.g., "N1 γ― N2 γ§γ")
|
| 174 |
+
* explanation: Clear explanation
|
| 175 |
+
* example: Japanese sentence demonstrating it
|
| 176 |
+
* translation: English translation
|
| 177 |
+
- questions: EXACTLY 4-5 multiple choice questions
|
| 178 |
+
* question: English question
|
| 179 |
+
* options: EXACTLY 4 answer choices
|
| 180 |
+
* correctIndex: Index of correct answer (0, 1, 2, or 3)
|
| 181 |
+
|
| 182 |
+
Create authentic, educational content. Every field must be filled with real, useful content."""
|
| 183 |
+
|
| 184 |
+
try:
|
| 185 |
+
print("π Generator Agent processing...")
|
| 186 |
+
lesson_result = generator_llm.invoke(generate_prompt)
|
| 187 |
+
state["final_lesson"] = lesson_result
|
| 188 |
+
print("β
Generator Agent completed successfully")
|
| 189 |
+
print(f"β
Generated lesson: {lesson_result.title}")
|
| 190 |
+
return state
|
| 191 |
+
except Exception as e:
|
| 192 |
+
state["error"] = f"Generator Agent Error: {str(e)}"
|
| 193 |
+
print(f"β Generator Error: {e}")
|
| 194 |
+
return state
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
# ============ BUILD LANGGRAPH ============
|
| 198 |
+
def build_graph():
|
| 199 |
+
"""Create the LangGraph workflow"""
|
| 200 |
+
graph = StateGraph(State)
|
| 201 |
+
|
| 202 |
+
# Add nodes
|
| 203 |
+
graph.add_node("organizer", organizer_agent)
|
| 204 |
+
graph.add_node("generator", generator_agent)
|
| 205 |
+
|
| 206 |
+
# Add edges
|
| 207 |
+
graph.add_edge("organizer", "generator")
|
| 208 |
+
graph.add_edge("generator", END)
|
| 209 |
+
|
| 210 |
+
# Set entry point
|
| 211 |
+
graph.set_entry_point("organizer")
|
| 212 |
+
|
| 213 |
+
return graph.compile()
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
workflow = build_graph()
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
# ============ API ENDPOINT ============
|
| 220 |
+
@app.post("/api/generate-lesson")
|
| 221 |
+
async def generate_lesson(request: LessonRequest):
|
| 222 |
+
"""
|
| 223 |
+
Main API endpoint to generate Japanese lessons
|
| 224 |
+
|
| 225 |
+
Input: Raw lesson topic/content
|
| 226 |
+
Process: Two-agent LangGraph workflow with structured output
|
| 227 |
+
Output: Strictly validated lesson JSON following exact schema
|
| 228 |
+
"""
|
| 229 |
+
|
| 230 |
+
try:
|
| 231 |
+
if not request.lesson_topic or request.lesson_topic.strip() == "":
|
| 232 |
+
raise HTTPException(status_code=400, detail="lesson_topic cannot be empty")
|
| 233 |
+
|
| 234 |
+
print(f"\nπ Starting lesson generation for: {request.lesson_topic[:50]}...")
|
| 235 |
+
|
| 236 |
+
# Prepare initial state
|
| 237 |
+
initial_state: State = {
|
| 238 |
+
"raw_input": request.lesson_topic,
|
| 239 |
+
"organized_data": None,
|
| 240 |
+
"final_lesson": None,
|
| 241 |
+
"error": None
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
# Run the workflow
|
| 245 |
+
result = workflow.invoke(initial_state)
|
| 246 |
+
|
| 247 |
+
# Check for errors
|
| 248 |
+
if result.get("error"):
|
| 249 |
+
print(f"β οΈ Workflow Error: {result['error']}")
|
| 250 |
+
raise HTTPException(status_code=500, detail=result["error"])
|
| 251 |
+
|
| 252 |
+
if not result.get("final_lesson"):
|
| 253 |
+
raise HTTPException(status_code=500, detail="Failed to generate lesson")
|
| 254 |
+
|
| 255 |
+
print("β
Lesson generation completed successfully!")
|
| 256 |
+
|
| 257 |
+
# Return as JSON
|
| 258 |
+
return result["final_lesson"].model_dump()
|
| 259 |
+
|
| 260 |
+
except HTTPException:
|
| 261 |
+
raise
|
| 262 |
+
except Exception as e:
|
| 263 |
+
print(f"β API Error: {str(e)}")
|
| 264 |
+
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}")
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
# ============ HEALTH CHECK ENDPOINT ============
|
| 268 |
+
@app.get("/health")
|
| 269 |
+
async def health_check():
|
| 270 |
+
"""Health check endpoint"""
|
| 271 |
+
return {
|
| 272 |
+
"status": "healthy",
|
| 273 |
+
"message": "LangGraph Lesson Generator with Structured Output is running"
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
# ============ EXAMPLE ENDPOINT ============
|
| 278 |
+
@app.get("/example")
|
| 279 |
+
async def example():
|
| 280 |
+
"""Return example of expected output format"""
|
| 281 |
+
example_lesson = {
|
| 282 |
+
"title": "Self Introduction (γγγγγγγ)",
|
| 283 |
+
"description": "Learn greetings, how to introduce yourself, say your age and birthday, and talk about your family.",
|
| 284 |
+
"color": "#3498db",
|
| 285 |
+
"subSections": [
|
| 286 |
+
{
|
| 287 |
+
"title": "Hello & Basic Introduction",
|
| 288 |
+
"conversation": "A: γγγ«γ‘γ―!\\nB: γγγ«γ‘γ―γγ―γγγΎγγ¦γ\\nA: γγγγ―γγ γ§γγγγγγγγγγΎγγγ\\nB: γγγγγγγγγγΎγγ",
|
| 289 |
+
"vocabularies": [
|
| 290 |
+
{
|
| 291 |
+
"japanese": "γγγ«γ‘γ―",
|
| 292 |
+
"romaji": "Konnichiwa",
|
| 293 |
+
"meaning": "Hello / Good afternoon",
|
| 294 |
+
"example": "γγγ«γ‘γ―!"
|
| 295 |
+
},
|
| 296 |
+
{
|
| 297 |
+
"japanese": "γ―γγγΎγγ¦",
|
| 298 |
+
"romaji": "Hajimemashite",
|
| 299 |
+
"meaning": "Nice to meet you (first time)",
|
| 300 |
+
"example": "γ―γγγΎγγ¦γ"
|
| 301 |
+
},
|
| 302 |
+
{
|
| 303 |
+
"japanese": "γγγ",
|
| 304 |
+
"romaji": "Watashi",
|
| 305 |
+
"meaning": "I / Me",
|
| 306 |
+
"example": "γγγγ―ε¦ηγ§γγ"
|
| 307 |
+
},
|
| 308 |
+
{
|
| 309 |
+
"japanese": "γ§γ",
|
| 310 |
+
"romaji": "Desu",
|
| 311 |
+
"meaning": "to be (polite)",
|
| 312 |
+
"example": "γγγγ―ε¦ηγ§γγ"
|
| 313 |
+
},
|
| 314 |
+
{
|
| 315 |
+
"japanese": "γγγγΎγγ",
|
| 316 |
+
"romaji": "Kara kimashita",
|
| 317 |
+
"meaning": "Came from",
|
| 318 |
+
"example": "γγ³γ°γ©γγ·γ₯γγγγΎγγγ"
|
| 319 |
+
},
|
| 320 |
+
{
|
| 321 |
+
"japanese": "γγγγγγγγγγΎγ",
|
| 322 |
+
"romaji": "Yoroshiku onegaishimasu",
|
| 323 |
+
"meaning": "Please take care of me",
|
| 324 |
+
"example": "γγγγγγγγγγΎγγ"
|
| 325 |
+
},
|
| 326 |
+
{
|
| 327 |
+
"japanese": "γγγγ",
|
| 328 |
+
"romaji": "Kankoku",
|
| 329 |
+
"meaning": "Korea",
|
| 330 |
+
"example": "γγγγγγγγΎγγγ"
|
| 331 |
+
}
|
| 332 |
+
],
|
| 333 |
+
"grammarPoints": [
|
| 334 |
+
{
|
| 335 |
+
"pattern": "N γ― N γ§γ",
|
| 336 |
+
"explanation": "Used to state what something/someone is.",
|
| 337 |
+
"example": "γγγγ―ε¦ηγ§γγ",
|
| 338 |
+
"translation": "I am a student."
|
| 339 |
+
},
|
| 340 |
+
{
|
| 341 |
+
"pattern": "N γ― N γγζ₯γΎγγ",
|
| 342 |
+
"explanation": "Used to say where someone came from.",
|
| 343 |
+
"example": "γγγγ―γγ³γ°γ©γγ·γ₯γγγγΎγγγ",
|
| 344 |
+
"translation": "I came from Bangladesh."
|
| 345 |
+
},
|
| 346 |
+
{
|
| 347 |
+
"pattern": "γγγγγγγγγγΎγ",
|
| 348 |
+
"explanation": "Polite greeting used when meeting someone for the first time.",
|
| 349 |
+
"example": "γ―γγγΎγγ¦γγγγγγγγγγγΎγγ",
|
| 350 |
+
"translation": "Nice to meet you. Please take care of me."
|
| 351 |
+
}
|
| 352 |
+
],
|
| 353 |
+
"questions": [
|
| 354 |
+
{
|
| 355 |
+
"question": "How do you say 'Hello' in Japanese?",
|
| 356 |
+
"options": ["γγγγͺγ", "γγγ«γ‘γ―", "γγ―γγ", "γγγγΏγͺγγ"],
|
| 357 |
+
"correctIndex": 1
|
| 358 |
+
},
|
| 359 |
+
{
|
| 360 |
+
"question": "What does 'γ―γγγΎγγ¦' mean?",
|
| 361 |
+
"options": ["Goodbye", "Nice to meet you", "Thank you", "Good morning"],
|
| 362 |
+
"correctIndex": 1
|
| 363 |
+
},
|
| 364 |
+
{
|
| 365 |
+
"question": "How would you say you came from Bangladesh?",
|
| 366 |
+
"options": ["γγ³γ°γ©γγ·γ₯γ§γγ", "γγ³γ°γ©γγ·γ₯γγγγΎγγγ", "γγ³γ°γ©γγ·γ₯γΈγγγΎγγ", "γγ³γ°γ©γγ·γ₯γ§εγγΎγγ"],
|
| 367 |
+
"correctIndex": 1
|
| 368 |
+
},
|
| 369 |
+
{
|
| 370 |
+
"question": "Which sentence is correct for 'I am a student'?",
|
| 371 |
+
"options": ["γγγγγγοΏ½οΏ½γ§γγ", "γγγγ―ε¦ηγ§γγ", "ε¦ηγ―γγγγ§γγ", "γγγγε¦ηγ§γγ"],
|
| 372 |
+
"correctIndex": 1
|
| 373 |
+
}
|
| 374 |
+
]
|
| 375 |
+
}
|
| 376 |
+
]
|
| 377 |
+
}
|
| 378 |
+
return example_lesson
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
# ============ RUN SERVER ============
|
| 382 |
+
if __name__ == "__main__":
|
| 383 |
+
import uvicorn
|
| 384 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn[standard]
|
| 3 |
+
langchain
|
| 4 |
+
langchain-groq
|
| 5 |
+
pydantic
|
| 6 |
+
langgraph
|
| 7 |
+
python-dotenv
|