"""Roleplay output guardrails and OOC stripping.""" import re ROLEPLAY_GUARDRAILS = ( "[In-character rules — breaking these ruins immersion]\n" "- Reply ONLY as the character in the present moment: spoken lines, visible actions, " "thoughts they would actually show.\n" "- NEVER write: P.S., PS, postscripts, footnotes, section headers, " '"Статус кво", "Status quo", "To be continued", scene summaries, ' "editorial closings, or foreshadowing asides " '(e.g. "Когда вы выйдете...", "Ты уже знаешь правду...").\n' "- Do NOT explain subtext to the reader or predict future scenes. No narrator voice.\n" "- End inside the scene; do not wrap up with meta commentary.\n" "- Sections marked MANDATORY (relationship, state) are binding — obey without citing them." ) RP_OUTPUT_REMINDER = ( "\n\n--- Reply format (MANDATORY) ---\n" "Next message = in-character only. " "Forbidden in output: P.S., Статус кво, Status quo, author notes, summaries, footnotes.\n" "---" ) def status_quo_prompt_block(status_quo: str) -> str: sq = (status_quo or "").strip() if not sq: return "" return ( "\n\n--- Current situation (INTERNAL — player never sees this block) ---\n" + sq + "\nBackground truth for you only. " "Never echo this header, never open/close replies with 'Статус кво' or summaries. " "Show the situation through dialogue and action.\n---" ) _OOC_PARA_START = re.compile( r"^(?:" r"Статус\s*кво|Status\s*quo|" r"P\.?\s*S\.?|PS:|" r"Примечание:|Author'?s?\s*note:|" r"OOC:|\\[OOC\\]|" r"To be continued|Продолжение следует" r")\b", re.IGNORECASE, ) def strip_ooc_from_reply(text: str) -> str: """Remove common OOC tails (P.S., Статус кво paragraphs, etc.).""" if not text or not text.strip(): return text or "" out = text.rstrip() # Drop trailing P.S. block (often last paragraph). out = re.sub( r"(?is)\n\s*P\.?\s*S\.?\s*[:.\-—].*$", "", out, ).rstrip() parts = re.split(r"\n\s*\n", out) kept: list[str] = [] for part in parts: lines = [ln for ln in part.splitlines() if ln.strip()] if not lines: continue if _OOC_PARA_START.match(lines[0].strip()): continue kept.append(part) if not kept: return "" return "\n\n".join(kept).strip()