General.svelte 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <script lang="ts">
  2. import { getDocs } from '$lib/apis/documents';
  3. import {
  4. getRAGConfig,
  5. updateRAGConfig,
  6. getQuerySettings,
  7. scanDocs,
  8. updateQuerySettings,
  9. resetVectorDB
  10. } from '$lib/apis/rag';
  11. import { documents } from '$lib/stores';
  12. import { onMount, getContext } from 'svelte';
  13. import { toast } from 'svelte-sonner';
  14. const i18n = getContext('i18n');
  15. export let saveHandler: Function;
  16. let loading = false;
  17. let showResetConfirm = false;
  18. let chunkSize = 0;
  19. let chunkOverlap = 0;
  20. let pdfExtractImages = true;
  21. let querySettings = {
  22. template: '',
  23. k: 4
  24. };
  25. const scanHandler = async () => {
  26. loading = true;
  27. const res = await scanDocs(localStorage.token);
  28. loading = false;
  29. if (res) {
  30. await documents.set(await getDocs(localStorage.token));
  31. toast.success($i18n.t('Scan complete!'));
  32. }
  33. };
  34. const submitHandler = async () => {
  35. const res = await updateRAGConfig(localStorage.token, {
  36. pdf_extract_images: pdfExtractImages,
  37. chunk: {
  38. chunk_overlap: chunkOverlap,
  39. chunk_size: chunkSize
  40. }
  41. });
  42. querySettings = await updateQuerySettings(localStorage.token, querySettings);
  43. };
  44. onMount(async () => {
  45. const res = await getRAGConfig(localStorage.token);
  46. if (res) {
  47. pdfExtractImages = res.pdf_extract_images;
  48. chunkSize = res.chunk.chunk_size;
  49. chunkOverlap = res.chunk.chunk_overlap;
  50. }
  51. querySettings = await getQuerySettings(localStorage.token);
  52. });
  53. </script>
  54. <form
  55. class="flex flex-col h-full justify-between space-y-3 text-sm"
  56. on:submit|preventDefault={() => {
  57. submitHandler();
  58. saveHandler();
  59. }}
  60. >
  61. <div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
  62. <div>
  63. <div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
  64. <div class=" flex w-full justify-between">
  65. <div class=" self-center text-xs font-medium">
  66. {$i18n.t('Scan for documents from {{path}}', { path: '/data/docs' })}
  67. </div>
  68. <button
  69. class=" self-center text-xs p-1 px-3 bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded flex flex-row space-x-1 items-center {loading
  70. ? ' cursor-not-allowed'
  71. : ''}"
  72. on:click={() => {
  73. scanHandler();
  74. console.log('check');
  75. }}
  76. type="button"
  77. disabled={loading}
  78. >
  79. <div class="self-center font-medium">{$i18n.t('Scan')}</div>
  80. <!-- <svg
  81. xmlns="http://www.w3.org/2000/svg"
  82. viewBox="0 0 16 16"
  83. fill="currentColor"
  84. class="w-3 h-3"
  85. >
  86. <path
  87. fill-rule="evenodd"
  88. d="M13.836 2.477a.75.75 0 0 1 .75.75v3.182a.75.75 0 0 1-.75.75h-3.182a.75.75 0 0 1 0-1.5h1.37l-.84-.841a4.5 4.5 0 0 0-7.08.932.75.75 0 0 1-1.3-.75 6 6 0 0 1 9.44-1.242l.842.84V3.227a.75.75 0 0 1 .75-.75Zm-.911 7.5A.75.75 0 0 1 13.199 11a6 6 0 0 1-9.44 1.241l-.84-.84v1.371a.75.75 0 0 1-1.5 0V9.591a.75.75 0 0 1 .75-.75H5.35a.75.75 0 0 1 0 1.5H3.98l.841.841a4.5 4.5 0 0 0 7.08-.932.75.75 0 0 1 1.025-.273Z"
  89. clip-rule="evenodd"
  90. />
  91. </svg> -->
  92. {#if loading}
  93. <div class="ml-3 self-center">
  94. <svg
  95. class=" w-3 h-3"
  96. viewBox="0 0 24 24"
  97. fill="currentColor"
  98. xmlns="http://www.w3.org/2000/svg"
  99. ><style>
  100. .spinner_ajPY {
  101. transform-origin: center;
  102. animation: spinner_AtaB 0.75s infinite linear;
  103. }
  104. @keyframes spinner_AtaB {
  105. 100% {
  106. transform: rotate(360deg);
  107. }
  108. }
  109. </style><path
  110. d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
  111. opacity=".25"
  112. /><path
  113. d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
  114. class="spinner_ajPY"
  115. /></svg
  116. >
  117. </div>
  118. {/if}
  119. </button>
  120. </div>
  121. </div>
  122. <hr class=" dark:border-gray-700" />
  123. <div class=" ">
  124. <div class=" text-sm font-medium">{$i18n.t('Chunk Params')}</div>
  125. <div class=" flex">
  126. <div class=" flex w-full justify-between">
  127. <div class="self-center text-xs font-medium min-w-fit">{$i18n.t('Chunk Size')}</div>
  128. <div class="self-center p-3">
  129. <input
  130. class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
  131. type="number"
  132. placeholder={$i18n.t('Enter Chunk Size')}
  133. bind:value={chunkSize}
  134. autocomplete="off"
  135. min="0"
  136. />
  137. </div>
  138. </div>
  139. <div class="flex w-full">
  140. <div class=" self-center text-xs font-medium min-w-fit">{$i18n.t('Chunk Overlap')}</div>
  141. <div class="self-center p-3">
  142. <input
  143. class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
  144. type="number"
  145. placeholder={$i18n.t('Enter Chunk Overlap')}
  146. bind:value={chunkOverlap}
  147. autocomplete="off"
  148. min="0"
  149. />
  150. </div>
  151. </div>
  152. </div>
  153. <div>
  154. <div class="flex justify-between items-center text-xs">
  155. <div class=" text-xs font-medium">{$i18n.t('PDF Extract Images (OCR)')}</div>
  156. <button
  157. class=" text-xs font-medium text-gray-500"
  158. type="button"
  159. on:click={() => {
  160. pdfExtractImages = !pdfExtractImages;
  161. }}>{pdfExtractImages ? $i18n.t('On') : $i18n.t('Off')}</button
  162. >
  163. </div>
  164. </div>
  165. </div>
  166. <div>
  167. <div class=" text-sm font-medium">{$i18n.t('Query Params')}</div>
  168. <div class=" flex">
  169. <div class=" flex w-full justify-between">
  170. <div class="self-center text-xs font-medium flex-1">{$i18n.t('Top K')}</div>
  171. <div class="self-center p-3">
  172. <input
  173. class=" w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
  174. type="number"
  175. placeholder={$i18n.t('Enter Top K')}
  176. bind:value={querySettings.k}
  177. autocomplete="off"
  178. min="0"
  179. />
  180. </div>
  181. </div>
  182. <!-- <div class="flex w-full">
  183. <div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div>
  184. <div class="self-center p-3">
  185. <input
  186. class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
  187. type="number"
  188. placeholder="Enter Chunk Overlap"
  189. bind:value={chunkOverlap}
  190. autocomplete="off"
  191. min="0"
  192. />
  193. </div>
  194. </div> -->
  195. </div>
  196. <div>
  197. <div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
  198. <textarea
  199. bind:value={querySettings.template}
  200. class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
  201. rows="4"
  202. />
  203. </div>
  204. </div>
  205. <hr class=" dark:border-gray-700" />
  206. {#if showResetConfirm}
  207. <div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
  208. <div class="flex items-center space-x-3">
  209. <svg
  210. xmlns="http://www.w3.org/2000/svg"
  211. viewBox="0 0 16 16"
  212. fill="currentColor"
  213. class="w-4 h-4"
  214. >
  215. <path d="M2 3a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3Z" />
  216. <path
  217. fill-rule="evenodd"
  218. d="M13 6H3v6a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V6ZM5.72 7.47a.75.75 0 0 1 1.06 0L8 8.69l1.22-1.22a.75.75 0 1 1 1.06 1.06L9.06 9.75l1.22 1.22a.75.75 0 1 1-1.06 1.06L8 10.81l-1.22 1.22a.75.75 0 0 1-1.06-1.06l1.22-1.22-1.22-1.22a.75.75 0 0 1 0-1.06Z"
  219. clip-rule="evenodd"
  220. />
  221. </svg>
  222. <span>{$i18n.t('Are you sure?')}</span>
  223. </div>
  224. <div class="flex space-x-1.5 items-center">
  225. <button
  226. class="hover:text-white transition"
  227. on:click={() => {
  228. const res = resetVectorDB(localStorage.token).catch((error) => {
  229. toast.error(error);
  230. return null;
  231. });
  232. if (res) {
  233. toast.success($i18n.t('Success'));
  234. }
  235. showResetConfirm = false;
  236. }}
  237. >
  238. <svg
  239. xmlns="http://www.w3.org/2000/svg"
  240. viewBox="0 0 20 20"
  241. fill="currentColor"
  242. class="w-4 h-4"
  243. >
  244. <path
  245. fill-rule="evenodd"
  246. d="M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z"
  247. clip-rule="evenodd"
  248. />
  249. </svg>
  250. </button>
  251. <button
  252. class="hover:text-white transition"
  253. on:click={() => {
  254. showResetConfirm = false;
  255. }}
  256. >
  257. <svg
  258. xmlns="http://www.w3.org/2000/svg"
  259. viewBox="0 0 20 20"
  260. fill="currentColor"
  261. class="w-4 h-4"
  262. >
  263. <path
  264. d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
  265. />
  266. </svg>
  267. </button>
  268. </div>
  269. </div>
  270. {:else}
  271. <button
  272. class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
  273. on:click={() => {
  274. showResetConfirm = true;
  275. }}
  276. >
  277. <div class=" self-center mr-3">
  278. <svg
  279. xmlns="http://www.w3.org/2000/svg"
  280. viewBox="0 0 16 16"
  281. fill="currentColor"
  282. class="w-4 h-4"
  283. >
  284. <path
  285. fill-rule="evenodd"
  286. d="M3.5 2A1.5 1.5 0 0 0 2 3.5v9A1.5 1.5 0 0 0 3.5 14h9a1.5 1.5 0 0 0 1.5-1.5v-7A1.5 1.5 0 0 0 12.5 4H9.621a1.5 1.5 0 0 1-1.06-.44L7.439 2.44A1.5 1.5 0 0 0 6.38 2H3.5Zm6.75 7.75a.75.75 0 0 0 0-1.5h-4.5a.75.75 0 0 0 0 1.5h4.5Z"
  287. clip-rule="evenodd"
  288. />
  289. </svg>
  290. </div>
  291. <div class=" self-center text-sm font-medium">{$i18n.t('Reset Vector Storage')}</div>
  292. </button>
  293. {/if}
  294. </div>
  295. <div class="flex justify-end pt-3 text-sm font-medium">
  296. <button
  297. class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
  298. type="submit"
  299. >
  300. {$i18n.t('Save')}
  301. </button>
  302. </div>
  303. </form>