mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-19 13:38:04 +00:00
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:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user