fix: static model catalog for jules/linkup/ollama/searchapi search providers (#5569, #5571, #5573, #5575) (#5672)

This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-30 12:55:12 -03:00
committed by GitHub
parent 53875bf565
commit bafc65aed3
3 changed files with 46 additions and 0 deletions

View File

@@ -10,6 +10,8 @@
### 🔧 Bug Fixes
- **dashboard (provider add):** non-LLM search/agent providers no longer fail the model-import step with a red `Provider <id> does not support models listing`. **Jules** (Google Labs coding agent), **linkup-search** (Linkup web search), **ollama-search** (Ollama Cloud web search — distinct from the local Ollama LLM), and **searchapi-search** (SearchAPI SERP) have no `/v1/models` endpoint, so the import surfaced a failure for expected behavior. Each now ships a small static catalog of its selectable capability ids — Linkup's `fast`/`standard`/`deep` search depths, SearchAPI's `google`/`bing`/`youtube`/… engines, a single Jules/Ollama-web-search entry — so the import step returns a usable list (`source: local_catalog`) instead of an error. Regression guard: `tests/unit/provider-models-route.test.ts`. ([#5569](https://github.com/diegosouzapw/OmniRoute/issues/5569), [#5571](https://github.com/diegosouzapw/OmniRoute/issues/5571), [#5573](https://github.com/diegosouzapw/OmniRoute/issues/5573), [#5575](https://github.com/diegosouzapw/OmniRoute/issues/5575))
- **dashboard (provider add):** providers without a live key/cookie validator (e.g. **LMArena (Free)**, **PiAPI**) can now be saved. The Add-connection modal treated the backend's `"Provider validation not supported"` response as a hard **Invalid** state and blocked Save entirely, leaving those providers impossible to add. The validate route now returns `unsupported: true` alongside the message, and the modal treats that as a non-blocking warning — the "Check" badge still shows "validation not supported" (informational), but Save persists the credential as-is. Regression guards: `tests/unit/ui/add-api-key-modal-unsupported-save-5565.test.tsx` (Save proceeds) and `tests/unit/providers-validate-route.test.ts` (wire-format). ([#5565](https://github.com/diegosouzapw/OmniRoute/issues/5565), [#5567](https://github.com/diegosouzapw/OmniRoute/issues/5567))
- **providers (codex):** fix the **Codex Responses WebSocket** path (`/v1/responses`), which regressed in v3.8.40 with a client-visible `Invalid JSON body` and bypassed the configured proxy. (1) #5591 — PR #5237 bumped the impersonation TLS profile to `chrome_149`, but `wreq-js@2.3.1` only supports up to `chrome_147`; the unknown profile produced a degenerate fingerprint and ChatGPT rejected the upstream upgrade. The Codex WS path is reverted to the proven `chrome_142` (the v3.8.39 value), and the over-bumped `grok-web`/`claude-web` profiles (masked by their circuit-breaker but silently dropping TLS impersonation) are restored to `chrome_146`. A new regression guard asserts every configured `chrome_*` profile exists in the installed `wreq-js` typings (`tests/unit/tls-profiles-valid-5591.test.mjs`). (2) #5611 — the upstream `wreq-js.websocket()` connect ignored the Proxy Registry, so a no-direct-egress Docker container failed with a DNS error; the prepare route now resolves the Global/provider proxy and threads it through to the WS connect. Regression guard in `tests/unit/responses-ws-proxy.test.mjs`. ([#5591](https://github.com/diegosouzapw/OmniRoute/issues/5591), [#5611](https://github.com/diegosouzapw/OmniRoute/issues/5611))

View File

@@ -71,6 +71,31 @@ const STATIC_MODEL_PROVIDERS: Record<string, () => Array<{ id: string; name: str
name: model.name || model.id,
})),
qoder: () => getStaticQoderModels(),
// Non-LLM providers with no /v1/models endpoint — expose their selectable
// capability ids as a static catalog so the model-import step shows a usable
// list instead of a red "does not support models listing" failure.
jules: () => [
// Google Labs async coding agent — single async session, no model selection.
{ id: "jules", name: "Jules (Google Labs coding agent)" },
],
"linkup-search": () => [
// Linkup web search — the "model" is the search depth (docs.linkup.so #5571).
{ id: "standard", name: "Standard (single-iteration agentic search)" },
{ id: "deep", name: "Deep (multi-iteration search & scrape)" },
{ id: "fast", name: "Fast (sub-second, no LLM)" },
],
"ollama-search": () => [
// ollama.com/api/web_search (cloud web search, not the local Ollama LLM) #5573.
{ id: "web_search", name: "Ollama Web Search" },
],
"searchapi-search": () => [
// SearchAPI (searchapi.io) is a SERP API — the "model" is the engine #5575.
{ id: "google", name: "Google" },
{ id: "bing", name: "Bing" },
{ id: "youtube", name: "YouTube" },
{ id: "google_scholar", name: "Google Scholar" },
{ id: "duckduckgo", name: "DuckDuckGo" },
],
};
export function getStaticModelsForProvider(provider: string): LocalCatalogModel[] | undefined {

View File

@@ -61,6 +61,25 @@ test.after(async () => {
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("provider models route returns a static local catalog for non-LLM search/agent providers (#5569/#5571/#5573/#5575)", async () => {
const cases = [
{ provider: "jules", expectId: "jules" },
{ provider: "linkup-search", expectId: "standard" },
{ provider: "ollama-search", expectId: "web_search" },
{ provider: "searchapi-search", expectId: "google" },
];
for (const { provider, expectId } of cases) {
const connection = await seedConnection(provider, { apiKey: `${provider}-key` });
const response = await callRoute(connection.id);
// RED before the fix: these had no static catalog → 400 "does not support models listing".
assert.equal(response.status, 200, `${provider} should not 400 on model import`);
const body = await response.json();
assert.equal(body.source, "local_catalog", `${provider} should serve a local catalog`);
const ids = (body.models || []).map((m) => m.id);
assert.ok(ids.includes(expectId), `${provider} should list "${expectId}"; got: ${ids.join(", ")}`);
}
});
test("provider models route returns 404 for unknown connections", async () => {
const response = await callRoute("missing-connection");