From f98392478bb8a4b787e58e7afbaa484ba902e7a5 Mon Sep 17 00:00:00 2001 From: Dejvino Date: Sat, 4 Jul 2026 22:01:00 +0200 Subject: [PATCH] Better tools params handling --- tools/engine.py | 50 +++++++++++++++++++++++++++++++++++++++++++++---- tools/run.py | 10 ++++++++-- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/tools/engine.py b/tools/engine.py index be1c705..6c6b99b 100644 --- a/tools/engine.py +++ b/tools/engine.py @@ -20,9 +20,38 @@ from engine_lib.paths import CHARACTER_CREATION_PATH, RULES_INJECTION_PATH class GameEngine: + REQUIRED_TOOL_ARGS: dict[str, list[str]] = { + "add_to_inventory": ["item"], + "remove_from_inventory": ["item"], + "replace_gear": ["before", "after"], + "add_note": ["note"], + "replace_note": ["before", "after"], + "world_update": ["content"], + "journal_update": ["add", "done"], + } + def __init__(self, session_dir: str | Path | None = None): self.config = config.load_config() + def _check_required_tool_args(self, state_changes: list[dict]) -> str: + """Check that all state-changing tool calls have required args. Returns empty string if OK, or a description of what's missing.""" + missing = [] + for tc in state_changes: + name = tc.get("tool", "") + req = self.REQUIRED_TOOL_ARGS.get(name) + if not req: + continue + args = tc.get("args") or {k: v for k, v in tc.items() if k != "tool"} + if name == "journal_update": + if not args.get("add") and not args.get("done"): + missing.append(f"{name}: needs at least one of `add` or `done`") + continue + for arg in req: + val = args.get(arg) + if val is None or (isinstance(val, str) and not val.strip()): + missing.append(f"{name}: missing required `{arg}`") + return "; ".join(missing) + def generate_turn( self, player_action: str | None = None, @@ -130,10 +159,10 @@ class GameEngine: for tc in tool_calls: name = tc.get("tool", "") - args = tc.get("args", {}) + args = tc.get("args") or {k: v for k, v in tc.items() if k != "tool"} if name == "narrative": - text = args.get("text", "") + text = args.get("text", "") or "" if text: book_log = (book_log + "\n\n" + text) if book_log else text elif name == "finalize_turn": @@ -148,13 +177,26 @@ class GameEngine: if args.get("log_entry"): log_entry = args["log_entry"] elif name == "read_rules": - result = execute_tool("read_rules", {}) + cat = args.get("category", "mechanics") + result = execute_tool("read_rules", {"category": cat}) state.append_llm_log(f"\n[READ RULES] loaded {len(result)} chars") RULES_INJECTION_PATH.parent.mkdir(parents=True, exist_ok=True) RULES_INJECTION_PATH.write_text(result) else: state_changes.append(tc) + # Required args check — reject if any state-changing tool is missing required arguments + missing_args = self._check_required_tool_args(state_changes) + if missing_args: + state.append_llm_log(f"\n[TURN MISSING ARGS] {missing_args}") + if attempt < MAX_RETRIES: + feedback = f"The following tool calls are missing required arguments: {missing_args}. Include all required fields for each tool and regenerate." + state.append_llm_log(f"\n[TURN REGENERATE] (missing args) attempt {attempt + 2}") + if on_action: + on_action("DM is consulting the fates...") + continue + state.append_llm_log(f"\n[TURN MISSING ARGS EXCEEDED] accepting despite missing args") + # Meta check — reject if state changes produced for a meta action if is_meta and state_changes: state.append_llm_log(f"\n[TURN META REJECTED] state changes not allowed for meta action") @@ -257,7 +299,7 @@ class GameEngine: for tc in tool_calls: name = tc.get("tool", "") - args = tc.get("args", {}) + args = tc.get("args") or {k: v for k, v in tc.items() if k != "tool"} if name in ("narrative", "read_rules"): pass diff --git a/tools/run.py b/tools/run.py index d0d4c91..09c90ac 100755 --- a/tools/run.py +++ b/tools/run.py @@ -325,7 +325,13 @@ class ChaosTUI(App): if result.error: self._show_error(result.error, result.debug_info) return - if result.book_log and not result.is_meta: + + if result.is_meta: + self._display_scene(result) + self._enable_input() + return + + if result.book_log: turn_num = state.archive_turn(result.book_log) if result.log_entry: state.append_log(f"- **Turn {turn_num}** — {result.log_entry}") @@ -333,7 +339,7 @@ class ChaosTUI(App): summary = result.book_log.strip().split(chr(10))[0][:80] state.append_log(f"- **Turn {turn_num}** — {summary}") result.book_log = load_book_pages()[-1] - elif result.log_entry and not result.is_meta: + elif result.log_entry: state.append_log(f"- {result.log_entry}") state.apply_state(result)