mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 14:57:59 -05:00
refactor: Refactor pause/resume functionality and improve state handling in CLI (#8152)
This commit is contained in:
committed by
GitHub
parent
03aa5d7456
commit
6e0fbfeeda
@@ -101,7 +101,7 @@ async def run_session(
|
||||
|
||||
sid = str(uuid4())
|
||||
is_loaded = asyncio.Event()
|
||||
is_paused = asyncio.Event()
|
||||
is_paused = asyncio.Event() # Event to track agent pause requests
|
||||
|
||||
# Show runtime initialization message
|
||||
display_runtime_initialization_message(config.runtime)
|
||||
@@ -157,20 +157,15 @@ async def run_session(
|
||||
display_event(event, config)
|
||||
update_usage_metrics(event, usage_metrics)
|
||||
|
||||
# Pause the agent if the pause event is set (if Ctrl-P is pressed)
|
||||
if is_paused.is_set():
|
||||
event_stream.add_event(
|
||||
ChangeAgentStateAction(AgentState.PAUSED),
|
||||
EventSource.USER,
|
||||
)
|
||||
is_paused.clear()
|
||||
|
||||
if isinstance(event, AgentStateChangedObservation):
|
||||
if event.agent_state in [
|
||||
AgentState.AWAITING_USER_INPUT,
|
||||
AgentState.FINISHED,
|
||||
AgentState.PAUSED,
|
||||
]:
|
||||
# If the agent is paused, do not prompt for input as it's already handled by PAUSED state change
|
||||
if is_paused.is_set():
|
||||
return
|
||||
|
||||
# Reload microagents after initialization of repo.md
|
||||
if reload_microagents:
|
||||
microagents: list[BaseMicroagent] = (
|
||||
@@ -181,25 +176,32 @@ async def run_session(
|
||||
await prompt_for_next_task(event.agent_state)
|
||||
|
||||
if event.agent_state == AgentState.AWAITING_USER_CONFIRMATION:
|
||||
# Only display the confirmation prompt if the agent is not paused
|
||||
if not is_paused.is_set():
|
||||
user_confirmed = await read_confirmation_input()
|
||||
if user_confirmed:
|
||||
event_stream.add_event(
|
||||
ChangeAgentStateAction(AgentState.USER_CONFIRMED),
|
||||
EventSource.USER,
|
||||
)
|
||||
else:
|
||||
event_stream.add_event(
|
||||
ChangeAgentStateAction(AgentState.USER_REJECTED),
|
||||
EventSource.USER,
|
||||
)
|
||||
# If the agent is paused, do not prompt for confirmation
|
||||
# The confirmation step will re-run after the agent has been resumed
|
||||
if is_paused.is_set():
|
||||
return
|
||||
|
||||
user_confirmed = await read_confirmation_input()
|
||||
if user_confirmed:
|
||||
event_stream.add_event(
|
||||
ChangeAgentStateAction(AgentState.USER_CONFIRMED),
|
||||
EventSource.USER,
|
||||
)
|
||||
else:
|
||||
event_stream.add_event(
|
||||
ChangeAgentStateAction(AgentState.USER_REJECTED),
|
||||
EventSource.USER,
|
||||
)
|
||||
|
||||
if event.agent_state == AgentState.PAUSED:
|
||||
is_paused.clear() # Revert the event state before prompting for user input
|
||||
await prompt_for_next_task(event.agent_state)
|
||||
|
||||
if event.agent_state == AgentState.RUNNING:
|
||||
# Enable pause/resume functionality only if the confirmation mode is disabled
|
||||
if not config.security.confirmation_mode:
|
||||
display_agent_running_message()
|
||||
loop.create_task(process_agent_pause(is_paused))
|
||||
display_agent_running_message()
|
||||
loop.create_task(
|
||||
process_agent_pause(is_paused, event_stream)
|
||||
) # Create a task to track agent pause requests from the user
|
||||
|
||||
def on_event(event: Event) -> None:
|
||||
loop.create_task(on_event_async(event))
|
||||
|
||||
@@ -25,10 +25,11 @@ from prompt_toolkit.widgets import Frame, TextArea
|
||||
from openhands import __version__
|
||||
from openhands.core.config import AppConfig
|
||||
from openhands.core.schema import AgentState
|
||||
from openhands.events import EventSource
|
||||
from openhands.events import EventSource, EventStream
|
||||
from openhands.events.action import (
|
||||
Action,
|
||||
ActionConfirmationStatus,
|
||||
ChangeAgentStateAction,
|
||||
CmdRunAction,
|
||||
FileEditAction,
|
||||
MessageAction,
|
||||
@@ -60,7 +61,7 @@ COMMANDS = {
|
||||
'/status': 'Display session details and usage metrics',
|
||||
'/new': 'Create a new session',
|
||||
'/settings': 'Display and modify current settings',
|
||||
'/resume': 'Resume the agent',
|
||||
'/resume': 'Resume the agent when paused',
|
||||
}
|
||||
|
||||
|
||||
@@ -396,7 +397,7 @@ def display_status(usage_metrics: UsageMetrics, session_id: str):
|
||||
def display_agent_running_message():
|
||||
print_formatted_text('')
|
||||
print_formatted_text(
|
||||
HTML('<gold>Agent running...</gold> <grey>(Ctrl-P to pause)</grey>')
|
||||
HTML('<gold>Agent running...</gold> <grey>(Press Ctrl-P to pause)</grey>')
|
||||
)
|
||||
|
||||
|
||||
@@ -405,7 +406,7 @@ def display_agent_paused_message(agent_state: str):
|
||||
return
|
||||
print_formatted_text('')
|
||||
print_formatted_text(
|
||||
HTML('<gold>Agent paused</gold> <grey>(type /resume to resume)</grey>')
|
||||
HTML('<gold>Agent paused...</gold> <grey>(Enter /resume to continue)</grey>')
|
||||
)
|
||||
|
||||
|
||||
@@ -430,7 +431,7 @@ class CommandCompleter(Completer):
|
||||
command,
|
||||
start_position=-len(text),
|
||||
display_meta=description,
|
||||
style='bg:ansidarkgray fg:ansiwhite',
|
||||
style='bg:ansidarkgray fg:gold',
|
||||
)
|
||||
|
||||
|
||||
@@ -488,7 +489,7 @@ async def read_confirmation_input() -> bool:
|
||||
return False
|
||||
|
||||
|
||||
async def process_agent_pause(done: asyncio.Event) -> None:
|
||||
async def process_agent_pause(done: asyncio.Event, event_stream: EventStream) -> None:
|
||||
input = create_input()
|
||||
|
||||
def keys_ready():
|
||||
@@ -496,6 +497,10 @@ async def process_agent_pause(done: asyncio.Event) -> None:
|
||||
if key_press.key == Keys.ControlP:
|
||||
print_formatted_text('')
|
||||
print_formatted_text(HTML('<gold>Pausing the agent...</gold>'))
|
||||
event_stream.add_event(
|
||||
ChangeAgentStateAction(AgentState.PAUSED),
|
||||
EventSource.USER,
|
||||
)
|
||||
done.set()
|
||||
|
||||
with input.raw_mode():
|
||||
|
||||
Reference in New Issue
Block a user