Download app.py from ramedde/spell: direct link, hf CLI and curl.
- Browser
- Download file 24 kB
-
https://huggingface.co/spaces/ramedde/spell/resolve/main/app.py
- Command line
-
hf download hf://spaces/ramedde/spell/app.py
-
curl -L -o app.py https://huggingface.co/spaces/ramedde/spell/resolve/main/app.py
24 kB
| """ | |
| Spelling Practice β Hugging Face Space | |
| Multi-user safe via gr.State (JSON string per session). | |
| """ | |
| # ββ Import gradio first, then patch the gradio_client 0.8 schema bug ββββββββββ | |
| # gradio_client 0.8 (bundled with gradio 4.40) crashes in get_api_info() when | |
| # any component schema contains `additionalProperties: true` (a bool). | |
| # The fix: wrap json_schema_to_python_type to catch the exception, patching | |
| # both the gradio_client module AND the already-imported reference in gradio.blocks. | |
| import gradio as gr | |
| try: | |
| import gradio_client.utils as _gcu | |
| import gradio.blocks as _gb | |
| _orig_jspt = _gcu.json_schema_to_python_type | |
| def _safe_jspt(schema): | |
| try: | |
| return _orig_jspt(schema) | |
| except Exception: | |
| return "Any" | |
| _gcu.json_schema_to_python_type = _safe_jspt | |
| _gb.client_utils.json_schema_to_python_type = _safe_jspt | |
| except Exception: | |
| pass | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| import os | |
| import glob | |
| import random | |
| import tempfile | |
| import difflib | |
| import json | |
| import time | |
| import threading | |
| from gtts import gTTS | |
| try: | |
| from llama_cpp import Llama | |
| _LLAMA_AVAILABLE = True | |
| except ImportError: | |
| _LLAMA_AVAILABLE = False | |
| _llm = None | |
| _llm_lock = threading.Lock() | |
| def get_llm(): | |
| global _llm | |
| if _llm is not None: | |
| return _llm | |
| if not _LLAMA_AVAILABLE: | |
| return None | |
| with _llm_lock: | |
| if _llm is not None: | |
| return _llm | |
| gguf_files = glob.glob("/app/*.gguf") + glob.glob("*.gguf") | |
| if not gguf_files: | |
| print("[spelling-app] No .gguf file found β AI tips disabled.") | |
| return None | |
| model_path = gguf_files[0] | |
| print(f"[spelling-app] Loading model: {model_path}") | |
| _llm = Llama(model_path=model_path, n_ctx=512, n_threads=2, | |
| n_gpu_layers=int(os.getenv("N_GPU_LAYERS", "-1")), verbose=False) | |
| print("[spelling-app] Model ready.") | |
| return _llm | |
| SENTENCES = { | |
| "easy": [ | |
| "The student read a book.", "I like to learn English.", | |
| "The bank has no money.", "She has a new job.", | |
| "He can read very fast.", "The map shows a river.", | |
| "We write notes in class.", "Put the book on the desk.", | |
| "I see a small farm.", "The goal is very clear.", | |
| ], | |
| "medium": [ | |
| "The Southern colonies grew tobacco for Europe.", | |
| "The workers found jobs in the factory.", | |
| "He forgot to cite the original source.", | |
| "They experienced a major economic boom.", | |
| "My teacher helps me write better essays.", | |
| "The market was very weak in December.", | |
| "She received a new evening gown quickly.", | |
| "We need to manage our time properly.", | |
| "The group discussion went very well.", | |
| "The online platform closes at midnight.", | |
| ], | |
| "hard": [ | |
| "The government announced policies to address the downturn.", | |
| "She has an extraordinary talent for analyzing economic literature.", | |
| "The archaeologist made a fascinating discovery of ancient artifacts.", | |
| "It is necessary to manage your time effectively.", | |
| "The corporation was highly profitable after the merger.", | |
| "He struggled to translate the complex economic metaphors.", | |
| "The instructor approved the research outline immediately.", | |
| "She demonstrated remarkable perseverance throughout her academic journey.", | |
| "Maintaining a regular study schedule is very important.", | |
| "The researcher examined the primary historical document.", | |
| ], | |
| "expert": [ | |
| "The multinational corporation acknowledged the financial discrepancy.", | |
| "His idiosyncratic research methodologies bewildered his academic colleagues.", | |
| "The distinguished economist delivered a highly influential speech.", | |
| "Technological advancements fundamentally transformed nineteenth-century transportation.", | |
| "Maslow conceptualized self-actualization as the highest human need.", | |
| "Agricultural consolidation overwhelmed small family farming operations.", | |
| "Translating metaphorical expressions requires extraordinary cultural competence.", | |
| "Plagiarism often leads to severe academic consequences.", | |
| "The entrepreneur demonstrated extraordinary economic decision-making.", | |
| "The researcher meticulously catalogued primary historical sources.", | |
| ], | |
| } | |
| LEVELS = ["easy", "medium", "hard", "expert"] | |
| LEVEL_LABELS = ["π’ Beginner", "π΅ Intermediate", "π Advanced", "π΄ Expert"] | |
| PASS_REQ = 3 | |
| def fresh_state_str(): | |
| return json.dumps({"level": 0, "streak": 0, "sentence": "", | |
| "score": 0, "total": 0, "history": []}) | |
| def S(s): return json.loads(s) | |
| def D(d): return json.dumps(d) | |
| def _make_audio(text): | |
| try: | |
| tts = gTTS(text=text, lang="en", slow=True) | |
| path = tempfile.mktemp(suffix=".mp3") | |
| tts.save(path) | |
| return path | |
| except Exception as exc: | |
| print(f"[spelling-app] gTTS error: {exc}") | |
| return None | |
| def _find_errors(correct, attempt): | |
| c_words = correct.rstrip(".").lower().split() | |
| a_words = attempt.rstrip(".").lower().split() | |
| errors = [] | |
| for tag, i1, i2, j1, j2 in difflib.SequenceMatcher(None, a_words, c_words).get_opcodes(): | |
| if tag == "replace": | |
| for w, r in zip(a_words[i1:i2], c_words[j1:j2]): | |
| if w != r: errors.append((w, r)) | |
| elif tag == "delete": | |
| for w in a_words[i1:i2]: errors.append((w, "[extra word]")) | |
| elif tag == "insert": | |
| for r in c_words[j1:j2]: errors.append(("[missing]", r)) | |
| return errors | |
| def _ask_gemma(correct, attempt, errors): | |
| llm = get_llm() | |
| if not llm or not errors: return "" | |
| error_list = "\n".join(f'- You wrote "{w}", correct is "{r}"' for w, r in errors) | |
| prompt = (f"You are a friendly English spelling teacher.\n" | |
| f"The student had to write: {correct}\n" | |
| f"The student wrote: {attempt}\n" | |
| f"Spelling mistakes:\n{error_list}\n\n" | |
| "For each mistake, write one short sentence explaining the error " | |
| "and one memory tip. Be encouraging. Keep it brief.") | |
| out = llm(prompt, max_tokens=200, temperature=0.3, stop=["###"]) | |
| return out["choices"][0]["text"].strip() | |
| def _build_diff(correct, attempt): | |
| c_words, a_words = correct.split(), attempt.split() | |
| parts = [] | |
| sm = difflib.SequenceMatcher( | |
| None, [w.rstrip(".,").lower() for w in a_words], | |
| [w.rstrip(".,").lower() for w in c_words]) | |
| for tag, i1, i2, j1, j2 in sm.get_opcodes(): | |
| if tag == "equal": parts.append(" ".join(a_words[i1:i2])) | |
| elif tag == "replace": parts.append("~~"+" ".join(a_words[i1:i2])+"~~ **"+" ".join(c_words[j1:j2])+"**") | |
| elif tag == "delete": parts.append("~~"+" ".join(a_words[i1:i2])+"~~") | |
| elif tag == "insert": parts.append("**["+" ".join(c_words[j1:j2])+"]**") | |
| return " ".join(parts) | |
| def _level_info(state): | |
| lvl = state["level"] | |
| extra = f" Β· streak {state['streak']}/{PASS_REQ} to level up" if lvl < len(LEVELS)-1 else " Β· MAX LEVEL π" | |
| return f"**{LEVEL_LABELS[lvl]}**{extra}" | |
| def _score_info(state): | |
| t, s = state["total"], state["score"] | |
| return f"**Score: {s}/{t}** ({int(s/t*100) if t else 0}%)" | |
| def new_sentence(ss): | |
| state = S(ss) | |
| sent = random.choice(SENTENCES[LEVELS[state["level"]]]) | |
| state["sentence"] = sent | |
| audio = _make_audio(sent) | |
| hint = "π§ Listen carefully, then type what you heard and press **Submit**." | |
| if audio is None: | |
| hint = f"β οΈ Audio unavailable. Sentence: **{sent}**" | |
| return audio, "", hint, _level_info(state), _score_info(state), D(state) | |
| def submit_answer(attempt, ss): | |
| state = S(ss) | |
| if not state.get("sentence"): | |
| return "", "β οΈ Press **New Sentence** first!", _level_info(state), _score_info(state), ss | |
| correct = state["sentence"] | |
| state["total"] += 1 | |
| errors = _find_errors(correct, attempt) | |
| clean_a = attempt.strip().rstrip(".").lower() | |
| clean_c = correct.strip().rstrip(".").lower() | |
| if not errors and clean_a == clean_c: | |
| state["score"] += 1 | |
| state["streak"] += 1 | |
| msg = "β **Correct!** Every word spelled perfectly." | |
| if state["streak"] >= PASS_REQ and state["level"] < len(LEVELS)-1: | |
| state["level"] += 1 | |
| state["streak"] = 0 | |
| msg += f"\n\nπ **Level up! You are now {LEVEL_LABELS[state['level']]}.**" | |
| else: | |
| state["streak"] = 0 | |
| diff_str = _build_diff(correct, attempt) | |
| gemma_tip = _ask_gemma(correct, attempt, errors) | |
| ai_block = f"\n\n---\n\n**π‘ Tips:**\n\n{gemma_tip}" if gemma_tip else "" | |
| msg = ("β **Your answer with corrections:**\n\n" + diff_str + | |
| "\n\n*(~~strikethrough~~ = your error Β· **bold** = correct spelling)*" + ai_block) | |
| state["history"].append({"correct": correct, "attempt": attempt, | |
| "ok": not bool(errors) and clean_a == clean_c, | |
| "level": LEVELS[state["level"]], "ts": time.strftime("%H:%M:%S")}) | |
| state["sentence"] = "" | |
| return attempt, msg, _level_info(state), _score_info(state), D(state) | |
| def replay_audio(ss): | |
| state = S(ss) | |
| return _make_audio(state["sentence"]) if state.get("sentence") else None | |
| def reset_game(_ss): | |
| state = S(fresh_state_str()) | |
| return None, "", "Game reset. Press **New Sentence** to start!", _level_info(state), _score_info(state), fresh_state_str() | |
| def export_history(ss): | |
| history = S(ss).get("history", []) | |
| if not history: | |
| return "No history yet." | |
| lines = [] | |
| for h in history: | |
| ok = "β " if h["ok"] else "β" | |
| lines.append(f'{ok} [{h["level"]}] {h["ts"]} | {h["correct"]} β {h["attempt"]}') | |
| return "\n".join(lines) | |
| CSS = "footer { display: none !important; }" | |
| _init = S(fresh_state_str()) | |
| with gr.Blocks(title="Spelling Practice", css=CSS) as demo: | |
| session = gr.State(fresh_state_str()) | |
| gr.Markdown("# π Spelling Practice\nListen Β· Type Β· See errors Β· Get tips") | |
| with gr.Row(): | |
| level_md = gr.Markdown(_level_info(_init)) | |
| score_md = gr.Markdown(_score_info(_init)) | |
| with gr.Row(): | |
| new_btn = gr.Button("π² New Sentence", variant="primary") | |
| replay_btn = gr.Button("π Replay Audio", variant="secondary") | |
| reset_btn = gr.Button("π Reset Game", variant="secondary") | |
| audio_out = gr.Audio(label="Listen to the sentence", autoplay=True, interactive=False) | |
| hint_md = gr.Markdown("Press **New Sentence** to begin.") | |
| answer_box = gr.Textbox(label="Type the sentence you heard", | |
| placeholder="Write the full sentence hereβ¦", lines=2) | |
| submit_btn = gr.Button("β Submit", variant="primary") | |
| feedback = gr.Markdown("") | |
| with gr.Accordion("π Session History", open=False): | |
| export_btn = gr.Button("π Show history") | |
| history_box = gr.Textbox(label="Your session", lines=10, interactive=False) | |
| OUTS = [audio_out, answer_box, hint_md, level_md, score_md, session] | |
| new_btn.click(new_sentence, [session], OUTS) | |
| replay_btn.click(replay_audio, [session], [audio_out]) | |
| submit_btn.click(submit_answer, [answer_box, session], [answer_box, feedback, level_md, score_md, session]) | |
| answer_box.submit(submit_answer,[answer_box, session], [answer_box, feedback, level_md, score_md, session]) | |
| reset_btn.click(reset_game, [session], OUTS) | |
| export_btn.click(export_history,[session], [history_box]) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |
| """ | |
| Spelling Practice β Hugging Face Space | |
| Multi-user safe via gr.State (JSON string per session). | |
| """ | |
| # ββ Import gradio first, then patch the gradio_client 0.8 schema bug ββββββββββ | |
| # gradio_client 0.8 (bundled with gradio 4.40) crashes in get_api_info() when | |
| # any component schema contains `additionalProperties: true` (a bool). | |
| # The fix: wrap json_schema_to_python_type to catch the exception, patching | |
| # both the gradio_client module AND the already-imported reference in gradio.blocks. | |
| import gradio as gr | |
| try: | |
| import gradio_client.utils as _gcu | |
| import gradio.blocks as _gb | |
| _orig_jspt = _gcu.json_schema_to_python_type | |
| def _safe_jspt(schema): | |
| try: | |
| return _orig_jspt(schema) | |
| except Exception: | |
| return "Any" | |
| _gcu.json_schema_to_python_type = _safe_jspt | |
| _gb.client_utils.json_schema_to_python_type = _safe_jspt | |
| except Exception: | |
| pass | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| import os | |
| import glob | |
| import random | |
| import tempfile | |
| import difflib | |
| import json | |
| import time | |
| import threading | |
| from gtts import gTTS | |
| try: | |
| from llama_cpp import Llama | |
| _LLAMA_AVAILABLE = True | |
| except ImportError: | |
| _LLAMA_AVAILABLE = False | |
| _llm = None | |
| _llm_lock = threading.Lock() | |
| def get_llm(): | |
| global _llm | |
| if _llm is not None: | |
| return _llm | |
| if not _LLAMA_AVAILABLE: | |
| return None | |
| with _llm_lock: | |
| if _llm is not None: | |
| return _llm | |
| gguf_files = glob.glob("/app/*.gguf") + glob.glob("*.gguf") | |
| if not gguf_files: | |
| print("[spelling-app] No .gguf file found β AI tips disabled.") | |
| return None | |
| model_path = gguf_files[0] | |
| print(f"[spelling-app] Loading model: {model_path}") | |
| _llm = Llama(model_path=model_path, n_ctx=512, n_threads=2, | |
| n_gpu_layers=int(os.getenv("N_GPU_LAYERS", "-1")), verbose=False) | |
| print("[spelling-app] Model ready.") | |
| return _llm | |
| SENTENCES = { | |
| "easy": [ | |
| "The student read a book.", "I like to learn English.", | |
| "The bank has no money.", "She has a new job.", | |
| "He can read very fast.", "The map shows a river.", | |
| "We write notes in class.", "Put the book on the desk.", | |
| "I see a small farm.", "The goal is very clear.", | |
| ], | |
| "medium": [ | |
| "The Southern colonies grew tobacco for Europe.", | |
| "The workers found jobs in the factory.", | |
| "He forgot to cite the original source.", | |
| "They experienced a major economic boom.", | |
| "My teacher helps me write better essays.", | |
| "The market was very weak in December.", | |
| "She received a new evening gown quickly.", | |
| "We need to manage our time properly.", | |
| "The group discussion went very well.", | |
| "The online platform closes at midnight.", | |
| ], | |
| "hard": [ | |
| "The government announced policies to address the downturn.", | |
| "She has an extraordinary talent for analyzing economic literature.", | |
| "The archaeologist made a fascinating discovery of ancient artifacts.", | |
| "It is necessary to manage your time effectively.", | |
| "The corporation was highly profitable after the merger.", | |
| "He struggled to translate the complex economic metaphors.", | |
| "The instructor approved the research outline immediately.", | |
| "She demonstrated remarkable perseverance throughout her academic journey.", | |
| "Maintaining a regular study schedule is very important.", | |
| "The researcher examined the primary historical document.", | |
| ], | |
| "expert": [ | |
| "The multinational corporation acknowledged the financial discrepancy.", | |
| "His idiosyncratic research methodologies bewildered his academic colleagues.", | |
| "The distinguished economist delivered a highly influential speech.", | |
| "Technological advancements fundamentally transformed nineteenth-century transportation.", | |
| "Maslow conceptualized self-actualization as the highest human need.", | |
| "Agricultural consolidation overwhelmed small family farming operations.", | |
| "Translating metaphorical expressions requires extraordinary cultural competence.", | |
| "Plagiarism often leads to severe academic consequences.", | |
| "The entrepreneur demonstrated extraordinary economic decision-making.", | |
| "The researcher meticulously catalogued primary historical sources.", | |
| ], | |
| } | |
| LEVELS = ["easy", "medium", "hard", "expert"] | |
| LEVEL_LABELS = ["π’ Beginner", "π΅ Intermediate", "π Advanced", "π΄ Expert"] | |
| PASS_REQ = 3 | |
| def fresh_state_str(): | |
| return json.dumps({"level": 0, "streak": 0, "sentence": "", | |
| "score": 0, "total": 0, "history": []}) | |
| def S(s): return json.loads(s) | |
| def D(d): return json.dumps(d) | |
| def _make_audio(text): | |
| try: | |
| tts = gTTS(text=text, lang="en", slow=True) | |
| path = tempfile.mktemp(suffix=".mp3") | |
| tts.save(path) | |
| return path | |
| except Exception as exc: | |
| print(f"[spelling-app] gTTS error: {exc}") | |
| return None | |
| def _find_errors(correct, attempt): | |
| c_words = correct.rstrip(".").lower().split() | |
| a_words = attempt.rstrip(".").lower().split() | |
| errors = [] | |
| for tag, i1, i2, j1, j2 in difflib.SequenceMatcher(None, a_words, c_words).get_opcodes(): | |
| if tag == "replace": | |
| for w, r in zip(a_words[i1:i2], c_words[j1:j2]): | |
| if w != r: errors.append((w, r)) | |
| elif tag == "delete": | |
| for w in a_words[i1:i2]: errors.append((w, "[extra word]")) | |
| elif tag == "insert": | |
| for r in c_words[j1:j2]: errors.append(("[missing]", r)) | |
| return errors | |
| def _ask_gemma(correct, attempt, errors): | |
| llm = get_llm() | |
| if not llm or not errors: return "" | |
| error_list = "\n".join(f'- You wrote "{w}", correct is "{r}"' for w, r in errors) | |
| prompt = (f"You are a friendly English spelling teacher.\n" | |
| f"The student had to write: {correct}\n" | |
| f"The student wrote: {attempt}\n" | |
| f"Spelling mistakes:\n{error_list}\n\n" | |
| "For each mistake, write one short sentence explaining the error " | |
| "and one memory tip. Be encouraging. Keep it brief.") | |
| out = llm(prompt, max_tokens=200, temperature=0.3, stop=["###"]) | |
| return out["choices"][0]["text"].strip() | |
| def _build_diff(correct, attempt): | |
| c_words, a_words = correct.split(), attempt.split() | |
| parts = [] | |
| sm = difflib.SequenceMatcher( | |
| None, [w.rstrip(".,").lower() for w in a_words], | |
| [w.rstrip(".,").lower() for w in c_words]) | |
| for tag, i1, i2, j1, j2 in sm.get_opcodes(): | |
| if tag == "equal": parts.append(" ".join(a_words[i1:i2])) | |
| elif tag == "replace": parts.append("~~"+" ".join(a_words[i1:i2])+"~~ **"+" ".join(c_words[j1:j2])+"**") | |
| elif tag == "delete": parts.append("~~"+" ".join(a_words[i1:i2])+"~~") | |
| elif tag == "insert": parts.append("**["+" ".join(c_words[j1:j2])+"]**") | |
| return " ".join(parts) | |
| def _level_info(state): | |
| lvl = state["level"] | |
| extra = f" Β· streak {state['streak']}/{PASS_REQ} to level up" if lvl < len(LEVELS)-1 else " Β· MAX LEVEL π" | |
| return f"**{LEVEL_LABELS[lvl]}**{extra}" | |
| def _score_info(state): | |
| t, s = state["total"], state["score"] | |
| return f"**Score: {s}/{t}** ({int(s/t*100) if t else 0}%)" | |
| def new_sentence(ss): | |
| state = S(ss) | |
| sent = random.choice(SENTENCES[LEVELS[state["level"]]]) | |
| state["sentence"] = sent | |
| audio = _make_audio(sent) | |
| hint = "π§ Listen carefully, then type what you heard and press **Submit**." | |
| if audio is None: | |
| hint = f"β οΈ Audio unavailable. Sentence: **{sent}**" | |
| return audio, "", hint, _level_info(state), _score_info(state), D(state) | |
| def submit_answer(attempt, ss): | |
| state = S(ss) | |
| if not state.get("sentence"): | |
| return "", "β οΈ Press **New Sentence** first!", _level_info(state), _score_info(state), ss | |
| correct = state["sentence"] | |
| state["total"] += 1 | |
| errors = _find_errors(correct, attempt) | |
| clean_a = attempt.strip().rstrip(".").lower() | |
| clean_c = correct.strip().rstrip(".").lower() | |
| if not errors and clean_a == clean_c: | |
| state["score"] += 1 | |
| state["streak"] += 1 | |
| msg = "β **Correct!** Every word spelled perfectly." | |
| if state["streak"] >= PASS_REQ and state["level"] < len(LEVELS)-1: | |
| state["level"] += 1 | |
| state["streak"] = 0 | |
| msg += f"\n\nπ **Level up! You are now {LEVEL_LABELS[state['level']]}.**" | |
| else: | |
| state["streak"] = 0 | |
| diff_str = _build_diff(correct, attempt) | |
| gemma_tip = _ask_gemma(correct, attempt, errors) | |
| ai_block = f"\n\n---\n\n**π‘ Tips:**\n\n{gemma_tip}" if gemma_tip else "" | |
| msg = ("β **Your answer with corrections:**\n\n" + diff_str + | |
| "\n\n*(~~strikethrough~~ = your error Β· **bold** = correct spelling)*" + ai_block) | |
| state["history"].append({"correct": correct, "attempt": attempt, | |
| "ok": not bool(errors) and clean_a == clean_c, | |
| "level": LEVELS[state["level"]], "ts": time.strftime("%H:%M:%S")}) | |
| state["sentence"] = "" | |
| return attempt, msg, _level_info(state), _score_info(state), D(state) | |
| def replay_audio(ss): | |
| state = S(ss) | |
| return _make_audio(state["sentence"]) if state.get("sentence") else None | |
| def reset_game(_ss): | |
| state = S(fresh_state_str()) | |
| return None, "", "Game reset. Press **New Sentence** to start!", _level_info(state), _score_info(state), fresh_state_str() | |
| def export_history(ss): | |
| history = S(ss).get("history", []) | |
| if not history: | |
| return "No history yet." | |
| lines = [] | |
| for h in history: | |
| ok = "β " if h["ok"] else "β" | |
| lines.append(f'{ok} [{h["level"]}] {h["ts"]} | {h["correct"]} β {h["attempt"]}') | |
| return "\n".join(lines) | |
| CSS = "footer { display: none !important; }" | |
| _init = S(fresh_state_str()) | |
| with gr.Blocks(title="Spelling Practice", css=CSS) as demo: | |
| session = gr.State(fresh_state_str()) | |
| gr.Markdown("# π Spelling Practice\nListen Β· Type Β· See errors Β· Get tips") | |
| with gr.Row(): | |
| level_md = gr.Markdown(_level_info(_init)) | |
| score_md = gr.Markdown(_score_info(_init)) | |
| with gr.Row(): | |
| new_btn = gr.Button("π² New Sentence", variant="primary") | |
| replay_btn = gr.Button("π Replay Audio", variant="secondary") | |
| reset_btn = gr.Button("π Reset Game", variant="secondary") | |
| audio_out = gr.Audio(label="Listen to the sentence", autoplay=True, interactive=False) | |
| hint_md = gr.Markdown("Press **New Sentence** to begin.") | |
| answer_box = gr.Textbox(label="Type the sentence you heard", | |
| placeholder="Write the full sentence hereβ¦", lines=2) | |
| submit_btn = gr.Button("β Submit", variant="primary") | |
| feedback = gr.Markdown("") | |
| with gr.Accordion("π Session History", open=False): | |
| export_btn = gr.Button("π Show history") | |
| history_box = gr.Textbox(label="Your session", lines=10, interactive=False) | |
| OUTS = [audio_out, answer_box, hint_md, level_md, score_md, session] | |
| new_btn.click(new_sentence, [session], OUTS) | |
| replay_btn.click(replay_audio, [session], [audio_out]) | |
| submit_btn.click(submit_answer, [answer_box, session], [answer_box, feedback, level_md, score_md, session]) | |
| answer_box.submit(submit_answer,[answer_box, session], [answer_box, feedback, level_md, score_md, session]) | |
| reset_btn.click(reset_game, [session], OUTS) | |
| export_btn.click(export_history,[session], [history_box]) | |
| if __name__ == "__main__": | |
| demo.launch(server_name="0.0.0.0", server_port=7860) | |