Skip to content

Commit

Permalink
fix: Use boolean True instead of string 'true' for is_input parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
openhands-agent committed Feb 20, 2025
1 parent 5866e6c commit 18d89cc
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 33 deletions.
6 changes: 4 additions & 2 deletions openhands/controller/agent_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,10 @@ async def set_agent_state_to(self, new_state: AgentState) -> None:
await self.update_state_after_step()
self.state.metrics.merge(self.state.local_metrics)
# Send C-c to stop any running process
if self._pending_action is not None and isinstance(self._pending_action, CmdRunAction):
stop_action = CmdRunAction(command="C-c", is_input="true")
if self._pending_action is not None and isinstance(
self._pending_action, CmdRunAction
):
stop_action = CmdRunAction(command='C-c', is_input=True)
self.event_stream.add_event(stop_action, EventSource.AGENT)
self._reset()
elif (
Expand Down
79 changes: 48 additions & 31 deletions tests/unit/test_bash_session_stop.py
Original file line number Diff line number Diff line change
@@ -1,88 +1,98 @@
import pytest
import time
from openhands.events.action import CmdRunAction
from openhands.events.observation import CmdOutputObservation
from openhands.runtime.utils.bash import BashSession


def test_bash_session_stop_behavior():
"""Test that stopping a long-running process works correctly."""
session = BashSession(work_dir="/tmp", no_change_timeout_seconds=1)
session = BashSession(work_dir='/tmp', no_change_timeout_seconds=1)
session.initialize()

# Start a long-running process that will timeout
action = CmdRunAction(command="sleep 10")
action = CmdRunAction(command='sleep 10')
action.set_hard_timeout(2) # Set a timeout so test doesn't hang
action.blocking = False # Allow no-change timeout

# Execute the command and wait for timeout
result = session.execute(action)
assert isinstance(result, CmdOutputObservation)
assert "command timed out" in result.metadata.suffix.lower() or "no new output" in result.metadata.suffix.lower()

assert (
'command timed out' in result.metadata.suffix.lower()
or 'no new output' in result.metadata.suffix.lower()
)

# Try to send a new command - this should fail since process is still running
new_action = CmdRunAction(command="echo test")
new_action = CmdRunAction(command='echo test')
new_result = session.execute(new_action)
assert isinstance(new_result, CmdOutputObservation)
assert "previous command is still running" in new_result.metadata.suffix.lower()
assert "not executed" in new_result.metadata.suffix.lower()
assert 'previous command is still running' in new_result.metadata.suffix.lower()
assert 'not executed' in new_result.metadata.suffix.lower()

# Try to send an empty command - this should show the running process output
empty_action = CmdRunAction(command="")
empty_action = CmdRunAction(command='')
empty_result = session.execute(empty_action)
assert isinstance(empty_result, CmdOutputObservation)
assert "output of the previous command" in empty_result.metadata.prefix.lower()
assert 'output of the previous command' in empty_result.metadata.prefix.lower()

# Clean up
session.close()


def test_bash_session_stop_command():
"""Test that sending C-c to stop a process works correctly."""
session = BashSession(work_dir="/tmp", no_change_timeout_seconds=1)
session = BashSession(work_dir='/tmp', no_change_timeout_seconds=1)
session.initialize()

# Start a long-running process that will timeout
action = CmdRunAction(command="sleep 10")
action = CmdRunAction(command='sleep 10')
action.set_hard_timeout(2) # Set a timeout so test doesn't hang
action.blocking = False # Allow no-change timeout

# Execute the command and wait for timeout
result = session.execute(action)
assert isinstance(result, CmdOutputObservation)
assert "command timed out" in result.metadata.suffix.lower() or "no new output" in result.metadata.suffix.lower()

assert (
'command timed out' in result.metadata.suffix.lower()
or 'no new output' in result.metadata.suffix.lower()
)

# Send C-c to stop the process
stop_action = CmdRunAction(command="C-c", is_input="true")
stop_action = CmdRunAction(command='C-c', is_input=True)
stop_result = session.execute(stop_action)
assert isinstance(stop_result, CmdOutputObservation)
assert stop_result.metadata.exit_code == 130 # 130 is the exit code for SIGINT

# Now we should be able to run a new command
new_action = CmdRunAction(command="echo test")
new_action = CmdRunAction(command='echo test')
new_result = session.execute(new_action)
assert isinstance(new_result, CmdOutputObservation)
assert new_result.metadata.exit_code == 0
assert "test" in new_result.content
assert 'test' in new_result.content

# Clean up
session.close()


def test_agent_controller_stop():
"""Test that the agent controller sends C-c when stopping."""
from openhands.controller.agent_controller import AgentController
from openhands.controller.agent import Agent
from openhands.controller.agent_controller import AgentController
from openhands.core.config import AgentConfig, LLMConfig
from openhands.core.schema import AgentState
from openhands.events import EventStream
from openhands.llm.llm import LLM
from openhands.core.config import AppConfig, LLMConfig, AgentConfig

# Create a mock event stream to capture events
from openhands.storage.local import LocalFileStore
file_store = LocalFileStore("/tmp")
event_stream = EventStream(sid="test", file_store=file_store)

file_store = LocalFileStore('/tmp')
event_stream = EventStream(sid='test', file_store=file_store)
events = []

def on_event(event):
events.append(event)
event_stream.subscribe("test", on_event, "test")

event_stream.subscribe('test', on_event, 'test')

# Create a mock agent
class MockAgent(Agent):
Expand All @@ -98,19 +108,26 @@ def step(self, history):
max_iterations=10,
agent_configs={},
agent_to_llm_config={},
sid="test",
sid='test',
confirmation_mode=False,
headless_mode=True,
)

# Set up a pending action
pending_action = CmdRunAction(command="sleep 10")
pending_action = CmdRunAction(command='sleep 10')
controller._pending_action = pending_action

# Change state to STOPPED
import asyncio
asyncio.get_event_loop().run_until_complete(controller.set_agent_state_to(AgentState.STOPPED))

asyncio.get_event_loop().run_until_complete(
controller.set_agent_state_to(AgentState.STOPPED)
)

# Verify that C-c was sent
stop_events = [e for e in events if isinstance(e, CmdRunAction) and e.command == "C-c" and e.is_input == "true"]
assert len(stop_events) == 1, "Expected exactly one C-c command to be sent"
stop_events = [
e
for e in events
if isinstance(e, CmdRunAction) and e.command == 'C-c' and e.is_input is True
]
assert len(stop_events) == 1, 'Expected exactly one C-c command to be sent'

0 comments on commit 18d89cc

Please sign in to comment.