Add equip/inventory commands, report results without AI
New commands: - "Doug, equip your sword" → equips item, reports "Equipped sword." - "Doug, what do you have?" → lists actual inventory items - "Doug, check your inventory" → same Key change: command results now report DIRECTLY to chat without going through AI. No more hallucinated responses about items Doug doesn't have. Commands execute → report real result. AI is ONLY used for conversation, not for task responses. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5a42c2b881
commit
e6d4c8d377
2 changed files with 113 additions and 3 deletions
|
|
@ -592,6 +592,12 @@ class DougBrain(QObject):
|
||||||
self._tasks.complete()
|
self._tasks.complete()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Callbacks that need special response handling
|
||||||
|
if task.callback in ("on_equip_result", "on_inventory_report"):
|
||||||
|
# Execute then report result directly to chat (no AI)
|
||||||
|
self._execute_action_with_report(task)
|
||||||
|
return
|
||||||
|
|
||||||
# Skip placeholder actions
|
# Skip placeholder actions
|
||||||
if task.action == "status":
|
if task.action == "status":
|
||||||
self._tasks.complete()
|
self._tasks.complete()
|
||||||
|
|
@ -648,6 +654,48 @@ class DougBrain(QObject):
|
||||||
|
|
||||||
self._ws.send_request(task.action, task.params, on_response)
|
self._ws.send_request(task.action, task.params, on_response)
|
||||||
|
|
||||||
|
def _execute_action_with_report(self, task: Task):
|
||||||
|
"""Execute an action and report the result directly to chat (no AI)."""
|
||||||
|
self._state = BrainState.EXECUTING_TASK
|
||||||
|
self._action_sent_time = time.time()
|
||||||
|
|
||||||
|
if task.description and task.priority >= Priority.LOW:
|
||||||
|
log.info(f"[{task.priority.name}] {task.description}")
|
||||||
|
|
||||||
|
def on_response(resp: ResponseMessage):
|
||||||
|
self._state = BrainState.IDLE
|
||||||
|
|
||||||
|
if task.callback == "on_equip_result":
|
||||||
|
if resp.status == "success":
|
||||||
|
item = task.params.get("name", "item").replace("_", " ")
|
||||||
|
self._ws.send_request("send_chat", {"message": f"Equipped {item}."})
|
||||||
|
self._tasks.complete()
|
||||||
|
else:
|
||||||
|
error = resp.error or "I don't have that item."
|
||||||
|
self._ws.send_request("send_chat", {"message": error})
|
||||||
|
self._tasks.cancel()
|
||||||
|
|
||||||
|
elif task.callback == "on_inventory_report":
|
||||||
|
if resp.status == "success":
|
||||||
|
items = resp.data.get("items", [])
|
||||||
|
if items:
|
||||||
|
# List items concisely
|
||||||
|
item_strs = [f"{i['count']}x {i['name'].replace('_',' ')}" for i in items[:8]]
|
||||||
|
msg = "I have: " + ", ".join(item_strs)
|
||||||
|
if len(items) > 8:
|
||||||
|
msg += f" and {len(items) - 8} more items"
|
||||||
|
self._ws.send_request("send_chat", {"message": msg})
|
||||||
|
else:
|
||||||
|
self._ws.send_request("send_chat", {"message": "My inventory is empty."})
|
||||||
|
self._tasks.complete()
|
||||||
|
else:
|
||||||
|
self._ws.send_request("send_chat", {"message": "Can't check inventory right now."})
|
||||||
|
self._tasks.cancel()
|
||||||
|
else:
|
||||||
|
self._tasks.complete()
|
||||||
|
|
||||||
|
self._ws.send_request(task.action, task.params, on_response)
|
||||||
|
|
||||||
# ── Helpers ──
|
# ── Helpers ──
|
||||||
|
|
||||||
def _handle_idle_chat(self, task: Task):
|
def _handle_idle_chat(self, task: Task):
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,19 @@ class CommandParser:
|
||||||
r"(?:give|hand|pass|toss)\s+(?:me|us)\s+(?:a\s+|an\s+|some\s+)?(.+)",
|
r"(?:give|hand|pass|toss)\s+(?:me|us)\s+(?:a\s+|an\s+|some\s+)?(.+)",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Social commands
|
EQUIP_PATTERNS = [
|
||||||
|
r"(?:equip|hold|use|grab|wield|switch to|pull out|get out)\s+(?:your\s+|the\s+|a\s+|my\s+)?(.+)",
|
||||||
|
r"(?:put on|wear)\s+(?:your\s+|the\s+|a\s+)?(.+)",
|
||||||
|
]
|
||||||
|
|
||||||
|
INVENTORY_PATTERNS = [
|
||||||
|
r"what(?:'s| is| do you have)\s+in\s+(?:your\s+)?(?:inventory|bag|pocket|backpack)",
|
||||||
|
r"what\s+(?:do you have|are you carrying|items do you have)",
|
||||||
|
r"show\s+(?:me\s+)?(?:your\s+)?(?:inventory|items|stuff)",
|
||||||
|
r"check\s+(?:your\s+)?(?:inventory|items|stuff)",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Social/combat commands
|
||||||
ATTACK_PATTERNS = [
|
ATTACK_PATTERNS = [
|
||||||
r"(?:attack|fight|kill|hit)\s+(?:that|this|the)?\s*(.+)",
|
r"(?:attack|fight|kill|hit)\s+(?:that|this|the)?\s*(.+)",
|
||||||
]
|
]
|
||||||
|
|
@ -115,6 +127,12 @@ class CommandParser:
|
||||||
cmd = self._try_give(msg, sender)
|
cmd = self._try_give(msg, sender)
|
||||||
if cmd: return cmd
|
if cmd: return cmd
|
||||||
|
|
||||||
|
cmd = self._try_equip(msg, sender)
|
||||||
|
if cmd: return cmd
|
||||||
|
|
||||||
|
cmd = self._try_inventory(msg, sender)
|
||||||
|
if cmd: return cmd
|
||||||
|
|
||||||
cmd = self._try_attack(msg, sender)
|
cmd = self._try_attack(msg, sender)
|
||||||
if cmd: return cmd
|
if cmd: return cmd
|
||||||
|
|
||||||
|
|
@ -236,6 +254,30 @@ class CommandParser:
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _try_equip(self, msg: str, sender: str) -> Optional[ParsedCommand]:
|
||||||
|
for pattern in self.EQUIP_PATTERNS:
|
||||||
|
match = re.search(pattern, msg, re.IGNORECASE)
|
||||||
|
if match:
|
||||||
|
raw_item = match.group(1).strip() if match.lastindex else ""
|
||||||
|
# Clean up item name
|
||||||
|
filler = {"your", "the", "a", "my", "that"}
|
||||||
|
words = [w for w in raw_item.split() if w.lower().rstrip(".,!?") not in filler]
|
||||||
|
if not words:
|
||||||
|
return None
|
||||||
|
item = "_".join(w.lower().rstrip(".,!?") for w in words[:3])
|
||||||
|
return ParsedCommand(
|
||||||
|
action="equip",
|
||||||
|
target=item,
|
||||||
|
raw_message=msg,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _try_inventory(self, msg: str, sender: str) -> Optional[ParsedCommand]:
|
||||||
|
for pattern in self.INVENTORY_PATTERNS:
|
||||||
|
if re.search(pattern, msg, re.IGNORECASE):
|
||||||
|
return ParsedCommand(action="check_inventory", raw_message=msg)
|
||||||
|
return None
|
||||||
|
|
||||||
def _try_attack(self, msg: str, sender: str) -> Optional[ParsedCommand]:
|
def _try_attack(self, msg: str, sender: str) -> Optional[ParsedCommand]:
|
||||||
for pattern in self.ATTACK_PATTERNS:
|
for pattern in self.ATTACK_PATTERNS:
|
||||||
match = re.search(pattern, msg, re.IGNORECASE)
|
match = re.search(pattern, msg, re.IGNORECASE)
|
||||||
|
|
@ -348,9 +390,29 @@ def command_to_task(cmd: ParsedCommand, behaviors) -> Optional[Task]:
|
||||||
callback="on_look_around_report",
|
callback="on_look_around_report",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
elif cmd.action == "equip":
|
||||||
|
return Task(
|
||||||
|
name=f"equip_{cmd.target}",
|
||||||
|
priority=Priority.HIGH,
|
||||||
|
action="equip_item",
|
||||||
|
params={"name": cmd.target, "destination": "hand"},
|
||||||
|
description=f"Equipping {cmd.target.replace('_', ' ')}",
|
||||||
|
timeout=10,
|
||||||
|
callback="on_equip_result",
|
||||||
|
)
|
||||||
|
|
||||||
|
elif cmd.action == "check_inventory":
|
||||||
|
return Task(
|
||||||
|
name="check_inventory",
|
||||||
|
priority=Priority.HIGH,
|
||||||
|
action="get_inventory",
|
||||||
|
params={},
|
||||||
|
description="Checking inventory",
|
||||||
|
timeout=10,
|
||||||
|
callback="on_inventory_report",
|
||||||
|
)
|
||||||
|
|
||||||
elif cmd.action == "go_to":
|
elif cmd.action == "go_to":
|
||||||
# For named destinations, we'd need a memory system
|
|
||||||
# For now, try to interpret as a player name
|
|
||||||
return Task(
|
return Task(
|
||||||
name=f"go_to_{cmd.target}",
|
name=f"go_to_{cmd.target}",
|
||||||
priority=Priority.HIGH,
|
priority=Priority.HIGH,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue