1
0

WebSearch.svelte 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <script lang="ts">
  2. import { getRAGConfig, updateRAGConfig } from '$lib/apis/retrieval';
  3. import Switch from '$lib/components/common/Switch.svelte';
  4. import { models } from '$lib/stores';
  5. import { onMount, getContext } from 'svelte';
  6. import { toast } from 'svelte-sonner';
  7. import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
  8. const i18n = getContext('i18n');
  9. export let saveHandler: Function;
  10. let webConfig = null;
  11. let webSearchEngines = [
  12. 'searxng',
  13. 'google_pse',
  14. 'brave',
  15. 'mojeek',
  16. 'serpstack',
  17. 'serper',
  18. 'serply',
  19. 'searchapi',
  20. 'duckduckgo',
  21. 'tavily',
  22. 'jina',
  23. 'bing'
  24. ];
  25. let youtubeLanguage = 'en';
  26. let youtubeTranslation = null;
  27. const submitHandler = async () => {
  28. const res = await updateRAGConfig(localStorage.token, {
  29. web: webConfig,
  30. youtube: {
  31. language: youtubeLanguage.split(',').map((lang) => lang.trim()),
  32. translation: youtubeTranslation
  33. }
  34. });
  35. };
  36. onMount(async () => {
  37. const res = await getRAGConfig(localStorage.token);
  38. if (res) {
  39. webConfig = res.web;
  40. youtubeLanguage = res.youtube.language.join(',');
  41. youtubeTranslation = res.youtube.translation;
  42. }
  43. });
  44. </script>
  45. <form
  46. class="flex flex-col h-full justify-between space-y-3 text-sm"
  47. on:submit|preventDefault={async () => {
  48. await submitHandler();
  49. saveHandler();
  50. }}
  51. >
  52. <div class=" space-y-3 overflow-y-scroll scrollbar-hidden h-full">
  53. {#if webConfig}
  54. <div>
  55. <div class=" mb-1 text-sm font-medium">
  56. {$i18n.t('Web Search')}
  57. </div>
  58. <div>
  59. <div class=" py-0.5 flex w-full justify-between">
  60. <div class=" self-center text-xs font-medium">
  61. {$i18n.t('Enable Web Search')}
  62. </div>
  63. <Switch bind:state={webConfig.search.enabled} />
  64. </div>
  65. </div>
  66. <div class=" py-0.5 flex w-full justify-between">
  67. <div class=" self-center text-xs font-medium">{$i18n.t('Web Search Engine')}</div>
  68. <div class="flex items-center relative">
  69. <select
  70. class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
  71. bind:value={webConfig.search.engine}
  72. placeholder={$i18n.t('Select a engine')}
  73. required
  74. >
  75. <option disabled selected value="">{$i18n.t('Select a engine')}</option>
  76. {#each webSearchEngines as engine}
  77. <option value={engine}>{engine}</option>
  78. {/each}
  79. </select>
  80. </div>
  81. </div>
  82. {#if webConfig.search.engine !== ''}
  83. <div class="mt-1.5">
  84. {#if webConfig.search.engine === 'searxng'}
  85. <div>
  86. <div class=" self-center text-xs font-medium mb-1">
  87. {$i18n.t('Searxng Query URL')}
  88. </div>
  89. <div class="flex w-full">
  90. <div class="flex-1">
  91. <input
  92. class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
  93. type="text"
  94. placeholder={$i18n.t('Enter Searxng Query URL')}
  95. bind:value={webConfig.search.searxng_query_url}
  96. autocomplete="off"
  97. />
  98. </div>
  99. </div>
  100. </div>
  101. {:else if webConfig.search.engine === 'google_pse'}
  102. <div>
  103. <div class=" self-center text-xs font-medium mb-1">
  104. {$i18n.t('Google PSE API Key')}
  105. </div>
  106. <SensitiveInput
  107. placeholder={$i18n.t('Enter Google PSE API Key')}
  108. bind:value={webConfig.search.google_pse_api_key}
  109. />
  110. </div>
  111. <div class="mt-1.5">
  112. <div class=" self-center text-xs font-medium mb-1">
  113. {$i18n.t('Google PSE Engine Id')}
  114. </div>
  115. <div class="flex w-full">
  116. <div class="flex-1">
  117. <input
  118. class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
  119. type="text"
  120. placeholder={$i18n.t('Enter Google PSE Engine Id')}
  121. bind:value={webConfig.search.google_pse_engine_id}
  122. autocomplete="off"
  123. />
  124. </div>
  125. </div>
  126. </div>
  127. {:else if webConfig.search.engine === 'brave'}
  128. <div>
  129. <div class=" self-center text-xs font-medium mb-1">
  130. {$i18n.t('Brave Search API Key')}
  131. </div>
  132. <SensitiveInput
  133. placeholder={$i18n.t('Enter Brave Search API Key')}
  134. bind:value={webConfig.search.brave_search_api_key}
  135. />
  136. </div>
  137. {:else if webConfig.search.engine === 'mojeek'}
  138. <div>
  139. <div class=" self-center text-xs font-medium mb-1">
  140. {$i18n.t('Mojeek Search API Key')}
  141. </div>
  142. <SensitiveInput
  143. placeholder={$i18n.t('Enter Mojeek Search API Key')}
  144. bind:value={webConfig.search.mojeek_search_api_key}
  145. />
  146. </div>
  147. {:else if webConfig.search.engine === 'serpstack'}
  148. <div>
  149. <div class=" self-center text-xs font-medium mb-1">
  150. {$i18n.t('Serpstack API Key')}
  151. </div>
  152. <SensitiveInput
  153. placeholder={$i18n.t('Enter Serpstack API Key')}
  154. bind:value={webConfig.search.serpstack_api_key}
  155. />
  156. </div>
  157. {:else if webConfig.search.engine === 'serper'}
  158. <div>
  159. <div class=" self-center text-xs font-medium mb-1">
  160. {$i18n.t('Serper API Key')}
  161. </div>
  162. <SensitiveInput
  163. placeholder={$i18n.t('Enter Serper API Key')}
  164. bind:value={webConfig.search.serper_api_key}
  165. />
  166. </div>
  167. {:else if webConfig.search.engine === 'serply'}
  168. <div>
  169. <div class=" self-center text-xs font-medium mb-1">
  170. {$i18n.t('Serply API Key')}
  171. </div>
  172. <SensitiveInput
  173. placeholder={$i18n.t('Enter Serply API Key')}
  174. bind:value={webConfig.search.serply_api_key}
  175. />
  176. </div>
  177. {:else if webConfig.search.engine === 'searchapi'}
  178. <div>
  179. <div class=" self-center text-xs font-medium mb-1">
  180. {$i18n.t('SearchApi API Key')}
  181. </div>
  182. <SensitiveInput
  183. placeholder={$i18n.t('Enter SearchApi API Key')}
  184. bind:value={webConfig.search.searchapi_api_key}
  185. />
  186. </div>
  187. <div class="mt-1.5">
  188. <div class=" self-center text-xs font-medium mb-1">
  189. {$i18n.t('SearchApi Engine')}
  190. </div>
  191. <div class="flex w-full">
  192. <div class="flex-1">
  193. <input
  194. class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
  195. type="text"
  196. placeholder={$i18n.t('Enter SearchApi Engine')}
  197. bind:value={webConfig.search.searchapi_engine}
  198. autocomplete="off"
  199. />
  200. </div>
  201. </div>
  202. </div>
  203. {:else if webConfig.search.engine === 'tavily'}
  204. <div>
  205. <div class=" self-center text-xs font-medium mb-1">
  206. {$i18n.t('Tavily API Key')}
  207. </div>
  208. <SensitiveInput
  209. placeholder={$i18n.t('Enter Tavily API Key')}
  210. bind:value={webConfig.search.tavily_api_key}
  211. />
  212. </div>
  213. {:else if webConfig.search.engine === 'jina'}
  214. <div>
  215. <div class=" self-center text-xs font-medium mb-1">
  216. {$i18n.t('Jina API Key')}
  217. </div>
  218. <SensitiveInput
  219. placeholder={$i18n.t('Enter Jina API Key')}
  220. bind:value={webConfig.search.jina_api_key}
  221. />
  222. </div>
  223. {:else if webConfig.search.engine === 'bing'}
  224. <div>
  225. <div class=" self-center text-xs font-medium mb-1">
  226. {$i18n.t('Bing Search V7 Endpoint')}
  227. </div>
  228. <div class="flex w-full">
  229. <div class="flex-1">
  230. <input
  231. class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
  232. type="text"
  233. placeholder={$i18n.t('Enter Bing Search V7 Endpoint')}
  234. bind:value={webConfig.search.bing_search_v7_endpoint}
  235. autocomplete="off"
  236. />
  237. </div>
  238. </div>
  239. </div>
  240. <div class="mt-2">
  241. <div class=" self-center text-xs font-medium mb-1">
  242. {$i18n.t('Bing Search V7 Subscription Key')}
  243. </div>
  244. <SensitiveInput
  245. placeholder={$i18n.t('Enter Bing Search V7 Subscription Key')}
  246. bind:value={webConfig.search.bing_search_v7_subscription_key}
  247. />
  248. </div>
  249. {/if}
  250. </div>
  251. {/if}
  252. {#if webConfig.search.enabled}
  253. <div class="mt-2 flex gap-2 mb-1">
  254. <div class="w-full">
  255. <div class=" self-center text-xs font-medium mb-1">
  256. {$i18n.t('Search Result Count')}
  257. </div>
  258. <input
  259. class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
  260. placeholder={$i18n.t('Search Result Count')}
  261. bind:value={webConfig.search.result_count}
  262. required
  263. />
  264. </div>
  265. <div class="w-full">
  266. <div class=" self-center text-xs font-medium mb-1">
  267. {$i18n.t('Concurrent Requests')}
  268. </div>
  269. <input
  270. class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
  271. placeholder={$i18n.t('Concurrent Requests')}
  272. bind:value={webConfig.search.concurrent_requests}
  273. required
  274. />
  275. </div>
  276. </div>
  277. {/if}
  278. </div>
  279. <hr class=" dark:border-gray-850 my-2" />
  280. <div>
  281. <div class=" mb-1 text-sm font-medium">
  282. {$i18n.t('Web Loader Settings')}
  283. </div>
  284. <div>
  285. <div class=" py-0.5 flex w-full justify-between">
  286. <div class=" self-center text-xs font-medium">
  287. {$i18n.t('Bypass SSL verification for Websites')}
  288. </div>
  289. <button
  290. class="p-1 px-3 text-xs flex rounded transition"
  291. on:click={() => {
  292. webConfig.web_loader_ssl_verification = !webConfig.web_loader_ssl_verification;
  293. submitHandler();
  294. }}
  295. type="button"
  296. >
  297. {#if webConfig.web_loader_ssl_verification === false}
  298. <span class="ml-2 self-center">{$i18n.t('On')}</span>
  299. {:else}
  300. <span class="ml-2 self-center">{$i18n.t('Off')}</span>
  301. {/if}
  302. </button>
  303. </div>
  304. </div>
  305. <div class=" mt-2 mb-1 text-sm font-medium">
  306. {$i18n.t('Youtube Loader Settings')}
  307. </div>
  308. <div>
  309. <div class=" py-0.5 flex w-full justify-between">
  310. <div class=" w-20 text-xs font-medium self-center">{$i18n.t('Language')}</div>
  311. <div class=" flex-1 self-center">
  312. <input
  313. class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
  314. type="text"
  315. placeholder={$i18n.t('Enter language codes')}
  316. bind:value={youtubeLanguage}
  317. autocomplete="off"
  318. />
  319. </div>
  320. </div>
  321. </div>
  322. </div>
  323. {/if}
  324. </div>
  325. <div class="flex justify-end pt-3 text-sm font-medium">
  326. <button
  327. class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full"
  328. type="submit"
  329. >
  330. {$i18n.t('Save')}
  331. </button>
  332. </div>
  333. </form>