riddle/wyr: fix repeat shadow answers and truncate long WYR options
riddle:
- Cache answers separately so the same answer (e.g. 'shadow') can't
appear twice in a session even if the riddle text differs
- Explicitly ban 'shadow' in the prompt and append avoid-answers clause
- Ban question endings ('what am I?', 'what could it be?') more strictly
wyr:
- Hard-cap options at 10 words server-side so the model can't ignore
the word limit and generate paragraph-length options
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+20
-9
@@ -1685,8 +1685,10 @@ async def _generate_wyr() -> dict | None:
|
|||||||
parsed = json.loads(text)
|
parsed = json.loads(text)
|
||||||
a = parsed.get("option_a", "").strip()
|
a = parsed.get("option_a", "").strip()
|
||||||
b = parsed.get("option_b", "").strip()
|
b = parsed.get("option_b", "").strip()
|
||||||
# Rebuild question from options so it's always well-formed
|
# Hard cap: truncate options that are too long
|
||||||
if a and b:
|
if a and b:
|
||||||
|
a = " ".join(a.split()[:10])
|
||||||
|
b = " ".join(b.split()[:10])
|
||||||
q = f"Would you rather {a.rstrip('.')} OR {b.rstrip('.')}?"
|
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:
|
||||||
@@ -1770,16 +1772,22 @@ async def cmd_wyr(client: AsyncClient, room_id: str, sender: str, args: str):
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
_RIDDLE_ACTIVE: dict[str, dict] = {}
|
_RIDDLE_ACTIVE: dict[str, dict] = {}
|
||||||
_riddle_recent: list[str] = []
|
_riddle_recent: list[str] = [] # past riddle texts
|
||||||
|
_riddle_recent_answers: list[str] = [] # past answers (lowercase)
|
||||||
_RIDDLE_RECENT_MAX = 30
|
_RIDDLE_RECENT_MAX = 30
|
||||||
|
|
||||||
|
|
||||||
async def _generate_riddle() -> dict | None:
|
async def _generate_riddle() -> dict | None:
|
||||||
avoid_clause = (
|
avoid_riddles = (
|
||||||
" Do NOT use any of these riddles that were recently asked: "
|
" Do NOT reuse any of these recent riddles: "
|
||||||
+ "; ".join(f'"{r}"' for r in _riddle_recent[-15:])
|
+ "; ".join(f'"{r}"' for r in _riddle_recent[-10:])
|
||||||
+ "."
|
+ "."
|
||||||
) if _riddle_recent else ""
|
) if _riddle_recent else ""
|
||||||
|
avoid_answers = (
|
||||||
|
" Do NOT use any of these answers that were recently used: "
|
||||||
|
+ ", ".join(f'"{a}"' for a in _riddle_recent_answers[-15:])
|
||||||
|
+ "."
|
||||||
|
) if _riddle_recent_answers else ""
|
||||||
system_msg = (
|
system_msg = (
|
||||||
"You are a riddle generator. Always respond with ONLY a JSON object — no markdown fences, no explanation. "
|
"You are a riddle generator. Always respond with ONLY a JSON object — no markdown fences, no explanation. "
|
||||||
'Format: {"riddle": "the riddle text", "answer": "short answer"}\n'
|
'Format: {"riddle": "the riddle text", "answer": "short answer"}\n'
|
||||||
@@ -1787,11 +1795,11 @@ async def _generate_riddle() -> dict | None:
|
|||||||
"- The answer must be a specific, unambiguous noun (1-3 words). Avoid abstract answers.\n"
|
"- The answer must be a specific, unambiguous noun (1-3 words). Avoid abstract answers.\n"
|
||||||
"- The riddle must describe the answer through metaphor or wordplay — NOT by literally describing it.\n"
|
"- The riddle must describe the answer through metaphor or wordplay — NOT by literally describing it.\n"
|
||||||
"- Do NOT include the answer word anywhere in the riddle text.\n"
|
"- Do NOT include the answer word anywhere in the riddle text.\n"
|
||||||
"- Do NOT end the riddle with 'what am I?' or 'what could it possibly mean?' — the riddle should stand alone.\n"
|
"- Do NOT end with 'what am I?', 'what could it be?', or any question — the riddle should stand alone as a statement.\n"
|
||||||
"- The clues must logically point to ONE specific answer. Test it: would most people agree this answer is correct?\n"
|
"- The clues must logically point to ONE specific answer that most people would agree on.\n"
|
||||||
"- Avoid riddles about riddles, shadows, or abstract concepts. Prefer concrete things: candle, mirror, clock, river, echo, stamp, etc."
|
"- Avoid 'shadow' as an answer. Prefer concrete things: candle, mirror, clock, river, echo, stamp, key, glove, envelope, etc."
|
||||||
)
|
)
|
||||||
user_msg = f"Generate a clever, original riddle with a clear unambiguous answer.{avoid_clause}"
|
user_msg = f"Generate a clever, original riddle with a clear unambiguous answer.{avoid_answers}{avoid_riddles}"
|
||||||
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:
|
||||||
@@ -1820,6 +1828,9 @@ async def _generate_riddle() -> dict | None:
|
|||||||
_riddle_recent.append(riddle)
|
_riddle_recent.append(riddle)
|
||||||
if len(_riddle_recent) > _RIDDLE_RECENT_MAX:
|
if len(_riddle_recent) > _RIDDLE_RECENT_MAX:
|
||||||
_riddle_recent.pop(0)
|
_riddle_recent.pop(0)
|
||||||
|
_riddle_recent_answers.append(answer.lower())
|
||||||
|
if len(_riddle_recent_answers) > _RIDDLE_RECENT_MAX:
|
||||||
|
_riddle_recent_answers.pop(0)
|
||||||
return {"riddle": riddle, "answer": answer}
|
return {"riddle": riddle, "answer": answer}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"riddle generation error: {e}", exc_info=True)
|
logger.error(f"riddle generation error: {e}", exc_info=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user