From b985df0017e67c0252463891a1316574f70bd707 Mon Sep 17 00:00:00 2001 From: truetype Date: Sun, 28 Jun 2026 10:01:33 +0200 Subject: [PATCH] feat(tools): add update-quest tool, switch to Big Pickle (opencode/zen) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update_quest: aggiorna stato/nome/descrizione missioni - db: get_quest(), update_quest con nome+desc - prompt: quest_id nel contesto, esempio nei tool examples - Switch da deepseek-v4-flash (go) a big-pickle (zen) — gratuito --- docker-compose.yml | 6 +++--- src/engine/config.py | 4 ++-- src/engine/db.py | 19 +++++++++++++++++-- src/engine/prompts.py | 4 +++- src/engine/tools.py | 33 +++++++++++++++++++++++++++++++++ start.sh | 4 ++-- 6 files changed, 60 insertions(+), 10 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a16e780..402c899 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,8 +14,8 @@ services: - DCC_OPENROUTER_MODEL=${DCC_OPENROUTER_MODEL:-openrouter/free} - DCC_OLLAMA_URL=${DCC_OLLAMA_URL:-http://192.168.0.50:11434} - DCC_OLLAMA_MODEL=${DCC_OLLAMA_MODEL:-gemma2:9b} - - DCC_LOCAL_URL=${DCC_LOCAL_URL:-https://opencode.ai/zen/go/v1} + - DCC_LOCAL_URL=${DCC_LOCAL_URL:-https://opencode.ai/zen/v1} - DCC_LOCAL_KEY=${DCC_LOCAL_KEY:-} - - DCC_LOCAL_MODEL=${DCC_LOCAL_MODEL:-deepseek-v4-flash} - - DCC_LLM_TIMEOUT=${DCC_LLM_TIMEOUT:-180} + - DCC_LOCAL_MODEL=${DCC_LOCAL_MODEL:-big-pickle} + - DCC_LLM_TIMEOUT=${DCC_LLM_TIMEOUT:-120} restart: unless-stopped diff --git a/src/engine/config.py b/src/engine/config.py index 37e1b49..7c414d1 100644 --- a/src/engine/config.py +++ b/src/engine/config.py @@ -15,9 +15,9 @@ class ProviderConfig: openrouter_model: str = "openrouter/free" openrouter_key_env: str = "DCC_OPENROUTER_KEY" openrouter_free_fallback: bool = True - local_base_url: str = "https://opencode.ai/zen/go/v1" + local_base_url: str = "https://opencode.ai/zen/v1" local_api_key: str = "" - local_model: str = "deepseek-v4-flash" + local_model: str = "big-pickle" timeout: int = 120 diff --git a/src/engine/db.py b/src/engine/db.py index 246cd55..fe720c5 100644 --- a/src/engine/db.py +++ b/src/engine/db.py @@ -477,9 +477,24 @@ def add_quest(campaign_id: int, name: str, description: str) -> int: return cur.lastrowid or 0 -def update_quest(quest_id: int, status: str) -> bool: +def get_quest(quest_id: int) -> dict | None: conn = get_conn() - conn.execute("UPDATE quests SET status = ? WHERE id = ?", (status, quest_id)) + row = conn.execute( + "SELECT * FROM quests WHERE id = ?", (quest_id,) + ).fetchone() + return dict(row) if row else None + + +def update_quest(quest_id: int, status: str, name: str = "", description: str = "") -> bool: + updates = {"status": status} + if name: + updates["name"] = name + if description: + updates["description"] = description + cols = ", ".join(f"{k} = ?" for k in updates) + vals = list(updates.values()) + [quest_id] + conn = get_conn() + conn.execute(f"UPDATE quests SET {cols} WHERE id = ?", vals) conn.commit() return conn.total_changes > 0 diff --git a/src/engine/prompts.py b/src/engine/prompts.py index 1baef72..2850074 100644 --- a/src/engine/prompts.py +++ b/src/engine/prompts.py @@ -25,6 +25,7 @@ _KNOWN_TOOLS: dict[str, dict[str, str]] = { "spellcasting": {"character_id": "id pg", "spell_name": "nome incantesimo", "spell_check": "valore tiro"}, "update_inventory": {"character_id": "id pg", "item": "nome oggetto", "action": "add|remove|equip|unequip|list", "quantity": "quantità (default 1)"}, "award_xp": {"character_id": "id pg", "xp": "quantità PE"}, + "update_quest": {"quest_id": "id missione", "status": "active|completed|failed", "name": "nuovo nome (opzionale)", "description": "nuova descrizione (opzionale)"}, "dm_override": {"command": "narrate|set|force", "params": "parametri opzionali"}, } @@ -34,6 +35,7 @@ Esempi: - Tiro d20 per sentire rumori → {"tool": "roll_dice", "params": {"die": "d20"}, "narration": "Tendi l'orecchio nel buio..."} - Esploro la stanza → {"tool": "dm_override", "params": {"command": "narrate", "params": {"text": "La stanza è polverosa..."}}, "narration": "Scrutando nell'oscurità, noti..."} - Prova di percezione CD 12 → {"tool": "ability_check", "params": {"character_id": 1, "dc": 12}, "narration": "Cerchi indizi nella stanza..."} +- Missione completata → {"tool": "update_quest", "params": {"quest_id": 1, "status": "completed"}, "narration": "La missione è stata completata con successo!"} """ SYSTEM_PROMPT = f"""Sei il Dungeon Master di DCC RPG (Dungeon Crawl Classics) — narrazione epico-oscura, tono sword & sorcery. @@ -110,7 +112,7 @@ def format_context(ctx: GameContext) -> str: if ctx.quests: lines.append("MISSIONI:") for q in ctx.quests: - lines.append(f" [{q['status']}] {q['name']}") + lines.append(f" [ID:{q['id']}] {q['name']} ({q['status']})") lines.append("") enriched = _enrich_areas(ctx.areas) diff --git a/src/engine/tools.py b/src/engine/tools.py index fb051c3..ad75453 100644 --- a/src/engine/tools.py +++ b/src/engine/tools.py @@ -376,6 +376,39 @@ def award_xp(character_id: int, xp: int) -> ToolResult: } +@tool +def update_quest(quest_id: int, status: str = "active", name: str = "", description: str = "") -> ToolResult: + VALID_STATUSES = ["active", "completed", "failed"] + if status not in VALID_STATUSES: + return { + "success": False, + "tool": "update-quest", + "message": f"Status '{status}' non valido. Usa: {', '.join(VALID_STATUSES)}", + } + quest = storage.get_quest(quest_id) + if not quest: + return {"success": False, "tool": "update-quest", "message": f"Missione #{quest_id} non trovata."} + + changed = [] + if status != quest["status"]: + changed.append(f"stato: {quest['status']}→{status}") + if name and name != quest.get("name"): + changed.append(f"nome: {quest.get('name')}→{name}") + if description and description != quest.get("description"): + changed.append("descrizione aggiornata") + + storage.update_quest(quest_id, status=status, name=name, description=description) + display_name = name or quest["name"] + return { + "success": True, + "tool": "update-quest", + "quest_id": quest_id, + "name": display_name, + "status": status, + "message": f"Missione '{display_name}' → {status}." + (f" ({', '.join(changed)})" if changed else ""), + } + + @tool def dm_override(command: str, params: dict | None = None) -> ToolResult: manual = { diff --git a/start.sh b/start.sh index 04a5a95..b32413e 100755 --- a/start.sh +++ b/start.sh @@ -16,9 +16,9 @@ export DCC_OPENROUTER_KEY="${DCC_OPENROUTER_KEY:-}" export DCC_OPENROUTER_MODEL="${DCC_OPENROUTER_MODEL:-openrouter/free}" export DCC_OLLAMA_URL="${DCC_OLLAMA_URL:-http://192.168.0.50:11434}" export DCC_OLLAMA_MODEL="${DCC_OLLAMA_MODEL:-gemma2:9b}" -export DCC_LOCAL_URL="${DCC_LOCAL_URL:-https://opencode.ai/zen/go/v1}" +export DCC_LOCAL_URL="${DCC_LOCAL_URL:-https://opencode.ai/zen/v1}" export DCC_LOCAL_KEY="${DCC_LOCAL_KEY:-}" -export DCC_LOCAL_MODEL="${DCC_LOCAL_MODEL:-deepseek-v4-flash}" +export DCC_LOCAL_MODEL="${DCC_LOCAL_MODEL:-big-pickle}" # ── create data dir ──────────────────────────────────────────────── mkdir -p data