Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 13 additions & 17 deletions parallel_web_tools/cli/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,18 +880,14 @@ def parse_bool(v: str) -> bool:
# Search Command
# =============================================================================

# Beta -> V1 mode mapping. Beta had three modes; V1 has three (turbo/basic/
# advanced). We keep the old values as accepted CLI inputs and translate them so
# existing scripts work.
_SEARCH_MODE_MAP = {
# V1 modes are listed first in CLI help. Beta modes remain accepted as
# deprecated aliases so existing scripts keep working.
_SEARCH_MODES = ("turbo", "basic", "advanced")
_DEPRECATED_SEARCH_MODE_ALIASES = {
"fast": "basic",
"one-shot": "basic",
"agentic": "advanced",
"turbo": "turbo",
"basic": "basic",
"advanced": "advanced",
}
_DEPRECATED_SEARCH_MODES = {"fast", "one-shot", "agentic"}


def _emit_deprecation(message: str) -> None:
Expand Down Expand Up @@ -926,7 +922,7 @@ def build_search_v1_kwargs(
if objective:
kwargs["objective"] = objective
if mode:
kwargs["mode"] = _SEARCH_MODE_MAP.get(mode, mode)
kwargs["mode"] = _DEPRECATED_SEARCH_MODE_ALIASES.get(mode, mode)
if excerpt_max_chars_total is not None:
kwargs["max_chars_total"] = excerpt_max_chars_total
if session_id:
Expand Down Expand Up @@ -956,9 +952,12 @@ def build_search_v1_kwargs(
@click.option("-q", "--query", multiple=True, help="Keyword search query (can be repeated)")
@click.option(
"--mode",
type=click.Choice(list(_SEARCH_MODE_MAP.keys())),
type=click.Choice([*_SEARCH_MODES, *_DEPRECATED_SEARCH_MODE_ALIASES]),
default="basic",
help="Search mode: turbo (fastest), basic, or advanced (one-shot/fast → basic, agentic → advanced)",
help=(
"Search mode: turbo (fastest), basic, or advanced. "
"Deprecated aliases: one-shot/fast → basic, agentic → advanced"
),
show_default=True,
)
@click.option("--max-results", type=int, help="Maximum results (defaults to server-side default of 10)")
Expand Down Expand Up @@ -1012,12 +1011,9 @@ def search(
if not objective and not query:
raise click.UsageError("Provide an OBJECTIVE argument or at least one --query option.")

if mode in _DEPRECATED_SEARCH_MODES:
new_mode = _SEARCH_MODE_MAP[mode]
_emit_deprecation(
f"--mode {mode} is a Beta value and will stop working after the Beta API sunset (June 2026). "
f"Use --mode {new_mode} instead."
)
if mode in _DEPRECATED_SEARCH_MODE_ALIASES:
new_mode = _DEPRECATED_SEARCH_MODE_ALIASES[mode]
_emit_deprecation(f"--mode {mode} is a deprecated alias. Use --mode {new_mode} instead.")

source_policy: dict[str, Any] = {}
if include_domains:
Expand Down
15 changes: 11 additions & 4 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,13 @@ def test_search_help_shows_comma_separated(self, runner):
assert "--exclude-domains" in result.output
assert "comma-separated" in result.output

def test_search_help_prioritizes_native_modes(self, runner):
"""Should list V1-native modes before deprecated aliases."""
result = runner.invoke(main, ["search", "--help"])
assert result.exit_code == 0
assert "--mode [turbo|basic|advanced|fast|one-shot|agentic]" in result.output
assert "Deprecated aliases" in result.output

def test_search_no_args(self, runner):
"""Should error without objective or query."""
result = runner.invoke(main, ["search"])
Expand Down Expand Up @@ -1635,16 +1642,16 @@ def test_deprecated_modes_emit_warning_to_stderr(self, runner, mock_cli_client,
)

assert result.exit_code == 0
assert "[deprecated]" in result.stderr
assert deprecated_mode in result.stderr
assert expected_new in result.stderr
assert result.stderr.strip() == (
f"[deprecated] --mode {deprecated_mode} is a deprecated alias. Use --mode {expected_new} instead."
)
# JSON stdout must remain clean
json.loads(result.stdout)
# SDK call uses translated mode
call_kwargs = mock_cli_client.search.call_args.kwargs
assert call_kwargs["mode"] == expected_new

@pytest.mark.parametrize("new_mode", ["basic", "advanced"])
@pytest.mark.parametrize("new_mode", ["turbo", "basic", "advanced"])
def test_new_modes_do_not_emit_warning(self, runner, mock_cli_client, new_mode):
"""Should not warn when V1-native mode values are used."""
self._setup_mock_search(mock_cli_client)
Expand Down
Loading