feat(models): define capability schema and readers (#2739)

* feat(models): define capability schema and readers

* fix(models): harden Google catalog probing

Restrict native catalog probing to the Gemini host, keep provider keys out of request URLs, filter non-chat model resources, and preserve the manual refresh default in the built-in Google add flow.
This commit is contained in:
RaresKeY
2026-07-18 10:40:58 +02:00
committed by GitHub
parent f87107e16f
commit 4f04c347cc
17 changed files with 3861 additions and 4 deletions
+122
View File
@@ -48,6 +48,9 @@ with preserve_import_state("core.database", "src.database", "core.session_manage
_ping_endpoint,
_parse_model_list,
_normalize_refresh_mode,
_normalize_endpoint_refresh_mode,
_endpoint_refresh_mode,
_is_google_api_base,
_truthy,
_speech_settings_using_endpoint,
_clear_speech_settings_for_endpoint,
@@ -464,6 +467,28 @@ class TestClassifyEndpoint:
assert _normalize_refresh_mode("manual", "proxy") == "manual"
assert _normalize_refresh_mode("auto", "api") == "auto"
def test_google_refresh_mode_defaults_manual_unless_explicit(self):
base = "https://generativelanguage.googleapis.com/v1beta/openai"
assert _normalize_endpoint_refresh_mode("", "api", base) == "manual"
assert _normalize_endpoint_refresh_mode(None, "auto", base) == "manual"
assert _normalize_endpoint_refresh_mode("auto", "api", base) == "auto"
def test_only_gemini_native_host_uses_google_models_api(self):
assert _is_google_api_base("https://generativelanguage.googleapis.com/v1beta/openai") is True
assert _is_google_api_base(
"https://us-central1-aiplatform.googleapis.com/v1/projects/p/locations/us-central1/endpoints/openapi"
) is False
def test_existing_google_endpoint_refresh_mode_defaults_manual(self):
ep = SimpleNamespace(
model_refresh_mode=None,
endpoint_kind="api",
base_url="https://generativelanguage.googleapis.com/v1beta/openai",
)
assert _endpoint_refresh_mode(ep, "api") == "manual"
ep.model_refresh_mode = "auto"
assert _endpoint_refresh_mode(ep, "api") == "auto"
def test_parse_model_list_accepts_json_and_text(self):
assert _parse_model_list('["a", "b", "a"]') == ["a", "b"]
assert _parse_model_list("a, b\nc") == ["a", "b", "c"]
@@ -568,6 +593,83 @@ class TestSetupProbeSafety:
assert _probe_endpoint("https://api.groq.com/openai/v1") == _PROVIDER_CURATED["groq"]
def test_google_probe_uses_native_paginated_models_api(self, monkeypatch):
monkeypatch.setattr(endpoint_resolver, "resolve_url", lambda url: url, raising=False)
monkeypatch.setattr(model_routes, "_normalize_base", lambda url: url.rstrip("/"))
seen = []
def fake_get(url, headers=None, params=None, timeout=None, verify=None, **kwargs):
seen.append((url, headers, params, timeout, verify))
request = httpx.Request("GET", url)
page_token = (params or {}).get("pageToken")
if page_token:
return httpx.Response(
200,
request=request,
json={
"models": [{
"name": "models/gemini-page-two",
"supportedGenerationMethods": ["generateContent"],
}]
},
)
return httpx.Response(
200,
request=request,
json={
"models": [
{
"name": "models/gemini-page-one",
"supportedGenerationMethods": ["generateContent"],
},
{
"baseModelId": "gemini-base-id",
"name": "models/ignored-version",
"supportedGenerationMethods": ["generateText"],
},
{
"name": "models/imagen-4.0-generate-001",
"supportedGenerationMethods": ["predict"],
},
{
"name": "models/text-embedding-example",
"supportedGenerationMethods": ["embedContent"],
},
{"name": "models/missing-method-metadata"},
],
"nextPageToken": "next-page",
},
)
monkeypatch.setattr(model_routes.httpx, "get", fake_get)
assert _probe_endpoint("https://generativelanguage.googleapis.com/v1beta/openai", "google-key") == [
"gemini-page-one",
"gemini-base-id",
"gemini-page-two",
]
assert [call[0] for call in seen] == [
"https://generativelanguage.googleapis.com/v1beta/models",
"https://generativelanguage.googleapis.com/v1beta/models",
]
assert seen[0][1] == {"Accept": "application/json", "x-goog-api-key": "google-key"}
assert seen[0][2] == {"pageSize": 1000}
assert seen[1][1] == {"Accept": "application/json", "x-goog-api-key": "google-key"}
assert seen[1][2] == {"pageSize": 1000, "pageToken": "next-page"}
def test_google_probe_does_not_use_curated_fallback_on_failure(self, monkeypatch):
monkeypatch.setattr(endpoint_resolver, "resolve_url", lambda url: url, raising=False)
monkeypatch.setattr(model_routes, "_normalize_base", lambda url: url.rstrip("/"))
def fake_get(url, headers=None, params=None, timeout=None, verify=None, **kwargs):
request = httpx.Request("GET", url)
response = httpx.Response(401, request=request)
raise httpx.HTTPStatusError("unauthorized", request=request, response=response)
monkeypatch.setattr(model_routes.httpx, "get", fake_get)
assert _probe_endpoint("https://generativelanguage.googleapis.com/v1beta/openai", "bad-key") == []
def test_keyed_anthropic_probe_does_not_fallback_on_failure(self, monkeypatch):
monkeypatch.setattr(endpoint_resolver, "resolve_url", lambda url: url, raising=False)
monkeypatch.setattr(model_routes, "_normalize_base", lambda url: url.rstrip("/"))
@@ -1101,6 +1203,26 @@ def test_post_creates_endpoint_with_pinned_models(monkeypatch):
assert json.loads(db.added[0].pinned_models) == ["deploy-1", "deploy-2"]
def test_post_google_endpoint_defaults_to_manual_refresh_when_mode_omitted(monkeypatch):
db = _PinnedFakeDb([])
_patch_create_deps(monkeypatch, db)
monkeypatch.setattr(model_routes, "_probe_endpoint", lambda *args, **kwargs: ["gemini-test"])
create = _get_route("/api/model-endpoints", "POST")
create(
_PinnedFakeRequest(),
base_url="https://generativelanguage.googleapis.com/v1beta/openai",
**_create_form_kwargs(
api_key="google-key",
endpoint_kind="api",
model_refresh_mode="",
),
)
assert len(db.added) == 1
assert db.added[0].model_refresh_mode == "manual"
def test_post_dedupe_existing_merges_and_returns_pinned(monkeypatch):
existing = _make_endpoint(
base_url="http://host:1234/v1",