main
py 98 lines 2.9 KB
Raw
1 import sys
2 import threading
3 import types
4 from pathlib import Path
5
6 from flask import Flask
7
8 PROJECT_ROOT = Path(__file__).resolve().parents[1]
9 if str(PROJECT_ROOT) not in sys.path:
10 sys.path.insert(0, str(PROJECT_ROOT))
11
12 sys.modules.setdefault("giturlparse", types.SimpleNamespace(parse=lambda *args, **kwargs: None))
13 sys.modules.setdefault("whisper", types.SimpleNamespace(load_model=lambda *args, **kwargs: None))
14
15 from plugins._model_config.api.model_search import ModelSearch
16
17
18 def _handler():
19 return ModelSearch(Flask(__name__), threading.Lock())
20
21
22 def test_model_search_parses_openai_style_data():
23 handler = _handler()
24
25 assert handler._parse({"data": [{"id": "gpt-4.1"}, {"id": "gpt-4o-mini"}]}, "openai") == [
26 "gpt-4.1",
27 "gpt-4o-mini",
28 ]
29
30
31 def test_model_search_parses_google_models_and_strips_prefix():
32 handler = _handler()
33
34 assert handler._parse({"models": [{"name": "models/gemini-pro"}]}, "google") == ["gemini-pro"]
35
36
37 def test_model_search_parses_ollama_models():
38 handler = _handler()
39
40 assert handler._parse({"models": [{"name": "llama3.2"}]}, "ollama") == ["llama3.2"]
41
42
43 def test_model_search_builds_ollama_running_models_url():
44 handler = _handler()
45
46 assert handler._ollama_ps_url("http://host.docker.internal:11434/api/tags") == (
47 "http://host.docker.internal:11434/api/ps"
48 )
49
50
51 def test_model_search_parses_list_style_dicts_and_strings():
52 handler = _handler()
53
54 assert handler._parse([{"id": "mistral-large"}, "mistral-small"], "openai") == [
55 "mistral-large",
56 "mistral-small",
57 ]
58
59
60 def test_model_search_resolves_v1_base_without_duplicate_v1():
61 handler = _handler()
62
63 url, fmt = handler._resolve_url({"endpoint_url": "/v1/models"}, "http://host.docker.internal:1234/v1")
64
65 assert url == "http://host.docker.internal:1234/v1/models"
66 assert fmt == "openai"
67
68
69 def test_model_search_omits_auth_header_for_omlx_placeholder_key():
70 handler = _handler()
71
72 assert handler._build_headers("omlx", "omlx", {}) == {}
73
74
75 def test_model_search_omits_auth_header_for_local_placeholder_keys():
76 handler = _handler()
77
78 assert handler._build_headers("llama_cpp", "llama-cpp", {}) == {}
79 assert handler._build_headers("vllm", "vllm", {}) == {}
80 assert handler._build_headers("llama_cpp", "real-local-key", {}) == {
81 "Authorization": "Bearer real-local-key"
82 }
83
84
85 def test_model_search_filters_non_chat_models():
86 handler = _handler()
87
88 assert handler._filter_models(["gpt-4.1", "text-embedding-3-small", "gpt-image-1"], "chat") == ["gpt-4.1"]
89
90
91 def test_model_search_falls_back_to_litellm_registry(monkeypatch):
92 handler = _handler()
93 fake_litellm = types.SimpleNamespace(
94 models_by_provider={"openai": {"openai/gpt-4.1", "text-embedding-3-small"}}
95 )
96 monkeypatch.setitem(sys.modules, "litellm", fake_litellm)
97
98 assert set(handler._litellm_fallback("openai", {"litellm_provider": "openai"})) == {"gpt-4.1"}