Spaces:
Runtime error
Runtime error
| from __future__ import annotations | |
| import re | |
| from dataclasses import dataclass | |
| from agents.master.base import SUPPORTED_DIRECTIONS | |
| _TOKEN_RE = re.compile(r"^[a-z0-9]+(?: [a-z0-9]+)*$") | |
| _BANNED_OBJECT_TOKENS = {"a", "an", "the"} | |
| class CliCommandAst: | |
| kind: str | |
| normalized_command: str | |
| arguments: tuple[str, ...] = () | |
| class CliCommandParseResult: | |
| valid: bool | |
| normalized_command: str | None = None | |
| ast: CliCommandAst | None = None | |
| error: str | None = None | |
| def parse_cli_command(raw_command: str) -> CliCommandParseResult: | |
| normalized = normalize_cli_command(raw_command) | |
| if not normalized: | |
| return CliCommandParseResult(valid=False, error="Command must not be empty.") | |
| if normalized in {"look", "inventory", "wait"}: | |
| return _ok(normalized, normalized) | |
| if normalized in SUPPORTED_DIRECTIONS: | |
| return _ok("move", f"go {normalized}", normalized) | |
| if normalized.startswith("go "): | |
| direction = normalized[3:].strip() | |
| if direction in SUPPORTED_DIRECTIONS: | |
| return _ok("move", f"go {direction}", direction) | |
| return CliCommandParseResult(valid=False, error="Unknown direction.") | |
| if match := re.fullmatch(r"look in (?P<object>.+)", normalized): | |
| object_text = match.group("object").strip() | |
| return _object_result("look_in", normalized, object_text) | |
| if match := re.fullmatch(r"take (?P<object>.+) from (?P<source>.+)", normalized): | |
| return _two_object_result("take_from", normalized, match.group("object"), match.group("source")) | |
| one_target_patterns = { | |
| "open": r"open (?P<object>.+)", | |
| "read": r"read (?P<object>.+)", | |
| "talk": r"talk (?P<object>.+)", | |
| "examine": r"examine (?P<object>.+)", | |
| } | |
| for kind, pattern in one_target_patterns.items(): | |
| if match := re.fullmatch(pattern, normalized): | |
| object_text = match.group("object").strip() | |
| return _object_result(kind, normalized, object_text) | |
| if match := re.fullmatch(r"take (?P<object>.+)", normalized): | |
| object_text = match.group("object").strip() | |
| return _object_result("take", normalized, object_text) | |
| if match := re.fullmatch(r"unlock (?P<object>.+) with (?P<tool>.+)", normalized): | |
| return _two_object_result("unlock", normalized, match.group("object"), match.group("tool")) | |
| if match := re.fullmatch(r"use (?P<object>.+) on (?P<target>.+)", normalized): | |
| return _two_object_result("use", normalized, match.group("object"), match.group("target")) | |
| if match := re.fullmatch(r"combine (?P<object>.+) with (?P<target>.+)", normalized): | |
| return _two_object_result("combine", normalized, match.group("object"), match.group("target")) | |
| if match := re.fullmatch(r"give (?P<object>.+) to (?P<target>.+)", normalized): | |
| return _two_object_result("give", normalized, match.group("object"), match.group("target")) | |
| if match := re.fullmatch(r"submit (?P<answer>[a-z0-9]+(?: [a-z0-9]+)*)", normalized): | |
| answer = match.group("answer").strip() | |
| return _ok("submit", normalized, answer) | |
| return CliCommandParseResult(valid=False, error="Command does not match the strict CLI grammar.") | |
| def normalize_cli_command(raw_command: str) -> str: | |
| return re.sub(r"\s+", " ", raw_command.strip().lower()) | |
| def _object_result(kind: str, normalized_command: str, object_text: str) -> CliCommandParseResult: | |
| object_error = _validate_object_text(object_text) | |
| if object_error is not None: | |
| return CliCommandParseResult(valid=False, error=object_error) | |
| return _ok(kind, normalized_command, object_text) | |
| def _two_object_result(kind: str, normalized_command: str, first: str, second: str) -> CliCommandParseResult: | |
| first_error = _validate_object_text(first) | |
| if first_error is not None: | |
| return CliCommandParseResult(valid=False, error=first_error) | |
| second_error = _validate_object_text(second) | |
| if second_error is not None: | |
| return CliCommandParseResult(valid=False, error=second_error) | |
| return _ok(kind, normalized_command, first.strip(), second.strip()) | |
| def _validate_object_text(value: str) -> str | None: | |
| candidate = value.strip() | |
| if not candidate: | |
| return "Command target must not be empty." | |
| if not _TOKEN_RE.fullmatch(candidate): | |
| return "Command targets must use lowercase letters, numbers, and spaces only." | |
| if any(token in _BANNED_OBJECT_TOKENS for token in candidate.split()): | |
| return "Strict CLI commands must use exact parser-safe object names without articles." | |
| return None | |
| def _ok(kind: str, normalized_command: str, *arguments: str) -> CliCommandParseResult: | |
| return CliCommandParseResult( | |
| valid=True, | |
| normalized_command=normalized_command, | |
| ast=CliCommandAst(kind=kind, normalized_command=normalized_command, arguments=arguments), | |
| ) | |