refactor: preserve cold-start lazy boundaries
This commit is contained in:
@@ -111,6 +111,23 @@ def test_discover_plugins_loads_entry_points():
|
||||
assert result["line"] is _FakePlugin
|
||||
|
||||
|
||||
def test_discover_plugins_skips_names_outside_enabled_set():
|
||||
from nanobot.channels.registry import discover_plugins
|
||||
|
||||
loaded: list[str] = []
|
||||
|
||||
def _load_disabled():
|
||||
loaded.append("disabled")
|
||||
return _FakePlugin
|
||||
|
||||
ep = SimpleNamespace(name="disabled", load=_load_disabled)
|
||||
with patch(_EP_TARGET, return_value=[ep]):
|
||||
result = discover_plugins({"enabled"})
|
||||
|
||||
assert result == {}
|
||||
assert loaded == []
|
||||
|
||||
|
||||
def test_discover_plugins_handles_load_error():
|
||||
from nanobot.channels.registry import discover_plugins
|
||||
|
||||
@@ -152,6 +169,25 @@ def test_discover_all_includes_external_plugin():
|
||||
assert result["line"] is _FakePlugin
|
||||
|
||||
|
||||
def test_discover_enabled_imports_only_enabled_builtins():
|
||||
from nanobot.channels.registry import discover_enabled
|
||||
|
||||
loaded: list[str] = []
|
||||
|
||||
def _load_channel(name: str):
|
||||
loaded.append(name)
|
||||
return _FakePlugin
|
||||
|
||||
with (
|
||||
patch("nanobot.channels.registry.load_channel_class", side_effect=_load_channel),
|
||||
patch(_EP_TARGET, return_value=[]),
|
||||
):
|
||||
result = discover_enabled({"enabled"}, _names=["enabled", "disabled"])
|
||||
|
||||
assert result == {"enabled": _FakePlugin}
|
||||
assert loaded == ["enabled"]
|
||||
|
||||
|
||||
def test_discover_all_builtin_shadows_plugin():
|
||||
from nanobot.channels.registry import discover_all
|
||||
|
||||
|
||||
@@ -572,6 +572,7 @@ async def test_github_copilot_provider_refreshes_client_api_key_before_chat():
|
||||
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI", return_value=mock_client):
|
||||
provider = GitHubCopilotProvider(default_model="github-copilot/gpt-4")
|
||||
await provider._ensure_client()
|
||||
|
||||
provider._get_copilot_access_token = AsyncMock(return_value="copilot-access-token")
|
||||
|
||||
@@ -611,7 +612,8 @@ def test_make_provider_passes_extra_headers_to_custom_provider():
|
||||
)
|
||||
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI") as mock_async_openai:
|
||||
make_provider(config)
|
||||
provider = make_provider(config)
|
||||
asyncio.run(provider._ensure_client())
|
||||
|
||||
kwargs = mock_async_openai.call_args.kwargs
|
||||
assert kwargs["api_key"] == "test-key"
|
||||
|
||||
@@ -65,6 +65,7 @@ async def test_github_copilot_does_not_fall_back_from_responses_error():
|
||||
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI", return_value=mock_client):
|
||||
provider = GitHubCopilotProvider(default_model="github_copilot/gpt-5.4-mini")
|
||||
await provider._ensure_client()
|
||||
provider._get_copilot_access_token = AsyncMock(return_value="copilot-access-token")
|
||||
|
||||
response = await provider.chat(
|
||||
|
||||
@@ -449,27 +449,28 @@ def test_gemma_routes_to_gemini_provider() -> None:
|
||||
assert "gemma" in spec.keywords
|
||||
|
||||
|
||||
def test_openrouter_sets_default_attribution_headers() -> None:
|
||||
async def test_openrouter_sets_default_attribution_headers() -> None:
|
||||
spec = find_by_name("openrouter")
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI") as MockClient:
|
||||
OpenAICompatProvider(
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI") as mock_client_cls:
|
||||
provider = OpenAICompatProvider(
|
||||
api_key="sk-or-test-key",
|
||||
api_base="https://openrouter.ai/api/v1",
|
||||
default_model="anthropic/claude-sonnet-4-5",
|
||||
spec=spec,
|
||||
)
|
||||
await provider._ensure_client()
|
||||
|
||||
headers = MockClient.call_args.kwargs["default_headers"]
|
||||
headers = mock_client_cls.call_args.kwargs["default_headers"]
|
||||
assert headers["HTTP-Referer"] == "https://github.com/HKUDS/nanobot"
|
||||
assert headers["X-OpenRouter-Title"] == "nanobot"
|
||||
assert headers["X-OpenRouter-Categories"] == "cli-agent,personal-agent"
|
||||
assert "x-session-affinity" in headers
|
||||
|
||||
|
||||
def test_openrouter_user_headers_override_default_attribution() -> None:
|
||||
async def test_openrouter_user_headers_override_default_attribution() -> None:
|
||||
spec = find_by_name("openrouter")
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI") as MockClient:
|
||||
OpenAICompatProvider(
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI") as mock_client_cls:
|
||||
provider = OpenAICompatProvider(
|
||||
api_key="sk-or-test-key",
|
||||
api_base="https://openrouter.ai/api/v1",
|
||||
default_model="anthropic/claude-sonnet-4-5",
|
||||
@@ -480,8 +481,9 @@ def test_openrouter_user_headers_override_default_attribution() -> None:
|
||||
},
|
||||
spec=spec,
|
||||
)
|
||||
await provider._ensure_client()
|
||||
|
||||
headers = MockClient.call_args.kwargs["default_headers"]
|
||||
headers = mock_client_cls.call_args.kwargs["default_headers"]
|
||||
assert headers["HTTP-Referer"] == "https://nanobot.ai"
|
||||
assert headers["X-OpenRouter-Title"] == "Nanobot Pro"
|
||||
assert headers["X-OpenRouter-Categories"] == "cli-agent,personal-agent"
|
||||
|
||||
@@ -8,16 +8,18 @@ def _assert_openai_compat_timeout(timeout) -> None:
|
||||
assert timeout == 120.0
|
||||
|
||||
|
||||
def test_openai_compat_provider_sets_sdk_timeout() -> None:
|
||||
async def test_openai_compat_provider_defers_sdk_client_until_first_use() -> None:
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI") as mock_async_openai:
|
||||
OpenAICompatProvider(api_key="test-key", api_base="https://example.com/v1")
|
||||
provider = OpenAICompatProvider(api_key="test-key", api_base="https://example.com/v1")
|
||||
mock_async_openai.assert_not_called()
|
||||
await provider._ensure_client()
|
||||
|
||||
kwargs = mock_async_openai.call_args.kwargs
|
||||
_assert_openai_compat_timeout(kwargs["timeout"])
|
||||
assert kwargs["http_client"] is None
|
||||
|
||||
|
||||
def test_openai_compat_provider_sets_timeout_on_local_http_client() -> None:
|
||||
async def test_openai_compat_provider_sets_timeout_on_local_http_client() -> None:
|
||||
spec = ProviderSpec(
|
||||
name="local",
|
||||
keywords=(),
|
||||
@@ -33,7 +35,9 @@ def test_openai_compat_provider_sets_timeout_on_local_http_client() -> None:
|
||||
return_value=sentinel.http_client,
|
||||
) as mock_http_client,
|
||||
):
|
||||
OpenAICompatProvider(spec=spec)
|
||||
provider = OpenAICompatProvider(spec=spec)
|
||||
mock_async_openai.assert_not_called()
|
||||
await provider._ensure_client()
|
||||
|
||||
client_kwargs = mock_http_client.call_args.kwargs
|
||||
_assert_openai_compat_timeout(client_kwargs["timeout"])
|
||||
@@ -44,10 +48,11 @@ def test_openai_compat_provider_sets_timeout_on_local_http_client() -> None:
|
||||
assert openai_kwargs["http_client"] is sentinel.http_client
|
||||
|
||||
|
||||
def test_openai_compat_provider_timeout_can_be_overridden_by_env(monkeypatch) -> None:
|
||||
async def test_openai_compat_provider_timeout_can_be_overridden_by_env(monkeypatch) -> None:
|
||||
monkeypatch.setenv("NANOBOT_OPENAI_COMPAT_TIMEOUT_S", "45")
|
||||
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI") as mock_async_openai:
|
||||
OpenAICompatProvider(api_key="test-key", api_base="https://example.com/v1")
|
||||
provider = OpenAICompatProvider(api_key="test-key", api_base="https://example.com/v1")
|
||||
await provider._ensure_client()
|
||||
|
||||
assert mock_async_openai.call_args.kwargs["timeout"] == 45.0
|
||||
|
||||
@@ -5,9 +5,10 @@ from nanobot.providers.azure_openai_provider import AzureOpenAIProvider
|
||||
from nanobot.providers.openai_compat_provider import OpenAICompatProvider
|
||||
|
||||
|
||||
def test_openai_compat_disables_sdk_retries_by_default() -> None:
|
||||
async def test_openai_compat_disables_sdk_retries_by_default() -> None:
|
||||
with patch("nanobot.providers.openai_compat_provider.AsyncOpenAI") as mock_client:
|
||||
OpenAICompatProvider(api_key="sk-test", default_model="gpt-4o")
|
||||
provider = OpenAICompatProvider(api_key="sk-test", default_model="gpt-4o")
|
||||
await provider._ensure_client()
|
||||
|
||||
kwargs = mock_client.call_args.kwargs
|
||||
assert kwargs["max_retries"] == 0
|
||||
|
||||
Reference in New Issue
Block a user