diff --git a/routes/model_routes.py b/routes/model_routes.py index bacb575ff..e3b9223bb 100644 --- a/routes/model_routes.py +++ b/routes/model_routes.py @@ -418,6 +418,7 @@ _HOST_TO_CURATED = ( ("nvidia.com", "nvidia"), ("openrouter.ai", "openrouter"), ("ollama.com", "ollama"), + ("conifer.build", "openrouter"), ) diff --git a/static/js/providers.js b/static/js/providers.js index 64cea0b2c..d9a77f322 100644 --- a/static/js/providers.js +++ b/static/js/providers.js @@ -124,6 +124,7 @@ const _ENDPOINT_LABELS = [ [/(^|\.)perplexity\.ai$/i, "Perplexity"], [/(^|\.)nvidia\.com$/i, "NVIDIA"], [/(^|\.)x\.ai$/i, "xAI"], + [/(^|\.)conifer\.build$/i, "Conifer"], ]; /** diff --git a/tests/test_model_routes.py b/tests/test_model_routes.py index 307dbb5fa..2be7e686d 100644 --- a/tests/test_model_routes.py +++ b/tests/test_model_routes.py @@ -232,6 +232,13 @@ class TestMatchProviderCurated: def test_kimi_code_url(self): assert _match_provider_curated("https://api.kimi.com/coding/v1", "openai") == "kimi-code" + def test_conifer_url(self): + assert _match_provider_curated("https://api.conifer.build/v1", "openai") == "openrouter" + assert _match_provider_curated("https://conifer.build/v1", "openai") == "openrouter" + + def test_conifer_lookalike_url_not_matched(self): + assert _match_provider_curated("https://conifer.build.attacker.example/v1", "openai") == "openai" + def test_no_url_match_returns_provider(self): assert _match_provider_curated("https://localhost:1234", "openai") == "openai" @@ -339,6 +346,19 @@ class TestCurateModels: assert curated == models assert extra == [] + def test_openrouter_sentinel_returns_all_as_curated(self): + models = ["model-a", "model-b", "any-custom-model"] + curated, extra = _curate_models(models, "openrouter") + assert curated == models + assert extra == [] + + def test_conifer_curate_models_via_match(self): + models = ["conifer-model-1", "conifer-model-2"] + curated_key = _match_provider_curated("https://api.conifer.build/v1", "openai") + curated, extra = _curate_models(models, curated_key) + assert curated == models + assert extra == [] + def test_kimi_code_partitions(self): models = ["kimi-for-coding", "other-model"] curated, extra = _curate_models(models, "kimi-code") diff --git a/tests/test_provider_classification.py b/tests/test_provider_classification.py index 62c713e31..315b0d357 100644 --- a/tests/test_provider_classification.py +++ b/tests/test_provider_classification.py @@ -45,6 +45,7 @@ class TestDetectProvider: ("https://api.openai.com/v1", "openai"), ("https://api.x.ai/v1", "openai"), ("https://api.deepseek.com", "openai"), + ("https://api.conifer.build/v1", "openai"), ("https://generativelanguage.googleapis.com/v1beta/openai", "openai"), # Ollama's OpenAI-compatible /v1 surface is generic, not native ollama. ("http://localhost:11434/v1", "openai"), diff --git a/tests/test_provider_label_js.py b/tests/test_provider_label_js.py index 39b1a1f5d..be35ee901 100644 --- a/tests/test_provider_label_js.py +++ b/tests/test_provider_label_js.py @@ -52,3 +52,14 @@ def _provider_label(url: str) -> str | None: ]) def test_provider_label_neutral_for_loopback(url, expected): assert _provider_label(url) == expected + + +@pytest.mark.skipif(not _HAS_NODE, reason="node binary not on PATH") +@pytest.mark.parametrize("url,expected", [ + ("https://api.conifer.build/v1", "Conifer"), + ("https://conifer.build/v1", "Conifer"), + ("https://API.CONIFER.BUILD/v1", "Conifer"), + ("https://conifer.build.attacker.example/v1", "conifer.build.attacker.example"), +]) +def test_provider_label_conifer(url, expected): + assert _provider_label(url) == expected