7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
213 lines
7.2 KiB
Python
213 lines
7.2 KiB
Python
"""
|
|
Behavioral tests for config/llm_config module.
|
|
|
|
Tests availability checks with snapshots, provider validation,
|
|
model/provider name cleaning, fallback model, and provider selection.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
|
|
class TestApiKeyAvailabilityChecks:
|
|
"""Tests for is_*_available() functions with settings snapshots.
|
|
|
|
These functions check bool(api_key) from the snapshot — the core
|
|
logic is: present and non-empty → True, else → False.
|
|
"""
|
|
|
|
def test_openai_available_with_api_key(self):
|
|
from local_deep_research.llm.providers.implementations.openai import (
|
|
OpenAIProvider,
|
|
)
|
|
|
|
result = OpenAIProvider.is_available(
|
|
settings_snapshot={"llm.openai.api_key": "sk-test"}
|
|
)
|
|
assert result is True
|
|
|
|
def test_openai_unavailable_without_api_key(self):
|
|
from local_deep_research.llm.providers.implementations.openai import (
|
|
OpenAIProvider,
|
|
)
|
|
|
|
assert (
|
|
OpenAIProvider.is_available(
|
|
settings_snapshot={"search.tool": "searxng"}
|
|
)
|
|
is False
|
|
)
|
|
|
|
def test_openai_unavailable_with_empty_api_key(self):
|
|
"""Empty string API key is falsy — treated as missing."""
|
|
from local_deep_research.llm.providers.implementations.openai import (
|
|
OpenAIProvider,
|
|
)
|
|
|
|
result = OpenAIProvider.is_available(
|
|
settings_snapshot={"llm.openai.api_key": ""}
|
|
)
|
|
assert result is False
|
|
|
|
def test_anthropic_available_with_api_key(self):
|
|
from local_deep_research.llm.providers.implementations.anthropic import (
|
|
AnthropicProvider,
|
|
)
|
|
|
|
result = AnthropicProvider.is_available(
|
|
settings_snapshot={"llm.anthropic.api_key": "sk-ant-test"}
|
|
)
|
|
assert result is True
|
|
|
|
def test_anthropic_unavailable_without_api_key(self):
|
|
from local_deep_research.llm.providers.implementations.anthropic import (
|
|
AnthropicProvider,
|
|
)
|
|
|
|
assert (
|
|
AnthropicProvider.is_available(
|
|
settings_snapshot={"search.tool": "searxng"}
|
|
)
|
|
is False
|
|
)
|
|
|
|
def test_openai_endpoint_available_with_api_key(self):
|
|
from local_deep_research.llm.providers.implementations.custom_openai_endpoint import (
|
|
CustomOpenAIEndpointProvider,
|
|
)
|
|
|
|
result = CustomOpenAIEndpointProvider.is_available(
|
|
settings_snapshot={"llm.openai_endpoint.api_key": "key123"}
|
|
)
|
|
assert result is True
|
|
|
|
def test_openai_endpoint_unavailable_without_api_key(self):
|
|
from local_deep_research.llm.providers.implementations.custom_openai_endpoint import (
|
|
CustomOpenAIEndpointProvider,
|
|
)
|
|
|
|
assert (
|
|
CustomOpenAIEndpointProvider.is_available(
|
|
settings_snapshot={"search.tool": "searxng"}
|
|
)
|
|
is False
|
|
)
|
|
|
|
|
|
class TestProviderValidation:
|
|
"""Tests for provider validation in get_llm()."""
|
|
|
|
def test_invalid_provider_raises_value_error(self):
|
|
"""get_llm raises ValueError for unknown provider name."""
|
|
from local_deep_research.config.llm_config import get_llm
|
|
|
|
with pytest.raises(ValueError, match="Invalid provider"):
|
|
get_llm(
|
|
provider="nonexistent_provider",
|
|
settings_snapshot={"search.tool": "searxng"},
|
|
)
|
|
|
|
def test_error_message_lists_valid_providers(self):
|
|
"""ValueError message enumerates the auto-discovered providers.
|
|
|
|
The valid set is derived from auto-discovery (no hardcoded
|
|
VALID_PROVIDERS), so the error lists every discovered provider.
|
|
"""
|
|
from local_deep_research.config.llm_config import get_llm
|
|
from local_deep_research.llm.providers import (
|
|
get_discovered_provider_options,
|
|
)
|
|
from local_deep_research.llm.providers.base import normalize_provider
|
|
|
|
with pytest.raises(ValueError) as exc_info:
|
|
get_llm(
|
|
provider="bad_provider",
|
|
settings_snapshot={"search.tool": "searxng"},
|
|
)
|
|
message = str(exc_info.value)
|
|
for option in get_discovered_provider_options():
|
|
assert normalize_provider(option["value"]) in message
|
|
|
|
|
|
class TestModelAndProviderNameCleaning:
|
|
"""Tests for model_name and provider name cleaning in get_llm().
|
|
|
|
get_llm strips quotes, whitespace, and lowercases the provider name
|
|
before validation.
|
|
"""
|
|
|
|
def test_quoted_provider_name_cleaned(self):
|
|
"""Surrounding quotes are stripped: \"'none'\" → 'none' (valid but unimplemented)."""
|
|
import pytest
|
|
from local_deep_research.config.llm_config import get_llm
|
|
|
|
# 'none' is valid but has no implementation, so it raises ValueError
|
|
# The point is the quotes are stripped before validation
|
|
with pytest.raises(ValueError, match="No LLM provider configured"):
|
|
get_llm(
|
|
model_name="x",
|
|
provider="'none'",
|
|
settings_snapshot={"search.tool": "searxng"},
|
|
)
|
|
|
|
def test_whitespace_provider_name_cleaned(self):
|
|
"""Surrounding whitespace is stripped: ' none ' → 'none'."""
|
|
import pytest
|
|
from local_deep_research.config.llm_config import get_llm
|
|
|
|
with pytest.raises(ValueError, match="No LLM provider configured"):
|
|
get_llm(
|
|
model_name="x",
|
|
provider=" none ",
|
|
settings_snapshot={"search.tool": "searxng"},
|
|
)
|
|
|
|
def test_uppercase_provider_lowercased(self):
|
|
"""Provider name is lowercased: 'NONE' → 'none'."""
|
|
import pytest
|
|
from local_deep_research.config.llm_config import get_llm
|
|
|
|
with pytest.raises(ValueError, match="No LLM provider configured"):
|
|
get_llm(
|
|
model_name="x",
|
|
provider="NONE",
|
|
settings_snapshot={"search.tool": "searxng"},
|
|
)
|
|
|
|
def test_combined_quotes_whitespace_case_cleaning(self):
|
|
"""Combined cleaning: \" 'None' \" → 'none'."""
|
|
import pytest
|
|
from local_deep_research.config.llm_config import get_llm
|
|
|
|
with pytest.raises(ValueError, match="No LLM provider configured"):
|
|
get_llm(
|
|
model_name="x",
|
|
provider=" 'None' ",
|
|
settings_snapshot={"search.tool": "searxng"},
|
|
)
|
|
|
|
|
|
class TestGetSelectedLlmProvider:
|
|
"""Tests for get_selected_llm_provider() function."""
|
|
|
|
def test_returns_lowercase(self):
|
|
"""Provider value from snapshot is lowercased."""
|
|
from local_deep_research.config.llm_config import (
|
|
get_selected_llm_provider,
|
|
)
|
|
|
|
result = get_selected_llm_provider(
|
|
settings_snapshot={"llm.provider": "OpenAI"}
|
|
)
|
|
assert result == "openai"
|
|
|
|
def test_default_is_ollama(self):
|
|
"""Default provider is 'ollama' when not specified in snapshot."""
|
|
from local_deep_research.config.llm_config import (
|
|
get_selected_llm_provider,
|
|
)
|
|
|
|
result = get_selected_llm_provider(
|
|
settings_snapshot={"search.tool": "searxng"}
|
|
)
|
|
assert result == "ollama"
|