fix: JSON repair, WS thread pool, switch to DeepSeek V4 Flash, funnel Lv0
LLM fixes: - _try_repair_json() — ripara JSON malformato (apici, virgole trailing) - _strip_json_from_text() — rimuove residui JSON dalla narration - parse_tool_call() — rimuove troncamento 200 caratteri dal fallback DM fixes: - Fallback euristico quando params vuoti (dm.py) - Estrazione narrazione da dm_override/narrate params.params.text Prompt (prompts.py): - Vietati termini tecnici/scientifici/anacronistici (HPLC, laser, ...) - Aggiunto vocabolario fantasy obbligatorio Server (server.py): - dm_cycle in ThreadPoolExecutor per non bloccare WS ping - WebSocketDisconnect gestiti - Rimosso import inesistente db_schema Frontend (index.html): - Nascosti tool name dalla chat - Personaggio -> PG, connessioni aree con nomi Infrastruttura: - Switch da openrouter/free a DeepSeek V4 Flash via OpenCode Go - pyproject.toml, Dockerfile, docker-compose.yml, start.sh - .env auto-load (start.sh) - Funnel Lv0 (funnel.py) — creazione PG livello 0 Docs: architecture, database, setup, game mechanics, codebase ref
This commit is contained in:
1 parent
6b048dc0cc
commit
eacc946868
23 files changed
+1786
-61
No files matched your search
+49
-4
@@ -27,6 +27,30 @@ def ask(prompt: str, system: str = "") -> str:
|
||||
return get_provider().ask(prompt, system=system)
|
||||
|
||||
|
||||
def _try_repair_json(text: str) -> str | None:
|
||||
"""Tenta di riparare errori JSON comuni prodotti dagli LLM."""
|
||||
m = re.search(r'(\{[\s\S]*\})', text)
|
||||
if not m:
|
||||
return None
|
||||
blob = m.group(1)
|
||||
|
||||
# Fix 1: chiavi con apici singoli → doppi ("params' " → "params": ")
|
||||
blob = re.sub(r"(?<!\\)'(params|narration|tool|command|text|character_id|dc|modifier|die|count)"
|
||||
r"'(?=\s*[:=])", r'"\1":', blob)
|
||||
# Fix 2: qualsiasi altra chiave con apici singoli
|
||||
blob = re.sub(r"(?<!\\)'(\w+)'(?=\s*[:=])", r'"\1":', blob)
|
||||
# Fix 3: trailing comma prima di } o ]
|
||||
blob = re.sub(r',\s*}', '}', blob)
|
||||
blob = re.sub(r',\s*\]', ']', blob)
|
||||
# Fix 4: = al posto di : nelle chiavi
|
||||
blob = re.sub(r'"(\w+)"\s*=', r'"\1":', blob)
|
||||
try:
|
||||
json.loads(blob)
|
||||
return blob
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
|
||||
|
||||
def _extract_json(text: str) -> str | None:
|
||||
for pat in [
|
||||
r'```json\s*([\s\S]*?)```',
|
||||
@@ -40,8 +64,27 @@ def _extract_json(text: str) -> str | None:
|
||||
json.loads(candidate)
|
||||
return candidate
|
||||
except json.JSONDecodeError:
|
||||
# tenta riparazione
|
||||
repaired = _try_repair_json(candidate)
|
||||
if repaired:
|
||||
return repaired
|
||||
continue
|
||||
return None
|
||||
# try repair on full text too
|
||||
return _try_repair_json(text)
|
||||
|
||||
|
||||
def _strip_json_from_text(text: str) -> str:
|
||||
"""Rimuove qualsiasi contenuto simile a JSON dal testo narrativo."""
|
||||
# toglie blocchi {...} completi di tool call
|
||||
cleaned = re.sub(r'\{"tool":[\s\S]*?"narration":\s*"[^"]*"\s*\}', '', text)
|
||||
# toglie blocchi JSON parziali o incompleti
|
||||
cleaned = re.sub(r'\{"tool":[\s\S]*?\}', '', cleaned)
|
||||
cleaned = re.sub(r'\{\s*"[a-zA-Z_]+"\s*[:=][^}]*\}', '', cleaned)
|
||||
# pulisce spaziature multiple
|
||||
cleaned = re.sub(r'\s+', ' ', cleaned).strip()
|
||||
# toglie virgolette pendenti all'inizio/fine
|
||||
cleaned = cleaned.strip('"\' ,.')
|
||||
return cleaned
|
||||
|
||||
|
||||
def parse_tool_call(text: str) -> dict:
|
||||
@@ -55,10 +98,12 @@ def parse_tool_call(text: str) -> dict:
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
|
||||
tool_match = re.search(r'(?:tool|strumento|chiamo|uso)\s*[":]*\s*(\w+)', text, re.I)
|
||||
# Fallback: estrae tool name via regex, ma PULISCE la narration dal JSON
|
||||
tool_match = re.search(r'\b(?:tool|strumento|chiamo|uso)\s*[":]*\s*(\w+)', text, re.I)
|
||||
narration = _strip_json_from_text(text) if text else ""
|
||||
if tool_match:
|
||||
return {"tool": tool_match.group(1), "params": {}, "narration": text[:200]}
|
||||
return {"tool": "dm_override", "params": {"command": "narrate", "params": {"text": text[:200]}}, "narration": ""}
|
||||
return {"tool": tool_match.group(1), "params": {}, "narration": narration}
|
||||
return {"tool": "dm_override", "params": {"command": "narrate", "params": {"text": narration or text[:500]}}, "narration": narration}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in new issue
Block a user