feat(search): add ollama-search and zai-search with credential fallback
Register two web search providers that reuse an existing chat provider's API key instead of requiring their own connection: - ollama-search (POST ollama.com/api/web_search) reuses the `ollama` key - zai-search (POST api.z.ai MCP web_search_prime) reuses the `glm` key A new `credentialFallback` registry field drives this: when a search provider has no connection of its own, the search handler falls back to the linked chat provider's credentials. Also teach getProviderIconSrc to serve .svg logos for providers that ship vector art.
This commit is contained in:
@@ -5,7 +5,7 @@ import { RISK_NOTICE } from "@/shared/constants/providersDisplay";
|
||||
const MEDIA_ENTRY_KEYS = [
|
||||
"serviceKinds", "ttsConfig", "sttConfig", "embeddingConfig",
|
||||
"imageConfig", "imageToTextConfig", "videoConfig", "musicConfig",
|
||||
"searchViaChat", "searchConfig", "fetchConfig",
|
||||
"searchViaChat", "searchConfig", "fetchConfig", "credentialFallback",
|
||||
"modelsFetcher", "mediaPriority", "hiddenKinds",
|
||||
];
|
||||
|
||||
|
||||
@@ -5,8 +5,12 @@ const ICON_ALIASES = {
|
||||
"perplexity-agent": "perplexity",
|
||||
"gitlab-duo": "gitlab",
|
||||
"vercel-ai-gateway": "vercel",
|
||||
"ollama-search": "ollama",
|
||||
};
|
||||
|
||||
// Providers that ship an .svg logo instead of .png.
|
||||
const SVG_PROVIDER_IDS = new Set(["zai-search"]);
|
||||
|
||||
// Runtime only — first 404 remembers id for the whole session
|
||||
const failedIds = new Set();
|
||||
|
||||
@@ -25,10 +29,13 @@ export function resolveProviderIconId(providerId) {
|
||||
return aliased;
|
||||
}
|
||||
|
||||
/** `/providers/{id}.png` or null when previously failed. */
|
||||
/** `/providers/{id}.{png|svg}` or null when previously failed. */
|
||||
export function getProviderIconSrc(providerId) {
|
||||
const id = resolveProviderIconId(providerId);
|
||||
return id ? `/providers/${id}.png` : null;
|
||||
if (!id) return null;
|
||||
// svg for vector logos, png for everything else
|
||||
const ext = SVG_PROVIDER_IDS.has(id) ? "svg" : "png";
|
||||
return `/providers/${id}.${ext}`;
|
||||
}
|
||||
|
||||
/** Call from img onError so later mounts skip the request. */
|
||||
|
||||
@@ -148,8 +148,23 @@ async function handleSingleProviderSearch(body, providerInput, request, apiKey,
|
||||
let lastError = null;
|
||||
let lastStatus = null;
|
||||
|
||||
// Credential fallback: some search providers reuse the API key of a related
|
||||
// chat provider (e.g. ollama-search reuses the `ollama` chat key, zai-search
|
||||
// reuses the `glm` chat key). When the search provider has no own connection,
|
||||
// fall back to the linked provider's credentials.
|
||||
const fallbackProviderId = resolvedProvider.credentialFallback;
|
||||
|
||||
while (true) {
|
||||
const credentials = await getProviderCredentials(providerId, excludeConnectionIds);
|
||||
let credentials = await getProviderCredentials(providerId, excludeConnectionIds);
|
||||
|
||||
// Fall back to the related chat provider's credentials when this search
|
||||
// provider has none of its own (one key, chat + search).
|
||||
if (!credentials && fallbackProviderId) {
|
||||
credentials = await getProviderCredentials(fallbackProviderId, excludeConnectionIds);
|
||||
if (credentials) {
|
||||
log.info("AUTH", `\x1b[32m${providerId} reusing ${fallbackProviderId} credentials\x1b[0m`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!credentials || credentials.allRateLimited) {
|
||||
if (credentials?.allRateLimited) {
|
||||
|
||||
Reference in New Issue
Block a user