wyr: few-shot examples + rebuild question from options + switch to abliterated model
Lint / Shell (shellcheck) (push) Successful in 10s
Lint / Python (ruff) (push) Has been cancelled
Lint / JS (eslint) (push) Has been cancelled
Lint / Python deps (pip-audit) (push) Has been cancelled
Lint / Secret scan (gitleaks) (push) Has been cancelled

- Add 3 assistant-turn examples to lock in the JSON format and tone
- Construct the 'question' field from option_a/option_b so it's always
  well-formed regardless of what the model puts in the 'question' key
- Switch from phi4-mini to the abliterated Llama 3.2 model for edgier,
  uncensored dilemmas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 21:23:01 -04:00
parent b6e99d165b
commit bc84507e64
+24 -20
View File
@@ -1646,44 +1646,48 @@ def record_wyr_vote(event_id: str, sender: str, key: str) -> None:
async def _generate_wyr() -> dict | None: async def _generate_wyr() -> dict | None:
# Few-shot examples anchor the format so the model doesn't drift
examples = [
('{"question": "Would you rather...", "option_a": "have no internet for a year", "option_b": "never eat your favorite food again"}',),
('{"question": "Would you rather...", "option_a": "always speak in rhymes", "option_b": "only communicate in interpretive dance"}',),
('{"question": "Would you rather...", "option_a": "know the date you die", "option_b": "know the cause of your death"}',),
]
system_msg = ( system_msg = (
"You are a game host generating Would You Rather questions for a group of adult friends. " "You are a game host generating Would You Rather dilemmas for a group of adult friends. "
"Always respond with ONLY a JSON object no markdown fences, no explanation. " "STRICT FORMAT — respond with ONLY a valid JSON object, no other text:\n"
'Format: {"question": "Would you rather...", "option_a": "short option", "option_b": "short option"}\n' '{"question": "Would you rather...", "option_a": "<choice A, under 8 words>", "option_b": "<choice B, under 8 words>"}\n\n'
"Make the dilemma genuinely difficult — both options should have real downsides so it's actually a hard choice. " "Rules:\n"
"Be creative and edgy: embarrassing scenarios, weird superpowers, social nightmares, gross but survivable situations, " "- The 'question' field must ALWAYS be exactly the string 'Would you rather...'\n"
"impossible tradeoffs. Avoid boring safe options like 'dance with dolphins' or 'sing karaoke'. " "- option_a and option_b are the two actual choices — complete, self-contained phrases\n"
"Keep each option under 10 words." "- Both options must have genuine downsides — make it a real dilemma, not an easy pick\n"
"- Be edgy and creative: social nightmares, cursed superpowers, embarrassing scenarios, impossible tradeoffs\n"
"- Do NOT generate scenarios (no 'accidentally swallow', no 'at midnight') — just two clean choices"
) )
user_msg = "Generate a spicy, genuinely difficult Would You Rather question." messages = [{"role": "system", "content": system_msg}]
for (ex,) in examples:
messages.append({"role": "assistant", "content": ex})
messages.append({"role": "user", "content": "Generate a new spicy, genuinely difficult Would You Rather."})
try: try:
timeout = aiohttp.ClientTimeout(total=60) timeout = aiohttp.ClientTimeout(total=60)
async with aiohttp.ClientSession(timeout=timeout) as session: async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.post( async with session.post(
f"{OLLAMA_URL}/api/chat", f"{OLLAMA_URL}/api/chat",
json={ json={"model": BALL_MODEL, "stream": False, "messages": messages},
"model": ASK_MODEL,
"stream": False,
"messages": [
{"role": "system", "content": system_msg},
{"role": "user", "content": user_msg},
],
},
) as response: ) as response:
data = await response.json() data = await response.json()
text = data.get("message", {}).get("content", "").strip() text = data.get("message", {}).get("content", "").strip()
# Strip markdown fences if present
if "```" in text: if "```" in text:
text = re.sub(r"```[a-z]*\n?", "", text).strip() text = re.sub(r"```[a-z]*\n?", "", text).strip()
# Extract the first JSON object found in the response
m = re.search(r"\{[^{}]+\}", text, re.DOTALL) m = re.search(r"\{[^{}]+\}", text, re.DOTALL)
if m: if m:
text = m.group(0) text = m.group(0)
parsed = json.loads(text) parsed = json.loads(text)
q = parsed.get("question", "").strip()
a = parsed.get("option_a", "").strip() a = parsed.get("option_a", "").strip()
b = parsed.get("option_b", "").strip() b = parsed.get("option_b", "").strip()
if q and a and b: # Rebuild question from options so it's always well-formed
if a and b:
q = f"Would you rather {a.rstrip('.')} OR {b.rstrip('.')}?"
return {"question": q, "option_a": a, "option_b": b} return {"question": q, "option_a": a, "option_b": b}
except Exception as e: except Exception as e:
logger.error(f"WYR generation error: {e}", exc_info=True) logger.error(f"WYR generation error: {e}", exc_info=True)