mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-22 07:02:16 +03:00
fix(search): skip catalog-default SearXNG localhost:8888 (#10981)
⭐5 — Default do catálogo searxng-search (localhost:8888) sempre falha em Docker/K8s, queima o próximo provider (Brave free-tier 429). Agora pula o default não-configurado a menos que baseUrl tenha sido sobrescrito; fallback duckduckgo-free continua disponível. 3/3 testes. Fecha #10976.
This commit is contained in:
1
changelog.d/fixes/10976-skip-default-searxng.md
Normal file
1
changelog.d/fixes/10976-skip-default-searxng.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(search):** skip catalog-default SearXNG `http://localhost:8888/search` so Docker/K8s search does not ECONNREFUSED then 502 into the next provider ([#10976](https://github.com/diegosouzapw/OmniRoute/issues/10976))
|
||||
@@ -327,6 +327,24 @@ export function resolveSearchProviderId(providerId: string): string {
|
||||
* Request routing should use resolveSearchProvider() so aliases work
|
||||
* without colliding with the Foundation jina-ai provider id.
|
||||
*/
|
||||
const CATALOG_SEARXNG_DEFAULT_URL = "http://localhost:8888/search";
|
||||
|
||||
/**
|
||||
* Catalog default SearXNG URL is a desktop convenience. In Docker/K8s nothing
|
||||
* listens on :8888, and OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS (needed for
|
||||
* ClusterIP providers) lets ProxyFetch attempt it, producing ECONNREFUSED and
|
||||
* a 502 that then burns the next fallback's quota. Skip unless the operator
|
||||
* overrode baseUrl.
|
||||
*/
|
||||
export function isUnconfiguredLoopbackSearchProvider(
|
||||
provider: SearchProviderConfig | null | undefined
|
||||
): boolean {
|
||||
if (!provider || provider.id !== "searxng-search") return false;
|
||||
const configured = String(provider.baseUrl || "").replace(/\/+$/, "");
|
||||
const catalog = CATALOG_SEARXNG_DEFAULT_URL.replace(/\/+$/, "");
|
||||
return configured === catalog;
|
||||
}
|
||||
|
||||
export function getSearchProvider(providerId: string): SearchProviderConfig | null {
|
||||
return SEARCH_PROVIDERS[providerId] || null;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ import { randomUUID } from "crypto";
|
||||
* }
|
||||
*/
|
||||
|
||||
import { getSearchProvider, type SearchProviderConfig } from "../config/searchRegistry.ts";
|
||||
import {
|
||||
getSearchProvider,
|
||||
isUnconfiguredLoopbackSearchProvider,
|
||||
type SearchProviderConfig,
|
||||
} from "../config/searchRegistry.ts";
|
||||
import { buildPerplexityRequest, parsePerplexitySearchOptions } from "./search/perplexitySearch.ts";
|
||||
import * as fcSearch from "./search/firecrawlSearch.ts";
|
||||
import { type FirecrawlSearchEnvelope } from "./search/firecrawlSearch.ts";
|
||||
@@ -1309,7 +1313,37 @@ export async function handleSearch(options: SearchHandlerOptions): Promise<Searc
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Try primary provider
|
||||
// 4. Try primary provider (skip catalog-default SearXNG localhost:8888)
|
||||
if (isUnconfiguredLoopbackSearchProvider(primaryConfig)) {
|
||||
if (log) {
|
||||
log.warn(
|
||||
"SEARCH",
|
||||
"skipping catalog-default searxng-search at http://localhost:8888/search; set a real SearXNG URL"
|
||||
);
|
||||
}
|
||||
if (
|
||||
alternateConfig &&
|
||||
alternateCredentials &&
|
||||
!isUnconfiguredLoopbackSearchProvider(alternateConfig)
|
||||
) {
|
||||
return tryProvider(
|
||||
alternateConfig,
|
||||
requestParams,
|
||||
alternateCredentials,
|
||||
startTime,
|
||||
log,
|
||||
alternateCredentials?.connectionId,
|
||||
apiKeyId
|
||||
);
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
status: 503,
|
||||
error:
|
||||
"SearXNG is still on the catalog default http://localhost:8888/search. Configure a real SearXNG URL or disable the provider.",
|
||||
};
|
||||
}
|
||||
|
||||
const result = await tryProvider(
|
||||
primaryConfig,
|
||||
requestParams,
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
resolveSearchProvider,
|
||||
selectProvider,
|
||||
supportsSearchType,
|
||||
isUnconfiguredLoopbackSearchProvider,
|
||||
SEARCH_PROVIDERS,
|
||||
SEARCH_CREDENTIAL_FALLBACKS,
|
||||
} from "@omniroute/open-sse/config/searchRegistry.ts";
|
||||
@@ -286,6 +287,7 @@ async function postHandler(request: Request, context: unknown) {
|
||||
if (!alternateProviderId) {
|
||||
for (const provider of Object.values(SEARCH_PROVIDERS)) {
|
||||
if (!provider.fallbackOnly || provider.id === providerConfig.id) continue;
|
||||
if (isUnconfiguredLoopbackSearchProvider(provider)) continue;
|
||||
if (!supportsSearchType(provider, body.search_type)) continue;
|
||||
const fallbackCreds = await resolveSearchExecutionCredentials(provider);
|
||||
if (fallbackCreds && !isAllRateLimitedCredentials(fallbackCreds)) {
|
||||
|
||||
30
tests/unit/searxng-loopback-default.test.ts
Normal file
30
tests/unit/searxng-loopback-default.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
SEARCH_PROVIDERS,
|
||||
isUnconfiguredLoopbackSearchProvider,
|
||||
} from "../../open-sse/config/searchRegistry.ts";
|
||||
|
||||
test("catalog searxng-search default is an unconfigured loopback URL", () => {
|
||||
const searxng = SEARCH_PROVIDERS["searxng-search"];
|
||||
assert.ok(searxng);
|
||||
assert.equal(isUnconfiguredLoopbackSearchProvider(searxng), true);
|
||||
});
|
||||
|
||||
test("overridden SearXNG URL is not skipped", () => {
|
||||
const searxng = SEARCH_PROVIDERS["searxng-search"];
|
||||
assert.equal(
|
||||
isUnconfiguredLoopbackSearchProvider({
|
||||
...searxng,
|
||||
baseUrl: "http://searxng.inference.svc/search",
|
||||
}),
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
test("other fallback providers are not treated as unconfigured loopback", () => {
|
||||
assert.equal(isUnconfiguredLoopbackSearchProvider(SEARCH_PROVIDERS["duckduckgo-free"]), false);
|
||||
assert.equal(isUnconfiguredLoopbackSearchProvider(SEARCH_PROVIDERS["brave-search"]), false);
|
||||
assert.equal(isUnconfiguredLoopbackSearchProvider(null), false);
|
||||
});
|
||||
Reference in New Issue
Block a user