fix: switch all JSON-returning game generators to api/chat + robust parsing
hangman, scramble, riddle, and wyr all used api/generate which has no system role. The model would wrap JSON in prose or markdown fences, causing json.loads() to throw and the command to silently die after the 'Generating...' message. Fix for all four: switch to api/chat with a system message enforcing raw JSON output, strip markdown fences, and use regex to extract the JSON object even if surrounded by extra text. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+54
-28
@@ -1231,24 +1231,32 @@ def _hangman_display(game: dict) -> str:
|
||||
|
||||
|
||||
async def _generate_hangman_word() -> dict | None:
|
||||
prompt = (
|
||||
"Generate a hangman word game. Pick a common English word between 5 and 8 letters. "
|
||||
"Respond with ONLY valid JSON, no markdown: "
|
||||
'{"word": "example", "hint": "a short category or hint about the word"}. '
|
||||
"The word must be all lowercase letters only, no spaces or hyphens."
|
||||
system_msg = (
|
||||
"You are a hangman game generator. Always respond with ONLY a JSON object — no markdown, no explanation. "
|
||||
'Format: {"word": "example", "hint": "short category or hint"}'
|
||||
)
|
||||
user_msg = "Pick a common English word between 5 and 8 letters (lowercase letters only, no hyphens or spaces) and give a short hint."
|
||||
try:
|
||||
timeout = aiohttp.ClientTimeout(total=20)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(
|
||||
f"{OLLAMA_URL}/api/generate",
|
||||
json={"model": ASK_MODEL, "prompt": prompt, "stream": False},
|
||||
f"{OLLAMA_URL}/api/chat",
|
||||
json={
|
||||
"model": ASK_MODEL,
|
||||
"stream": False,
|
||||
"messages": [
|
||||
{"role": "system", "content": system_msg},
|
||||
{"role": "user", "content": user_msg},
|
||||
],
|
||||
},
|
||||
) as response:
|
||||
data = await response.json()
|
||||
text = data.get("response", "").strip()
|
||||
# Strip markdown fences
|
||||
text = data.get("message", {}).get("content", "").strip()
|
||||
if "```" in text:
|
||||
text = text.split("```")[1].lstrip("json").strip()
|
||||
text = re.sub(r"```[a-z]*\n?", "", text).strip()
|
||||
m = re.search(r"\{[^{}]+\}", text, re.DOTALL)
|
||||
if m:
|
||||
text = m.group(0)
|
||||
parsed = json.loads(text)
|
||||
word = parsed.get("word", "").lower().strip()
|
||||
hint = parsed.get("hint", "").strip()
|
||||
@@ -1430,23 +1438,32 @@ _SCRAMBLE_GAMES: dict[str, dict] = {}
|
||||
|
||||
|
||||
async def _generate_scramble_word() -> dict | None:
|
||||
prompt = (
|
||||
"Pick a common English word between 4 and 8 letters. "
|
||||
"Respond with ONLY valid JSON, no markdown: "
|
||||
'{"word": "example"}. '
|
||||
"The word must be all lowercase letters only, no spaces or hyphens."
|
||||
system_msg = (
|
||||
"You are a word game generator. Always respond with ONLY a JSON object — no markdown, no explanation. "
|
||||
'Format: {"word": "example"}'
|
||||
)
|
||||
user_msg = "Pick a common English word between 4 and 8 letters (lowercase letters only, no hyphens or spaces)."
|
||||
try:
|
||||
timeout = aiohttp.ClientTimeout(total=20)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(
|
||||
f"{OLLAMA_URL}/api/generate",
|
||||
json={"model": ASK_MODEL, "prompt": prompt, "stream": False},
|
||||
f"{OLLAMA_URL}/api/chat",
|
||||
json={
|
||||
"model": ASK_MODEL,
|
||||
"stream": False,
|
||||
"messages": [
|
||||
{"role": "system", "content": system_msg},
|
||||
{"role": "user", "content": user_msg},
|
||||
],
|
||||
},
|
||||
) as response:
|
||||
data = await response.json()
|
||||
text = data.get("response", "").strip()
|
||||
text = data.get("message", {}).get("content", "").strip()
|
||||
if "```" in text:
|
||||
text = text.split("```")[1].lstrip("json").strip()
|
||||
text = re.sub(r"```[a-z]*\n?", "", text).strip()
|
||||
m = re.search(r"\{[^{}]+\}", text, re.DOTALL)
|
||||
if m:
|
||||
text = m.group(0)
|
||||
parsed = json.loads(text)
|
||||
word = parsed.get("word", "").lower().strip()
|
||||
if word.isalpha() and 4 <= len(word) <= 8:
|
||||
@@ -1641,23 +1658,32 @@ _RIDDLE_ACTIVE: dict[str, dict] = {}
|
||||
|
||||
|
||||
async def _generate_riddle() -> dict | None:
|
||||
prompt = (
|
||||
"Generate a clever riddle and its answer. "
|
||||
"Respond with ONLY valid JSON, no markdown: "
|
||||
'{"riddle": "...", "answer": "..."}. '
|
||||
"The answer should be a short word or phrase (1-4 words). Make it interesting!"
|
||||
system_msg = (
|
||||
"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"}'
|
||||
)
|
||||
user_msg = "Generate a clever riddle. The answer should be 1-4 words."
|
||||
try:
|
||||
timeout = aiohttp.ClientTimeout(total=20)
|
||||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||
async with session.post(
|
||||
f"{OLLAMA_URL}/api/generate",
|
||||
json={"model": ASK_MODEL, "prompt": prompt, "stream": False},
|
||||
f"{OLLAMA_URL}/api/chat",
|
||||
json={
|
||||
"model": ASK_MODEL,
|
||||
"stream": False,
|
||||
"messages": [
|
||||
{"role": "system", "content": system_msg},
|
||||
{"role": "user", "content": user_msg},
|
||||
],
|
||||
},
|
||||
) as response:
|
||||
data = await response.json()
|
||||
text = data.get("response", "").strip()
|
||||
text = data.get("message", {}).get("content", "").strip()
|
||||
if "```" in text:
|
||||
text = text.split("```")[1].lstrip("json").strip()
|
||||
text = re.sub(r"```[a-z]*\n?", "", text).strip()
|
||||
m = re.search(r"\{[^{}]+\}", text, re.DOTALL)
|
||||
if m:
|
||||
text = m.group(0)
|
||||
parsed = json.loads(text)
|
||||
riddle = parsed.get("riddle", "").strip()
|
||||
answer = parsed.get("answer", "").strip()
|
||||
|
||||
Reference in New Issue
Block a user