feat: update model version from claude-sonnet-4-5 to claude-sonnet-4-6 and related adjustments

This commit is contained in:
X-iZhang
2026-05-29 00:40:46 +01:00
parent f75bfcda51
commit 721a03c25b
14 changed files with 78 additions and 58 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 KiB

After

Width:  |  Height:  |  Size: 432 KiB

+1 -1
View File
@@ -102,7 +102,7 @@ def onboard(
workspace settings, and agent parameters. workspace settings, and agent parameters.
Any answer can be pre-set via a flag (``--provider anthropic Any answer can be pre-set via a flag (``--provider anthropic
--model claude-sonnet-4-5 ...``); prompts for unset answers stay --model claude-sonnet-4-6 ...``); prompts for unset answers stay
interactive unless ``--non-interactive`` is passed, in which case any interactive unless ``--non-interactive`` is passed, in which case any
missing required answer aborts the wizard. missing required answer aborts the wizard.
""" """
+1 -1
View File
@@ -572,7 +572,7 @@ def run_onboard(
# leaves a stale ``--model`` / ``--api-key`` that would # leaves a stale ``--model`` / ``--api-key`` that would
# be re-applied under a different provider, producing # be re-applied under a different provider, producing
# an invalid pair (e.g. ``provider=openai`` + # an invalid pair (e.g. ``provider=openai`` +
# ``model=claude-sonnet-4-5``). # ``model=claude-sonnet-4-6``).
for field_name in vars(loop_snapshot): for field_name in vars(loop_snapshot):
setattr( setattr(
config, field_name, getattr(loop_snapshot, field_name) config, field_name, getattr(loop_snapshot, field_name)
+1 -1
View File
@@ -85,7 +85,7 @@ class EvoScientistConfig:
# LLM Settings # LLM Settings
provider: str = "anthropic" provider: str = "anthropic"
model: str = "claude-sonnet-4-5" model: str = "claude-sonnet-4-6"
model_fallbacks: str = "" # "model:provider,model:provider" fallback chain model_fallbacks: str = "" # "model:provider,model:provider" fallback chain
# Async Sub-agent Settings # Async Sub-agent Settings
+7
View File
@@ -17,6 +17,11 @@ _KNOWN_MODEL_CONTEXT_WINDOWS: dict[str, int] = {
# Qwen 3.6 open-source variants — exceptions to the ``qwen3.6`` family. # Qwen 3.6 open-source variants — exceptions to the ``qwen3.6`` family.
"qwen3.6-27b": 262_000, "qwen3.6-27b": 262_000,
"qwen3.6-35b-a3b": 262_000, "qwen3.6-35b-a3b": 262_000,
# Qwen 3.7 Max — closed-source flagship (1M).
"qwen3.7-max": 1_000_000,
# xAI Grok — per-model windows (build-0.1: 256K, 4.3: 1M).
"grok-build-0.1": 256_000,
"grok-4.3": 1_000_000,
# Claude Haiku 4.5 — exception to the ``claude-`` family (200K, not 1M). # Claude Haiku 4.5 — exception to the ``claude-`` family (200K, not 1M).
"claude-haiku-4-5": 200_000, "claude-haiku-4-5": 200_000,
} }
@@ -29,6 +34,8 @@ _KNOWN_MODEL_FAMILIES: list[tuple[str, int]] = [
("claude-", 1_000_000), ("claude-", 1_000_000),
# OpenAI GPT-5.5 family — base, pro, future variants # OpenAI GPT-5.5 family — base, pro, future variants
("gpt-5.5", 1_050_000), ("gpt-5.5", 1_050_000),
# Google Gemini 3.x family — flash, flash-lite, pro (1.05M). Excludes 2.5.
("gemini-3", 1_050_000),
# Moonshot Kimi K2 family — k2.5, k2.6, k2-thinking, k2-thinking-turbo # Moonshot Kimi K2 family — k2.5, k2.6, k2-thinking, k2-thinking-turbo
("kimi-k2", 262_000), ("kimi-k2", 262_000),
# Zhipu GLM-5 family — base, 5.1, 5-turbo, 5v-turbo, etc. # Zhipu GLM-5 family — base, 5.1, 5-turbo, 5v-turbo, etc.
+16 -15
View File
@@ -66,10 +66,9 @@ _THINKING_CAPABLE_PROVIDERS: set[str] = {"minimax"}
# Model registry: list of (short_name, model_id, provider) # Model registry: list of (short_name, model_id, provider)
# Allows same short_name across different providers. # Allows same short_name across different providers.
_MODEL_ENTRIES: list[tuple[str, str, str]] = [ _MODEL_ENTRIES: list[tuple[str, str, str]] = [
# Custom Anthropic (third-party Claude-compatible endpoints, 3 defaults) # Custom Anthropic (third-party Claude-compatible endpoints, current-gen defaults)
# Listed BEFORE native anthropic so MODELS dict defaults to native provider # Listed BEFORE native anthropic so MODELS dict defaults to native provider
("claude-sonnet-4-6", "claude-sonnet-4-6", "custom-anthropic"), ("claude-sonnet-4-6", "claude-sonnet-4-6", "custom-anthropic"),
("claude-sonnet-4-5", "claude-sonnet-4-5", "custom-anthropic"),
("claude-haiku-4-5", "claude-haiku-4-5", "custom-anthropic"), ("claude-haiku-4-5", "claude-haiku-4-5", "custom-anthropic"),
# Custom OpenAI (third-party OpenAI-compatible endpoints, 3 defaults) # Custom OpenAI (third-party OpenAI-compatible endpoints, 3 defaults)
# Listed BEFORE native openai so MODELS dict defaults to native provider # Listed BEFORE native openai so MODELS dict defaults to native provider
@@ -78,12 +77,9 @@ _MODEL_ENTRIES: list[tuple[str, str, str]] = [
("gpt-5.4", "gpt-5.4", "custom-openai"), ("gpt-5.4", "gpt-5.4", "custom-openai"),
("gpt-5.3-codex", "gpt-5.3-codex", "custom-openai"), ("gpt-5.3-codex", "gpt-5.3-codex", "custom-openai"),
("gpt-5-mini", "gpt-5-mini", "custom-openai"), ("gpt-5-mini", "gpt-5-mini", "custom-openai"),
# Anthropic (ordered by capability) # Anthropic (current generation)
("claude-opus-4-7", "claude-opus-4-7", "anthropic"), ("claude-opus-4-8", "claude-opus-4-8", "anthropic"),
("claude-opus-4-6", "claude-opus-4-6", "anthropic"),
("claude-sonnet-4-6", "claude-sonnet-4-6", "anthropic"), ("claude-sonnet-4-6", "claude-sonnet-4-6", "anthropic"),
("claude-opus-4-5", "claude-opus-4-5", "anthropic"),
("claude-sonnet-4-5", "claude-sonnet-4-5", "anthropic"),
("claude-haiku-4-5", "claude-haiku-4-5", "anthropic"), ("claude-haiku-4-5", "claude-haiku-4-5", "anthropic"),
# OpenAI # OpenAI
("gpt-5.5-pro", "gpt-5.5-pro", "openai"), ("gpt-5.5-pro", "gpt-5.5-pro", "openai"),
@@ -99,6 +95,7 @@ _MODEL_ENTRIES: list[tuple[str, str, str]] = [
("gpt-5-mini", "gpt-5-mini", "openai"), ("gpt-5-mini", "gpt-5-mini", "openai"),
("gpt-5-nano", "gpt-5-nano", "openai"), ("gpt-5-nano", "gpt-5-nano", "openai"),
# Google GenAI # Google GenAI
("gemini-3.5-flash", "gemini-3.5-flash", "google-genai"),
("gemini-3.1-pro", "gemini-3.1-pro-preview", "google-genai"), ("gemini-3.1-pro", "gemini-3.1-pro-preview", "google-genai"),
( (
"gemini-3.1-pro-customtools", "gemini-3.1-pro-customtools",
@@ -133,13 +130,14 @@ _MODEL_ENTRIES: list[tuple[str, str, str]] = [
("kimi-k2.5", "Pro/moonshotai/Kimi-K2.5", "siliconflow"), ("kimi-k2.5", "Pro/moonshotai/Kimi-K2.5", "siliconflow"),
("glm-4.7", "Pro/zai-org/GLM-4.7", "siliconflow"), ("glm-4.7", "Pro/zai-org/GLM-4.7", "siliconflow"),
# OpenRouter # OpenRouter
("claude-opus-4.7", "anthropic/claude-opus-4.7", "openrouter"), ("claude-opus-4.8", "anthropic/claude-opus-4.8", "openrouter"),
("claude-opus-4.6", "anthropic/claude-opus-4.6", "openrouter"), ("claude-opus-4.8-fast", "anthropic/claude-opus-4.8-fast", "openrouter"),
("claude-sonnet-4.6", "anthropic/claude-sonnet-4.6", "openrouter"), ("claude-sonnet-4.6", "anthropic/claude-sonnet-4.6", "openrouter"),
("gpt-5.5-pro", "openai/gpt-5.5-pro", "openrouter"), ("gpt-5.5-pro", "openai/gpt-5.5-pro", "openrouter"),
("gpt-5.5", "openai/gpt-5.5", "openrouter"), ("gpt-5.5", "openai/gpt-5.5", "openrouter"),
("gpt-5.4", "openai/gpt-5.4", "openrouter"), ("gpt-5.4", "openai/gpt-5.4", "openrouter"),
("gpt-5.3-codex", "openai/gpt-5.3-codex", "openrouter"), ("gpt-5.3-codex", "openai/gpt-5.3-codex", "openrouter"),
("gemini-3.5-flash", "google/gemini-3.5-flash", "openrouter"),
("gemini-3.1-pro", "google/gemini-3.1-pro-preview", "openrouter"), ("gemini-3.1-pro", "google/gemini-3.1-pro-preview", "openrouter"),
("gemini-3-flash", "google/gemini-3-flash-preview", "openrouter"), ("gemini-3-flash", "google/gemini-3-flash-preview", "openrouter"),
("kimi-k2.6", "moonshotai/kimi-k2.6", "openrouter"), ("kimi-k2.6", "moonshotai/kimi-k2.6", "openrouter"),
@@ -147,7 +145,10 @@ _MODEL_ENTRIES: list[tuple[str, str, str]] = [
("minimax-m2.7", "minimax/minimax-m2.7", "openrouter"), ("minimax-m2.7", "minimax/minimax-m2.7", "openrouter"),
("mimo-v2.5-pro", "xiaomi/mimo-v2.5-pro", "openrouter"), ("mimo-v2.5-pro", "xiaomi/mimo-v2.5-pro", "openrouter"),
("mimo-v2.5", "xiaomi/mimo-v2.5", "openrouter"), ("mimo-v2.5", "xiaomi/mimo-v2.5", "openrouter"),
("grok-4.1-fast", "x-ai/grok-4.1-fast", "openrouter"), ("grok-build-0.1", "x-ai/grok-build-0.1", "openrouter"),
("grok-4.3", "x-ai/grok-4.3", "openrouter"),
("qwen3.7-max", "qwen/qwen3.7-max", "openrouter"),
("qwen3.6-flash", "qwen/qwen3.6-flash", "openrouter"),
("qwen3.5-122b", "qwen/qwen3.5-122b-a10b", "openrouter"), ("qwen3.5-122b", "qwen/qwen3.5-122b-a10b", "openrouter"),
("deepseek-v4-pro", "deepseek/deepseek-v4-pro", "openrouter"), ("deepseek-v4-pro", "deepseek/deepseek-v4-pro", "openrouter"),
("deepseek-v4-flash", "deepseek/deepseek-v4-flash", "openrouter"), ("deepseek-v4-flash", "deepseek/deepseek-v4-flash", "openrouter"),
@@ -246,7 +247,7 @@ def _apply_auto_config(
_is_proxy = False _is_proxy = False
if _is_proxy or (is_third_party and not _supports_thinking): if _is_proxy or (is_third_party and not _supports_thinking):
pass pass
elif model_id.endswith(("4-6", "4-7")): elif model_id.endswith(("4-6", "4-7", "4-8")):
kwargs["thinking"] = {"type": "adaptive"} kwargs["thinking"] = {"type": "adaptive"}
kwargs.setdefault("effort", "max") kwargs.setdefault("effort", "max")
else: else:
@@ -282,8 +283,8 @@ def get_chat_model(
"""Get a chat model instance. """Get a chat model instance.
Args: Args:
model: Model name (short name like 'claude-sonnet-4-5' or full ID model: Model name (short name like 'claude-sonnet-4-6' or full ID
like 'claude-sonnet-4-5-20250929'). Defaults to DEFAULT_MODEL. like 'claude-sonnet-4-6-20250929'). Defaults to DEFAULT_MODEL.
provider: Override the provider (e.g., 'anthropic', 'openai'). provider: Override the provider (e.g., 'anthropic', 'openai').
If not specified, inferred from model name or defaults to 'anthropic'. If not specified, inferred from model name or defaults to 'anthropic'.
**kwargs: Additional arguments passed to init_chat_model (e.g., temperature). **kwargs: Additional arguments passed to init_chat_model (e.g., temperature).
@@ -292,8 +293,8 @@ def get_chat_model(
A LangChain chat model instance. A LangChain chat model instance.
Examples: Examples:
>>> model = get_chat_model() # Uses default (claude-sonnet-4-5) >>> model = get_chat_model() # Uses default (claude-sonnet-4-6)
>>> model = get_chat_model("claude-opus-4-5") # Use short name >>> model = get_chat_model("claude-opus-4-8") # Use short name
>>> model = get_chat_model("gpt-4o") # OpenAI model >>> model = get_chat_model("gpt-4o") # OpenAI model
>>> model = get_chat_model("claude-3-opus-20240229", provider="anthropic") # Full ID >>> model = get_chat_model("claude-3-opus-20240229", provider="anthropic") # Full ID
""" """
+4 -4
View File
@@ -76,7 +76,7 @@ class TestEvoScientistConfig:
assert config.openai_api_key == "" assert config.openai_api_key == ""
assert config.tavily_api_key == "" assert config.tavily_api_key == ""
assert config.provider == "anthropic" assert config.provider == "anthropic"
assert config.model == "claude-sonnet-4-5" assert config.model == "claude-sonnet-4-6"
assert config.default_mode == "daemon" assert config.default_mode == "daemon"
assert config.default_workdir == "" assert config.default_workdir == ""
assert config.show_thinking is True assert config.show_thinking is True
@@ -157,7 +157,7 @@ class TestLoadSaveReset:
"""Test that load returns defaults when config file doesn't exist.""" """Test that load returns defaults when config file doesn't exist."""
config = load_config() config = load_config()
assert config.provider == "anthropic" assert config.provider == "anthropic"
assert config.model == "claude-sonnet-4-5" assert config.model == "claude-sonnet-4-6"
def test_save_creates_file(self, temp_config_dir, clean_env): def test_save_creates_file(self, temp_config_dir, clean_env):
"""Test that save creates the config file.""" """Test that save creates the config file."""
@@ -350,8 +350,8 @@ class TestPriorityChain:
"""Test CLI arguments override file config.""" """Test CLI arguments override file config."""
save_config(EvoScientistConfig(model="gpt-4o")) save_config(EvoScientistConfig(model="gpt-4o"))
config = get_effective_config(cli_overrides={"model": "claude-opus-4-5"}) config = get_effective_config(cli_overrides={"model": "claude-opus-4-8"})
assert config.model == "claude-opus-4-5" assert config.model == "claude-opus-4-8"
def test_env_ui_backend_override(self, temp_config_dir, monkeypatch): def test_env_ui_backend_override(self, temp_config_dir, monkeypatch):
"""UI backend can be selected via environment variable.""" """UI backend can be selected via environment variable."""
+2 -2
View File
@@ -196,7 +196,7 @@ class TestModelOverride:
with ( with (
_patched_config( _patched_config(
{"model": "claude-opus-4-7", "model_provider": "anthropic"} {"model": "claude-opus-4-8", "model_provider": "anthropic"}
), ),
patch( patch(
"EvoScientist.llm.get_chat_model", return_value=new_model "EvoScientist.llm.get_chat_model", return_value=new_model
@@ -205,7 +205,7 @@ class TestModelOverride:
result = _run(mw.awrap_model_call(req, handler)) result = _run(mw.awrap_model_call(req, handler))
assert result == "ok" assert result == "ok"
mock_get.assert_called_once_with(model="claude-opus-4-7", provider="anthropic") mock_get.assert_called_once_with(model="claude-opus-4-8", provider="anthropic")
def test_provider_omitted_passed_as_none(self): def test_provider_omitted_passed_as_none(self):
mw = ConfigurableModelMiddleware() mw = ConfigurableModelMiddleware()
+7 -7
View File
@@ -63,7 +63,7 @@ def test_resolve_context_window_falls_back_when_missing():
def test_patch_table_resolves_known_model_name(): def test_patch_table_resolves_known_model_name():
model = SimpleNamespace(model_name="claude-opus-4.7", profile=None) model = SimpleNamespace(model_name="claude-opus-4.8", profile=None)
assert get_context_window(model) == 1_000_000 assert get_context_window(model) == 1_000_000
@@ -78,7 +78,7 @@ def test_patch_table_strips_provider_prefix():
def test_real_attribute_beats_patch_table(): def test_real_attribute_beats_patch_table():
model = SimpleNamespace( model = SimpleNamespace(
model_name="claude-opus-4.7", model_name="claude-opus-4.8",
context_window=500_000, context_window=500_000,
profile=None, profile=None,
) )
@@ -95,10 +95,10 @@ def test_unknown_model_falls_through_to_fallback():
def test_claude_family_pattern_covers_all_variants(): def test_claude_family_pattern_covers_all_variants():
# Native Anthropic (dash form) # Native Anthropic (dash form)
native = SimpleNamespace(model_name="claude-opus-4-7", profile=None) native = SimpleNamespace(model_name="claude-opus-4-8", profile=None)
# OpenRouter (dot form, with vendor prefix) # OpenRouter (dot form, with vendor prefix)
openrouter = SimpleNamespace(model_name="anthropic/claude-sonnet-4.6", profile=None) openrouter = SimpleNamespace(model_name="anthropic/claude-sonnet-4.6", profile=None)
sonnet = SimpleNamespace(model_name="claude-sonnet-4-5", profile=None) sonnet = SimpleNamespace(model_name="claude-sonnet-4-6", profile=None)
# Haiku 4.5 is a dict-level exception (200K, not 1M). # Haiku 4.5 is a dict-level exception (200K, not 1M).
haiku = SimpleNamespace(model_name="claude-haiku-4-5", profile=None) haiku = SimpleNamespace(model_name="claude-haiku-4-5", profile=None)
@@ -141,7 +141,7 @@ def test_claude_3x_matches_family_but_upstream_profile_wins_in_practice():
def test_apply_injects_into_empty_profile(): def test_apply_injects_into_empty_profile():
model = SimpleNamespace(model_name="claude-opus-4-7", profile=None) model = SimpleNamespace(model_name="claude-opus-4-8", profile=None)
apply_known_context_window(model) apply_known_context_window(model)
@@ -164,7 +164,7 @@ def test_apply_preserves_existing_profile_keys():
def test_apply_does_not_overwrite_existing_max_input_tokens(): def test_apply_does_not_overwrite_existing_max_input_tokens():
model = SimpleNamespace( model = SimpleNamespace(
model_name="claude-opus-4-7", model_name="claude-opus-4-8",
profile={"max_input_tokens": 500_000}, profile={"max_input_tokens": 500_000},
) )
@@ -185,7 +185,7 @@ def test_apply_handles_non_dict_profile_safely():
# Non-dict profile (e.g. namespace from a future langchain version) must # Non-dict profile (e.g. namespace from a future langchain version) must
# not raise — fall back to a fresh dict so deepagents can still read it. # not raise — fall back to a fresh dict so deepagents can still read it.
model = SimpleNamespace( model = SimpleNamespace(
model_name="claude-opus-4-7", model_name="claude-opus-4-8",
profile=SimpleNamespace(name="opaque object"), profile=SimpleNamespace(name="opaque object"),
) )
+21 -9
View File
@@ -137,7 +137,7 @@ class TestListModels:
class TestGetModelInfo: class TestGetModelInfo:
def test_returns_tuple_for_valid_model(self): def test_returns_tuple_for_valid_model(self):
"""Test that get_model_info returns tuple for valid model.""" """Test that get_model_info returns tuple for valid model."""
result = get_model_info("claude-sonnet-4-5") result = get_model_info("claude-sonnet-4-6")
assert result is not None assert result is not None
assert isinstance(result, tuple) assert isinstance(result, tuple)
assert len(result) == 2 assert len(result) == 2
@@ -179,10 +179,10 @@ class TestGetChatModel:
"""Test that get_chat_model resolves short names correctly.""" """Test that get_chat_model resolves short names correctly."""
mock_init.return_value = "mock_model" mock_init.return_value = "mock_model"
get_chat_model("claude-opus-4-5") get_chat_model("claude-opus-4-8")
call_kwargs = mock_init.call_args[1] call_kwargs = mock_init.call_args[1]
assert call_kwargs["model"] == "claude-opus-4-5" assert call_kwargs["model"] == "claude-opus-4-8"
assert call_kwargs["model_provider"] == "anthropic" assert call_kwargs["model_provider"] == "anthropic"
@patch("EvoScientist.llm.models.init_chat_model") @patch("EvoScientist.llm.models.init_chat_model")
@@ -213,7 +213,7 @@ class TestGetChatModel:
"""Test that provider can be overridden.""" """Test that provider can be overridden."""
mock_init.return_value = "mock_model" mock_init.return_value = "mock_model"
get_chat_model("claude-sonnet-4-5", provider="custom_provider") get_chat_model("claude-sonnet-4-6", provider="custom_provider")
call_kwargs = mock_init.call_args[1] call_kwargs = mock_init.call_args[1]
assert call_kwargs["model_provider"] == "custom_provider" assert call_kwargs["model_provider"] == "custom_provider"
@@ -403,7 +403,7 @@ class TestThirdPartyRouting:
mock_init.return_value = "mock_model" mock_init.return_value = "mock_model"
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key-456") monkeypatch.setenv("OPENROUTER_API_KEY", "or-key-456")
get_chat_model("x-ai/grok-4.1-fast", provider="openrouter") get_chat_model("x-ai/grok-4.3", provider="openrouter")
call_kwargs = mock_init.call_args[1] call_kwargs = mock_init.call_args[1]
assert call_kwargs["model_provider"] == "openrouter" assert call_kwargs["model_provider"] == "openrouter"
@@ -417,7 +417,7 @@ class TestThirdPartyRouting:
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key") monkeypatch.setenv("OPENROUTER_API_KEY", "or-key")
get_chat_model( get_chat_model(
"x-ai/grok-4.1-fast", "x-ai/grok-4.3",
provider="openrouter", provider="openrouter",
reasoning={"effort": "low"}, reasoning={"effort": "low"},
) )
@@ -432,7 +432,7 @@ class TestThirdPartyRouting:
monkeypatch.setenv("OPENROUTER_API_KEY", "or-key") monkeypatch.setenv("OPENROUTER_API_KEY", "or-key")
monkeypatch.setenv("EVOSCIENTIST_REASONING_EFFORT", "medium") monkeypatch.setenv("EVOSCIENTIST_REASONING_EFFORT", "medium")
get_chat_model("x-ai/grok-4.1-fast", provider="openrouter") get_chat_model("x-ai/grok-4.3", provider="openrouter")
call_kwargs = mock_init.call_args[1] call_kwargs = mock_init.call_args[1]
assert call_kwargs["reasoning"] == {"effort": "medium", "summary": "disabled"} assert call_kwargs["reasoning"] == {"effort": "medium", "summary": "disabled"}
@@ -1176,7 +1176,7 @@ class TestAutoConfig:
mock_init.return_value = "mock_model" mock_init.return_value = "mock_model"
monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False) monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
get_chat_model("claude-sonnet-4-5") get_chat_model("claude-haiku-4-5")
call_kwargs = mock_init.call_args[1] call_kwargs = mock_init.call_args[1]
assert call_kwargs["thinking"] == {"type": "enabled", "budget_tokens": 10000} assert call_kwargs["thinking"] == {"type": "enabled", "budget_tokens": 10000}
@@ -1193,6 +1193,18 @@ class TestAutoConfig:
assert call_kwargs["thinking"] == {"type": "adaptive"} assert call_kwargs["thinking"] == {"type": "adaptive"}
assert call_kwargs["effort"] == "max" assert call_kwargs["effort"] == "max"
@patch("EvoScientist.llm.models.init_chat_model")
def test_anthropic_4_8_adaptive_thinking(self, mock_init, monkeypatch):
"""Anthropic 4-8 models get adaptive thinking with max effort."""
mock_init.return_value = "mock_model"
monkeypatch.delenv("ANTHROPIC_BASE_URL", raising=False)
get_chat_model("claude-opus-4-8")
call_kwargs = mock_init.call_args[1]
assert call_kwargs["thinking"] == {"type": "adaptive"}
assert call_kwargs["effort"] == "max"
@patch("EvoScientist.llm.models.init_chat_model") @patch("EvoScientist.llm.models.init_chat_model")
def test_anthropic_4_6_proxy_no_thinking(self, mock_init, monkeypatch): def test_anthropic_4_6_proxy_no_thinking(self, mock_init, monkeypatch):
"""Anthropic 4-6 models via proxy skip thinking (history round-trip 422).""" """Anthropic 4-6 models via proxy skip thinking (history round-trip 422)."""
@@ -1213,7 +1225,7 @@ class TestAutoConfig:
monkeypatch.setenv("ANTHROPIC_BASE_URL", "http://127.0.0.1:8000") monkeypatch.setenv("ANTHROPIC_BASE_URL", "http://127.0.0.1:8000")
monkeypatch.setenv("ANTHROPIC_API_KEY", "ccproxy-oauth") monkeypatch.setenv("ANTHROPIC_API_KEY", "ccproxy-oauth")
get_chat_model("claude-sonnet-4-5") get_chat_model("claude-haiku-4-5")
call_kwargs = mock_init.call_args[1] call_kwargs = mock_init.call_args[1]
assert "thinking" not in call_kwargs assert "thinking" not in call_kwargs
+8 -8
View File
@@ -158,10 +158,10 @@ class TestModelCommandSwitch:
return_value=new_agent, return_value=new_agent,
), ),
): ):
_run(cmd.execute(ctx, ["claude-opus-4-6"])) _run(cmd.execute(ctx, ["claude-opus-4-8"]))
# Config should be updated # Config should be updated
assert cfg.model == "claude-opus-4-6" assert cfg.model == "claude-opus-4-8"
assert cfg.provider == "anthropic" assert cfg.provider == "anthropic"
# Agent should be replaced on context # Agent should be replaced on context
@@ -170,7 +170,7 @@ class TestModelCommandSwitch:
# Success message shown # Success message shown
ui.append_system.assert_called_once() ui.append_system.assert_called_once()
msg = ui.append_system.call_args[0][0] msg = ui.append_system.call_args[0][0]
assert "claude-opus-4-6" in msg assert "claude-opus-4-8" in msg
assert "anthropic" in msg assert "anthropic" in msg
def test_switch_with_save_flag(self): def test_switch_with_save_flag(self):
@@ -198,10 +198,10 @@ class TestModelCommandSwitch:
), ),
patch("EvoScientist.config.settings.set_config_value") as mock_save, patch("EvoScientist.config.settings.set_config_value") as mock_save,
): ):
_run(cmd.execute(ctx, ["claude-opus-4-6", "--save"])) _run(cmd.execute(ctx, ["claude-opus-4-8", "--save"]))
# Config file should be updated # Config file should be updated
mock_save.assert_any_call("model", "claude-opus-4-6") mock_save.assert_any_call("model", "claude-opus-4-8")
mock_save.assert_any_call("provider", "anthropic") mock_save.assert_any_call("provider", "anthropic")
# Success message should mention save # Success message should mention save
@@ -233,7 +233,7 @@ class TestModelCommandSwitch:
), ),
patch("EvoScientist.config.settings.set_config_value") as mock_save, patch("EvoScientist.config.settings.set_config_value") as mock_save,
): ):
_run(cmd.execute(ctx, ["claude-opus-4-6"])) _run(cmd.execute(ctx, ["claude-opus-4-8"]))
# Config file should NOT be updated # Config file should NOT be updated
mock_save.assert_not_called() mock_save.assert_not_called()
@@ -273,7 +273,7 @@ class TestModelCommandFailure:
side_effect=RuntimeError("API key missing"), side_effect=RuntimeError("API key missing"),
) as mock_set, ) as mock_set,
): ):
_run(cmd.execute(ctx, ["claude-opus-4-6"])) _run(cmd.execute(ctx, ["claude-opus-4-8"]))
mock_set.assert_called_once() mock_set.assert_called_once()
ui.append_system.assert_called_once() ui.append_system.assert_called_once()
@@ -558,7 +558,7 @@ class TestModelCommandLoadAgentFailure:
# Pass ``--save`` to strengthen the assertion: if the ordering # Pass ``--save`` to strengthen the assertion: if the ordering
# ever regresses, ``set_config_value`` would be called with # ever regresses, ``set_config_value`` would be called with
# stale data. # stale data.
_run(cmd.execute(ctx, ["claude-opus-4-6", "--save"])) _run(cmd.execute(ctx, ["claude-opus-4-8", "--save"]))
# _load_agent was attempted (transactional first step). # _load_agent was attempted (transactional first step).
mock_load.assert_called_once() mock_load.assert_called_once()
+7 -7
View File
@@ -373,10 +373,10 @@ class TestStepModel:
config = EvoScientistConfig() config = EvoScientistConfig()
with patch("EvoScientist.config.onboard.steps.questionary") as mock_q: with patch("EvoScientist.config.onboard.steps.questionary") as mock_q:
mock_q.select.return_value.ask.return_value = "claude-sonnet-4-5" mock_q.select.return_value.ask.return_value = "claude-sonnet-4-6"
result = _step_model(config, "anthropic") result = _step_model(config, "anthropic")
assert result == "claude-sonnet-4-5" assert result == "claude-sonnet-4-6"
def test_raises_keyboard_interrupt_on_cancel(self): def test_raises_keyboard_interrupt_on_cancel(self):
"""Test that _step_model raises KeyboardInterrupt on cancel.""" """Test that _step_model raises KeyboardInterrupt on cancel."""
@@ -1142,7 +1142,7 @@ class TestRunOnboard:
"tui", # UI backend "tui", # UI backend
"anthropic", # Provider "anthropic", # Provider
"api_key", # Anthropic auth mode (API key, not OAuth) "api_key", # Anthropic auth mode (API key, not OAuth)
"claude-sonnet-4-5", # Model "claude-sonnet-4-6", # Model
"daemon", # Workspace mode "daemon", # Workspace mode
True, # Show thinking True, # Show thinking
] ]
@@ -1168,7 +1168,7 @@ class TestRunOnboard:
# downstream prompts to consume the wrong values. # downstream prompts to consume the wrong values.
final_config = mock_save.call_args_list[-1].args[0] final_config = mock_save.call_args_list[-1].args[0]
assert final_config.provider == "anthropic" assert final_config.provider == "anthropic"
assert final_config.model == "claude-sonnet-4-5" assert final_config.model == "claude-sonnet-4-6"
assert final_config.anthropic_auth_mode == "api_key" assert final_config.anthropic_auth_mode == "api_key"
assert final_config.ui_backend == "tui" assert final_config.ui_backend == "tui"
assert final_config.default_mode == "daemon" assert final_config.default_mode == "daemon"
@@ -1217,7 +1217,7 @@ class TestRunOnboard:
"tui", # UI backend "tui", # UI backend
"anthropic", # Provider "anthropic", # Provider
"api_key", # Anthropic auth mode "api_key", # Anthropic auth mode
"claude-sonnet-4-5", # Model "claude-sonnet-4-6", # Model
"daemon", # Workspace mode "daemon", # Workspace mode
True, # Show thinking True, # Show thinking
] ]
@@ -1287,7 +1287,7 @@ class TestRunOnboard:
"tui", "tui",
"anthropic", "anthropic",
"api_key", "api_key",
"claude-sonnet-4-5", "claude-sonnet-4-6",
"daemon", "daemon",
True, True,
] ]
@@ -1334,7 +1334,7 @@ class TestRunOnboard:
"tui", "tui",
"anthropic", "anthropic",
"api_key", "api_key",
"claude-sonnet-4-5", "claude-sonnet-4-6",
"daemon", "daemon",
True, True,
] ]
+1 -1
View File
@@ -120,7 +120,7 @@ class TestUpdateStatusHook:
def test_no_op(self): def test_no_op(self):
ui, console = _make_ui() ui, console = _make_ui()
ui.update_status_after_model_change("claude-opus-4-6", "anthropic") ui.update_status_after_model_change("claude-opus-4-8", "anthropic")
console.print.assert_not_called() console.print.assert_not_called()
+2 -2
View File
@@ -163,7 +163,7 @@ class TestThreadFunctions(unittest.TestCase):
"agent_name": AGENT_NAME, "agent_name": AGENT_NAME,
"updated_at": f"2025-01-{15 + i}T10:00:00+00:00", "updated_at": f"2025-01-{15 + i}T10:00:00+00:00",
"workspace_dir": f"/tmp/ws_{tid}", "workspace_dir": f"/tmp/ws_{tid}",
"model": "claude-sonnet-4-5", "model": "claude-sonnet-4-6",
} }
) )
await conn.execute( await conn.execute(
@@ -272,7 +272,7 @@ class TestThreadFunctions(unittest.TestCase):
meta = _run(get_thread_metadata("abc12345")) meta = _run(get_thread_metadata("abc12345"))
assert meta is not None assert meta is not None
assert meta["workspace_dir"] == "/tmp/ws_abc12345" assert meta["workspace_dir"] == "/tmp/ws_abc12345"
assert meta["model"] == "claude-sonnet-4-5" assert meta["model"] == "claude-sonnet-4-6"
def test_get_thread_metadata_missing(self): def test_get_thread_metadata_missing(self):
meta = _run(get_thread_metadata("nonexist")) meta = _run(get_thread_metadata("nonexist"))