mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-01-09 14:57:59 -05:00
CLI: lazy load conversation for /new command (#11601)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -143,8 +143,9 @@ def run_cli_entry(resume_conversation_id: str | None = None) -> None:
|
|||||||
elif command == '/new':
|
elif command == '/new':
|
||||||
try:
|
try:
|
||||||
# Start a fresh conversation (no resume ID = new conversation)
|
# Start a fresh conversation (no resume ID = new conversation)
|
||||||
conversation = setup_conversation(conversation_id)
|
conversation_id = uuid.uuid4()
|
||||||
runner = ConversationRunner(conversation)
|
runner = None
|
||||||
|
conversation = None
|
||||||
display_welcome(conversation_id, resume=False)
|
display_welcome(conversation_id, resume=False)
|
||||||
print_formatted_text(
|
print_formatted_text(
|
||||||
HTML('<green>✓ Started fresh conversation</green>')
|
HTML('<green>✓ Started fresh conversation</green>')
|
||||||
@@ -195,7 +196,7 @@ def run_cli_entry(resume_conversation_id: str | None = None) -> None:
|
|||||||
# Resume without new message
|
# Resume without new message
|
||||||
message = None
|
message = None
|
||||||
|
|
||||||
if not runner:
|
if not runner or not conversation:
|
||||||
conversation = setup_conversation(conversation_id)
|
conversation = setup_conversation(conversation_id)
|
||||||
runner = ConversationRunner(conversation)
|
runner = ConversationRunner(conversation)
|
||||||
runner.process_message(message)
|
runner.process_message(message)
|
||||||
|
|||||||
@@ -2,12 +2,18 @@
|
|||||||
|
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from prompt_toolkit.input.defaults import create_pipe_input
|
from prompt_toolkit.input.defaults import create_pipe_input
|
||||||
from prompt_toolkit.output.defaults import DummyOutput
|
from prompt_toolkit.output.defaults import DummyOutput
|
||||||
from openhands_cli.setup import MissingAgentSpec, verify_agent_exists_or_setup_agent, setup_conversation
|
|
||||||
|
from openhands_cli.setup import (
|
||||||
|
MissingAgentSpec,
|
||||||
|
verify_agent_exists_or_setup_agent,
|
||||||
|
)
|
||||||
from openhands_cli.user_actions import UserConfirmation
|
from openhands_cli.user_actions import UserConfirmation
|
||||||
|
|
||||||
@patch('openhands_cli.setup.load_agent_specs')
|
|
||||||
|
@patch("openhands_cli.setup.load_agent_specs")
|
||||||
def test_verify_agent_exists_or_setup_agent_success(mock_load_agent_specs):
|
def test_verify_agent_exists_or_setup_agent_success(mock_load_agent_specs):
|
||||||
"""Test that verify_agent_exists_or_setup_agent returns agent successfully."""
|
"""Test that verify_agent_exists_or_setup_agent returns agent successfully."""
|
||||||
# Mock the agent object
|
# Mock the agent object
|
||||||
@@ -22,11 +28,10 @@ def test_verify_agent_exists_or_setup_agent_success(mock_load_agent_specs):
|
|||||||
mock_load_agent_specs.assert_called_once_with()
|
mock_load_agent_specs.assert_called_once_with()
|
||||||
|
|
||||||
|
|
||||||
@patch('openhands_cli.setup.SettingsScreen')
|
@patch("openhands_cli.setup.SettingsScreen")
|
||||||
@patch('openhands_cli.setup.load_agent_specs')
|
@patch("openhands_cli.setup.load_agent_specs")
|
||||||
def test_verify_agent_exists_or_setup_agent_missing_agent_spec(
|
def test_verify_agent_exists_or_setup_agent_missing_agent_spec(
|
||||||
mock_load_agent_specs,
|
mock_load_agent_specs, mock_settings_screen_class
|
||||||
mock_settings_screen_class
|
|
||||||
):
|
):
|
||||||
"""Test that verify_agent_exists_or_setup_agent handles MissingAgentSpec exception."""
|
"""Test that verify_agent_exists_or_setup_agent handles MissingAgentSpec exception."""
|
||||||
# Mock the SettingsScreen instance
|
# Mock the SettingsScreen instance
|
||||||
@@ -37,7 +42,7 @@ def test_verify_agent_exists_or_setup_agent_missing_agent_spec(
|
|||||||
mock_agent = MagicMock()
|
mock_agent = MagicMock()
|
||||||
mock_load_agent_specs.side_effect = [
|
mock_load_agent_specs.side_effect = [
|
||||||
MissingAgentSpec("Agent spec missing"),
|
MissingAgentSpec("Agent spec missing"),
|
||||||
mock_agent
|
mock_agent,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Call the function
|
# Call the function
|
||||||
@@ -51,14 +56,11 @@ def test_verify_agent_exists_or_setup_agent_missing_agent_spec(
|
|||||||
mock_settings_screen.configure_settings.assert_called_once_with(first_time=True)
|
mock_settings_screen.configure_settings.assert_called_once_with(first_time=True)
|
||||||
|
|
||||||
|
|
||||||
|
@patch("openhands_cli.agent_chat.exit_session_confirmation")
|
||||||
|
@patch("openhands_cli.agent_chat.get_session_prompter")
|
||||||
|
@patch("openhands_cli.agent_chat.setup_conversation")
|
||||||
@patch('openhands_cli.agent_chat.exit_session_confirmation')
|
@patch("openhands_cli.agent_chat.verify_agent_exists_or_setup_agent")
|
||||||
@patch('openhands_cli.agent_chat.get_session_prompter')
|
@patch("openhands_cli.agent_chat.ConversationRunner")
|
||||||
@patch('openhands_cli.agent_chat.setup_conversation')
|
|
||||||
@patch('openhands_cli.agent_chat.verify_agent_exists_or_setup_agent')
|
|
||||||
@patch('openhands_cli.agent_chat.ConversationRunner')
|
|
||||||
def test_new_command_resets_confirmation_mode(
|
def test_new_command_resets_confirmation_mode(
|
||||||
mock_runner_cls,
|
mock_runner_cls,
|
||||||
mock_verify_agent,
|
mock_verify_agent,
|
||||||
@@ -74,27 +76,35 @@ def test_new_command_resets_confirmation_mode(
|
|||||||
mock_verify_agent.return_value = mock_agent
|
mock_verify_agent.return_value = mock_agent
|
||||||
|
|
||||||
# Mock conversation - only one is created when /new is called
|
# Mock conversation - only one is created when /new is called
|
||||||
conv1 = MagicMock(); conv1.id = UUID('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')
|
conv1 = MagicMock()
|
||||||
|
conv1.id = UUID("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")
|
||||||
mock_setup_conversation.return_value = conv1
|
mock_setup_conversation.return_value = conv1
|
||||||
|
|
||||||
# One runner instance for the conversation
|
# One runner instance for the conversation
|
||||||
runner1 = MagicMock(); runner1.is_confirmation_mode_active = True
|
runner1 = MagicMock()
|
||||||
|
runner1.is_confirmation_mode_active = True
|
||||||
mock_runner_cls.return_value = runner1
|
mock_runner_cls.return_value = runner1
|
||||||
|
|
||||||
# Real session fed by a pipe (no interactive confirmation now)
|
# Real session fed by a pipe (no interactive confirmation now)
|
||||||
from openhands_cli.user_actions.utils import get_session_prompter as real_get_session_prompter
|
from openhands_cli.user_actions.utils import (
|
||||||
|
get_session_prompter as real_get_session_prompter,
|
||||||
|
)
|
||||||
|
|
||||||
with create_pipe_input() as pipe:
|
with create_pipe_input() as pipe:
|
||||||
output = DummyOutput()
|
output = DummyOutput()
|
||||||
session = real_get_session_prompter(input=pipe, output=output)
|
session = real_get_session_prompter(input=pipe, output=output)
|
||||||
mock_get_session_prompter.return_value = session
|
mock_get_session_prompter.return_value = session
|
||||||
|
|
||||||
from openhands_cli.agent_chat import run_cli_entry
|
from openhands_cli.agent_chat import run_cli_entry
|
||||||
# Trigger /new, then /exit (exit will be auto-accepted)
|
|
||||||
for ch in "/new\r/exit\r":
|
# Trigger /new
|
||||||
|
# First user message should trigger runner creation
|
||||||
|
# Then /exit (exit will be auto-accepted)
|
||||||
|
for ch in "/new\rhello\r/exit\r":
|
||||||
pipe.send_text(ch)
|
pipe.send_text(ch)
|
||||||
|
|
||||||
run_cli_entry(None)
|
run_cli_entry(None)
|
||||||
|
|
||||||
# Assert we created one runner for the conversation when /new was called
|
# Assert we created one runner for the conversation when a message was processed after /new
|
||||||
assert mock_runner_cls.call_count == 1
|
assert mock_runner_cls.call_count == 1
|
||||||
assert mock_runner_cls.call_args_list[0].args[0] is conv1
|
assert mock_runner_cls.call_args_list[0].args[0] is conv1
|
||||||
|
|||||||
Reference in New Issue
Block a user