4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
140 lines
4.8 KiB
Python
140 lines
4.8 KiB
Python
"""Tests for the wizard's interactive model selection prompt."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import MagicMock
|
|
|
|
import pytest
|
|
|
|
from surfaces.cli.wizard import _ui, flow
|
|
from surfaces.cli.wizard.config import PROVIDER_BY_VALUE
|
|
|
|
|
|
def _wire_prompts(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
select_values: list[str],
|
|
text_values: list[str] | None = None,
|
|
) -> None:
|
|
select_iter = iter(select_values)
|
|
text_iter = iter(text_values or [])
|
|
|
|
def _mock_select(*_args: Any, **_kwargs: Any) -> Any:
|
|
m = MagicMock()
|
|
m.ask.return_value = next(select_iter)
|
|
return m
|
|
|
|
def _mock_text(*_args: Any, **_kwargs: Any) -> Any:
|
|
m = MagicMock()
|
|
m.ask.return_value = next(text_iter)
|
|
return m
|
|
|
|
monkeypatch.setattr(_ui, "select_prompt", _mock_select)
|
|
monkeypatch.setattr(flow.questionary, "text", _mock_text)
|
|
|
|
|
|
def test_choose_model_returns_curated_default(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
provider = PROVIDER_BY_VALUE["anthropic"]
|
|
_wire_prompts(monkeypatch, select_values=[provider.default_model])
|
|
|
|
model = _ui._choose_model(provider, default=provider.default_model)
|
|
|
|
assert model == provider.default_model
|
|
|
|
|
|
def test_choose_model_offers_full_curated_list(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
provider = PROVIDER_BY_VALUE["openai"]
|
|
|
|
captured: dict[str, list[str]] = {}
|
|
|
|
def _mock_select(_prompt: str, choices: list[Any], **_kwargs: Any) -> Any:
|
|
captured["values"] = [c.value for c in choices]
|
|
m = MagicMock()
|
|
m.ask.return_value = provider.default_model
|
|
return m
|
|
|
|
monkeypatch.setattr(_ui, "select_prompt", _mock_select)
|
|
|
|
_ui._choose_model(provider, default="")
|
|
|
|
expected_curated = [opt.value for opt in provider.models]
|
|
assert captured["values"][:-1] == expected_curated
|
|
assert captured["values"][-1] == _ui._CUSTOM_MODEL_SENTINEL
|
|
|
|
|
|
def test_choose_model_preserves_saved_model_not_in_curated(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
provider = PROVIDER_BY_VALUE["openai"]
|
|
|
|
captured: dict[str, list[str]] = {}
|
|
|
|
def _mock_select(_prompt: str, choices: list[Any], **_kwargs: Any) -> Any:
|
|
captured["values"] = [c.value for c in choices]
|
|
m = MagicMock()
|
|
m.ask.return_value = "my-tuned-gpt"
|
|
return m
|
|
|
|
monkeypatch.setattr(_ui, "select_prompt", _mock_select)
|
|
|
|
model = _ui._choose_model(provider, default="my-tuned-gpt")
|
|
|
|
assert model == "my-tuned-gpt"
|
|
assert "my-tuned-gpt" in captured["values"]
|
|
|
|
|
|
def test_choose_model_accepts_custom_entry(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
provider = PROVIDER_BY_VALUE["anthropic"]
|
|
_wire_prompts(
|
|
monkeypatch,
|
|
select_values=[_ui._CUSTOM_MODEL_SENTINEL],
|
|
text_values=["claude-future-preview"],
|
|
)
|
|
|
|
model = _ui._choose_model(provider, default=provider.default_model)
|
|
|
|
assert model == "claude-future-preview"
|
|
|
|
|
|
def test_choose_model_works_for_cli_provider(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""CLI providers (codex, claude-code, etc.) use the same curated picker."""
|
|
provider = PROVIDER_BY_VALUE["codex"]
|
|
_wire_prompts(monkeypatch, select_values=["gpt-5.4"])
|
|
|
|
model = _ui._choose_model(provider, default="")
|
|
|
|
assert model == "gpt-5.4"
|
|
|
|
|
|
class TestGpt56CatalogPresence:
|
|
"""The onboarding picker must offer Sol / Terra / Luna (#3931)."""
|
|
|
|
@pytest.mark.parametrize("model", ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"])
|
|
def test_openai_picker_lists_every_tier(self, model: str) -> None:
|
|
values = {option.value for option in PROVIDER_BY_VALUE["openai"].models}
|
|
assert model in values
|
|
|
|
@pytest.mark.parametrize("model", ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"])
|
|
def test_azure_picker_lists_every_tier(self, model: str) -> None:
|
|
values = {option.value for option in PROVIDER_BY_VALUE["azure-openai"].models}
|
|
assert model in values
|
|
|
|
def test_openrouter_picker_uses_namespaced_ids(self) -> None:
|
|
values = {option.value for option in PROVIDER_BY_VALUE["openrouter"].models}
|
|
assert "openai/gpt-5.6-sol" in values
|
|
|
|
def test_codex_picker_lists_sol(self) -> None:
|
|
values = {option.value for option in PROVIDER_BY_VALUE["codex"].models}
|
|
assert "gpt-5.6-sol" in values
|
|
|
|
def test_openai_default_model_is_unchanged(self) -> None:
|
|
# #3931 explicitly does not re-point defaults; adding quick-picks
|
|
# must not promote a GPT-5.6 tier to the default slot.
|
|
assert PROVIDER_BY_VALUE["openai"].default_model == "gpt-5.4-mini"
|
|
|
|
def test_openai_picker_selects_sol(self, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
provider = PROVIDER_BY_VALUE["openai"]
|
|
_wire_prompts(monkeypatch, select_values=["gpt-5.6-sol"])
|
|
|
|
assert _ui._choose_model(provider, default="") == "gpt-5.6-sol"
|