Skip to content

Interactive

See also

  • This agent subclass builds on top of the default agent, make sure to read that first.
  • This class powers the mini command line tool, see usage for more details.

minisweagent.agents.interactive

A small generalization of the default agent that puts the user in the loop.

There are three modes: - human: commands issued by the user are executed immediately - confirm: commands issued by the LM but not whitelisted are confirmed by the user - yolo: commands issued by the LM are executed immediately without confirmation

console module-attribute

console = Console(highlight=False)

prompt_session module-attribute

prompt_session = PromptSession(
    history=FileHistory(
        global_config_dir / "interactive_history.txt"
    )
)

InteractiveAgentConfig dataclass

InteractiveAgentConfig(
    system_template: str = "You are a helpful assistant that can do anything.",
    instance_template: str = "Your task: {{task}}. Please reply with a single shell command in triple backticks. To finish, the first line of the output of the shell command must be 'MINI_SWE_AGENT_FINAL_OUTPUT'.",
    timeout_template: str = "The last command <command>{{action['action']}}</command> timed out and has been killed.\nThe output of the command was:\n <output>\n{{output}}\n</output>\nPlease try another command and make sure to avoid those requiring interactive input.",
    format_error_template: str = "Please always provide EXACTLY ONE action in triple backticks.",
    action_observation_template: str = "Observation: {{output}}",
    step_limit: int = 0,
    cost_limit: float = 3.0,
    mode: Literal["human", "confirm", "yolo"] = "confirm",
    whitelist_actions: list[str] = list(),
    confirm_exit: bool = True,
)

Bases: AgentConfig

mode class-attribute instance-attribute

mode: Literal['human', 'confirm', 'yolo'] = 'confirm'

Whether to confirm actions.

whitelist_actions class-attribute instance-attribute

whitelist_actions: list[str] = field(default_factory=list)

Never confirm actions that match these regular expressions.

confirm_exit class-attribute instance-attribute

confirm_exit: bool = True

If the agent wants to finish, do we ask for confirmation from user?

InteractiveAgent

InteractiveAgent(*args, **kwargs)

Bases: DefaultAgent

Source code in src/minisweagent/agents/interactive.py
38
39
40
def __init__(self, *args, **kwargs):
    super().__init__(*args, config_class=InteractiveAgentConfig, **kwargs)
    self.cost_last_confirmed = 0.0

cost_last_confirmed instance-attribute

cost_last_confirmed = 0.0

add_message

add_message(role: str, content: str)
Source code in src/minisweagent/agents/interactive.py
42
43
44
45
46
47
48
49
50
51
52
53
def add_message(self, role: str, content: str):
    # Extend supermethod to print messages
    super().add_message(role, content)
    if role == "assistant":
        console.print(
            f"\n[red][bold]mini-swe-agent[/bold] (step [bold]{self.model.n_calls}[/bold], [bold]${self.model.cost:.2f}[/bold]):[/red]\n",
            end="",
            highlight=False,
        )
    else:
        console.print(f"\n[bold green]{role.capitalize()}[/bold green]:\n", end="", highlight=False)
    console.print(content, highlight=False, markup=False)

query

query() -> dict
Source code in src/minisweagent/agents/interactive.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def query(self) -> dict:
    # Extend supermethod to handle human mode
    if self.config.mode == "human":
        match command := self._prompt_and_handle_special("[bold yellow]>[/bold yellow] "):
            case "/y" | "/c":  # Just go to the super query, which queries the LM for the next action
                pass
            case _:
                return {"content": f"\n```bash\n{command}\n```"}
    try:
        with console.status("Waiting for the LM to respond..."):
            return super().query()
    except LimitsExceeded:
        console.print(
            f"Limits exceeded. Limits: {self.config.step_limit} steps, ${self.config.cost_limit}.\n"
            f"Current spend: {self.model.n_calls} steps, ${self.model.cost:.2f}."
        )
        self.config.step_limit = int(input("New step limit: "))
        self.config.cost_limit = float(input("New cost limit: "))
        return super().query()

step

step() -> dict
Source code in src/minisweagent/agents/interactive.py
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
def step(self) -> dict:
    # Override the step method to handle user interruption
    try:
        console.print(Rule())
        return super().step()
    except KeyboardInterrupt:
        # We always add a message about the interrupt and then just proceed to the next step
        interruption_message = self._prompt_and_handle_special(
            "\n\n[bold yellow]Interrupted.[/bold yellow] "
            "[green]Type a comment/command[/green] (/h for available commands)"
            "\n[bold yellow]>[/bold yellow] "
        ).strip()
        if not interruption_message or interruption_message in self._MODE_COMMANDS_MAPPING:
            interruption_message = "Temporary interruption caught."
        raise NonTerminatingException(f"Interrupted by user: {interruption_message}")

execute_action

execute_action(action: dict) -> dict
Source code in src/minisweagent/agents/interactive.py
91
92
93
94
95
def execute_action(self, action: dict) -> dict:
    # Override the execute_action method to handle user confirmation
    if self.should_ask_confirmation(action["action"]):
        self.ask_confirmation()
    return super().execute_action(action)

should_ask_confirmation

should_ask_confirmation(action: str) -> bool
Source code in src/minisweagent/agents/interactive.py
97
98
def should_ask_confirmation(self, action: str) -> bool:
    return self.config.mode == "confirm" and not any(re.match(r, action) for r in self.config.whitelist_actions)

ask_confirmation

ask_confirmation() -> None
Source code in src/minisweagent/agents/interactive.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def ask_confirmation(self) -> None:
    prompt = (
        "[bold yellow]Execute?[/bold yellow] [green][bold]Enter[/bold] to confirm[/green], "
        "or [green]Type a comment/command[/green] (/h for available commands)\n"
        "[bold yellow]>[/bold yellow] "
    )
    match user_input := self._prompt_and_handle_special(prompt).strip():
        case "" | "/y":
            pass  # confirmed, do nothing
        case "/u":  # Skip execution action and get back to query
            raise NonTerminatingException("Command not executed. Switching to human mode")
        case _:
            raise NonTerminatingException(
                f"Command not executed. The user rejected your command with the following message: {user_input}"
            )

has_finished

has_finished(output: dict[str, str])
Source code in src/minisweagent/agents/interactive.py
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def has_finished(self, output: dict[str, str]):
    try:
        return super().has_finished(output)
    except Submitted as e:
        if self.config.confirm_exit:
            console.print(
                "[bold green]Agent wants to finish.[/bold green] "
                "[green]Type a comment to give it a new task or press enter to quit.\n"
                "[bold yellow]>[/bold yellow] ",
                end="",
            )
            if new_task := self._prompt_and_handle_special("").strip():
                raise NonTerminatingException(f"The user added a new task: {new_task}")
        raise e