mirror of
https://github.com/arc53/DocsGPT.git
synced 2026-05-21 21:05:05 +00:00
804 lines
31 KiB
Python
804 lines
31 KiB
Python
"""Tests for application/core/model_settings.py"""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from application.core.model_settings import (
|
|
AvailableModel,
|
|
ModelCapabilities,
|
|
ModelProvider,
|
|
ModelRegistry,
|
|
)
|
|
|
|
|
|
class TestModelProvider:
|
|
|
|
@pytest.mark.unit
|
|
def test_all_providers_exist(self):
|
|
assert ModelProvider.OPENAI == "openai"
|
|
assert ModelProvider.ANTHROPIC == "anthropic"
|
|
assert ModelProvider.GOOGLE == "google"
|
|
assert ModelProvider.GROQ == "groq"
|
|
assert ModelProvider.DOCSGPT == "docsgpt"
|
|
assert ModelProvider.HUGGINGFACE == "huggingface"
|
|
assert ModelProvider.NOVITA == "novita"
|
|
assert ModelProvider.OPENROUTER == "openrouter"
|
|
assert ModelProvider.SAGEMAKER == "sagemaker"
|
|
assert ModelProvider.PREMAI == "premai"
|
|
assert ModelProvider.LLAMA_CPP == "llama.cpp"
|
|
assert ModelProvider.AZURE_OPENAI == "azure_openai"
|
|
|
|
|
|
class TestModelCapabilities:
|
|
|
|
@pytest.mark.unit
|
|
def test_defaults(self):
|
|
caps = ModelCapabilities()
|
|
assert caps.supports_tools is False
|
|
assert caps.supports_structured_output is False
|
|
assert caps.supports_streaming is True
|
|
assert caps.supported_attachment_types == []
|
|
assert caps.context_window == 128000
|
|
assert caps.input_cost_per_token is None
|
|
assert caps.output_cost_per_token is None
|
|
|
|
@pytest.mark.unit
|
|
def test_custom_values(self):
|
|
caps = ModelCapabilities(
|
|
supports_tools=True,
|
|
supports_structured_output=True,
|
|
context_window=32000,
|
|
input_cost_per_token=0.001,
|
|
)
|
|
assert caps.supports_tools is True
|
|
assert caps.context_window == 32000
|
|
|
|
|
|
class TestAvailableModel:
|
|
|
|
@pytest.mark.unit
|
|
def test_to_dict_basic(self):
|
|
model = AvailableModel(
|
|
id="gpt-4",
|
|
provider=ModelProvider.OPENAI,
|
|
display_name="GPT-4",
|
|
description="OpenAI GPT-4",
|
|
)
|
|
d = model.to_dict()
|
|
assert d["id"] == "gpt-4"
|
|
assert d["provider"] == "openai"
|
|
assert d["display_name"] == "GPT-4"
|
|
assert d["enabled"] is True
|
|
assert "base_url" not in d
|
|
|
|
@pytest.mark.unit
|
|
def test_to_dict_with_base_url(self):
|
|
model = AvailableModel(
|
|
id="local-model",
|
|
provider=ModelProvider.OPENAI,
|
|
display_name="Local",
|
|
base_url="http://localhost:11434",
|
|
)
|
|
d = model.to_dict()
|
|
assert d["base_url"] == "http://localhost:11434"
|
|
|
|
@pytest.mark.unit
|
|
def test_to_dict_includes_capabilities(self):
|
|
caps = ModelCapabilities(supports_tools=True, context_window=64000)
|
|
model = AvailableModel(
|
|
id="m1",
|
|
provider=ModelProvider.ANTHROPIC,
|
|
display_name="M1",
|
|
capabilities=caps,
|
|
)
|
|
d = model.to_dict()
|
|
assert d["supports_tools"] is True
|
|
assert d["context_window"] == 64000
|
|
|
|
|
|
class TestModelRegistry:
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _reset_singleton(self):
|
|
"""Reset singleton between tests."""
|
|
ModelRegistry._instance = None
|
|
ModelRegistry._initialized = False
|
|
yield
|
|
ModelRegistry._instance = None
|
|
ModelRegistry._initialized = False
|
|
|
|
@pytest.mark.unit
|
|
def test_singleton(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
r1 = ModelRegistry()
|
|
r2 = ModelRegistry()
|
|
assert r1 is r2
|
|
|
|
@pytest.mark.unit
|
|
def test_get_instance(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
r = ModelRegistry.get_instance()
|
|
assert isinstance(r, ModelRegistry)
|
|
|
|
@pytest.mark.unit
|
|
def test_get_model(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
model = AvailableModel(id="test", provider=ModelProvider.OPENAI, display_name="Test")
|
|
reg.models["test"] = model
|
|
assert reg.get_model("test") is model
|
|
assert reg.get_model("nonexistent") is None
|
|
|
|
@pytest.mark.unit
|
|
def test_get_all_models(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models["m1"] = AvailableModel(id="m1", provider=ModelProvider.OPENAI, display_name="M1")
|
|
reg.models["m2"] = AvailableModel(id="m2", provider=ModelProvider.ANTHROPIC, display_name="M2")
|
|
assert len(reg.get_all_models()) == 2
|
|
|
|
@pytest.mark.unit
|
|
def test_get_enabled_models(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models["m1"] = AvailableModel(id="m1", provider=ModelProvider.OPENAI, display_name="M1", enabled=True)
|
|
reg.models["m2"] = AvailableModel(id="m2", provider=ModelProvider.OPENAI, display_name="M2", enabled=False)
|
|
enabled = reg.get_enabled_models()
|
|
assert len(enabled) == 1
|
|
assert enabled[0].id == "m1"
|
|
|
|
@pytest.mark.unit
|
|
def test_model_exists(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models["m1"] = AvailableModel(id="m1", provider=ModelProvider.OPENAI, display_name="M1")
|
|
assert reg.model_exists("m1") is True
|
|
assert reg.model_exists("m2") is False
|
|
|
|
@pytest.mark.unit
|
|
def test_parse_model_names(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
assert reg._parse_model_names("model1,model2") == ["model1", "model2"]
|
|
assert reg._parse_model_names("model1 , model2 ") == ["model1", "model2"]
|
|
assert reg._parse_model_names("single") == ["single"]
|
|
assert reg._parse_model_names("") == []
|
|
assert reg._parse_model_names(None) == []
|
|
|
|
@pytest.mark.unit
|
|
def test_add_docsgpt_models(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
reg._add_docsgpt_models(mock_settings)
|
|
assert "docsgpt-local" in reg.models
|
|
|
|
@pytest.mark.unit
|
|
def test_add_huggingface_models(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
reg._add_huggingface_models(mock_settings)
|
|
assert "huggingface-local" in reg.models
|
|
|
|
@pytest.mark.unit
|
|
def test_load_models_with_openai_key(self):
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPENAI_BASE_URL = None
|
|
mock_settings.OPENAI_API_KEY = "sk-test"
|
|
mock_settings.OPENAI_API_BASE = None
|
|
mock_settings.ANTHROPIC_API_KEY = None
|
|
mock_settings.GOOGLE_API_KEY = None
|
|
mock_settings.GROQ_API_KEY = None
|
|
mock_settings.OPEN_ROUTER_API_KEY = None
|
|
mock_settings.NOVITA_API_KEY = None
|
|
mock_settings.HUGGINGFACE_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "openai"
|
|
mock_settings.LLM_NAME = ""
|
|
mock_settings.API_KEY = None
|
|
|
|
with patch("application.core.settings.settings", mock_settings):
|
|
reg = ModelRegistry()
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_load_models_custom_openai_base_url(self):
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPENAI_BASE_URL = "http://localhost:11434/v1"
|
|
mock_settings.OPENAI_API_KEY = "sk-test"
|
|
mock_settings.OPENAI_API_BASE = None
|
|
mock_settings.ANTHROPIC_API_KEY = None
|
|
mock_settings.GOOGLE_API_KEY = None
|
|
mock_settings.GROQ_API_KEY = None
|
|
mock_settings.OPEN_ROUTER_API_KEY = None
|
|
mock_settings.NOVITA_API_KEY = None
|
|
mock_settings.HUGGINGFACE_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "openai"
|
|
mock_settings.LLM_NAME = "llama3,gemma"
|
|
mock_settings.API_KEY = None
|
|
|
|
with patch("application.core.settings.settings", mock_settings):
|
|
reg = ModelRegistry()
|
|
assert "llama3" in reg.models
|
|
assert "gemma" in reg.models
|
|
|
|
@pytest.mark.unit
|
|
def test_default_model_selection_from_llm_name(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {"gpt-4": AvailableModel(id="gpt-4", provider=ModelProvider.OPENAI, display_name="GPT-4")}
|
|
reg.default_model_id = "gpt-4"
|
|
assert reg.default_model_id == "gpt-4"
|
|
|
|
@pytest.mark.unit
|
|
def test_add_anthropic_models_with_key(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.ANTHROPIC_API_KEY = "sk-ant-test"
|
|
mock_settings.LLM_PROVIDER = ""
|
|
mock_settings.LLM_NAME = ""
|
|
reg._add_anthropic_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_google_models_with_key(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.GOOGLE_API_KEY = "google-test"
|
|
mock_settings.LLM_PROVIDER = ""
|
|
mock_settings.LLM_NAME = ""
|
|
reg._add_google_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_groq_models_with_key(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.GROQ_API_KEY = "groq-test"
|
|
mock_settings.LLM_PROVIDER = ""
|
|
mock_settings.LLM_NAME = ""
|
|
reg._add_groq_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_openrouter_models_with_key(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPEN_ROUTER_API_KEY = "or-test"
|
|
mock_settings.LLM_PROVIDER = ""
|
|
mock_settings.LLM_NAME = ""
|
|
reg._add_openrouter_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_novita_models_with_key(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.NOVITA_API_KEY = "novita-test"
|
|
mock_settings.LLM_PROVIDER = ""
|
|
mock_settings.LLM_NAME = ""
|
|
reg._add_novita_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_azure_openai_models_specific(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.LLM_PROVIDER = "azure_openai"
|
|
mock_settings.LLM_NAME = "nonexistent-model"
|
|
reg._add_azure_openai_models(mock_settings)
|
|
# Falls through to adding all azure models
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_anthropic_models_no_key_with_provider(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.ANTHROPIC_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "anthropic"
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_anthropic_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_default_model_fallback_to_first(self):
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPENAI_BASE_URL = None
|
|
mock_settings.OPENAI_API_KEY = None
|
|
mock_settings.OPENAI_API_BASE = None
|
|
mock_settings.ANTHROPIC_API_KEY = None
|
|
mock_settings.GOOGLE_API_KEY = None
|
|
mock_settings.GROQ_API_KEY = None
|
|
mock_settings.OPEN_ROUTER_API_KEY = None
|
|
mock_settings.NOVITA_API_KEY = None
|
|
mock_settings.HUGGINGFACE_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = ""
|
|
mock_settings.LLM_NAME = ""
|
|
mock_settings.API_KEY = None
|
|
|
|
with patch("application.core.settings.settings", mock_settings):
|
|
reg = ModelRegistry()
|
|
# Should have at least docsgpt-local
|
|
assert reg.default_model_id is not None
|
|
|
|
@pytest.mark.unit
|
|
def test_default_model_from_provider_fallback(self):
|
|
"""When LLM_NAME is not set but LLM_PROVIDER and API_KEY are,
|
|
default should be first model of that provider."""
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPENAI_BASE_URL = None
|
|
mock_settings.OPENAI_API_KEY = "sk-test"
|
|
mock_settings.OPENAI_API_BASE = None
|
|
mock_settings.ANTHROPIC_API_KEY = None
|
|
mock_settings.GOOGLE_API_KEY = None
|
|
mock_settings.GROQ_API_KEY = None
|
|
mock_settings.OPEN_ROUTER_API_KEY = None
|
|
mock_settings.NOVITA_API_KEY = None
|
|
mock_settings.HUGGINGFACE_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "openai"
|
|
mock_settings.LLM_NAME = None
|
|
mock_settings.API_KEY = "sk-test"
|
|
|
|
with patch("application.core.settings.settings", mock_settings):
|
|
reg = ModelRegistry()
|
|
assert reg.default_model_id is not None
|
|
|
|
@pytest.mark.unit
|
|
def test_add_google_models_no_key_with_provider(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.GOOGLE_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "google"
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_google_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_groq_models_no_key_with_provider(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.GROQ_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "groq"
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_groq_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_openrouter_models_no_key_with_provider(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPEN_ROUTER_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "openrouter"
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_openrouter_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_novita_models_no_key_with_provider(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.NOVITA_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "novita"
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_novita_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_to_dict_disabled_model(self):
|
|
model = AvailableModel(
|
|
id="disabled",
|
|
provider=ModelProvider.OPENAI,
|
|
display_name="Disabled",
|
|
enabled=False,
|
|
)
|
|
d = model.to_dict()
|
|
assert d["enabled"] is False
|
|
|
|
@pytest.mark.unit
|
|
def test_to_dict_with_attachment_types(self):
|
|
caps = ModelCapabilities(
|
|
supported_attachment_types=["image/png", "application/pdf"],
|
|
)
|
|
model = AvailableModel(
|
|
id="vision",
|
|
provider=ModelProvider.OPENAI,
|
|
display_name="Vision",
|
|
capabilities=caps,
|
|
)
|
|
d = model.to_dict()
|
|
assert d["supported_attachment_types"] == ["image/png", "application/pdf"]
|
|
|
|
# ----------------------------------------------------------------
|
|
# Coverage for _add_* methods with matching LLM_NAME
|
|
# Lines: 100, 105, 147, 171, 179, 186, 199-201, 204, 210, 213,
|
|
# 218, 229, 233, 241, 250
|
|
# ----------------------------------------------------------------
|
|
|
|
@pytest.mark.unit
|
|
def test_add_azure_openai_models_with_matching_name(self):
|
|
"""Cover line 186: azure model matching LLM_NAME returns early."""
|
|
from application.core.model_configs import AZURE_OPENAI_MODELS
|
|
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.LLM_PROVIDER = "azure_openai"
|
|
if AZURE_OPENAI_MODELS:
|
|
mock_settings.LLM_NAME = AZURE_OPENAI_MODELS[0].id
|
|
else:
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_azure_openai_models(mock_settings)
|
|
# Should have added at least one model
|
|
assert len(reg.models) >= 1
|
|
|
|
@pytest.mark.unit
|
|
def test_add_anthropic_no_key_no_provider_fallthrough(self):
|
|
"""Cover lines 199-204: no key, provider set but name not found -> add all."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.ANTHROPIC_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "anthropic"
|
|
mock_settings.LLM_NAME = "nonexistent-model"
|
|
reg._add_anthropic_models(mock_settings)
|
|
# Falls through to add all anthropic models
|
|
assert len(reg.models) > 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_google_no_key_matching_name(self):
|
|
"""Cover lines 213-218: Google fallback with matching name."""
|
|
from application.core.model_configs import GOOGLE_MODELS
|
|
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.GOOGLE_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "google"
|
|
if GOOGLE_MODELS:
|
|
mock_settings.LLM_NAME = GOOGLE_MODELS[0].id
|
|
else:
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_google_models(mock_settings)
|
|
assert len(reg.models) >= 1
|
|
|
|
@pytest.mark.unit
|
|
def test_add_groq_no_key_matching_name(self):
|
|
"""Cover lines 229-233: Groq fallback with matching name."""
|
|
from application.core.model_configs import GROQ_MODELS
|
|
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.GROQ_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "groq"
|
|
if GROQ_MODELS:
|
|
mock_settings.LLM_NAME = GROQ_MODELS[0].id
|
|
else:
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_groq_models(mock_settings)
|
|
assert len(reg.models) >= 1
|
|
|
|
@pytest.mark.unit
|
|
def test_add_openrouter_no_key_matching_name(self):
|
|
"""Cover lines 241-250: OpenRouter fallback with matching name."""
|
|
from application.core.model_configs import OPENROUTER_MODELS
|
|
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPEN_ROUTER_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "openrouter"
|
|
if OPENROUTER_MODELS:
|
|
mock_settings.LLM_NAME = OPENROUTER_MODELS[0].id
|
|
else:
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_openrouter_models(mock_settings)
|
|
assert len(reg.models) >= 1
|
|
|
|
@pytest.mark.unit
|
|
def test_add_novita_no_key_matching_name(self):
|
|
"""Cover novita fallback with matching name."""
|
|
from application.core.model_configs import NOVITA_MODELS
|
|
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.NOVITA_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "novita"
|
|
if NOVITA_MODELS:
|
|
mock_settings.LLM_NAME = NOVITA_MODELS[0].id
|
|
else:
|
|
mock_settings.LLM_NAME = "nonexistent"
|
|
reg._add_novita_models(mock_settings)
|
|
assert len(reg.models) >= 1
|
|
|
|
@pytest.mark.unit
|
|
def test_load_models_default_from_llm_name_exact_match(self):
|
|
"""Cover line 136/147: exact LLM_NAME match for default model."""
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPENAI_BASE_URL = None
|
|
mock_settings.OPENAI_API_KEY = "sk-test"
|
|
mock_settings.OPENAI_API_BASE = None
|
|
mock_settings.ANTHROPIC_API_KEY = None
|
|
mock_settings.GOOGLE_API_KEY = None
|
|
mock_settings.GROQ_API_KEY = None
|
|
mock_settings.OPEN_ROUTER_API_KEY = None
|
|
mock_settings.NOVITA_API_KEY = None
|
|
mock_settings.HUGGINGFACE_API_KEY = None
|
|
mock_settings.LLM_PROVIDER = "openai"
|
|
mock_settings.API_KEY = None
|
|
|
|
from application.core.model_configs import OPENAI_MODELS
|
|
|
|
if OPENAI_MODELS:
|
|
mock_settings.LLM_NAME = OPENAI_MODELS[0].id
|
|
else:
|
|
mock_settings.LLM_NAME = "gpt-4o"
|
|
|
|
with patch("application.core.settings.settings", mock_settings):
|
|
reg = ModelRegistry()
|
|
assert reg.default_model_id is not None
|
|
|
|
@pytest.mark.unit
|
|
def test_add_openai_models_local_endpoint_no_name(self):
|
|
"""Cover line 171: local endpoint without LLM_NAME adds nothing."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPENAI_BASE_URL = "http://localhost:11434/v1"
|
|
mock_settings.OPENAI_API_KEY = "sk-test"
|
|
mock_settings.LLM_NAME = None
|
|
reg._add_openai_models(mock_settings)
|
|
assert len(reg.models) == 0
|
|
|
|
@pytest.mark.unit
|
|
def test_add_openai_standard_no_api_key(self):
|
|
"""Cover line 179: standard OpenAI without API key adds nothing."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPENAI_BASE_URL = None
|
|
mock_settings.OPENAI_API_KEY = None
|
|
reg._add_openai_models(mock_settings)
|
|
assert len(reg.models) == 0
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Coverage — additional uncovered lines: 100, 105, 147, 171, 179, 186, 250
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestModelRegistryAdditionalCoverage:
|
|
|
|
def test_add_azure_openai_models_specific_name(self):
|
|
"""Cover line 186: azure_openai with specific LLM_NAME match."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.LLM_PROVIDER = "azure_openai"
|
|
mock_settings.LLM_NAME = "gpt-4o"
|
|
|
|
# Create a fake model that matches
|
|
fake_model = MagicMock()
|
|
fake_model.id = "gpt-4o"
|
|
with patch(
|
|
"application.core.model_configs.AZURE_OPENAI_MODELS",
|
|
[fake_model],
|
|
):
|
|
reg._add_azure_openai_models(mock_settings)
|
|
assert "gpt-4o" in reg.models
|
|
|
|
def test_add_anthropic_models_with_api_key(self):
|
|
"""Cover line 100: anthropic with API key."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.ANTHROPIC_API_KEY = "sk-test"
|
|
mock_settings.LLM_PROVIDER = "anthropic"
|
|
reg._add_anthropic_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
def test_add_google_models_with_api_key(self):
|
|
"""Cover line 105: google with API key."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.GOOGLE_API_KEY = "test-key"
|
|
mock_settings.LLM_PROVIDER = "google"
|
|
reg._add_google_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
def test_default_model_from_provider(self):
|
|
"""Cover line 147: default model selected from provider."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
reg.default_model_id = None
|
|
|
|
fake_model = MagicMock()
|
|
fake_model.provider = MagicMock()
|
|
fake_model.provider.value = "openai"
|
|
reg.models["gpt-4o"] = fake_model
|
|
|
|
mock_settings = MagicMock()
|
|
mock_settings.LLM_NAME = None
|
|
mock_settings.LLM_PROVIDER = "openai"
|
|
mock_settings.API_KEY = "key"
|
|
|
|
# Simulate the default selection logic
|
|
if not reg.default_model_id:
|
|
for model_id, model in reg.models.items():
|
|
if model.provider.value == mock_settings.LLM_PROVIDER:
|
|
reg.default_model_id = model_id
|
|
break
|
|
|
|
assert reg.default_model_id == "gpt-4o"
|
|
|
|
def test_add_openai_local_endpoint_with_llm_name(self):
|
|
"""Cover line 171: local endpoint registers custom models from LLM_NAME."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPENAI_BASE_URL = "http://localhost:11434/v1"
|
|
mock_settings.OPENAI_API_KEY = "sk-test"
|
|
mock_settings.LLM_NAME = "llama3,phi3"
|
|
reg._add_openai_models(mock_settings)
|
|
assert "llama3" in reg.models
|
|
assert "phi3" in reg.models
|
|
|
|
def test_add_openai_standard_with_api_key(self):
|
|
"""Cover line 179: standard OpenAI with API key adds models."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPENAI_BASE_URL = None
|
|
mock_settings.OPENAI_API_KEY = "sk-real-key"
|
|
reg._add_openai_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
def test_add_openrouter_models(self):
|
|
"""Cover line 250: openrouter models added."""
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
mock_settings = MagicMock()
|
|
mock_settings.OPEN_ROUTER_API_KEY = "or-key"
|
|
mock_settings.LLM_PROVIDER = "openrouter"
|
|
reg._add_openrouter_models(mock_settings)
|
|
assert len(reg.models) > 0
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Additional coverage for model_settings.py
|
|
# Lines: 135-136 (backward compat LLM_NAME), 138-143 (provider fallback),
|
|
# 145-146 (first model as default)
|
|
# ---------------------------------------------------------------------------
|
|
# Imports already at the top of the file; no additional imports needed
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestDefaultModelSelectionBackwardCompat:
|
|
"""Cover lines 135-136: backward compat exact match on LLM_NAME."""
|
|
|
|
def test_llm_name_exact_match_as_default(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
reg.default_model_id = None
|
|
# Add a model with composite ID
|
|
model = AvailableModel(
|
|
id="my-composite-model",
|
|
provider=ModelProvider.OPENAI,
|
|
display_name="Composite",
|
|
description="test",
|
|
capabilities=ModelCapabilities(),
|
|
)
|
|
reg.models["my-composite-model"] = model
|
|
|
|
# Simulate _parse_model_names returning something different
|
|
# so that the first for-loop doesn't match
|
|
mock_settings = MagicMock()
|
|
mock_settings.LLM_NAME = "my-composite-model"
|
|
mock_settings.LLM_PROVIDER = None
|
|
mock_settings.API_KEY = None
|
|
|
|
# Call the logic directly
|
|
model_names = reg._parse_model_names(mock_settings.LLM_NAME)
|
|
for mn in model_names:
|
|
if mn in reg.models:
|
|
reg.default_model_id = mn
|
|
break
|
|
|
|
assert reg.default_model_id == "my-composite-model"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestDefaultModelSelectionByProvider:
|
|
"""Cover lines 138-143: default model by provider when LLM_NAME doesn't match."""
|
|
|
|
def test_default_by_provider(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
reg.default_model_id = None
|
|
model = AvailableModel(
|
|
id="gpt-4",
|
|
provider=ModelProvider.OPENAI,
|
|
display_name="GPT-4",
|
|
description="test",
|
|
capabilities=ModelCapabilities(),
|
|
)
|
|
reg.models["gpt-4"] = model
|
|
|
|
# Simulate: LLM_NAME doesn't exist/match, but LLM_PROVIDER + API_KEY set
|
|
if not reg.default_model_id:
|
|
for model_id, m in reg.models.items():
|
|
if m.provider.value == "openai":
|
|
reg.default_model_id = model_id
|
|
break
|
|
|
|
assert reg.default_model_id == "gpt-4"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestDefaultModelSelectionFirstModel:
|
|
"""Cover lines 145-146: first model as default when nothing else matches."""
|
|
|
|
def test_first_model_as_default(self):
|
|
with patch.object(ModelRegistry, "_load_models"):
|
|
reg = ModelRegistry()
|
|
reg.models = {}
|
|
reg.default_model_id = None
|
|
model = AvailableModel(
|
|
id="fallback-model",
|
|
provider=ModelProvider.OPENAI,
|
|
display_name="Fallback",
|
|
description="test",
|
|
capabilities=ModelCapabilities(),
|
|
)
|
|
reg.models["fallback-model"] = model
|
|
|
|
if not reg.default_model_id and reg.models:
|
|
reg.default_model_id = next(iter(reg.models.keys()))
|
|
|
|
assert reg.default_model_id == "fallback-model"
|