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:
|
async def _generate_hangman_word() -> dict | None:
|
||||||
prompt = (
|
system_msg = (
|
||||||
"Generate a hangman word game. Pick a common English word between 5 and 8 letters. "
|
"You are a hangman game generator. Always respond with ONLY a JSON object — no markdown, no explanation. "
|
||||||
"Respond with ONLY valid JSON, no markdown: "
|
'Format: {"word": "example", "hint": "short category or hint"}'
|
||||||
'{"word": "example", "hint": "a short category or hint about the word"}. '
|
|
||||||
"The word must be all lowercase letters only, no spaces or hyphens."
|
|
||||||
)
|
)
|
||||||
|
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:
|
try:
|
||||||
timeout = aiohttp.ClientTimeout(total=20)
|
timeout = aiohttp.ClientTimeout(total=20)
|
||||||
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/generate",
|
f"{OLLAMA_URL}/api/chat",
|
||||||
json={"model": ASK_MODEL, "prompt": prompt, "stream": False},
|
json={
|
||||||
|
"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("response", "").strip()
|
text = data.get("message", {}).get("content", "").strip()
|
||||||
# Strip markdown fences
|
|
||||||
if "```" in text:
|
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)
|
parsed = json.loads(text)
|
||||||
word = parsed.get("word", "").lower().strip()
|
word = parsed.get("word", "").lower().strip()
|
||||||
hint = parsed.get("hint", "").strip()
|
hint = parsed.get("hint", "").strip()
|
||||||
@@ -1430,23 +1438,32 @@ _SCRAMBLE_GAMES: dict[str, dict] = {}
|
|||||||
|
|
||||||
|
|
||||||
async def _generate_scramble_word() -> dict | None:
|
async def _generate_scramble_word() -> dict | None:
|
||||||
prompt = (
|
system_msg = (
|
||||||
"Pick a common English word between 4 and 8 letters. "
|
"You are a word game generator. Always respond with ONLY a JSON object — no markdown, no explanation. "
|
||||||
"Respond with ONLY valid JSON, no markdown: "
|
'Format: {"word": "example"}'
|
||||||
'{"word": "example"}. '
|
|
||||||
"The word must be all lowercase letters only, no spaces or hyphens."
|
|
||||||
)
|
)
|
||||||
|
user_msg = "Pick a common English word between 4 and 8 letters (lowercase letters only, no hyphens or spaces)."
|
||||||
try:
|
try:
|
||||||
timeout = aiohttp.ClientTimeout(total=20)
|
timeout = aiohttp.ClientTimeout(total=20)
|
||||||
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/generate",
|
f"{OLLAMA_URL}/api/chat",
|
||||||
json={"model": ASK_MODEL, "prompt": prompt, "stream": False},
|
json={
|
||||||
|
"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("response", "").strip()
|
text = data.get("message", {}).get("content", "").strip()
|
||||||
if "```" in text:
|
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)
|
parsed = json.loads(text)
|
||||||
word = parsed.get("word", "").lower().strip()
|
word = parsed.get("word", "").lower().strip()
|
||||||
if word.isalpha() and 4 <= len(word) <= 8:
|
if word.isalpha() and 4 <= len(word) <= 8:
|
||||||
@@ -1641,23 +1658,32 @@ _RIDDLE_ACTIVE: dict[str, dict] = {}
|
|||||||
|
|
||||||
|
|
||||||
async def _generate_riddle() -> dict | None:
|
async def _generate_riddle() -> dict | None:
|
||||||
prompt = (
|
system_msg = (
|
||||||
"Generate a clever riddle and its answer. "
|
"You are a riddle generator. Always respond with ONLY a JSON object — no markdown fences, no explanation. "
|
||||||
"Respond with ONLY valid JSON, no markdown: "
|
'Format: {"riddle": "the riddle text", "answer": "short answer"}'
|
||||||
'{"riddle": "...", "answer": "..."}. '
|
|
||||||
"The answer should be a short word or phrase (1-4 words). Make it interesting!"
|
|
||||||
)
|
)
|
||||||
|
user_msg = "Generate a clever riddle. The answer should be 1-4 words."
|
||||||
try:
|
try:
|
||||||
timeout = aiohttp.ClientTimeout(total=20)
|
timeout = aiohttp.ClientTimeout(total=20)
|
||||||
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/generate",
|
f"{OLLAMA_URL}/api/chat",
|
||||||
json={"model": ASK_MODEL, "prompt": prompt, "stream": False},
|
json={
|
||||||
|
"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("response", "").strip()
|
text = data.get("message", {}).get("content", "").strip()
|
||||||
if "```" in text:
|
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)
|
parsed = json.loads(text)
|
||||||
riddle = parsed.get("riddle", "").strip()
|
riddle = parsed.get("riddle", "").strip()
|
||||||
answer = parsed.get("answer", "").strip()
|
answer = parsed.get("answer", "").strip()
|
||||||
|
|||||||
Reference in New Issue
Block a user