MCPTest / app.py
KwabsHug's picture
Update app.py
2bcfdc5 verified
raw
history blame
7.3 kB
"""
NWFWO Practice – Gradio MCP app for ChatGPT
This file does two things:
1) Runs a normal Gradio web app (for debugging in your browser)
2) Exposes an MCP server + HTML UI card for ChatGPT Apps
"""
import gradio as gr
from dataclasses import dataclass
# ---- 1. Data model for the “game state” ----
@dataclass
class NWFWOExample:
# A short scenario or sentence in the foreign language (transliterated)
transliteration: str
# The correct NWFWO answer (what the learner should guess)
correct_nwfwo: str
# Optional: explanation to show after checking
explanation: str | None = None
# ---- 2. Core tool logic: check the learner’s guess ----
@gr.mcp.tool() # exposes the function as an MCP tool to ChatGPT / MCP clients
def check_nwfwo(
transliteration: str,
correct_nwfwo: str,
user_guess: str,
explanation: str | None = None,
):
"""
Simple checker for NWFWO practice.
Arguments (ChatGPT will send these as JSON):
- transliteration: the foreign sentence/word in transliteration
- correct_nwfwo: the correct NWFWO answer
- user_guess: what the learner typed or selected
- explanation: optional teacher explanation
Returns: a dict that ChatGPT AND the UI card can use.
"""
norm_correct = correct_nwfwo.strip().lower()
norm_guess = user_guess.strip().lower()
is_correct = norm_guess == norm_correct
# Build friendly feedback
if is_correct:
feedback = "✅ Correct! Your NWFWO matches the target."
else:
feedback = (
"❌ Not quite. Compare your NWFWO with the correct one "
"and think about which sounds or letters changed."
)
result = {
"transliteration": transliteration,
"correct_nwfwo": correct_nwfwo,
"user_guess": user_guess,
"is_correct": is_correct,
"feedback": feedback,
"explanation": explanation
or "This NWFWO shows how the foreign writing maps to the native word.",
}
return result
# ---- 3. Normal Gradio UI (handy while you’re developing) ----
def local_check_ui(transliteration, correct_nwfwo, user_guess, explanation):
res = check_nwfwo(
transliteration=transliteration,
correct_nwfwo=correct_nwfwo,
user_guess=user_guess,
explanation=explanation,
)
return (
f"Transliteration: {res['transliteration']}\n"
f"Your NWFWO: {res['user_guess']}\n"
f"Correct NWFWO: {res['correct_nwfwo']}\n\n"
f"{res['feedback']}\n\n"
f"Explanation: {res['explanation']}"
)
with gr.Blocks() as demo:
gr.Markdown("## NWFWO Practice – Local Debug UI")
with gr.Row():
with gr.Column():
transliteration_box = gr.Textbox(
label="Transliteration (foreign text written in your script)",
value="salaam",
)
correct_nwfwo_box = gr.Textbox(
label="Correct NWFWO",
value="salaam",
)
user_guess_box = gr.Textbox(
label="Your guess NWFWO",
value="salam",
)
explanation_box = gr.Textbox(
label="Explanation (optional)",
value="This example shows how long vs short vowels work.",
)
btn = gr.Button("Check")
with gr.Column():
result_box = gr.Textbox(
label="Result",
lines=8,
)
btn.click(
local_check_ui,
inputs=[
transliteration_box,
correct_nwfwo_box,
user_guess_box,
explanation_box,
],
outputs=[result_box],
)
# ---- 4. HTML UI card resource for ChatGPT App ----
@gr.mcp.resource(
"ui://widget/nwfwo-practice-card.html",
mime_type="text/html+skybridge",
)
def nwfwo_html_card():
"""
This HTML will appear as a card inside ChatGPT when this tool runs.
It can read:
- window.openai.toolInput (what ChatGPT sent into the tool)
- window.openai.toolOutput (what our Python tool returned)
"""
html = r"""
<div id="nwfwo-card-root"></div>
<script>
const root = document.getElementById("nwfwo-card-root");
function render() {
const input = (window.openai && window.openai.toolInput) || {};
const output = (window.openai && window.openai.toolOutput) || {};
const transliteration = input.transliteration || output.transliteration || "salaam";
const userGuess = input.user_guess || output.user_guess || "salam";
const correctNwfwo = input.correct_nwfwo || output.correct_nwfwo || "salaam";
const isCorrect = output.is_correct;
const feedback = output.feedback || "";
const explanation = output.explanation || "";
let badge = "";
if (typeof isCorrect === "boolean") {
badge = isCorrect
? '<span style="padding:4px 8px;border-radius:999px;background:#d4edda;">Correct</span>'
: '<span style="padding:4px 8px;border-radius:999px;background:#f8d7da;">Try again</span>';
}
root.innerHTML = `
<div style="
border-radius:16px;
border:1px solid #ddd;
padding:16px;
font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
max-width: 500px;
">
<div style="font-size:14px;color:#666;margin-bottom:4px;">
NWFWO Practice
</div>
<div style="font-size:18px;font-weight:600;margin-bottom:12px;">
Transliteration
</div>
<div style="padding:8px 12px;border-radius:12px;background:#f5f5f5;margin-bottom:12px;">
${transliteration}
</div>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
<div>
<div style="font-size:14px;color:#666;">Your NWFWO</div>
<div style="font-size:16px;">${userGuess}</div>
</div>
<div>
<div style="font-size:14px;color:#666;">Target NWFWO</div>
<div style="font-size:16px;font-weight:600;">${correctNwfwo}</div>
</div>
</div>
<div style="margin-bottom:8px;">
${badge}
</div>
<div style="font-size:14px;margin-top:8px;border-top:1px solid #eee;padding-top:8px;">
<div style="font-weight:600;margin-bottom:4px;">Feedback</div>
<div>${feedback}</div>
</div>
<div style="font-size:13px;color:#555;margin-top:8px;">
<div style="font-weight:600;margin-bottom:4px;">Explanation</div>
<div>${explanation}</div>
</div>
</div>
`;
}
render();
</script>
"""
return html
# ---- 5. Main entrypoint ----
if __name__ == "__main__":
# On Hugging Face Spaces, you don't need to set port/host manually.
# mcp_server=True exposes the MCP endpoint (on /gradio_api/mcp/ in Spaces).
demo.launch(
mcp_server=True,
)