WebSearch.svelte 9.5 KB

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