Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,11 +1,18 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import random
|
| 3 |
from openai import OpenAI
|
|
|
|
| 4 |
|
| 5 |
-
#
|
| 6 |
-
|
| 7 |
|
| 8 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
materials = {
|
| 10 |
300: [
|
| 11 |
"The cat is on the mat.",
|
|
@@ -34,45 +41,56 @@ materials = {
|
|
| 34 |
]
|
| 35 |
}
|
| 36 |
|
| 37 |
-
# 問題生成
|
| 38 |
def generate_question(text):
|
| 39 |
prompt = f"""
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
出力形式はJSONで:
|
| 43 |
{{
|
| 44 |
-
"question": "
|
| 45 |
-
"choices": ["A", "B", "C", "D"],
|
| 46 |
"answer": "A"
|
| 47 |
}}
|
| 48 |
-
|
|
|
|
| 49 |
{text}
|
| 50 |
"""
|
| 51 |
-
|
| 52 |
-
model="gpt-4.1-mini",
|
| 53 |
-
input=prompt
|
| 54 |
-
)
|
| 55 |
try:
|
| 56 |
-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
return q
|
| 59 |
-
except:
|
|
|
|
| 60 |
return {
|
| 61 |
-
"question": "Error
|
| 62 |
-
"choices": ["A", "B", "C", "D"],
|
| 63 |
"answer": "A"
|
| 64 |
}
|
| 65 |
|
| 66 |
-
# 初期
|
| 67 |
def start_test():
|
| 68 |
return {
|
| 69 |
-
"level": 850,
|
| 70 |
"asked": [],
|
| 71 |
"score": 0,
|
| 72 |
"count": 0
|
| 73 |
-
}, "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
-
# 回答処理
|
| 76 |
def next_step(state, user_choice):
|
| 77 |
if not state or "current_q" not in state:
|
| 78 |
return state, "Please start the test first.", "", "", [], ""
|
|
@@ -80,7 +98,7 @@ def next_step(state, user_choice):
|
|
| 80 |
correct = user_choice == state["current_q"]["answer"]
|
| 81 |
feedback = "✅ Correct!" if correct else f"❌ Wrong! The correct answer was {state['current_q']['answer']}."
|
| 82 |
|
| 83 |
-
# レベル
|
| 84 |
if correct:
|
| 85 |
state["level"] = min(1250, state["level"] + 250)
|
| 86 |
state["score"] += 1
|
|
@@ -89,35 +107,26 @@ def next_step(state, user_choice):
|
|
| 89 |
|
| 90 |
state["count"] += 1
|
| 91 |
|
| 92 |
-
# 5問終了
|
| 93 |
if state["count"] >= 5:
|
| 94 |
-
result = f"Test finished! Your estimated reading level: {state['level']}L"
|
| 95 |
return state, result, "", "", [], ""
|
| 96 |
|
| 97 |
-
#
|
| 98 |
available = [t for t in materials[state["level"]] if t not in state["asked"]]
|
| 99 |
if not available:
|
| 100 |
available = materials[state["level"]]
|
| 101 |
text = random.choice(available)
|
| 102 |
state["asked"].append(text)
|
| 103 |
|
| 104 |
-
# 新しい問題生成
|
| 105 |
q = generate_question(text)
|
| 106 |
state["current_q"] = q
|
| 107 |
|
| 108 |
return state, text, q["question"], "", q["choices"], feedback
|
| 109 |
|
| 110 |
-
#
|
| 111 |
-
def get_first_question(state):
|
| 112 |
-
text = random.choice(materials[state["level"]])
|
| 113 |
-
state["asked"].append(text)
|
| 114 |
-
q = generate_question(text)
|
| 115 |
-
state["current_q"] = q
|
| 116 |
-
return state, text, q["question"], "", q["choices"], ""
|
| 117 |
-
|
| 118 |
-
# Gradio UI
|
| 119 |
with gr.Blocks() as demo:
|
| 120 |
-
gr.Markdown("## 📘 Adaptive Reading Test (Lexile-based, 5
|
| 121 |
|
| 122 |
state = gr.State()
|
| 123 |
passage = gr.Textbox(label="Reading Passage", interactive=False)
|
|
@@ -128,11 +137,12 @@ with gr.Blocks() as demo:
|
|
| 128 |
|
| 129 |
with gr.Row():
|
| 130 |
start_btn = gr.Button("▶️ Start Test")
|
| 131 |
-
next_btn = gr.Button("➡️ Submit
|
| 132 |
|
| 133 |
start_btn.click(start_test, outputs=[state, msg, passage, question, choices, feedback])\
|
| 134 |
.then(get_first_question, inputs=state, outputs=[state, passage, question, choices, feedback])
|
| 135 |
|
| 136 |
-
next_btn.click(next_step, inputs=[state, choices],
|
|
|
|
| 137 |
|
| 138 |
demo.launch(share=True)
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import random
|
| 3 |
from openai import OpenAI
|
| 4 |
+
import json
|
| 5 |
|
| 6 |
+
# ✅ ここで直接APIキーを指定
|
| 7 |
+
API_KEY = "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"
|
| 8 |
|
| 9 |
+
# ✅ OpenRouter経由のクライアント設定(base_urlを必ず指定)
|
| 10 |
+
client = OpenAI(
|
| 11 |
+
base_url="https://openrouter.ai/api/v1",
|
| 12 |
+
api_key=API_KEY
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
# ===== Lexileレベル別教材 =====
|
| 16 |
materials = {
|
| 17 |
300: [
|
| 18 |
"The cat is on the mat.",
|
|
|
|
| 41 |
]
|
| 42 |
}
|
| 43 |
|
| 44 |
+
# ===== GPTで問題生成 =====
|
| 45 |
def generate_question(text):
|
| 46 |
prompt = f"""
|
| 47 |
+
Please create one English reading comprehension question about the following passage.
|
| 48 |
+
Format the response strictly as JSON in this format:
|
|
|
|
| 49 |
{{
|
| 50 |
+
"question": "Question text",
|
| 51 |
+
"choices": ["A) ...", "B) ...", "C) ...", "D) ..."],
|
| 52 |
"answer": "A"
|
| 53 |
}}
|
| 54 |
+
Ensure that the correct answer is NOT explicitly shown or hinted in the question or choices.
|
| 55 |
+
Passage:
|
| 56 |
{text}
|
| 57 |
"""
|
| 58 |
+
|
|
|
|
|
|
|
|
|
|
| 59 |
try:
|
| 60 |
+
response = client.chat.completions.create(
|
| 61 |
+
model="google/gemma-3-27b-it:free", # ✅ OpenRouter無料モデル
|
| 62 |
+
messages=[{"role": "user", "content": prompt}],
|
| 63 |
+
max_tokens=300
|
| 64 |
+
)
|
| 65 |
+
data = response.choices[0].message.content.strip()
|
| 66 |
+
q = json.loads(data)
|
| 67 |
return q
|
| 68 |
+
except Exception as e:
|
| 69 |
+
print("⚠️ Error generating question:", e)
|
| 70 |
return {
|
| 71 |
+
"question": "Error generating question.",
|
| 72 |
+
"choices": ["A) ---", "B) ---", "C) ---", "D) ---"],
|
| 73 |
"answer": "A"
|
| 74 |
}
|
| 75 |
|
| 76 |
+
# ===== テスト初期化 =====
|
| 77 |
def start_test():
|
| 78 |
return {
|
| 79 |
+
"level": 850,
|
| 80 |
"asked": [],
|
| 81 |
"score": 0,
|
| 82 |
"count": 0
|
| 83 |
+
}, "✅ Test started! Beginning from 850L.", "", "", [], ""
|
| 84 |
+
|
| 85 |
+
# ===== 最初の問題生成 =====
|
| 86 |
+
def get_first_question(state):
|
| 87 |
+
text = random.choice(materials[state["level"]])
|
| 88 |
+
state["asked"].append(text)
|
| 89 |
+
q = generate_question(text)
|
| 90 |
+
state["current_q"] = q
|
| 91 |
+
return state, text, q["question"], "", q["choices"], ""
|
| 92 |
|
| 93 |
+
# ===== 回答処理 =====
|
| 94 |
def next_step(state, user_choice):
|
| 95 |
if not state or "current_q" not in state:
|
| 96 |
return state, "Please start the test first.", "", "", [], ""
|
|
|
|
| 98 |
correct = user_choice == state["current_q"]["answer"]
|
| 99 |
feedback = "✅ Correct!" if correct else f"❌ Wrong! The correct answer was {state['current_q']['answer']}."
|
| 100 |
|
| 101 |
+
# レベル変動
|
| 102 |
if correct:
|
| 103 |
state["level"] = min(1250, state["level"] + 250)
|
| 104 |
state["score"] += 1
|
|
|
|
| 107 |
|
| 108 |
state["count"] += 1
|
| 109 |
|
| 110 |
+
# 5問で終了
|
| 111 |
if state["count"] >= 5:
|
| 112 |
+
result = f"🎯 Test finished! Your estimated reading level: {state['level']}L"
|
| 113 |
return state, result, "", "", [], ""
|
| 114 |
|
| 115 |
+
# 重複しない教材を選ぶ
|
| 116 |
available = [t for t in materials[state["level"]] if t not in state["asked"]]
|
| 117 |
if not available:
|
| 118 |
available = materials[state["level"]]
|
| 119 |
text = random.choice(available)
|
| 120 |
state["asked"].append(text)
|
| 121 |
|
|
|
|
| 122 |
q = generate_question(text)
|
| 123 |
state["current_q"] = q
|
| 124 |
|
| 125 |
return state, text, q["question"], "", q["choices"], feedback
|
| 126 |
|
| 127 |
+
# ===== Gradio UI =====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
with gr.Blocks() as demo:
|
| 129 |
+
gr.Markdown("## 📘 Adaptive Reading Test (Lexile-based, 5 questions)")
|
| 130 |
|
| 131 |
state = gr.State()
|
| 132 |
passage = gr.Textbox(label="Reading Passage", interactive=False)
|
|
|
|
| 137 |
|
| 138 |
with gr.Row():
|
| 139 |
start_btn = gr.Button("▶️ Start Test")
|
| 140 |
+
next_btn = gr.Button("➡️ Submit & Next")
|
| 141 |
|
| 142 |
start_btn.click(start_test, outputs=[state, msg, passage, question, choices, feedback])\
|
| 143 |
.then(get_first_question, inputs=state, outputs=[state, passage, question, choices, feedback])
|
| 144 |
|
| 145 |
+
next_btn.click(next_step, inputs=[state, choices],
|
| 146 |
+
outputs=[state, passage, question, choices, feedback, msg])
|
| 147 |
|
| 148 |
demo.launch(share=True)
|