fix: improve CLI help and version command performance (#10908)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Kaushik Ashodiya
2025-09-12 11:23:01 -07:00
committed by GitHub
parent 8dc46b7206
commit 30604c40fc
2 changed files with 188 additions and 18 deletions

View File

@@ -2,32 +2,24 @@
import sys
# Import only essential modules for CLI help
# Other imports are deferred until they're actually needed
import openhands
import openhands.cli.suppress_warnings # noqa: F401
from openhands.cli.gui_launcher import launch_gui_server
from openhands.cli.main import run_cli_command
from openhands.core.config import get_cli_parser
from openhands.core.config.arg_utils import get_subparser
from openhands.cli.fast_help import handle_fast_commands
def main():
"""Main entry point with subcommand support and backward compatibility."""
parser = get_cli_parser()
# If user only asks for --help or -h without a subcommand
if len(sys.argv) == 2 and sys.argv[1] in ('--help', '-h'):
# Print top-level help
print(parser.format_help())
# Also print help for `cli` subcommand
print('\n' + '=' * 80)
print('CLI command help:\n')
cli_parser = get_subparser(parser, 'cli')
print(cli_parser.format_help())
# Fast path for help and version commands
if handle_fast_commands():
sys.exit(0)
# Import parser only when needed - only if we're not just showing help
from openhands.core.config import get_cli_parser
parser = get_cli_parser()
# Special case: no subcommand provided, simulate "openhands cli"
if len(sys.argv) == 1 or (
len(sys.argv) > 1 and sys.argv[1] not in ['cli', 'serve']
@@ -42,8 +34,14 @@ def main():
sys.exit(0)
if args.command == 'serve':
# Import gui_launcher only when needed
from openhands.cli.gui_launcher import launch_gui_server
launch_gui_server(mount_cwd=args.mount_cwd, gpu=args.gpu)
elif args.command == 'cli' or args.command is None:
# Import main only when needed
from openhands.cli.main import run_cli_command
run_cli_command(args)
else:
parser.print_help()

172
openhands/cli/fast_help.py Normal file
View File

@@ -0,0 +1,172 @@
"""Fast help module for OpenHands CLI.
This module provides a lightweight implementation of the CLI help and version commands
without loading all the dependencies, which significantly improves the
performance of `openhands --help` and `openhands --version`.
The approach is to create a simplified version of the CLI parser that only includes
the necessary options for displaying help and version information. This avoids loading
the full OpenHands codebase, which can take several seconds.
This implementation addresses GitHub issue #10698, which reported that
`openhands --help` was taking around 20 seconds to run.
"""
import argparse
import sys
def get_fast_cli_parser() -> argparse.ArgumentParser:
"""Create a lightweight argument parser for CLI help command."""
# Create a description with welcome message explaining available commands
description = (
'Welcome to OpenHands: Code Less, Make More\n\n'
'OpenHands supports two main commands:\n'
' serve - Launch the OpenHands GUI server (web interface)\n'
' cli - Run OpenHands in CLI mode (terminal interface)\n\n'
'Running "openhands" without a command is the same as "openhands cli"'
)
parser = argparse.ArgumentParser(
description=description,
prog='openhands',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='For more information about a command, run: openhands COMMAND --help',
)
# Create subparsers
subparsers = parser.add_subparsers(
dest='command',
title='commands',
description='OpenHands supports two main commands:',
metavar='COMMAND',
)
# Add 'serve' subcommand
serve_parser = subparsers.add_parser(
'serve', help='Launch the OpenHands GUI server using Docker (web interface)'
)
serve_parser.add_argument(
'--mount-cwd',
help='Mount the current working directory into the GUI server container',
action='store_true',
default=False,
)
serve_parser.add_argument(
'--gpu',
help='Enable GPU support by mounting all GPUs into the Docker container via nvidia-docker',
action='store_true',
default=False,
)
# Add 'cli' subcommand with common arguments
cli_parser = subparsers.add_parser(
'cli', help='Run OpenHands in CLI mode (terminal interface)'
)
# Add common arguments
cli_parser.add_argument(
'--config-file',
type=str,
default='config.toml',
help='Path to the config file (default: config.toml in the current directory)',
)
cli_parser.add_argument(
'-t',
'--task',
type=str,
default='',
help='The task for the agent to perform',
)
cli_parser.add_argument(
'-f',
'--file',
type=str,
help='Path to a file containing the task. Overrides -t if both are provided.',
)
cli_parser.add_argument(
'-n',
'--name',
help='Session name',
type=str,
default='',
)
cli_parser.add_argument(
'--log-level',
help='Set the log level',
type=str,
default=None,
)
cli_parser.add_argument(
'-l',
'--llm-config',
default=None,
type=str,
help='Replace default LLM ([llm] section in config.toml) config with the specified LLM config, e.g. "llama3" for [llm.llama3] section in config.toml',
)
cli_parser.add_argument(
'--agent-config',
default=None,
type=str,
help='Replace default Agent ([agent] section in config.toml) config with the specified Agent config, e.g. "CodeAct" for [agent.CodeAct] section in config.toml',
)
cli_parser.add_argument(
'-v', '--version', action='store_true', help='Show version information'
)
cli_parser.add_argument(
'--override-cli-mode',
help='Override the default settings for CLI mode',
type=bool,
default=False,
)
parser.add_argument(
'--conversation',
help='The conversation id to continue',
type=str,
default=None,
)
return parser
def get_fast_subparser(
parser: argparse.ArgumentParser, name: str
) -> argparse.ArgumentParser:
"""Get a subparser by name."""
for action in parser._actions:
if isinstance(action, argparse._SubParsersAction):
if name in action.choices:
return action.choices[name]
raise ValueError(f"Subparser '{name}' not found")
def handle_fast_commands() -> bool:
"""Handle fast path commands like help and version.
Returns:
bool: True if a command was handled, False otherwise.
"""
# Handle --help or -h
if len(sys.argv) == 2 and sys.argv[1] in ('--help', '-h'):
parser = get_fast_cli_parser()
# Print top-level help
print(parser.format_help())
# Also print help for `cli` subcommand
print('\n' + '=' * 80)
print('CLI command help:\n')
cli_parser = get_fast_subparser(parser, 'cli')
print(cli_parser.format_help())
return True
# Handle --version or -v
if len(sys.argv) == 2 and sys.argv[1] in ('--version', '-v'):
import openhands
print(f'OpenHands CLI version: {openhands.get_version()}')
return True
return False