""" ui_strings.py — All user-facing copy for the v1 Query tab. Kept in one place so wording can be iterated on without touching app.py. """ from __future__ import annotations # ── Header ───────────────────────────────────────────────────────────── APP_TITLE = "Ask your school a question." APP_TAGLINE = ( "Ask a question in plain English about attendance, grades, or discipline. " "We'll write the query and bring you the answer." ) # ── Domain sections (starter questions) ──────────────────────────────── # Each section is a label, a short blurb shown under the label, and 2-3 # natural-language questions phrased the way a non-technical user # (secretary, principal, VP) would actually ask. DOMAIN_SECTIONS: list[dict] = [ { "title": "Chronic absenteeism trends", "questions": [ "How many students were chronically absent this year?", "Which school has the worst attendance?", "Show me the chronically absent students at Lincoln Elementary.", ], }, { "title": "Discipline instances by school", "questions": [ "How many discipline incidents happened at Washington Middle this year?", "What kinds of incidents are most common at Jefferson High?", "Show me total suspension days by school.", ], }, { "title": "Grades and academic comparisons", "questions": [ "What's the average GPA for chronically absent students?", "How do GPAs compare across schools this year?", "Which grade level has the highest average GPA?", ], }, ] # ── Input helpers ────────────────────────────────────────────────────── INPUT_HELPER = ( "You can ask about: schools, school years (like 2023-2024), grade levels, " "students, attendance, discipline incidents, and grades." ) INPUT_PROMPT_EMPTY = ( "Type a question above, or pick one from the suggestions to get started." ) FIRST_VISIT_NUDGE = ( '' "First time here?" "" " Type a question in plain English, or click any of the " "suggestions above to see how it works." ) # ── About / FAQ modal content (HTML) ───────────────────────────────── ABOUT_MODAL_TITLE = "About Local First Education Data Framework" ABOUT_MODAL_INTRO = ( "Local First Education Data Framework (LFED) is a local-first education data " "assistant. It lets school admins ask plain-English questions about district " "data and get answers instantly — without sending anything to the cloud." ) ABOUT_MODAL_HOW_IT_WORKS = ( "You type a question like “What’s the average GPA for chronically absent " "students in 2023-2024?” A language model running on this machine turns " "it into a read-only SQL query, runs it against an in-memory DuckDB database, " "and returns the result as a sentence and a table." ) ABOUT_MODAL_PRIVACY = ( "Everything stays on this machine. Your questions, the generated query, and " "the results are not sent anywhere, stored, or logged. When you close the " "page, the conversation is gone." ) ABOUT_MODAL_WHAT_IT_IS_BULLETS = [ "Ask attendance, discipline, grade, enrollment, and demographic questions in plain English.", "Get a plain-English summary plus a sortable table of results.", "Inspect the generated SQL with Show me how this was computed.", "Download any result table to CSV.", "Run entirely on your own hardware — no API keys or internet required (local build).", ] ABOUT_MODAL_WHAT_IT_ISNT_BULLETS = [ "It does not change any data — all queries are read-only.", "It is not a replacement for your student information system; it is a question-answering layer on top.", "It does not know individual students by name — only by anonymized ID.", "It does not store questions between sessions.", ] ABOUT_MODAL_FAQ = [ { "q": "What data can I ask about?", "a": "Five synthetic school tables: students, enrollment, attendance, discipline, and grades. You can ask about schools, school years, grade levels, demographics, absences, incidents, and academic performance.", }, { "q": "Why do I sometimes need to name a school or year?", "a": "The model is fine-tuned on school-data questions, but it still needs enough context to write a safe, correct query. Naming a school and school year usually produces the most reliable results.", }, { "q": "What model is running?", "a": "This demo runs a fine-tuned Qwen2.5-Coder-14B (QLoRA adapter on a bnb-4-bit base) via Transformers on Hugging Face ZeroGPU. The local build uses the same fine-tune as a GGUF in llama.cpp.", }, { "q": "Can I trust the SQL it writes?", "a": "Every generated query is validated: it must be SELECT-only, reference known tables and columns, and avoid forbidden tokens. If validation fails, you get a clear message instead of a result.", }, { "q": "Can I use my own real school data?", "a": "This Space runs on deterministic synthetic seed data. The local-first build can be pointed at your own DuckDB or Parquet files while keeping the same read-only guardrails.", }, ] ABOUT_MODAL_CLOSE = "Close" ABOUT_MODAL_HINT = ( "Tip: If the model misinterprets a question, rephrase it and include a " "specific school name and school year." ) # ── Summary templates ────────────────────────────────────────────────── # Plain-English one-liners used as the headline above the result table. SUMMARY_TEMPLATES = { "single_value": "The answer is **{value}**.", "single_pair": "**{label}**: {value}.", "by_school": "Here's the breakdown across {n} schools.", "generic": "Here are the {n} rows that match.", } # ── Error rephrasings ────────────────────────────────────────────────── # Map substrings of raw error messages → user-friendly message. # Order matters: more specific markers first. ERROR_REPHRASINGS: dict[str, str] = { "validation": ( "I couldn't turn that question into a query I trust. " "Try rephrasing it more simply — for example, name a school and a school year." ), "forbidden": ( "That question would ask for something I don't allow (like changing data). " "This tool is read-only, so try asking a question instead." ), "timeout": ( "That took too long to look up. " "Try narrowing your question to a specific school or school year." ), "model": ( "I'm having trouble understanding that question. " "Try rephrasing it the way you'd say it out loud." ), "missing from clause": ( "I couldn't figure out where to look. " "Try naming what you want to see and which school or year." ), } # ── Result UI copy ───────────────────────────────────────────────────── SQL_DISCLOSURE_LABEL = "Show me how this was computed" PREVIOUS_RIBBON_TEMPLATE = ( "Your previous answer: {summary}" ) # ── Footer + explainer ───────────────────────────────────────────────── WHAT_THIS_IS_ONE_LINER = ( "A way to ask questions about your school's data in plain English, " "that runs on your own machine." ) WHAT_THIS_IS_NOT_ONE_LINER = ( "It's not connected to the internet, it doesn't store your questions " "between sessions, and it isn't a replacement for your student information system." ) # Bulleted lists revealed by the 'Read the full explainer' footer button WHAT_THIS_IS = [ "A way to ask questions about your school's data in plain English.", "A read-only tool — it answers questions, it doesn't change anything.", "Something you can run on a single school computer, no internet required.", "A model that's been fine-tuned on common school-data questions.", ] WHAT_THIS_IS_NOT = [ "It does not connect to the internet or send data anywhere.", "It does not store your questions between sessions.", "It does not replace your student information system — it's a layer on top.", "It does not know about individual students by name, only by ID.", ] HOW_IT_WORKS = ( "You ask a question in plain English. A language model (running on this " "machine) translates your question into a database query, and a small " "in-memory database runs it. You see the answer as a sentence and a table. " "If you want to see exactly what was looked up, click " "\u201cShow me how this was computed\u201d under any result." ) PRIVACY_EXPLAINER = ( "Everything happens on the machine you're using right now. Your questions, " "the generated query, and the results never leave this network. No accounts, " "no analytics, no telemetry. When you close this page, the conversation is " "gone." )