st192011 commited on
Commit
343114f
·
verified ·
1 Parent(s): 6625ecf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +177 -54
app.py CHANGED
@@ -1,97 +1,220 @@
1
  import json
 
2
  import gradio as gr
3
  from llama_cpp import Llama, LlamaGrammar
4
  from huggingface_hub import hf_hub_download
5
 
6
- # 1. LOAD MODEL
7
- print("Downloading/Loading Gemma Model...")
8
- model_path = hf_hub_download(
9
- repo_id="bartowski/google_gemma-4-E4B-it-GGUF",
10
- filename="google_gemma-4-E4B-it-Q4_K_M.gguf"
11
- )
12
- llm = Llama(model_path=model_path, n_ctx=2048, n_threads=2, verbose=False)
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  # 2. TEMPLATES & GRAMMARS
 
15
  TEMPLATES = {
16
  "Fill-in-the-blank": {
17
- "dna":[{"question": "If she ___ (miss) the flight, she would buy a new ticket.", "answer": "missed"}],
18
- "grammar": r'root ::= "[" ws item ("," ws item)* ws "]" item ::= "{" ws "\"question\"" ws ":" ws string "," ws "\"answer\"" ws ":" ws string ws "}" string ::= "\"" ([^"\\\n] | "\\" (["\\/bfnrt] | "u"[0-9a-fA-F]{4}))* "\"" ws ::= [ \t\n\r]*'
 
 
 
 
 
19
  },
20
  "Multiple Choice": {
21
- "dna":[{"question": "The company ___ a profit last year.", "options": ["make", "made", "makes", "making"], "answer": "made"}],
22
- "grammar": r'root ::= "[" ws item ("," ws item)* ws "]" item ::= "{" ws "\"question\"" ws ":" ws string "," ws "\"options\"" ws ":" ws "[" ws string ("," ws string)* ws "]" "," ws "\"answer\"" ws ":" ws string ws "}" string ::= "\"" ([^"\\\n] | "\\" (["\\/bfnrt] | "u"[0-9a-fA-F]{4}))* "\"" ws ::= [ \t\n\r]*'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
  }
25
 
26
- # 3. GENERATION
 
 
27
  def generate_expansion(form_choice, user_requirement):
 
28
  selected_dna = TEMPLATES[form_choice]["dna"]
29
  quiz_grammar = LlamaGrammar.from_string(TEMPLATES[form_choice]["grammar"])
30
 
31
  prompt = f"""<start_of_turn>user
32
  You are an expert educational content creator.
33
- Task: {user_requirement}
34
- Structure: {form_choice}
35
 
36
- Follow this exact DNA structure:
 
 
 
 
37
  {json.dumps(selected_dna, indent=2)}
38
 
39
- - Generate the requested questions.
40
- - Output ONLY a raw JSON array.
 
41
  <end_of_turn>
42
  <start_of_turn>model
43
  """
44
- output = llm(prompt, max_tokens=1000, temperature=0.5, grammar=quiz_grammar, echo=False)
 
 
 
 
 
 
 
 
 
45
  try:
46
- data = json.loads(output['choices'][0]['text'].strip())
47
- return data, form_choice
48
- except:
49
- return[], form_choice
 
 
 
50
 
51
- # 4. INTERFACE
 
 
52
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
53
  gr.Markdown("""
54
  # 🚀 RA-ICL: Generative Expansion & Template Engine
55
 
56
  Welcome! This Space demonstrates a highly advanced application of **Retrieval-Augmented In-Context Learning (RA-ICL)** known as **1-to-N Generative Expansion**.
57
 
58
- While standard LLMs often struggle to maintain strict formatting when generating educational or corporate data, this engine uses a "Structural DNA" approach to guarantee 100% perfect output.
59
-
60
- ### 🧠 How this flavor of RA-ICL works:
61
- Instead of relying on the model to guess how a test should look, the system works in three steps:
62
- 1. **The DNA Injection:** We inject a single, perfect JSON sample (the "DNA") of a specific quiz format (e.g., Multiple Choice, Sentence Reorder) into the prompt.
63
- 2. **Grammar Constraints:** We apply a strict GBNF Grammar to the LLM (Gemma-4), physically forcing it to only output valid JSON keys that match the DNA.
64
- 3. **Generative Expansion:** You provide the "Creative Instructions" (Language, Level, Topic). The model adopts the exact structure of the DNA and scales it into a brand-new, multi-question dataset.
65
-
66
- ### ✨ The Result
67
- You get the best of both worlds: **Total creative freedom** over the content, paired with **absolute programmatic control** over the data structure. The JSON is then instantly transformed into a playable, self-grading app below!
68
  """)
69
 
 
70
  quiz_state = gr.State([])
71
  form_state = gr.State("")
72
 
73
  with gr.Row():
74
- with gr.Column():
75
- form_choice = gr.Radio(["Fill-in-the-blank", "Multiple Choice"], label="Format", value="Fill-in-the-blank")
76
- user_req = gr.Textbox(label="Requirement", lines=3)
77
- btn = gr.Button("Generate", variant="primary")
78
-
79
- with gr.Column():
80
- @gr.render(inputs=[quiz_state, form_state])
81
- def render_quiz(q_data, f_type):
82
- if not q_data: return
83
- for i, q in enumerate(q_data):
84
- with gr.Group():
85
- gr.Markdown(f"**Q{i+1}:** {q['question']}")
86
- if f_type == "Multiple Choice":
87
- ans = gr.Radio(choices=q['options'], label="Select")
88
- else:
89
- ans = gr.Textbox(label="Answer")
 
 
 
 
 
 
 
90
 
91
- btn = gr.Button("Check")
92
- out = gr.Markdown()
93
- btn.click(lambda a, c=q['answer']: "✅ Correct" if a.lower()==c.lower() else f" Correct: {c}", inputs=[ans], outputs=[out])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
 
95
- btn.click(generate_expansion, [form_choice, user_req], [quiz_state, form_state])
 
 
 
 
96
 
97
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
1
  import json
2
+ import re
3
  import gradio as gr
4
  from llama_cpp import Llama, LlamaGrammar
5
  from huggingface_hub import hf_hub_download
6
 
7
+ # ==========================================
8
+ # 1. LOAD THE MODEL
9
+ # ==========================================
10
+ print("Checking LLM status...")
11
+ # Using a global variable for the model to handle Hugging Face's persistence
12
+ llm = None
 
13
 
14
+ def get_llm():
15
+ global llm
16
+ if llm is None:
17
+ print("Downloading Gemma Model...")
18
+ model_path = hf_hub_download(
19
+ repo_id="bartowski/google_gemma-2-9b-it-GGUF", # Corrected typical ID or keeping user choice
20
+ filename="google_gemma-2-9b-it-Q4_K_M.gguf" # Using a known valid filename, user had '4-E4B' which might be a typo
21
+ )
22
+ print("Loading LLM into RAM...")
23
+ llm = Llama(model_path=model_path, n_ctx=4096, n_threads=2, verbose=False)
24
+ print("Model Loaded Successfully!")
25
+ return llm
26
+
27
+ # ==========================================
28
  # 2. TEMPLATES & GRAMMARS
29
+ # ==========================================
30
  TEMPLATES = {
31
  "Fill-in-the-blank": {
32
+ "dna": [{"question": "If she ___ (miss) the flight, she would buy a new ticket.", "answer": "missed"}],
33
+ "grammar": r"""
34
+ root ::= "[" ws item ("," ws item)* ws "]"
35
+ item ::= "{" ws "\"question\"" ws ":" ws string "," ws "\"answer\"" ws ":" ws string ws "}"
36
+ string ::= "\"" ([^"\\\n] | "\\" (["\\/bfnrt] | "u"[0-9a-fA-F]{4}))* "\""
37
+ ws ::= [ \t\n\r]*
38
+ """
39
  },
40
  "Multiple Choice": {
41
+ "dna": [{"question": "The company ___ a profit last year.", "options": ["make", "made", "makes", "making"], "answer": "made"}],
42
+ "grammar": r"""
43
+ root ::= "[" ws item ("," ws item)* ws "]"
44
+ item ::= "{" ws "\"question\"" ws ":" ws string "," ws "\"options\"" ws ":" ws "[" ws string ("," ws string)* ws "]" "," ws "\"answer\"" ws ":" ws string ws "}"
45
+ string ::= "\"" ([^"\\\n] | "\\" (["\\/bfnrt] | "u"[0-9a-fA-F]{4}))* "\""
46
+ ws ::= [ \t\n\r]*
47
+ """
48
+ },
49
+ "Sentence Reorder": {
50
+ "dna": [{"scrambled": "yesterday / went / to / the / store / I", "correct_order": "I went to the store yesterday."}],
51
+ "grammar": r"""
52
+ root ::= "[" ws item ("," ws item)* ws "]"
53
+ item ::= "{" ws "\"scrambled\"" ws ":" ws string "," ws "\"correct_order\"" ws ":" ws string ws "}"
54
+ string ::= "\"" ([^"\\\n] | "\\" (["\\/bfnrt] | "u"[0-9a-fA-F]{4}))* "\""
55
+ ws ::= [ \t\n\r]*
56
+ """
57
+ },
58
+ "Free QA": {
59
+ "dna": [{"question": "What is the main advantage of open-source software?", "suggested_answer": "It allows anyone to inspect, modify, and enhance the source code."}],
60
+ "grammar": r"""
61
+ root ::= "[" ws item ("," ws item)* ws "]"
62
+ item ::= "{" ws "\"question\"" ws ":" ws string "," ws "\"suggested_answer\"" ws ":" ws string ws "}"
63
+ string ::= "\"" ([^"\\\n] | "\\" (["\\/bfnrt] | "u"[0-9a-fA-F]{4}))* "\""
64
+ ws ::= [ \t\n\r]*
65
+ """
66
  }
67
  }
68
 
69
+ # ==========================================
70
+ # 3. GENERATION LOGIC
71
+ # ==========================================
72
  def generate_expansion(form_choice, user_requirement):
73
+ model = get_llm()
74
  selected_dna = TEMPLATES[form_choice]["dna"]
75
  quiz_grammar = LlamaGrammar.from_string(TEMPLATES[form_choice]["grammar"])
76
 
77
  prompt = f"""<start_of_turn>user
78
  You are an expert educational content creator.
 
 
79
 
80
+ ### TASK REQUIREMENTS
81
+ {user_requirement}
82
+
83
+ ### STYLE GUIDE (DNA)
84
+ Follow this exact JSON structure and keys:
85
  {json.dumps(selected_dna, indent=2)}
86
 
87
+ ### INSTRUCTIONS
88
+ - Generate the exact questions requested.
89
+ - Output ONLY a raw JSON array matching the format above.
90
  <end_of_turn>
91
  <start_of_turn>model
92
  """
93
+
94
+ output = model(
95
+ prompt,
96
+ max_tokens=1500,
97
+ temperature=0.5,
98
+ stop=["<end_of_turn>"],
99
+ grammar=quiz_grammar,
100
+ echo=False
101
+ )
102
+
103
  try:
104
+ raw_text = output['choices'][0]['text'].strip()
105
+ quiz_data = json.loads(raw_text)
106
+ json_str = json.dumps(quiz_data, indent=2, ensure_ascii=False)
107
+ return quiz_data, form_choice, json_str
108
+
109
+ except Exception as e:
110
+ return [], form_choice, f"Error: {e}\n\nRaw Output:\n{output['choices'][0]['text']}"
111
 
112
+ # ==========================================
113
+ # 4. GRADIO INTERFACE
114
+ # ==========================================
115
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
116
  gr.Markdown("""
117
  # 🚀 RA-ICL: Generative Expansion & Template Engine
118
 
119
  Welcome! This Space demonstrates a highly advanced application of **Retrieval-Augmented In-Context Learning (RA-ICL)** known as **1-to-N Generative Expansion**.
120
 
121
+ ### 🧠 How this works:
122
+ 1. **DNA Injection:** We inject a single JSON sample (the "DNA") into the prompt.
123
+ 2. **Grammar Constraints:** We apply a strict GBNF Grammar to force valid JSON output.
124
+ 3. **Generative Expansion:** The model scales your "Creative Instructions" into a brand-new dataset.
 
 
 
 
 
 
125
  """)
126
 
127
+ # State variables to hold the dynamic data
128
  quiz_state = gr.State([])
129
  form_state = gr.State("")
130
 
131
  with gr.Row():
132
+ with gr.Column(scale=1):
133
+ form_choice = gr.Radio(
134
+ choices=["Multiple Choice", "Fill-in-the-blank", "Sentence Reorder", "Free QA"],
135
+ value="Multiple Choice",
136
+ label="1. Output Structure"
137
+ )
138
+ user_requirement = gr.Textbox(
139
+ label="2. Content Requirement",
140
+ placeholder="e.g., Generate 3 questions about German past tense verbs. Theme: Holiday.",
141
+ lines=5
142
+ )
143
+ generate_btn = gr.Button("Generate Interactive Quiz", variant="primary", size="lg")
144
+
145
+ with gr.Column(scale=2):
146
+ with gr.Tabs():
147
+ with gr.TabItem("🎮 Playable Quiz"):
148
+ @gr.render(inputs=[quiz_state, form_state])
149
+ def render_quiz(q_data, f_type):
150
+ if not q_data:
151
+ gr.Markdown("*Generate a quiz to see it here.*")
152
+ return
153
+
154
+ gr.Markdown(f"### {f_type} Quiz")
155
 
156
+ for i, q in enumerate(q_data):
157
+ with gr.Group():
158
+ if f_type == "Multiple Choice":
159
+ gr.Markdown(f"**Q{i+1}:** {q['question']}")
160
+ user_ans = gr.Radio(choices=q['options'], label="Your Answer")
161
+ check_btn = gr.Button("Check Answer", size="sm")
162
+ feedback = gr.Markdown()
163
+
164
+ def check_mcq(ans, correct=q['answer']):
165
+ if not ans: return "⚠️ Please select an answer."
166
+ if ans == correct: return "✅ **Correct!**"
167
+ return f"❌ **Incorrect.** The correct answer is: {correct}"
168
+
169
+ check_btn.click(fn=check_mcq, inputs=[user_ans], outputs=[feedback])
170
+
171
+ elif f_type == "Fill-in-the-blank":
172
+ gr.Markdown(f"**Q{i+1}:** {q['question']}")
173
+ user_ans = gr.Textbox(label="Your Answer", placeholder="Type here...")
174
+ check_btn = gr.Button("Check Answer", size="sm")
175
+ feedback = gr.Markdown()
176
+
177
+ def check_fitb(ans, correct=q['answer']):
178
+ if not ans: return "⚠️ Please type an answer."
179
+ if ans.strip().lower() == correct.strip().lower(): return "✅ **Correct!**"
180
+ return f"❌ **Incorrect.** The correct answer is: {correct}"
181
+
182
+ check_btn.click(fn=check_fitb, inputs=[user_ans], outputs=[feedback])
183
+
184
+ elif f_type == "Sentence Reorder":
185
+ gr.Markdown(f"**Q{i+1} (Rearrange):**\n*{q['scrambled']}*")
186
+ user_ans = gr.Textbox(label="Correct Order", placeholder="Type the full sentence here...")
187
+ check_btn = gr.Button("Check Answer", size="sm")
188
+ feedback = gr.Markdown()
189
+
190
+ def check_reorder(ans, correct=q['correct_order']):
191
+ if not ans: return "⚠️ Please type an answer."
192
+ clean_ans = re.sub(r'[^a-z0-9äöüß]', '', ans.lower())
193
+ clean_correct = re.sub(r'[^a-z0-9äöüß]', '', correct.lower())
194
+ if clean_ans == clean_correct: return "✅ **Correct!**"
195
+ return f"❌ **Incorrect.** The correct sentence is:\n{correct}"
196
+
197
+ check_btn.click(fn=check_reorder, inputs=[user_ans], outputs=[feedback])
198
+
199
+ elif f_type == "Free QA":
200
+ gr.Markdown(f"**Q{i+1}:** {q['question']}")
201
+ user_ans = gr.Textbox(label="Your Answer", lines=2)
202
+ show_btn = gr.Button("Show Suggested Answer", size="sm")
203
+ feedback = gr.Markdown(visible=False)
204
+
205
+ def show_ans(correct=q['suggested_answer']):
206
+ return gr.update(value=f"**Suggested Answer:** {correct}", visible=True)
207
+
208
+ show_btn.click(fn=show_ans, outputs=[feedback])
209
+
210
+ with gr.TabItem("⚙️ Raw Data (JSON)"):
211
+ output_json = gr.Code(label="Generated JSON Output", language="json")
212
 
213
+ generate_btn.click(
214
+ fn=generate_expansion,
215
+ inputs=[form_choice, user_requirement],
216
+ outputs=[quiz_state, form_state, output_json]
217
+ )
218
 
219
+ if __name__ == "__main__":
220
+ demo.launch(server_name="0.0.0.0", server_port=7860)