chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,286 @@
|
||||
"""
|
||||
Comprehensive test for OpenAI API key configuration and usage.
|
||||
|
||||
This test specifically verifies that OpenAI API keys can be:
|
||||
1. Configured through settings
|
||||
2. Properly passed to the OpenAI client
|
||||
3. Used for actual research operations
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
import os
|
||||
|
||||
from local_deep_research.config.llm_config import get_llm
|
||||
|
||||
|
||||
@pytest.mark.requires_llm
|
||||
class TestOpenAIAPIKeyUsage:
|
||||
"""Test OpenAI API key configuration and usage throughout the system."""
|
||||
|
||||
@pytest.fixture
|
||||
def openai_settings_snapshot(self):
|
||||
"""Create settings snapshot with OpenAI configuration.
|
||||
|
||||
Uses simplified format (raw values) to bypass get_typed_setting_value
|
||||
type coercion in get_setting_from_snapshot().
|
||||
"""
|
||||
return {
|
||||
"search.tool": "searxng",
|
||||
"llm.provider": "openai",
|
||||
"llm.model": "gpt-3.5-turbo",
|
||||
"llm.temperature": 0.7,
|
||||
"llm.openai.api_key": "sk-test-1234567890abcdef",
|
||||
"llm.openai.api_base": None,
|
||||
"llm.openai.organization": None,
|
||||
"llm.streaming": None,
|
||||
"llm.max_retries": None,
|
||||
"llm.request_timeout": None,
|
||||
"llm.context_window_unrestricted": False,
|
||||
"llm.context_window_size": 128000,
|
||||
"llm.supports_max_tokens": True,
|
||||
"llm.max_tokens": 100000,
|
||||
"llm.provider.openai.context_window": 4096,
|
||||
"research.iterations": 2,
|
||||
"research.questions_per_iteration": 3,
|
||||
"research.search_engines": ["wikipedia"],
|
||||
"research.local_context": 2000,
|
||||
"research.web_context": 2000,
|
||||
"rate_limiting.llm_enabled": False,
|
||||
}
|
||||
|
||||
def test_openai_api_key_in_llm_config(self, openai_settings_snapshot):
|
||||
"""Test that OpenAI API key is properly passed to ChatOpenAI."""
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
# Mock the LLM instance
|
||||
mock_llm_instance = Mock()
|
||||
mock_openai.return_value = mock_llm_instance
|
||||
|
||||
# Get LLM with settings
|
||||
get_llm(settings_snapshot=openai_settings_snapshot)
|
||||
|
||||
# Verify ChatOpenAI was called with correct API key
|
||||
mock_openai.assert_called_once()
|
||||
call_args = mock_openai.call_args
|
||||
|
||||
# Check that API key was passed
|
||||
assert call_args is not None
|
||||
assert len(call_args) > 1
|
||||
assert call_args[1]["api_key"] == "sk-test-1234567890abcdef"
|
||||
assert call_args[1]["model"] == "gpt-3.5-turbo"
|
||||
assert call_args[1]["temperature"] == 0.7
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("CI") == "true"
|
||||
or os.environ.get("GITHUB_ACTIONS") == "true",
|
||||
reason="Skipped in CI - requires environment variable configuration",
|
||||
)
|
||||
def test_openai_api_key_from_environment(self, openai_settings_snapshot):
|
||||
"""Test fallback to environment variable if API key not in settings."""
|
||||
# Modify settings to have no API key
|
||||
settings_no_key = openai_settings_snapshot.copy()
|
||||
settings_no_key["llm.openai.api_key"] = None
|
||||
|
||||
# Set LDR-prefixed environment variable
|
||||
with patch.dict(
|
||||
os.environ, {"LDR_LLM_OPENAI_API_KEY": "sk-env-test-key"}
|
||||
):
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
mock_llm_instance = Mock()
|
||||
mock_openai.return_value = mock_llm_instance
|
||||
|
||||
get_llm(settings_snapshot=settings_no_key)
|
||||
|
||||
# Should use environment variable
|
||||
call_args = mock_openai.call_args
|
||||
assert call_args is not None
|
||||
assert len(call_args) > 1
|
||||
assert call_args[1]["api_key"] == "sk-env-test-key"
|
||||
|
||||
def test_openai_api_key_in_research_flow(self, openai_settings_snapshot):
|
||||
"""Test that API key is properly passed through research flow to OpenAI."""
|
||||
# Mock the ChatOpenAI class to verify API key is passed
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
# Create a mock LLM instance
|
||||
mock_llm = Mock()
|
||||
mock_openai.return_value = mock_llm
|
||||
|
||||
# Simply get the LLM to verify the API key is passed
|
||||
from local_deep_research.config.llm_config import get_llm
|
||||
|
||||
llm = get_llm(settings_snapshot=openai_settings_snapshot)
|
||||
|
||||
# Verify API key was passed correctly
|
||||
assert mock_openai.called
|
||||
assert (
|
||||
mock_openai.call_args[1]["api_key"]
|
||||
== "sk-test-1234567890abcdef"
|
||||
)
|
||||
assert mock_openai.call_args[1]["model"] == "gpt-3.5-turbo"
|
||||
assert llm is not None
|
||||
|
||||
def test_openai_with_custom_endpoint(self, openai_settings_snapshot):
|
||||
"""Test OpenAI with custom API endpoint (e.g., Azure OpenAI)."""
|
||||
# Add custom endpoint to settings
|
||||
custom_settings = openai_settings_snapshot.copy()
|
||||
custom_settings["llm.openai.api_base"] = (
|
||||
"https://custom-openai.azure.com"
|
||||
)
|
||||
custom_settings["llm.openai.api_key"] = "custom-azure-key"
|
||||
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
mock_llm_instance = Mock()
|
||||
mock_openai.return_value = mock_llm_instance
|
||||
|
||||
get_llm(settings_snapshot=custom_settings)
|
||||
|
||||
# Verify custom endpoint and key were used
|
||||
call_args = mock_openai.call_args
|
||||
assert call_args is not None
|
||||
assert len(call_args) > 1
|
||||
assert call_args[1]["api_key"] == "custom-azure-key"
|
||||
assert (
|
||||
call_args[1].get("openai_api_base")
|
||||
== "https://custom-openai.azure.com"
|
||||
)
|
||||
|
||||
def test_openai_error_handling_invalid_key(self, openai_settings_snapshot):
|
||||
"""Test error handling when OpenAI API key is invalid."""
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
# Simulate OpenAI authentication error
|
||||
mock_openai.side_effect = Exception("Invalid API key provided")
|
||||
|
||||
# Should raise exception with clear message
|
||||
with pytest.raises(Exception, match="Invalid API key"):
|
||||
get_llm(settings_snapshot=openai_settings_snapshot)
|
||||
|
||||
def test_openai_model_selection(self, openai_settings_snapshot):
|
||||
"""Test different OpenAI model selections."""
|
||||
models_to_test = [
|
||||
"gpt-4",
|
||||
"gpt-4-turbo-preview",
|
||||
"gpt-3.5-turbo-16k",
|
||||
"gpt-4-1106-preview",
|
||||
]
|
||||
|
||||
for model in models_to_test:
|
||||
settings = openai_settings_snapshot.copy()
|
||||
settings["llm.model"] = model
|
||||
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
mock_llm_instance = Mock()
|
||||
mock_openai.return_value = mock_llm_instance
|
||||
|
||||
get_llm(settings_snapshot=settings)
|
||||
|
||||
# Verify correct model was selected
|
||||
assert mock_openai.call_args[1]["model"] == model
|
||||
|
||||
def test_openai_streaming_configuration(self, openai_settings_snapshot):
|
||||
"""Test OpenAI streaming configuration."""
|
||||
# Add streaming setting
|
||||
streaming_settings = openai_settings_snapshot.copy()
|
||||
streaming_settings["llm.streaming"] = True
|
||||
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
mock_llm_instance = Mock()
|
||||
mock_openai.return_value = mock_llm_instance
|
||||
|
||||
get_llm(settings_snapshot=streaming_settings)
|
||||
|
||||
# Verify streaming was enabled
|
||||
assert mock_openai.call_args[1].get("streaming") is True
|
||||
|
||||
def test_openai_retry_configuration(self, openai_settings_snapshot):
|
||||
"""Test OpenAI retry and timeout configuration."""
|
||||
# Add retry settings
|
||||
retry_settings = openai_settings_snapshot.copy()
|
||||
retry_settings["llm.max_retries"] = 3
|
||||
retry_settings["llm.request_timeout"] = 60
|
||||
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
mock_llm_instance = Mock()
|
||||
mock_openai.return_value = mock_llm_instance
|
||||
|
||||
get_llm(settings_snapshot=retry_settings)
|
||||
|
||||
# Verify retry configuration
|
||||
call_args = mock_openai.call_args
|
||||
assert call_args[1].get("max_retries") == 3
|
||||
assert call_args[1].get("request_timeout") == 60
|
||||
|
||||
def test_openai_organization_id(self, openai_settings_snapshot):
|
||||
"""Test OpenAI organization ID configuration."""
|
||||
# Add organization ID
|
||||
org_settings = openai_settings_snapshot.copy()
|
||||
org_settings["llm.openai.organization"] = "org-test123"
|
||||
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
mock_llm_instance = Mock()
|
||||
mock_openai.return_value = mock_llm_instance
|
||||
|
||||
get_llm(settings_snapshot=org_settings)
|
||||
|
||||
# Verify organization ID was passed
|
||||
assert (
|
||||
mock_openai.call_args[1].get("openai_organization")
|
||||
== "org-test123"
|
||||
)
|
||||
|
||||
def test_openai_full_integration(self, openai_settings_snapshot):
|
||||
"""Full integration test verifying all OpenAI configuration parameters."""
|
||||
# Test full OpenAI configuration
|
||||
full_settings = openai_settings_snapshot.copy()
|
||||
full_settings["llm.streaming"] = True
|
||||
full_settings["llm.max_retries"] = 5
|
||||
full_settings["llm.request_timeout"] = 120
|
||||
full_settings["llm.openai.organization"] = "org-123"
|
||||
|
||||
with patch(
|
||||
"local_deep_research.llm.providers.implementations.openai.ChatOpenAI"
|
||||
) as mock_openai:
|
||||
# Create mock LLM
|
||||
mock_llm = Mock()
|
||||
mock_openai.return_value = mock_llm
|
||||
|
||||
from local_deep_research.config.llm_config import get_llm
|
||||
|
||||
# Get LLM with full configuration
|
||||
get_llm(settings_snapshot=full_settings)
|
||||
|
||||
# Verify all parameters were passed correctly
|
||||
assert mock_openai.called
|
||||
call_kwargs = mock_openai.call_args[1]
|
||||
|
||||
# Core parameters
|
||||
assert call_kwargs["api_key"] == "sk-test-1234567890abcdef"
|
||||
assert call_kwargs["model"] == "gpt-3.5-turbo"
|
||||
assert call_kwargs["temperature"] == 0.7
|
||||
|
||||
# Additional parameters
|
||||
assert call_kwargs["streaming"] is True
|
||||
assert call_kwargs["max_retries"] == 5
|
||||
assert call_kwargs["request_timeout"] == 120
|
||||
assert call_kwargs["openai_organization"] == "org-123"
|
||||
|
||||
# Verify max_tokens was set
|
||||
assert "max_tokens" in call_kwargs
|
||||
assert call_kwargs["max_tokens"] > 0
|
||||
Reference in New Issue
Block a user