Toya0421 commited on
Commit
8417298
·
verified ·
1 Parent(s): ea3af2a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -135
app.py CHANGED
@@ -1,153 +1,138 @@
1
  import gradio as gr
2
- from openai import OpenAI
3
  import random
4
- import re
5
-
6
- # ✅ OpenRouter APIキーをここに直接書く
7
- API_KEY = "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"
8
- BASE_URL = "https://openrouter.ai/api/v1"
9
-
10
- # --- Lexile難易度別教材 ---
11
- texts = {
12
- 300: "Tom has a red ball. He plays with it in the park. The sun is bright.",
13
- 600: "A young boy found a lost puppy near the river. He decided to take care of it.",
14
- 850: "Sarah enjoyed reading stories about ancient civilizations and their discoveries.",
15
- 1050: "The scientist developed a new hypothesis about the evolution of animal behavior.",
16
- 1250: "Philosophers have long debated the intricate relationship between free will and determinism."
17
- }
18
-
19
- levels = [300, 600, 850, 1050, 1250]
20
 
21
- # --- OpenAIクライアント設定 ---
22
- client = OpenAI(
23
- base_url=BASE_URL,
24
- api_key=API_KEY
25
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
- # --- AIに問題生成を依頼 ---
28
  def generate_question(text):
29
  prompt = f"""
30
- Read the following passage and generate ONE multiple-choice question with 4 options (A–D).
31
- Clearly mark the correct answer in the format below.
32
- Return ONLY in this format:
33
-
34
- Q: <question text>
35
- A. <option>
36
- B. <option>
37
- C. <option>
38
- D. <option>
39
- Correct: <A/B/C/D>
40
-
41
- Passage:
42
  {text}
43
  """
44
-
45
- response = client.chat.completions.create(
46
- model="google/gemma-3-27b-it:free",
47
- messages=[{"role": "user", "content": prompt}],
48
- max_tokens=400,
49
- temperature=0.7,
50
  )
51
- full_output = response.choices[0].message.content.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- # --- 正解を抽出 ---
54
- match = re.search(r"Correct:\s*([A-D])", full_output, re.IGNORECASE)
55
- correct_option = match.group(1).upper() if match else None
56
 
57
- # --- 「Correct:」行を削除して受験者には非表示にする ---
58
- visible_question = re.sub(r"(?i)Correct:\s*[A-D]", "", full_output).strip()
 
 
59
 
60
- return visible_question, correct_option
 
 
 
 
 
61
 
62
- # --- 適応型ロジック ---
63
- def adaptive_test(prev_level, prev_correct):
64
- idx = levels.index(prev_level)
65
- if prev_correct and idx < len(levels) - 1:
66
- new_level = levels[idx + 1]
67
- elif not prev_correct and idx > 0:
68
- new_level = levels[idx - 1]
69
- else:
70
- new_level = prev_level
71
- return new_level
72
 
73
- # --- テスト開始 ---
74
- def start_test():
75
- level = 850 # 中間レベルから開始
76
- text = texts[level]
77
- question, correct = generate_question(text)
78
- return f"Lexile: {level}L", text, question, level, correct, "", ""
79
-
80
- # --- 回答を処理して次へ ---
81
- def next_step(prev_level, user_answer, correct_option):
82
- correct = (user_answer == correct_option)
83
- new_level = adaptive_test(prev_level, correct)
84
- new_text = texts[new_level]
85
- new_question, new_correct = generate_question(new_text)
86
-
87
- feedback = "✅ Correct!" if correct else "❌ Incorrect."
88
- if new_level == prev_level:
89
- feedback += f"\n🎯 Your estimated reading level is **{new_level}L** (final)."
90
- else:
91
- feedback += f"\n➡️ Moving to next level: **{new_level}L**"
92
-
93
- # 回答欄をリセット
94
- return (
95
- feedback,
96
- f"Lexile: {new_level}L",
97
- new_text,
98
- new_question,
99
- new_level,
100
- new_correct,
101
- None, # user_answerリセット
102
- ""
103
- )
104
 
105
- # --- Gradio UI ---
 
 
 
 
 
 
 
 
106
  with gr.Blocks() as demo:
107
- gr.Markdown("# 📘 Adaptive Reading Level Test (Lexile-based)")
108
-
109
- start_btn = gr.Button("▶️ Start Test")
110
-
111
- level_display = gr.Textbox(label="Current Level", interactive=False)
112
- text_display = gr.Textbox(label="Reading Passage", lines=6, interactive=False)
113
- question_display = gr.Textbox(label="Question", lines=8, interactive=False)
114
-
115
- user_answer = gr.Radio(choices=["A", "B", "C", "D"], label="Your Answer")
116
- submit_btn = gr.Button("Submit Answer")
117
-
118
- feedback_display = gr.Markdown()
119
- hidden_level = gr.Number(visible=False)
120
- hidden_correct = gr.Textbox(visible=False)
121
-
122
- # --- Start Test ---
123
- start_btn.click(
124
- fn=start_test,
125
- inputs=[],
126
- outputs=[
127
- level_display,
128
- text_display,
129
- question_display,
130
- hidden_level,
131
- hidden_correct,
132
- user_answer,
133
- feedback_display,
134
- ],
135
- )
136
 
137
- # --- Submit & Move to Next ---
138
- submit_btn.click(
139
- fn=next_step,
140
- inputs=[hidden_level, user_answer, hidden_correct],
141
- outputs=[
142
- feedback_display,
143
- level_display,
144
- text_display,
145
- question_display,
146
- hidden_level,
147
- hidden_correct,
148
- user_answer,
149
- feedback_display,
150
- ],
151
- )
152
 
153
- demo.launch()
 
1
  import gradio as gr
 
2
  import random
3
+ from openai import OpenAI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
+ # OpenAI APIキーをコード内で指定
6
+ client = OpenAI(api_key="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")
7
+
8
+ # 各Lexileレベルの教材(例として簡単な文を配置)
9
+ materials = {
10
+ 300: [
11
+ "The cat is on the mat.",
12
+ "A boy runs in the park.",
13
+ "She has a red apple."
14
+ ],
15
+ 600: [
16
+ "The young girl likes to read books after school.",
17
+ "A small bird made a nest in the tree near the house.",
18
+ "The sun sets behind the mountains every evening."
19
+ ],
20
+ 850: [
21
+ "Many students find mathematics both challenging and rewarding.",
22
+ "Farmers often depend on weather forecasts to plan their work.",
23
+ "The history of flight began with dreams of human wings."
24
+ ],
25
+ 1050: [
26
+ "Scientific discoveries often arise from unexpected observations.",
27
+ "Economic systems rely on trust between consumers and producers.",
28
+ "The poet’s work explores the relationship between nature and emotion."
29
+ ],
30
+ 1250: [
31
+ "Philosophical inquiry seeks to understand the foundations of knowledge and ethics.",
32
+ "Technological innovation continuously reshapes modern societies.",
33
+ "Globalization has transformed the way nations interact politically and economically."
34
+ ]
35
+ }
36
 
37
+ # 問題生成(GPTで作成)
38
  def generate_question(text):
39
  prompt = f"""
40
+ 以下の英文に関する英語の読解問題を1問だけ作成してください。
41
+ 問題形式は4択(A〜D)で、答えは1つだけ正しいものにしてください。
42
+ 出力形式はJSONで:
43
+ {{
44
+ "question": "質問文",
45
+ "choices": ["A", "B", "C", "D"],
46
+ "answer": "A"
47
+ }}
48
+ 英文:
 
 
 
49
  {text}
50
  """
51
+ response = client.responses.create(
52
+ model="gpt-4.1-mini",
53
+ input=prompt
 
 
 
54
  )
55
+ try:
56
+ import json
57
+ q = json.loads(response.output[0].content[0].text.strip())
58
+ return q
59
+ except:
60
+ return {
61
+ "question": "Error: failed to generate question.",
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
+ }, "Let's begin the adaptive reading test!", "", "", [], ""
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.", "", "", [], ""
79
+
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
87
+ else:
88
+ state["level"] = max(300, state["level"] - 250)
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 levels)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
+ state = gr.State()
123
+ passage = gr.Textbox(label="Reading Passage", interactive=False)
124
+ question = gr.Textbox(label="Question", interactive=False)
125
+ feedback = gr.Textbox(label="Feedback", interactive=False)
126
+ choices = gr.Radio(label="Your Answer", choices=["A", "B", "C", "D"])
127
+ msg = gr.Textbox(label="System Message", interactive=False)
128
+
129
+ with gr.Row():
130
+ start_btn = gr.Button("▶️ Start Test")
131
+ next_btn = gr.Button("➡️ Submit Answer & Next")
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], outputs=[state, passage, question, choices, feedback, msg])
137
 
138
+ demo.launch(share=True)