chore: bump model defaults to Opus 4.7, Sonnet 4.6
- Default model: anthropic/claude-opus-4-5 → anthropic/claude-opus-4-7 - Quota switcher: claude-opus-4-6 → claude-opus-4-7 - Update all provider defaults and test fixtures - Update comments/docstrings referencing old model names - Claude Opus 4.7 released 2026-04-16, same pricing as 4.6
This commit is contained in:
@@ -143,7 +143,7 @@ Add or merge these **two parts** into your config (other options have defaults).
|
|||||||
{
|
{
|
||||||
"agents": {
|
"agents": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"model": "anthropic/claude-opus-4-5",
|
"model": "anthropic/claude-opus-4-7",
|
||||||
"provider": "openrouter"
|
"provider": "openrouter"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ class AgentLoop:
|
|||||||
return self._quota_cache["model"]
|
return self._quota_cache["model"]
|
||||||
|
|
||||||
# Default models
|
# Default models
|
||||||
OPUS = "claude-opus-4-6"
|
OPUS = "claude-opus-4-7"
|
||||||
SONNET = "claude-sonnet-4-6"
|
SONNET = "claude-sonnet-4-6"
|
||||||
TOLERANCE = 1.17 # 17% overage triggers downgrade
|
TOLERANCE = 1.17 # 17% overage triggers downgrade
|
||||||
|
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ class AgentDefaults(Base):
|
|||||||
"""Default agent configuration."""
|
"""Default agent configuration."""
|
||||||
|
|
||||||
workspace: str = "~/.nanobot/workspace"
|
workspace: str = "~/.nanobot/workspace"
|
||||||
model: str = "anthropic/claude-opus-4-5"
|
model: str = "anthropic/claude-opus-4-7"
|
||||||
provider: str = "auto" # Provider name (e.g. "anthropic", "openrouter") or "auto" for auto-detection
|
provider: str = "auto" # Provider name (e.g. "anthropic", "openrouter") or "auto" for auto-detection
|
||||||
max_tokens: int = 8192
|
max_tokens: int = 8192
|
||||||
temperature: float = 0.1
|
temperature: float = 0.1
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class AnthropicOAuthProvider(LLMProvider):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
oauth_token: str,
|
oauth_token: str,
|
||||||
default_model: str = "claude-opus-4-5",
|
default_model: str = "claude-opus-4-7",
|
||||||
api_base: str | None = None,
|
api_base: str | None = None,
|
||||||
thinking_budget: int = 0,
|
thinking_budget: int = 0,
|
||||||
):
|
):
|
||||||
@@ -51,8 +51,8 @@ class AnthropicOAuthProvider(LLMProvider):
|
|||||||
def _normalize_model(model: str) -> str:
|
def _normalize_model(model: str) -> str:
|
||||||
"""Normalize model name for the Anthropic API.
|
"""Normalize model name for the Anthropic API.
|
||||||
|
|
||||||
Anthropic model IDs use hyphens (claude-sonnet-4-5), but users often
|
Anthropic model IDs use hyphens (claude-sonnet-4-6), but users often
|
||||||
write dots (claude-sonnet-4.5). Normalize so both work.
|
write dots (claude-sonnet-4.6). Normalize so both work.
|
||||||
"""
|
"""
|
||||||
return model.replace(".", "-")
|
return model.replace(".", "-")
|
||||||
|
|
||||||
@@ -557,7 +557,7 @@ class AnthropicOAuthProvider(LLMProvider):
|
|||||||
if "/" in model:
|
if "/" in model:
|
||||||
model = model.split("/")[-1]
|
model = model.split("/")[-1]
|
||||||
|
|
||||||
# Normalize dots to hyphens (claude-sonnet-4.5 -> claude-sonnet-4-5)
|
# Normalize dots to hyphens (claude-sonnet-4.6 -> claude-sonnet-4-6)
|
||||||
model = self._normalize_model(model)
|
model = self._normalize_model(model)
|
||||||
|
|
||||||
system, prepared_messages = self._prepare_messages(messages)
|
system, prepared_messages = self._prepare_messages(messages)
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class LiteLLMProvider(LLMProvider):
|
|||||||
self,
|
self,
|
||||||
api_key: str | None = None,
|
api_key: str | None = None,
|
||||||
api_base: str | None = None,
|
api_base: str | None = None,
|
||||||
default_model: str = "anthropic/claude-opus-4-5",
|
default_model: str = "anthropic/claude-opus-4-7",
|
||||||
extra_headers: dict[str, str] | None = None,
|
extra_headers: dict[str, str] | None = None,
|
||||||
provider_name: str | None = None,
|
provider_name: str | None = None,
|
||||||
):
|
):
|
||||||
@@ -187,7 +187,7 @@ class LiteLLMProvider(LLMProvider):
|
|||||||
Args:
|
Args:
|
||||||
messages: List of message dicts with 'role' and 'content'.
|
messages: List of message dicts with 'role' and 'content'.
|
||||||
tools: Optional list of tool definitions in OpenAI format.
|
tools: Optional list of tool definitions in OpenAI format.
|
||||||
model: Model identifier (e.g., 'anthropic/claude-sonnet-4-5').
|
model: Model identifier (e.g., 'anthropic/claude-sonnet-4-6').
|
||||||
max_tokens: Maximum tokens in response.
|
max_tokens: Maximum tokens in response.
|
||||||
temperature: Sampling temperature.
|
temperature: Sampling temperature.
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ def provider():
|
|||||||
"""Create provider with test OAuth token."""
|
"""Create provider with test OAuth token."""
|
||||||
return AnthropicOAuthProvider(
|
return AnthropicOAuthProvider(
|
||||||
oauth_token="sk-ant-oat01-test-token",
|
oauth_token="sk-ant-oat01-test-token",
|
||||||
default_model="claude-opus-4-5"
|
default_model="claude-opus-4-7"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_provider_init(provider):
|
def test_provider_init(provider):
|
||||||
"""Provider should initialize with OAuth token."""
|
"""Provider should initialize with OAuth token."""
|
||||||
assert provider.oauth_token == "sk-ant-oat01-test-token"
|
assert provider.oauth_token == "sk-ant-oat01-test-token"
|
||||||
assert provider.default_model == "claude-opus-4-5"
|
assert provider.default_model == "claude-opus-4-7"
|
||||||
|
|
||||||
|
|
||||||
def test_provider_uses_bearer_auth(provider):
|
def test_provider_uses_bearer_auth(provider):
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ def test_oauth_token_injected_into_config(tmp_path, monkeypatch):
|
|||||||
# Create a minimal config file (no api key set)
|
# Create a minimal config file (no api key set)
|
||||||
config_path = tmp_path / "config.json"
|
config_path = tmp_path / "config.json"
|
||||||
config_path.write_text(json.dumps({
|
config_path.write_text(json.dumps({
|
||||||
"agents": {"defaults": {"model": "anthropic/claude-opus-4-5"}},
|
"agents": {"defaults": {"model": "anthropic/claude-opus-4-7"}},
|
||||||
"providers": {"anthropic": {"apiKey": ""}}
|
"providers": {"anthropic": {"apiKey": ""}}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ async def test_provider_raises_long_context_error_on_long_context_429():
|
|||||||
|
|
||||||
provider = AnthropicOAuthProvider(
|
provider = AnthropicOAuthProvider(
|
||||||
oauth_token="sk-ant-oat01-test-token",
|
oauth_token="sk-ant-oat01-test-token",
|
||||||
default_model="claude-sonnet-4-5",
|
default_model="claude-sonnet-4-6",
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_response = MagicMock()
|
mock_response = MagicMock()
|
||||||
@@ -48,7 +48,7 @@ async def test_provider_retries_normal_429():
|
|||||||
|
|
||||||
provider = AnthropicOAuthProvider(
|
provider = AnthropicOAuthProvider(
|
||||||
oauth_token="sk-ant-oat01-test-token",
|
oauth_token="sk-ant-oat01-test-token",
|
||||||
default_model="claude-sonnet-4-5",
|
default_model="claude-sonnet-4-6",
|
||||||
)
|
)
|
||||||
|
|
||||||
rate_limit_response = MagicMock()
|
rate_limit_response = MagicMock()
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ async def test_extract_facts_passes_thinking_budget_zero(mock_provider):
|
|||||||
{"role": "assistant", "content": "That's great! Python is versatile."},
|
{"role": "assistant", "content": "That's great! Python is versatile."},
|
||||||
]
|
]
|
||||||
|
|
||||||
facts = await store.extract_facts(messages, mock_provider, "claude-sonnet-4-5")
|
facts = await store.extract_facts(messages, mock_provider, "claude-sonnet-4-6")
|
||||||
|
|
||||||
# Verify provider.chat was called with thinking_budget=0
|
# Verify provider.chat was called with thinking_budget=0
|
||||||
mock_provider.chat.assert_called_once()
|
mock_provider.chat.assert_called_once()
|
||||||
@@ -75,7 +75,7 @@ async def test_extract_facts_returns_parsed_facts(mock_provider):
|
|||||||
{"role": "user", "content": "I like Python programming"},
|
{"role": "user", "content": "I like Python programming"},
|
||||||
]
|
]
|
||||||
|
|
||||||
facts = await store.extract_facts(messages, mock_provider, "claude-sonnet-4-5")
|
facts = await store.extract_facts(messages, mock_provider, "claude-sonnet-4-6")
|
||||||
|
|
||||||
assert facts == ["user likes Python", "user works on nanobot"]
|
assert facts == ["user likes Python", "user works on nanobot"]
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ async def test_extract_facts_handles_empty_response():
|
|||||||
|
|
||||||
messages = [{"role": "user", "content": "Hello there"}]
|
messages = [{"role": "user", "content": "Hello there"}]
|
||||||
|
|
||||||
facts = await store.extract_facts(messages, provider, "claude-sonnet-4-5")
|
facts = await store.extract_facts(messages, provider, "claude-sonnet-4-6")
|
||||||
|
|
||||||
assert facts == []
|
assert facts == []
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ async def test_extract_facts_skips_empty_messages():
|
|||||||
{"role": "assistant", "content": ""},
|
{"role": "assistant", "content": ""},
|
||||||
]
|
]
|
||||||
|
|
||||||
facts = await store.extract_facts(messages, provider, "claude-sonnet-4-5")
|
facts = await store.extract_facts(messages, provider, "claude-sonnet-4-6")
|
||||||
|
|
||||||
assert facts == []
|
assert facts == []
|
||||||
# Provider should not be called when there's no content
|
# Provider should not be called when there's no content
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ IDENTITY_TEXT = get_claude_code_system_prefix()
|
|||||||
def provider():
|
def provider():
|
||||||
return AnthropicOAuthProvider(
|
return AnthropicOAuthProvider(
|
||||||
oauth_token="sk-ant-oat01-test-token",
|
oauth_token="sk-ant-oat01-test-token",
|
||||||
default_model="claude-opus-4-5",
|
default_model="claude-opus-4-7",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ def test_create_provider_oauth_token():
|
|||||||
"""OAuth tokens should create AnthropicOAuthProvider."""
|
"""OAuth tokens should create AnthropicOAuthProvider."""
|
||||||
provider = create_provider(
|
provider = create_provider(
|
||||||
api_key="sk-ant-oat01-test-token",
|
api_key="sk-ant-oat01-test-token",
|
||||||
model="anthropic/claude-opus-4-5"
|
model="anthropic/claude-opus-4-7"
|
||||||
)
|
)
|
||||||
assert isinstance(provider, AnthropicOAuthProvider)
|
assert isinstance(provider, AnthropicOAuthProvider)
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ def test_create_provider_regular_key():
|
|||||||
"""Regular API keys should create LiteLLMProvider."""
|
"""Regular API keys should create LiteLLMProvider."""
|
||||||
provider = create_provider(
|
provider = create_provider(
|
||||||
api_key="sk-ant-api03-regular-key",
|
api_key="sk-ant-api03-regular-key",
|
||||||
model="anthropic/claude-opus-4-5"
|
model="anthropic/claude-opus-4-7"
|
||||||
)
|
)
|
||||||
assert isinstance(provider, LiteLLMProvider)
|
assert isinstance(provider, LiteLLMProvider)
|
||||||
|
|
||||||
@@ -27,6 +27,6 @@ def test_create_provider_openrouter():
|
|||||||
"""OpenRouter keys should create LiteLLMProvider."""
|
"""OpenRouter keys should create LiteLLMProvider."""
|
||||||
provider = create_provider(
|
provider = create_provider(
|
||||||
api_key="sk-or-v1-xxx",
|
api_key="sk-or-v1-xxx",
|
||||||
model="anthropic/claude-opus-4-5"
|
model="anthropic/claude-opus-4-7"
|
||||||
)
|
)
|
||||||
assert isinstance(provider, LiteLLMProvider)
|
assert isinstance(provider, LiteLLMProvider)
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ from nanobot.providers.registry import should_use_oauth_provider
|
|||||||
|
|
||||||
def test_should_use_oauth_for_oat_token():
|
def test_should_use_oauth_for_oat_token():
|
||||||
"""OAuth provider should be used for sk-ant-oat tokens."""
|
"""OAuth provider should be used for sk-ant-oat tokens."""
|
||||||
assert should_use_oauth_provider("sk-ant-oat01-xxx", "anthropic/claude-opus-4-5") is True
|
assert should_use_oauth_provider("sk-ant-oat01-xxx", "anthropic/claude-opus-4-7") is True
|
||||||
assert should_use_oauth_provider("sk-ant-oat01-xxx", "claude-sonnet-4") is True
|
assert should_use_oauth_provider("sk-ant-oat01-xxx", "claude-sonnet-4") is True
|
||||||
|
|
||||||
|
|
||||||
def test_should_not_use_oauth_for_regular_key():
|
def test_should_not_use_oauth_for_regular_key():
|
||||||
"""Regular API keys should not use OAuth provider."""
|
"""Regular API keys should not use OAuth provider."""
|
||||||
assert should_use_oauth_provider("sk-ant-api03-xxx", "claude-opus-4-5") is False
|
assert should_use_oauth_provider("sk-ant-api03-xxx", "claude-opus-4-7") is False
|
||||||
assert should_use_oauth_provider("sk-or-v1-xxx", "anthropic/claude-opus-4-5") is False
|
assert should_use_oauth_provider("sk-or-v1-xxx", "anthropic/claude-opus-4-7") is False
|
||||||
|
|
||||||
|
|
||||||
def test_should_not_use_oauth_for_non_anthropic():
|
def test_should_not_use_oauth_for_non_anthropic():
|
||||||
|
|||||||
Reference in New Issue
Block a user