| """Dictation generation prompt + light text cleanup for the Gradio Space. |
| |
| The LLM turns a learner's word list + CEFR level into ONE short German |
| dictation paragraph. This is distinct from the Anki flashcard prompts in |
| sys.md / sys1.md, which emit JSON — here we want plain prose to be read aloud. |
| """ |
|
|
| import re |
|
|
| DICTATION_SYSTEM_PROMPT = ( |
| "You are a German teacher writing a short dictation (Diktat) for a language " |
| "learner.\n" |
| "\n" |
| "Write ONE coherent German paragraph of 2-4 natural, everyday sentences that " |
| "uses EVERY target word the user provides at least once (inflect it naturally; " |
| "the exact base form need not appear). The text must suit the given CEFR level:\n" |
| "- A1/A2: short main clauses, common vocabulary, present tense, simple word order.\n" |
| "- B1/B2: allow subordinate clauses, connectors and varied tenses, still everyday.\n" |
| "\n" |
| "Use correct standard German spelling, capitalization, umlauts (ä/ö/ü) and ß.\n" |
| "Output ONLY the German dictation text. No title, no label, no quotation marks, " |
| "no translation, no commentary, no markdown." |
| ) |
|
|
|
|
| def build_user_prompt(words: list[str], level: str) -> str: |
| """The user turn: the target words and the requested level.""" |
| joined = ", ".join(words) |
| return f"Level: {level}\nTarget words: {joined}" |
|
|
|
|
| def parse_word_list(raw: str) -> list[str]: |
| """Split learner input on commas and/or newlines into trimmed words, |
| dropping blanks while preserving order (spec §4).""" |
| parts = re.split(r"[,\n]+", raw or "") |
| return [w.strip() for w in parts if w.strip()] |
|
|
|
|
| _LABEL_RE = re.compile(r"^\s*(diktat|dictation|text)\s*:\s*", re.IGNORECASE) |
|
|
|
|
| def clean_dictation(text: str) -> str: |
| """Strip a leading label line (e.g. 'Diktat:') and wrapping quotes the LLM |
| may add despite instructions (spec §4).""" |
| text = (text or "").strip() |
| text = _LABEL_RE.sub("", text, count=1).strip() |
| |
| pairs = [('"', '"'), ("'", "'"), ("“", "”"), ("„", "“")] |
| for open_q, close_q in pairs: |
| if len(text) >= 2 and text[0] == open_q and text[-1] == close_q: |
| text = text[1:-1].strip() |
| break |
| return text |
|
|