feat(tools): add update-quest tool, switch to Big Pickle (opencode/zen)

- 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
This commit is contained in:
truetype committed 2026-06-28 10:01:33 +02:00
1 parent eacc946868
commit b985df0017
6 files changed
+60 -10

No files matched your search

+3 -3
View File
@@ -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
+2 -2
View File
@@ -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
+17 -2
View File
@@ -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
+3 -1
View File
@@ -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)
+33
View File
@@ -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 = {
+2 -2
View File
@@ -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