diff --git a/src/routes/config.ts b/src/routes/config.ts index 18b370d..c4fe167 100644 --- a/src/routes/config.ts +++ b/src/routes/config.ts @@ -13,6 +13,16 @@ import { getCustomOpenaiApiUrl, getCustomOpenaiApiKey, getCustomOpenaiModelName, + getSearchEngineBackend, + getImageSearchEngineBackend, + getVideoSearchEngineBackend, + getNewsSearchEngineBackend, + getSearxngApiEndpoint, + getGoogleApiKey, + getGoogleCseId, + getBingSubscriptionKey, + getBraveApiKey, + getYacyJsonEndpoint, } from '../config'; import logger from '../utils/logger'; @@ -60,6 +70,21 @@ router.get('/', async (_, res) => { config['customOpenaiApiUrl'] = getCustomOpenaiApiUrl(); config['customOpenaiApiKey'] = getCustomOpenaiApiKey(); config['customOpenaiModelName'] = getCustomOpenaiModelName(); + + // Add search engine configuration + config['searchEngineBackends'] = { + search: getSearchEngineBackend(), + image: getImageSearchEngineBackend(), + video: getVideoSearchEngineBackend(), + news: getNewsSearchEngineBackend(), + }; + + config['searxngEndpoint'] = getSearxngApiEndpoint(); + config['googleApiKey'] = getGoogleApiKey(); + config['googleCseId'] = getGoogleCseId(); + config['bingSubscriptionKey'] = getBingSubscriptionKey(); + config['braveApiKey'] = getBraveApiKey(); + config['yacyEndpoint'] = getYacyJsonEndpoint(); res.status(200).json(config); } catch (err: any) { @@ -94,6 +119,30 @@ router.post('/', async (req, res) => { MODEL_NAME: config.customOpenaiModelName, }, }, + SEARCH_ENGINE_BACKENDS: config.searchEngineBackends ? { + SEARCH: config.searchEngineBackends.search, + IMAGE: config.searchEngineBackends.image, + VIDEO: config.searchEngineBackends.video, + NEWS: config.searchEngineBackends.news, + } : undefined, + SEARCH_ENGINES: { + GOOGLE: { + API_KEY: config.googleApiKey, + CSE_ID: config.googleCseId, + }, + SEARXNG: { + ENDPOINT: config.searxngEndpoint, + }, + BING: { + SUBSCRIPTION_KEY: config.bingSubscriptionKey, + }, + BRAVE: { + API_KEY: config.braveApiKey, + }, + YACY: { + ENDPOINT: config.yacyEndpoint, + }, + }, }; updateConfig(updatedConfig); diff --git a/ui/app/settings/page.tsx b/ui/app/settings/page.tsx index 6aff1b0..ea8a912 100644 --- a/ui/app/settings/page.tsx +++ b/ui/app/settings/page.tsx @@ -23,6 +23,18 @@ interface SettingsType { customOpenaiApiKey: string; customOpenaiApiUrl: string; customOpenaiModelName: string; + searchEngineBackends: { + search: string; + image: string; + video: string; + news: string; + }; + searxngEndpoint: string; + googleApiKey: string; + googleCseId: string; + bingSubscriptionKey: string; + braveApiKey: string; + yacyEndpoint: string; } interface InputProps extends React.InputHTMLAttributes { @@ -112,6 +124,12 @@ const Page = () => { const [automaticImageSearch, setAutomaticImageSearch] = useState(false); const [automaticVideoSearch, setAutomaticVideoSearch] = useState(false); const [savingStates, setSavingStates] = useState>({}); + const [searchEngineBackends, setSearchEngineBackends] = useState({ + search: '', + image: '', + video: '', + news: '', + }); useEffect(() => { const fetchConfig = async () => { @@ -125,6 +143,16 @@ const Page = () => { const data = (await res.json()) as SettingsType; setConfig(data); + // Set search engine backends if they exist in the response + if (data.searchEngineBackends) { + setSearchEngineBackends({ + search: data.searchEngineBackends.search || '', + image: data.searchEngineBackends.image || '', + video: data.searchEngineBackends.video || '', + news: data.searchEngineBackends.news || '', + }); + } + const chatModelProvidersKeys = Object.keys(data.chatModelProviders || {}); const embeddingModelProvidersKeys = Object.keys( data.embeddingModelProviders || {}, @@ -331,6 +359,8 @@ const Page = () => { localStorage.setItem('embeddingModelProvider', value); } else if (key === 'embeddingModel') { localStorage.setItem('embeddingModel', value); + } else if (key === 'searchEngineBackends') { + localStorage.setItem('searchEngineBackends', value); } } catch (err) { console.error('Failed to save:', err); @@ -793,6 +823,234 @@ const Page = () => { + + +
+
+

+ Default Search Engine +

+ { + const value = e.target.value; + setSearchEngineBackends((prev) => ({ + ...prev, + image: value, + })); + saveConfig('searchEngineBackends', { + ...searchEngineBackends, + image: value, + }); + }} + options={[ + { value: '', label: 'Use Default Search Engine' }, + { value: 'searxng', label: 'SearXNG' }, + { value: 'google', label: 'Google' }, + { value: 'bing', label: 'Bing' }, + { value: 'brave', label: 'Brave' }, + ]} + /> +
+ +
+

+ Video Search Engine +

+ { + const value = e.target.value; + setSearchEngineBackends((prev) => ({ + ...prev, + news: value, + })); + saveConfig('searchEngineBackends', { + ...searchEngineBackends, + news: value, + }); + }} + options={[ + { value: '', label: 'Use Default Search Engine' }, + { value: 'searxng', label: 'SearXNG' }, + { value: 'google', label: 'Google' }, + { value: 'bing', label: 'Bing' }, + { value: 'brave', label: 'Brave' }, + ]} + /> +
+ +
+
+

+ SearXNG Endpoint +

+ { + setConfig((prev) => ({ + ...prev!, + searxngEndpoint: e.target.value, + })); + }} + onSave={(value) => saveConfig('searxngEndpoint', value)} + /> +
+
+ +
+

+ Google API Key +

+ { + setConfig((prev) => ({ + ...prev!, + googleApiKey: e.target.value, + })); + }} + onSave={(value) => saveConfig('googleApiKey', value)} + /> +
+ +
+

+ Google CSE ID +

+ { + setConfig((prev) => ({ + ...prev!, + googleCseId: e.target.value, + })); + }} + onSave={(value) => saveConfig('googleCseId', value)} + /> +
+ +
+

+ Bing Subscription Key +

+ { + setConfig((prev) => ({ + ...prev!, + bingSubscriptionKey: e.target.value, + })); + }} + onSave={(value) => saveConfig('bingSubscriptionKey', value)} + /> +
+ +
+

+ Brave API Key +

+ { + setConfig((prev) => ({ + ...prev!, + braveApiKey: e.target.value, + })); + }} + onSave={(value) => saveConfig('braveApiKey', value)} + /> +
+ +
+

+ YaCy Endpoint +

+ { + setConfig((prev) => ({ + ...prev!, + yacyEndpoint: e.target.value, + })); + }} + onSave={(value) => saveConfig('yacyEndpoint', value)} + /> +
+
+
) )}