Documents.svelte 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431
  1. <script lang="ts">
  2. import { toast } from 'svelte-sonner';
  3. import { onMount, getContext, createEventDispatcher } from 'svelte';
  4. const dispatch = createEventDispatcher();
  5. import {
  6. getQuerySettings,
  7. updateQuerySettings,
  8. resetVectorDB,
  9. getEmbeddingConfig,
  10. updateEmbeddingConfig,
  11. getRerankingConfig,
  12. updateRerankingConfig,
  13. getRAGConfig,
  14. updateRAGConfig
  15. } from '$lib/apis/retrieval';
  16. import { reindexKnowledgeFiles } from '$lib/apis/knowledge';
  17. import { deleteAllFiles } from '$lib/apis/files';
  18. import ResetUploadDirConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
  19. import ResetVectorDBConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
  20. import ReindexKnowledgeFilesConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
  21. import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';
  22. import Tooltip from '$lib/components/common/Tooltip.svelte';
  23. import Switch from '$lib/components/common/Switch.svelte';
  24. import Textarea from '$lib/components/common/Textarea.svelte';
  25. import Spinner from '$lib/components/common/Spinner.svelte';
  26. const i18n = getContext('i18n');
  27. let updateEmbeddingModelLoading = false;
  28. let updateRerankingModelLoading = false;
  29. let showResetConfirm = false;
  30. let showResetUploadDirConfirm = false;
  31. let showReindexConfirm = false;
  32. let embeddingEngine = '';
  33. let embeddingModel = '';
  34. let embeddingBatchSize = 1;
  35. let rerankingModel = '';
  36. let OpenAIUrl = '';
  37. let OpenAIKey = '';
  38. let AzureOpenAIUrl = '';
  39. let AzureOpenAIKey = '';
  40. let AzureOpenAIVersion = '';
  41. let OllamaUrl = '';
  42. let OllamaKey = '';
  43. let querySettings = {
  44. template: '',
  45. r: 0.0,
  46. k: 4,
  47. k_reranker: 4,
  48. hybrid: false
  49. };
  50. let RAGConfig = null;
  51. const embeddingModelUpdateHandler = async () => {
  52. if (embeddingEngine === '' && embeddingModel.split('/').length - 1 > 1) {
  53. toast.error(
  54. $i18n.t(
  55. 'Model filesystem path detected. Model shortname is required for update, cannot continue.'
  56. )
  57. );
  58. return;
  59. }
  60. if (embeddingEngine === 'ollama' && embeddingModel === '') {
  61. toast.error(
  62. $i18n.t(
  63. 'Model filesystem path detected. Model shortname is required for update, cannot continue.'
  64. )
  65. );
  66. return;
  67. }
  68. if (embeddingEngine === 'openai' && embeddingModel === '') {
  69. toast.error(
  70. $i18n.t(
  71. 'Model filesystem path detected. Model shortname is required for update, cannot continue.'
  72. )
  73. );
  74. return;
  75. }
  76. if (
  77. embeddingEngine === 'azure_openai' &&
  78. (AzureOpenAIKey === '' || AzureOpenAIUrl === '' || AzureOpenAIVersion === '')
  79. ) {
  80. toast.error($i18n.t('OpenAI URL/Key required.'));
  81. return;
  82. }
  83. console.debug('Update embedding model attempt:', embeddingModel);
  84. updateEmbeddingModelLoading = true;
  85. const res = await updateEmbeddingConfig(localStorage.token, {
  86. embedding_engine: embeddingEngine,
  87. embedding_model: embeddingModel,
  88. embedding_batch_size: embeddingBatchSize,
  89. ollama_config: {
  90. key: OllamaKey,
  91. url: OllamaUrl
  92. },
  93. openai_config: {
  94. key: OpenAIKey,
  95. url: OpenAIUrl
  96. },
  97. azure_openai_config: {
  98. key: AzureOpenAIKey,
  99. url: AzureOpenAIUrl,
  100. version: AzureOpenAIVersion
  101. }
  102. }).catch(async (error) => {
  103. toast.error(`${error}`);
  104. await setEmbeddingConfig();
  105. return null;
  106. });
  107. updateEmbeddingModelLoading = false;
  108. if (res) {
  109. console.debug('embeddingModelUpdateHandler:', res);
  110. if (res.status === true) {
  111. toast.success($i18n.t('Embedding model set to "{{embedding_model}}"', res), {
  112. duration: 1000 * 10
  113. });
  114. }
  115. }
  116. };
  117. const submitHandler = async () => {
  118. if (
  119. RAGConfig.CONTENT_EXTRACTION_ENGINE === 'external' &&
  120. RAGConfig.EXTERNAL_DOCUMENT_LOADER_URL === ''
  121. ) {
  122. toast.error($i18n.t('External Document Loader URL required.'));
  123. return;
  124. }
  125. if (RAGConfig.CONTENT_EXTRACTION_ENGINE === 'tika' && RAGConfig.TIKA_SERVER_URL === '') {
  126. toast.error($i18n.t('Tika Server URL required.'));
  127. return;
  128. }
  129. if (RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling' && RAGConfig.DOCLING_SERVER_URL === '') {
  130. toast.error($i18n.t('Docling Server URL required.'));
  131. return;
  132. }
  133. if (
  134. RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling' &&
  135. RAGConfig.DOCLING_DO_OCR &&
  136. ((RAGConfig.DOCLING_OCR_ENGINE === '' && RAGConfig.DOCLING_OCR_LANG !== '') ||
  137. (RAGConfig.DOCLING_OCR_ENGINE !== '' && RAGConfig.DOCLING_OCR_LANG === ''))
  138. ) {
  139. toast.error(
  140. $i18n.t('Both Docling OCR Engine and Language(s) must be provided or both left empty.')
  141. );
  142. return;
  143. }
  144. if (
  145. RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling' &&
  146. RAGConfig.DOCLING_DO_OCR === false &&
  147. RAGConfig.DOCLING_FORCE_OCR === true
  148. ) {
  149. toast.error($i18n.t('In order to force OCR, performing OCR must be enabled.'));
  150. return;
  151. }
  152. if (
  153. RAGConfig.CONTENT_EXTRACTION_ENGINE === 'datalab_marker' &&
  154. !RAGConfig.DATALAB_MARKER_API_KEY
  155. ) {
  156. toast.error($i18n.t('Datalab Marker API Key required.'));
  157. return;
  158. }
  159. if (
  160. RAGConfig.CONTENT_EXTRACTION_ENGINE === 'datalab_marker' &&
  161. RAGConfig.DATALAB_MARKER_ADDITIONAL_CONFIG &&
  162. RAGConfig.DATALAB_MARKER_ADDITIONAL_CONFIG.trim() !== ''
  163. ) {
  164. try {
  165. JSON.parse(RAGConfig.DATALAB_MARKER_ADDITIONAL_CONFIG);
  166. } catch (e) {
  167. toast.error($i18n.t('Invalid JSON format in Additional Config'));
  168. return;
  169. }
  170. }
  171. if (
  172. RAGConfig.CONTENT_EXTRACTION_ENGINE === 'document_intelligence' &&
  173. RAGConfig.DOCUMENT_INTELLIGENCE_ENDPOINT === ''
  174. ) {
  175. toast.error($i18n.t('Document Intelligence endpoint required.'));
  176. return;
  177. }
  178. if (
  179. RAGConfig.CONTENT_EXTRACTION_ENGINE === 'mistral_ocr' &&
  180. RAGConfig.MISTRAL_OCR_API_KEY === ''
  181. ) {
  182. toast.error($i18n.t('Mistral OCR API Key required.'));
  183. return;
  184. }
  185. if (!RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL) {
  186. await embeddingModelUpdateHandler();
  187. }
  188. const res = await updateRAGConfig(localStorage.token, {
  189. ...RAGConfig,
  190. ALLOWED_FILE_EXTENSIONS: RAGConfig.ALLOWED_FILE_EXTENSIONS.split(',')
  191. .map((ext) => ext.trim())
  192. .filter((ext) => ext !== ''),
  193. DOCLING_PICTURE_DESCRIPTION_LOCAL: JSON.parse(
  194. RAGConfig.DOCLING_PICTURE_DESCRIPTION_LOCAL || '{}'
  195. ),
  196. DOCLING_PICTURE_DESCRIPTION_API: JSON.parse(RAGConfig.DOCLING_PICTURE_DESCRIPTION_API || '{}')
  197. });
  198. dispatch('save');
  199. };
  200. const setEmbeddingConfig = async () => {
  201. const embeddingConfig = await getEmbeddingConfig(localStorage.token);
  202. if (embeddingConfig) {
  203. embeddingEngine = embeddingConfig.embedding_engine;
  204. embeddingModel = embeddingConfig.embedding_model;
  205. embeddingBatchSize = embeddingConfig.embedding_batch_size ?? 1;
  206. OpenAIKey = embeddingConfig.openai_config.key;
  207. OpenAIUrl = embeddingConfig.openai_config.url;
  208. OllamaKey = embeddingConfig.ollama_config.key;
  209. OllamaUrl = embeddingConfig.ollama_config.url;
  210. AzureOpenAIKey = embeddingConfig.azure_openai_config.key;
  211. AzureOpenAIUrl = embeddingConfig.azure_openai_config.url;
  212. AzureOpenAIVersion = embeddingConfig.azure_openai_config.version;
  213. }
  214. };
  215. onMount(async () => {
  216. await setEmbeddingConfig();
  217. const config = await getRAGConfig(localStorage.token);
  218. config.ALLOWED_FILE_EXTENSIONS = (config?.ALLOWED_FILE_EXTENSIONS ?? []).join(', ');
  219. config.DOCLING_PICTURE_DESCRIPTION_LOCAL = JSON.stringify(
  220. config.DOCLING_PICTURE_DESCRIPTION_LOCAL ?? {},
  221. null,
  222. 2
  223. );
  224. config.DOCLING_PICTURE_DESCRIPTION_API = JSON.stringify(
  225. config.DOCLING_PICTURE_DESCRIPTION_API ?? {},
  226. null,
  227. 2
  228. );
  229. RAGConfig = config;
  230. });
  231. </script>
  232. <ResetUploadDirConfirmDialog
  233. bind:show={showResetUploadDirConfirm}
  234. on:confirm={async () => {
  235. const res = await deleteAllFiles(localStorage.token).catch((error) => {
  236. toast.error(`${error}`);
  237. return null;
  238. });
  239. if (res) {
  240. toast.success($i18n.t('Success'));
  241. }
  242. }}
  243. />
  244. <ResetVectorDBConfirmDialog
  245. bind:show={showResetConfirm}
  246. on:confirm={() => {
  247. const res = resetVectorDB(localStorage.token).catch((error) => {
  248. toast.error(`${error}`);
  249. return null;
  250. });
  251. if (res) {
  252. toast.success($i18n.t('Success'));
  253. }
  254. }}
  255. />
  256. <ReindexKnowledgeFilesConfirmDialog
  257. bind:show={showReindexConfirm}
  258. on:confirm={async () => {
  259. const res = await reindexKnowledgeFiles(localStorage.token).catch((error) => {
  260. toast.error(`${error}`);
  261. return null;
  262. });
  263. if (res) {
  264. toast.success($i18n.t('Success'));
  265. }
  266. }}
  267. />
  268. <form
  269. class="flex flex-col h-full justify-between space-y-3 text-sm"
  270. on:submit|preventDefault={() => {
  271. submitHandler();
  272. }}
  273. >
  274. {#if RAGConfig}
  275. <div class=" space-y-2.5 overflow-y-scroll scrollbar-hidden h-full pr-1.5">
  276. <div class="">
  277. <div class="mb-3">
  278. <div class=" mb-2.5 text-base font-medium">{$i18n.t('General')}</div>
  279. <hr class=" border-gray-100 dark:border-gray-850 my-2" />
  280. <div class="mb-2.5 flex flex-col w-full justify-between">
  281. <div class="flex w-full justify-between mb-1">
  282. <div class="self-center text-xs font-medium">
  283. {$i18n.t('Content Extraction Engine')}
  284. </div>
  285. <div class="">
  286. <select
  287. class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
  288. bind:value={RAGConfig.CONTENT_EXTRACTION_ENGINE}
  289. >
  290. <option value="">{$i18n.t('Default')}</option>
  291. <option value="external">{$i18n.t('External')}</option>
  292. <option value="tika">{$i18n.t('Tika')}</option>
  293. <option value="docling">{$i18n.t('Docling')}</option>
  294. <option value="datalab_marker">{$i18n.t('Datalab Marker API')}</option>
  295. <option value="document_intelligence">{$i18n.t('Document Intelligence')}</option>
  296. <option value="mistral_ocr">{$i18n.t('Mistral OCR')}</option>
  297. </select>
  298. </div>
  299. </div>
  300. {#if RAGConfig.CONTENT_EXTRACTION_ENGINE === ''}
  301. <div class="flex w-full mt-1">
  302. <div class="flex-1 flex justify-between">
  303. <div class=" self-center text-xs font-medium">
  304. {$i18n.t('PDF Extract Images (OCR)')}
  305. </div>
  306. <div class="flex items-center relative">
  307. <Switch bind:state={RAGConfig.PDF_EXTRACT_IMAGES} />
  308. </div>
  309. </div>
  310. </div>
  311. {:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'datalab_marker'}
  312. <div class="my-0.5 flex gap-2 pr-2">
  313. <Tooltip
  314. content={$i18n.t(
  315. 'API Base URL for Datalab Marker service. Defaults to: https://www.datalab.to/api/v1/marker'
  316. )}
  317. placement="top-start"
  318. className="w-full"
  319. >
  320. <input
  321. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  322. placeholder={$i18n.t('Enter Datalab Marker API Base URL')}
  323. bind:value={RAGConfig.DATALAB_MARKER_API_BASE_URL}
  324. />
  325. </Tooltip>
  326. </div>
  327. <div class="my-0.5 flex gap-2 pr-2">
  328. <SensitiveInput
  329. placeholder={$i18n.t('Enter Datalab Marker API Key')}
  330. required={false}
  331. bind:value={RAGConfig.DATALAB_MARKER_API_KEY}
  332. />
  333. </div>
  334. <div class="flex flex-col gap-2 mt-2">
  335. <div class=" flex flex-col w-full justify-between">
  336. <div class=" mb-1 text-xs font-medium">
  337. {$i18n.t('Additional Config')}
  338. </div>
  339. <div class="flex w-full items-center relative">
  340. <Tooltip
  341. content={$i18n.t(
  342. 'Additional configuration options for marker. This should be a JSON string with key-value pairs. For example, \'{"key": "value"}\'. Supported keys include: disable_links, keep_pageheader_in_output, keep_pagefooter_in_output, filter_blank_pages, drop_repeated_text, layout_coverage_threshold, merge_threshold, height_tolerance, gap_threshold, image_threshold, min_line_length, level_count, default_level'
  343. )}
  344. placement="top-start"
  345. className="w-full"
  346. >
  347. <Textarea
  348. bind:value={RAGConfig.DATALAB_MARKER_ADDITIONAL_CONFIG}
  349. placeholder={$i18n.t('Enter JSON config (e.g., {"disable_links": true})')}
  350. />
  351. </Tooltip>
  352. </div>
  353. </div>
  354. </div>
  355. <div class="flex justify-between w-full mt-2">
  356. <div class="self-center text-xs font-medium">
  357. <Tooltip
  358. content={$i18n.t(
  359. 'Significantly improves accuracy by using an LLM to enhance tables, forms, inline math, and layout detection. Will increase latency. Defaults to False.'
  360. )}
  361. placement="top-start"
  362. >
  363. {$i18n.t('Use LLM')}
  364. </Tooltip>
  365. </div>
  366. <div class="flex items-center">
  367. <Switch bind:state={RAGConfig.DATALAB_MARKER_USE_LLM} />
  368. </div>
  369. </div>
  370. <div class="flex justify-between w-full mt-2">
  371. <div class="self-center text-xs font-medium">
  372. <Tooltip
  373. content={$i18n.t('Skip the cache and re-run the inference. Defaults to False.')}
  374. placement="top-start"
  375. >
  376. {$i18n.t('Skip Cache')}
  377. </Tooltip>
  378. </div>
  379. <div class="flex items-center">
  380. <Switch bind:state={RAGConfig.DATALAB_MARKER_SKIP_CACHE} />
  381. </div>
  382. </div>
  383. <div class="flex justify-between w-full mt-2">
  384. <div class="self-center text-xs font-medium">
  385. <Tooltip
  386. content={$i18n.t(
  387. 'Force OCR on all pages of the PDF. This can lead to worse results if you have good text in your PDFs. Defaults to False.'
  388. )}
  389. placement="top-start"
  390. >
  391. {$i18n.t('Force OCR')}
  392. </Tooltip>
  393. </div>
  394. <div class="flex items-center">
  395. <Switch bind:state={RAGConfig.DATALAB_MARKER_FORCE_OCR} />
  396. </div>
  397. </div>
  398. <div class="flex justify-between w-full mt-2">
  399. <div class="self-center text-xs font-medium">
  400. <Tooltip
  401. content={$i18n.t(
  402. 'Whether to paginate the output. Each page will be separated by a horizontal rule and page number. Defaults to False.'
  403. )}
  404. placement="top-start"
  405. >
  406. {$i18n.t('Paginate')}
  407. </Tooltip>
  408. </div>
  409. <div class="flex items-center">
  410. <Switch bind:state={RAGConfig.DATALAB_MARKER_PAGINATE} />
  411. </div>
  412. </div>
  413. <div class="flex justify-between w-full mt-2">
  414. <div class="self-center text-xs font-medium">
  415. <Tooltip
  416. content={$i18n.t(
  417. 'Strip existing OCR text from the PDF and re-run OCR. Ignored if Force OCR is enabled. Defaults to False.'
  418. )}
  419. placement="top-start"
  420. >
  421. {$i18n.t('Strip Existing OCR')}
  422. </Tooltip>
  423. </div>
  424. <div class="flex items-center">
  425. <Switch bind:state={RAGConfig.DATALAB_MARKER_STRIP_EXISTING_OCR} />
  426. </div>
  427. </div>
  428. <div class="flex justify-between w-full mt-2">
  429. <div class="self-center text-xs font-medium">
  430. <Tooltip
  431. content={$i18n.t(
  432. 'Disable image extraction from the PDF. If Use LLM is enabled, images will be automatically captioned. Defaults to False.'
  433. )}
  434. placement="top-start"
  435. >
  436. {$i18n.t('Disable Image Extraction')}
  437. </Tooltip>
  438. </div>
  439. <div class="flex items-center">
  440. <Switch bind:state={RAGConfig.DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION} />
  441. </div>
  442. </div>
  443. <div class="flex justify-between w-full mt-2">
  444. <div class="self-center text-xs font-medium">
  445. <Tooltip
  446. content={$i18n.t(
  447. 'Format the lines in the output. Defaults to False. If set to True, the lines will be formatted to detect inline math and styles.'
  448. )}
  449. placement="top-start"
  450. >
  451. {$i18n.t('Format Lines')}
  452. </Tooltip>
  453. </div>
  454. <div class="flex items-center">
  455. <Switch bind:state={RAGConfig.DATALAB_MARKER_FORMAT_LINES} />
  456. </div>
  457. </div>
  458. <div class="flex justify-between w-full mt-2">
  459. <div class="self-center text-xs font-medium">
  460. <Tooltip
  461. content={$i18n.t(
  462. "The output format for the text. Can be 'json', 'markdown', or 'html'. Defaults to 'markdown'."
  463. )}
  464. placement="top-start"
  465. >
  466. {$i18n.t('Output Format')}
  467. </Tooltip>
  468. </div>
  469. <div class="">
  470. <select
  471. class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
  472. bind:value={RAGConfig.DATALAB_MARKER_OUTPUT_FORMAT}
  473. >
  474. <option value="markdown">{$i18n.t('Markdown')}</option>
  475. <option value="json">{$i18n.t('JSON')}</option>
  476. <option value="html">{$i18n.t('HTML')}</option>
  477. </select>
  478. </div>
  479. </div>
  480. {:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'external'}
  481. <div class="my-0.5 flex gap-2 pr-2">
  482. <input
  483. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  484. placeholder={$i18n.t('Enter External Document Loader URL')}
  485. bind:value={RAGConfig.EXTERNAL_DOCUMENT_LOADER_URL}
  486. />
  487. <SensitiveInput
  488. placeholder={$i18n.t('Enter External Document Loader API Key')}
  489. required={false}
  490. bind:value={RAGConfig.EXTERNAL_DOCUMENT_LOADER_API_KEY}
  491. />
  492. </div>
  493. {:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'tika'}
  494. <div class="flex w-full mt-1">
  495. <div class="flex-1 mr-2">
  496. <input
  497. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  498. placeholder={$i18n.t('Enter Tika Server URL')}
  499. bind:value={RAGConfig.TIKA_SERVER_URL}
  500. />
  501. </div>
  502. </div>
  503. {:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling'}
  504. <div class="flex w-full mt-1">
  505. <input
  506. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  507. placeholder={$i18n.t('Enter Docling Server URL')}
  508. bind:value={RAGConfig.DOCLING_SERVER_URL}
  509. />
  510. </div>
  511. <div class="flex w-full mt-2">
  512. <div class="flex-1 flex justify-between">
  513. <div class=" self-center text-xs font-medium">
  514. {$i18n.t('Perform OCR')}
  515. </div>
  516. <div class="flex items-center relative">
  517. <Switch bind:state={RAGConfig.DOCLING_DO_OCR} />
  518. </div>
  519. </div>
  520. </div>
  521. {#if RAGConfig.DOCLING_DO_OCR}
  522. <div class="flex w-full mt-2">
  523. <input
  524. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  525. placeholder={$i18n.t('Enter Docling OCR Engine')}
  526. bind:value={RAGConfig.DOCLING_OCR_ENGINE}
  527. />
  528. <input
  529. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  530. placeholder={$i18n.t('Enter Docling OCR Language(s)')}
  531. bind:value={RAGConfig.DOCLING_OCR_LANG}
  532. />
  533. </div>
  534. {/if}
  535. <div class="flex w-full mt-2">
  536. <div class="flex-1 flex justify-between">
  537. <div class=" self-center text-xs font-medium">
  538. {$i18n.t('Force OCR')}
  539. </div>
  540. <div class="flex items-center relative">
  541. <Switch bind:state={RAGConfig.DOCLING_FORCE_OCR} />
  542. </div>
  543. </div>
  544. </div>
  545. <div class="flex justify-between w-full mt-2">
  546. <div class="self-center text-xs font-medium">
  547. <Tooltip content={''} placement="top-start">
  548. {$i18n.t('PDF Backend')}
  549. </Tooltip>
  550. </div>
  551. <div class="">
  552. <select
  553. class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
  554. bind:value={RAGConfig.DOCLING_PDF_BACKEND}
  555. >
  556. <option value="pypdfium2">{$i18n.t('pypdfium2')}</option>
  557. <option value="dlparse_v1">{$i18n.t('dlparse_v1')}</option>
  558. <option value="dlparse_v2">{$i18n.t('dlparse_v2')}</option>
  559. <option value="dlparse_v4">{$i18n.t('dlparse_v4')}</option>
  560. </select>
  561. </div>
  562. </div>
  563. <div class="flex justify-between w-full mt-2">
  564. <div class="self-center text-xs font-medium">
  565. <Tooltip content={''} placement="top-start">
  566. {$i18n.t('Table Mode')}
  567. </Tooltip>
  568. </div>
  569. <div class="">
  570. <select
  571. class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
  572. bind:value={RAGConfig.DOCLING_TABLE_MODE}
  573. >
  574. <option value="fast">{$i18n.t('fast')}</option>
  575. <option value="accurate">{$i18n.t('accurate')}</option>
  576. </select>
  577. </div>
  578. </div>
  579. <div class="flex justify-between w-full mt-2">
  580. <div class="self-center text-xs font-medium">
  581. <Tooltip content={''} placement="top-start">
  582. {$i18n.t('Pipeline')}
  583. </Tooltip>
  584. </div>
  585. <div class="">
  586. <select
  587. class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
  588. bind:value={RAGConfig.DOCLING_PIPELINE}
  589. >
  590. <option value="standard">{$i18n.t('standard')}</option>
  591. <option value="vlm">{$i18n.t('vlm')}</option>
  592. </select>
  593. </div>
  594. </div>
  595. <div class="flex w-full mt-2">
  596. <div class="flex-1 flex justify-between">
  597. <div class=" self-center text-xs font-medium">
  598. {$i18n.t('Describe Pictures in Documents')}
  599. </div>
  600. <div class="flex items-center relative">
  601. <Switch bind:state={RAGConfig.DOCLING_DO_PICTURE_DESCRIPTION} />
  602. </div>
  603. </div>
  604. </div>
  605. {#if RAGConfig.DOCLING_DO_PICTURE_DESCRIPTION}
  606. <div class="flex justify-between w-full mt-2">
  607. <div class="self-center text-xs font-medium">
  608. <Tooltip content={''} placement="top-start">
  609. {$i18n.t('Picture Description Mode')}
  610. </Tooltip>
  611. </div>
  612. <div class="">
  613. <select
  614. class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
  615. bind:value={RAGConfig.DOCLING_PICTURE_DESCRIPTION_MODE}
  616. >
  617. <option value="">{$i18n.t('Default')}</option>
  618. <option value="local">{$i18n.t('Local')}</option>
  619. <option value="api">{$i18n.t('API')}</option>
  620. </select>
  621. </div>
  622. </div>
  623. {#if RAGConfig.DOCLING_PICTURE_DESCRIPTION_MODE === 'local'}
  624. <div class="flex flex-col gap-2 mt-2">
  625. <div class=" flex flex-col w-full justify-between">
  626. <div class=" mb-1 text-xs font-medium">
  627. {$i18n.t('Picture Description Local Config')}
  628. </div>
  629. <div class="flex w-full items-center relative">
  630. <Tooltip
  631. content={$i18n.t(
  632. 'Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.'
  633. )}
  634. placement="top-start"
  635. className="w-full"
  636. >
  637. <Textarea
  638. bind:value={RAGConfig.DOCLING_PICTURE_DESCRIPTION_LOCAL}
  639. placeholder={$i18n.t('Enter Config in JSON format')}
  640. />
  641. </Tooltip>
  642. </div>
  643. </div>
  644. </div>
  645. {:else if RAGConfig.DOCLING_PICTURE_DESCRIPTION_MODE === 'api'}
  646. <div class="flex flex-col gap-2 mt-2">
  647. <div class=" flex flex-col w-full justify-between">
  648. <div class=" mb-1 text-xs font-medium">
  649. {$i18n.t('Picture Description API Config')}
  650. </div>
  651. <div class="flex w-full items-center relative">
  652. <Tooltip
  653. content={$i18n.t(
  654. 'API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.'
  655. )}
  656. placement="top-start"
  657. className="w-full"
  658. >
  659. <Textarea
  660. bind:value={RAGConfig.DOCLING_PICTURE_DESCRIPTION_API}
  661. placeholder={$i18n.t('Enter Config in JSON format')}
  662. />
  663. </Tooltip>
  664. </div>
  665. </div>
  666. </div>
  667. {/if}
  668. {/if}
  669. {:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'document_intelligence'}
  670. <div class="my-0.5 flex gap-2 pr-2">
  671. <input
  672. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  673. placeholder={$i18n.t('Enter Document Intelligence Endpoint')}
  674. bind:value={RAGConfig.DOCUMENT_INTELLIGENCE_ENDPOINT}
  675. />
  676. <SensitiveInput
  677. placeholder={$i18n.t('Enter Document Intelligence Key')}
  678. bind:value={RAGConfig.DOCUMENT_INTELLIGENCE_KEY}
  679. required={false}
  680. />
  681. </div>
  682. {:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'mistral_ocr'}
  683. <div class="my-0.5 flex gap-2 pr-2">
  684. <SensitiveInput
  685. placeholder={$i18n.t('Enter Mistral API Key')}
  686. bind:value={RAGConfig.MISTRAL_OCR_API_KEY}
  687. />
  688. </div>
  689. {/if}
  690. </div>
  691. <div class=" mb-2.5 flex w-full justify-between">
  692. <div class=" self-center text-xs font-medium">
  693. <Tooltip content={$i18n.t('Full Context Mode')} placement="top-start">
  694. {$i18n.t('Bypass Embedding and Retrieval')}
  695. </Tooltip>
  696. </div>
  697. <div class="flex items-center relative">
  698. <Tooltip
  699. content={RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL
  700. ? $i18n.t(
  701. 'Inject the entire content as context for comprehensive processing, this is recommended for complex queries.'
  702. )
  703. : $i18n.t(
  704. 'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'
  705. )}
  706. >
  707. <Switch bind:state={RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL} />
  708. </Tooltip>
  709. </div>
  710. </div>
  711. {#if !RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
  712. <div class=" mb-2.5 flex w-full justify-between">
  713. <div class=" self-center text-xs font-medium">{$i18n.t('Text Splitter')}</div>
  714. <div class="flex items-center relative">
  715. <select
  716. class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
  717. bind:value={RAGConfig.TEXT_SPLITTER}
  718. >
  719. <option value="">{$i18n.t('Default')} ({$i18n.t('Character')})</option>
  720. <option value="token">{$i18n.t('Token')} ({$i18n.t('Tiktoken')})</option>
  721. <option value="markdown_header">{$i18n.t('Markdown (Header)')}</option>
  722. </select>
  723. </div>
  724. </div>
  725. <div class=" mb-2.5 flex w-full justify-between">
  726. <div class=" flex gap-1.5 w-full">
  727. <div class=" w-full justify-between">
  728. <div class="self-center text-xs font-medium min-w-fit mb-1">
  729. {$i18n.t('Chunk Size')}
  730. </div>
  731. <div class="self-center">
  732. <input
  733. class=" w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
  734. type="number"
  735. placeholder={$i18n.t('Enter Chunk Size')}
  736. bind:value={RAGConfig.CHUNK_SIZE}
  737. autocomplete="off"
  738. min="0"
  739. />
  740. </div>
  741. </div>
  742. <div class="w-full">
  743. <div class=" self-center text-xs font-medium min-w-fit mb-1">
  744. {$i18n.t('Chunk Overlap')}
  745. </div>
  746. <div class="self-center">
  747. <input
  748. class="w-full rounded-lg py-1.5 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
  749. type="number"
  750. placeholder={$i18n.t('Enter Chunk Overlap')}
  751. bind:value={RAGConfig.CHUNK_OVERLAP}
  752. autocomplete="off"
  753. min="0"
  754. />
  755. </div>
  756. </div>
  757. </div>
  758. </div>
  759. {/if}
  760. </div>
  761. {#if !RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
  762. <div class="mb-3">
  763. <div class=" mb-2.5 text-base font-medium">{$i18n.t('Embedding')}</div>
  764. <hr class=" border-gray-100 dark:border-gray-850 my-2" />
  765. <div class=" mb-2.5 flex flex-col w-full justify-between">
  766. <div class="flex w-full justify-between">
  767. <div class=" self-center text-xs font-medium">
  768. {$i18n.t('Embedding Model Engine')}
  769. </div>
  770. <div class="flex items-center relative">
  771. <select
  772. class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
  773. bind:value={embeddingEngine}
  774. placeholder={$i18n.t('Select an embedding model engine')}
  775. on:change={(e) => {
  776. if (e.target.value === 'ollama') {
  777. embeddingModel = '';
  778. } else if (e.target.value === 'openai') {
  779. embeddingModel = 'text-embedding-3-small';
  780. } else if (e.target.value === 'azure_openai') {
  781. embeddingModel = 'text-embedding-3-small';
  782. } else if (e.target.value === '') {
  783. embeddingModel = 'sentence-transformers/all-MiniLM-L6-v2';
  784. }
  785. }}
  786. >
  787. <option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
  788. <option value="ollama">{$i18n.t('Ollama')}</option>
  789. <option value="openai">{$i18n.t('OpenAI')}</option>
  790. <option value="azure_openai">{$i18n.t('Azure OpenAI')}</option>
  791. </select>
  792. </div>
  793. </div>
  794. {#if embeddingEngine === 'openai'}
  795. <div class="my-0.5 flex gap-2 pr-2">
  796. <input
  797. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  798. placeholder={$i18n.t('API Base URL')}
  799. bind:value={OpenAIUrl}
  800. required
  801. />
  802. <SensitiveInput
  803. placeholder={$i18n.t('API Key')}
  804. bind:value={OpenAIKey}
  805. required={false}
  806. />
  807. </div>
  808. {:else if embeddingEngine === 'ollama'}
  809. <div class="my-0.5 flex gap-2 pr-2">
  810. <input
  811. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  812. placeholder={$i18n.t('API Base URL')}
  813. bind:value={OllamaUrl}
  814. required
  815. />
  816. <SensitiveInput
  817. placeholder={$i18n.t('API Key')}
  818. bind:value={OllamaKey}
  819. required={false}
  820. />
  821. </div>
  822. {:else if embeddingEngine === 'azure_openai'}
  823. <div class="my-0.5 flex flex-col gap-2 pr-2 w-full">
  824. <div class="flex gap-2">
  825. <input
  826. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  827. placeholder={$i18n.t('API Base URL')}
  828. bind:value={AzureOpenAIUrl}
  829. required
  830. />
  831. <SensitiveInput placeholder={$i18n.t('API Key')} bind:value={AzureOpenAIKey} />
  832. </div>
  833. <div class="flex gap-2">
  834. <input
  835. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  836. placeholder={$i18n.t('Version')}
  837. bind:value={AzureOpenAIVersion}
  838. required
  839. />
  840. </div>
  841. </div>
  842. {/if}
  843. </div>
  844. <div class=" mb-2.5 flex flex-col w-full">
  845. <div class=" mb-1 text-xs font-medium">{$i18n.t('Embedding Model')}</div>
  846. <div class="">
  847. {#if embeddingEngine === 'ollama'}
  848. <div class="flex w-full">
  849. <div class="flex-1 mr-2">
  850. <input
  851. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  852. bind:value={embeddingModel}
  853. placeholder={$i18n.t('Set embedding model')}
  854. required
  855. />
  856. </div>
  857. </div>
  858. {:else}
  859. <div class="flex w-full">
  860. <div class="flex-1 mr-2">
  861. <input
  862. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  863. placeholder={$i18n.t('Set embedding model (e.g. {{model}})', {
  864. model: embeddingModel.slice(-40)
  865. })}
  866. bind:value={embeddingModel}
  867. />
  868. </div>
  869. {#if embeddingEngine === ''}
  870. <button
  871. class="px-2.5 bg-transparent text-gray-800 dark:bg-transparent dark:text-gray-100 rounded-lg transition"
  872. on:click={() => {
  873. embeddingModelUpdateHandler();
  874. }}
  875. disabled={updateEmbeddingModelLoading}
  876. >
  877. {#if updateEmbeddingModelLoading}
  878. <div class="self-center">
  879. <Spinner />
  880. </div>
  881. {:else}
  882. <svg
  883. xmlns="http://www.w3.org/2000/svg"
  884. viewBox="0 0 16 16"
  885. fill="currentColor"
  886. class="w-4 h-4"
  887. >
  888. <path
  889. d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
  890. />
  891. <path
  892. d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
  893. />
  894. </svg>
  895. {/if}
  896. </button>
  897. {/if}
  898. </div>
  899. {/if}
  900. </div>
  901. <div class="mt-1 mb-1 text-xs text-gray-400 dark:text-gray-500">
  902. {$i18n.t(
  903. 'Warning: If you update or change your embedding model, you will need to re-import all documents.'
  904. )}
  905. </div>
  906. </div>
  907. {#if embeddingEngine === 'ollama' || embeddingEngine === 'openai' || embeddingEngine === 'azure_openai'}
  908. <div class=" mb-2.5 flex w-full justify-between">
  909. <div class=" self-center text-xs font-medium">
  910. {$i18n.t('Embedding Batch Size')}
  911. </div>
  912. <div class="">
  913. <input
  914. bind:value={embeddingBatchSize}
  915. type="number"
  916. class=" bg-transparent text-center w-14 outline-none"
  917. min="-2"
  918. max="16000"
  919. step="1"
  920. />
  921. </div>
  922. </div>
  923. {/if}
  924. </div>
  925. <div class="mb-3">
  926. <div class=" mb-2.5 text-base font-medium">{$i18n.t('Retrieval')}</div>
  927. <hr class=" border-gray-100 dark:border-gray-850 my-2" />
  928. <div class=" mb-2.5 flex w-full justify-between">
  929. <div class=" self-center text-xs font-medium">{$i18n.t('Full Context Mode')}</div>
  930. <div class="flex items-center relative">
  931. <Tooltip
  932. content={RAGConfig.RAG_FULL_CONTEXT
  933. ? $i18n.t(
  934. 'Inject the entire content as context for comprehensive processing, this is recommended for complex queries.'
  935. )
  936. : $i18n.t(
  937. 'Default to segmented retrieval for focused and relevant content extraction, this is recommended for most cases.'
  938. )}
  939. >
  940. <Switch bind:state={RAGConfig.RAG_FULL_CONTEXT} />
  941. </Tooltip>
  942. </div>
  943. </div>
  944. {#if !RAGConfig.RAG_FULL_CONTEXT}
  945. <div class=" mb-2.5 flex w-full justify-between">
  946. <div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>
  947. <div class="flex items-center relative">
  948. <Switch bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH} />
  949. </div>
  950. </div>
  951. {#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
  952. <div class=" mb-2.5 flex flex-col w-full justify-between">
  953. <div class="flex w-full justify-between">
  954. <div class=" self-center text-xs font-medium">
  955. {$i18n.t('Reranking Engine')}
  956. </div>
  957. <div class="flex items-center relative">
  958. <select
  959. class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 p-1 text-xs bg-transparent outline-hidden text-right"
  960. bind:value={RAGConfig.RAG_RERANKING_ENGINE}
  961. placeholder={$i18n.t('Select a reranking model engine')}
  962. on:change={(e) => {
  963. if (e.target.value === 'external') {
  964. RAGConfig.RAG_RERANKING_MODEL = '';
  965. } else if (e.target.value === '') {
  966. RAGConfig.RAG_RERANKING_MODEL = 'BAAI/bge-reranker-v2-m3';
  967. }
  968. }}
  969. >
  970. <option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
  971. <option value="external">{$i18n.t('External')}</option>
  972. </select>
  973. </div>
  974. </div>
  975. {#if RAGConfig.RAG_RERANKING_ENGINE === 'external'}
  976. <div class="my-0.5 flex gap-2 pr-2">
  977. <input
  978. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  979. placeholder={$i18n.t('API Base URL')}
  980. bind:value={RAGConfig.RAG_EXTERNAL_RERANKER_URL}
  981. required
  982. />
  983. <SensitiveInput
  984. placeholder={$i18n.t('API Key')}
  985. bind:value={RAGConfig.RAG_EXTERNAL_RERANKER_API_KEY}
  986. required={false}
  987. />
  988. </div>
  989. {/if}
  990. </div>
  991. <div class=" mb-2.5 flex flex-col w-full">
  992. <div class=" mb-1 text-xs font-medium">{$i18n.t('Reranking Model')}</div>
  993. <div class="">
  994. <div class="flex w-full">
  995. <div class="flex-1 mr-2">
  996. <input
  997. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  998. placeholder={$i18n.t('Set reranking model (e.g. {{model}})', {
  999. model: 'BAAI/bge-reranker-v2-m3'
  1000. })}
  1001. bind:value={RAGConfig.RAG_RERANKING_MODEL}
  1002. />
  1003. </div>
  1004. </div>
  1005. </div>
  1006. </div>
  1007. {/if}
  1008. <div class=" mb-2.5 flex w-full justify-between">
  1009. <div class=" self-center text-xs font-medium">{$i18n.t('Top K')}</div>
  1010. <div class="flex items-center relative">
  1011. <input
  1012. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  1013. type="number"
  1014. placeholder={$i18n.t('Enter Top K')}
  1015. bind:value={RAGConfig.TOP_K}
  1016. autocomplete="off"
  1017. min="0"
  1018. />
  1019. </div>
  1020. </div>
  1021. {#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
  1022. <div class="mb-2.5 flex w-full justify-between">
  1023. <div class="self-center text-xs font-medium">{$i18n.t('Top K Reranker')}</div>
  1024. <div class="flex items-center relative">
  1025. <input
  1026. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  1027. type="number"
  1028. placeholder={$i18n.t('Enter Top K Reranker')}
  1029. bind:value={RAGConfig.TOP_K_RERANKER}
  1030. autocomplete="off"
  1031. min="0"
  1032. />
  1033. </div>
  1034. </div>
  1035. {/if}
  1036. {#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
  1037. <div class=" mb-2.5 flex flex-col w-full justify-between">
  1038. <div class=" flex w-full justify-between">
  1039. <div class=" self-center text-xs font-medium">
  1040. {$i18n.t('Relevance Threshold')}
  1041. </div>
  1042. <div class="flex items-center relative">
  1043. <input
  1044. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  1045. type="number"
  1046. step="0.01"
  1047. placeholder={$i18n.t('Enter Score')}
  1048. bind:value={RAGConfig.RELEVANCE_THRESHOLD}
  1049. autocomplete="off"
  1050. min="0.0"
  1051. title={$i18n.t(
  1052. 'The score should be a value between 0.0 (0%) and 1.0 (100%).'
  1053. )}
  1054. />
  1055. </div>
  1056. </div>
  1057. <div class="mt-1 text-xs text-gray-400 dark:text-gray-500">
  1058. {$i18n.t(
  1059. 'Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.'
  1060. )}
  1061. </div>
  1062. </div>
  1063. {/if}
  1064. {#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
  1065. <div class=" mb-2.5 py-0.5 w-full justify-between">
  1066. <Tooltip
  1067. content={$i18n.t(
  1068. 'The Weight of BM25 Hybrid Search. 0 more lexical, 1 more semantic. Default 0.5'
  1069. )}
  1070. placement="top-start"
  1071. className="inline-tooltip"
  1072. >
  1073. <div class="flex w-full justify-between">
  1074. <div class=" self-center text-xs font-medium">
  1075. {$i18n.t('BM25 Weight')}
  1076. </div>
  1077. <button
  1078. class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
  1079. type="button"
  1080. on:click={() => {
  1081. RAGConfig.HYBRID_BM25_WEIGHT =
  1082. (RAGConfig?.HYBRID_BM25_WEIGHT ?? null) === null ? 0.5 : null;
  1083. }}
  1084. >
  1085. {#if (RAGConfig?.HYBRID_BM25_WEIGHT ?? null) === null}
  1086. <span class="ml-2 self-center"> {$i18n.t('Default')} </span>
  1087. {:else}
  1088. <span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
  1089. {/if}
  1090. </button>
  1091. </div>
  1092. </Tooltip>
  1093. {#if (RAGConfig?.HYBRID_BM25_WEIGHT ?? null) !== null}
  1094. <div class="flex mt-0.5 space-x-2">
  1095. <div class=" flex-1">
  1096. <input
  1097. id="steps-range"
  1098. type="range"
  1099. min="0"
  1100. max="1"
  1101. step="0.05"
  1102. bind:value={RAGConfig.HYBRID_BM25_WEIGHT}
  1103. class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
  1104. />
  1105. <div class="py-0.5">
  1106. <div class="flex w-full justify-between">
  1107. <div class=" text-left text-xs font-small">
  1108. {$i18n.t('semantic')}
  1109. </div>
  1110. <div class=" text-right text-xs font-small">
  1111. {$i18n.t('lexical')}
  1112. </div>
  1113. </div>
  1114. </div>
  1115. </div>
  1116. <div>
  1117. <input
  1118. bind:value={RAGConfig.HYBRID_BM25_WEIGHT}
  1119. type="number"
  1120. class=" bg-transparent text-center w-14"
  1121. min="0"
  1122. max="1"
  1123. step="any"
  1124. />
  1125. </div>
  1126. </div>
  1127. {/if}
  1128. </div>
  1129. {/if}
  1130. {/if}
  1131. <div class=" mb-2.5 flex flex-col w-full justify-between">
  1132. <div class=" mb-1 text-xs font-medium">{$i18n.t('RAG Template')}</div>
  1133. <div class="flex w-full items-center relative">
  1134. <Tooltip
  1135. content={$i18n.t(
  1136. 'Leave empty to use the default prompt, or enter a custom prompt'
  1137. )}
  1138. placement="top-start"
  1139. className="w-full"
  1140. >
  1141. <Textarea
  1142. bind:value={RAGConfig.RAG_TEMPLATE}
  1143. placeholder={$i18n.t(
  1144. 'Leave empty to use the default prompt, or enter a custom prompt'
  1145. )}
  1146. />
  1147. </Tooltip>
  1148. </div>
  1149. </div>
  1150. </div>
  1151. {/if}
  1152. <div class="mb-3">
  1153. <div class=" mb-2.5 text-base font-medium">{$i18n.t('Files')}</div>
  1154. <hr class=" border-gray-100 dark:border-gray-850 my-2" />
  1155. <div class=" mb-2.5 flex w-full justify-between">
  1156. <div class=" self-center text-xs font-medium">{$i18n.t('Allowed File Extensions')}</div>
  1157. <div class="flex items-center relative">
  1158. <Tooltip
  1159. content={$i18n.t(
  1160. 'Allowed file extensions for upload. Separate multiple extensions with commas. Leave empty for all file types.'
  1161. )}
  1162. placement="top-start"
  1163. >
  1164. <input
  1165. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  1166. type="text"
  1167. placeholder={$i18n.t('e.g. pdf, docx, txt')}
  1168. bind:value={RAGConfig.ALLOWED_FILE_EXTENSIONS}
  1169. autocomplete="off"
  1170. />
  1171. </Tooltip>
  1172. </div>
  1173. </div>
  1174. <div class=" mb-2.5 flex w-full justify-between">
  1175. <div class=" self-center text-xs font-medium">{$i18n.t('Max Upload Size')}</div>
  1176. <div class="flex items-center relative">
  1177. <Tooltip
  1178. content={$i18n.t(
  1179. 'The maximum file size in MB. If the file size exceeds this limit, the file will not be uploaded.'
  1180. )}
  1181. placement="top-start"
  1182. >
  1183. <input
  1184. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  1185. type="number"
  1186. placeholder={$i18n.t('Leave empty for unlimited')}
  1187. bind:value={RAGConfig.FILE_MAX_SIZE}
  1188. autocomplete="off"
  1189. min="0"
  1190. />
  1191. </Tooltip>
  1192. </div>
  1193. </div>
  1194. <div class=" mb-2.5 flex w-full justify-between">
  1195. <div class=" self-center text-xs font-medium">{$i18n.t('Max Upload Count')}</div>
  1196. <div class="flex items-center relative">
  1197. <Tooltip
  1198. content={$i18n.t(
  1199. 'The maximum number of files that can be used at once in chat. If the number of files exceeds this limit, the files will not be uploaded.'
  1200. )}
  1201. placement="top-start"
  1202. >
  1203. <input
  1204. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  1205. type="number"
  1206. placeholder={$i18n.t('Leave empty for unlimited')}
  1207. bind:value={RAGConfig.FILE_MAX_COUNT}
  1208. autocomplete="off"
  1209. min="0"
  1210. />
  1211. </Tooltip>
  1212. </div>
  1213. </div>
  1214. <div class=" mb-2.5 flex w-full justify-between">
  1215. <div class=" self-center text-xs font-medium">{$i18n.t('Image Compression Width')}</div>
  1216. <div class="flex items-center relative">
  1217. <Tooltip
  1218. content={$i18n.t(
  1219. 'The width in pixels to compress images to. Leave empty for no compression.'
  1220. )}
  1221. placement="top-start"
  1222. >
  1223. <input
  1224. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  1225. type="number"
  1226. placeholder={$i18n.t('Leave empty for no compression')}
  1227. bind:value={RAGConfig.FILE_IMAGE_COMPRESSION_WIDTH}
  1228. autocomplete="off"
  1229. min="0"
  1230. />
  1231. </Tooltip>
  1232. </div>
  1233. </div>
  1234. <div class=" mb-2.5 flex w-full justify-between">
  1235. <div class=" self-center text-xs font-medium">
  1236. {$i18n.t('Image Compression Height')}
  1237. </div>
  1238. <div class="flex items-center relative">
  1239. <Tooltip
  1240. content={$i18n.t(
  1241. 'The height in pixels to compress images to. Leave empty for no compression.'
  1242. )}
  1243. placement="top-start"
  1244. >
  1245. <input
  1246. class="flex-1 w-full text-sm bg-transparent outline-hidden"
  1247. type="number"
  1248. placeholder={$i18n.t('Leave empty for no compression')}
  1249. bind:value={RAGConfig.FILE_IMAGE_COMPRESSION_HEIGHT}
  1250. autocomplete="off"
  1251. min="0"
  1252. />
  1253. </Tooltip>
  1254. </div>
  1255. </div>
  1256. </div>
  1257. <div class="mb-3">
  1258. <div class=" mb-2.5 text-base font-medium">{$i18n.t('Integration')}</div>
  1259. <hr class=" border-gray-100 dark:border-gray-850 my-2" />
  1260. <div class=" mb-2.5 flex w-full justify-between">
  1261. <div class=" self-center text-xs font-medium">{$i18n.t('Google Drive')}</div>
  1262. <div class="flex items-center relative">
  1263. <Switch bind:state={RAGConfig.ENABLE_GOOGLE_DRIVE_INTEGRATION} />
  1264. </div>
  1265. </div>
  1266. <div class=" mb-2.5 flex w-full justify-between">
  1267. <div class=" self-center text-xs font-medium">{$i18n.t('OneDrive')}</div>
  1268. <div class="flex items-center relative">
  1269. <Switch bind:state={RAGConfig.ENABLE_ONEDRIVE_INTEGRATION} />
  1270. </div>
  1271. </div>
  1272. </div>
  1273. <div class="mb-3">
  1274. <div class=" mb-2.5 text-base font-medium">{$i18n.t('Danger Zone')}</div>
  1275. <hr class=" border-gray-100 dark:border-gray-850 my-2" />
  1276. <div class=" mb-2.5 flex w-full justify-between">
  1277. <div class=" self-center text-xs font-medium">{$i18n.t('Reset Upload Directory')}</div>
  1278. <div class="flex items-center relative">
  1279. <button
  1280. class="text-xs"
  1281. on:click={() => {
  1282. showResetUploadDirConfirm = true;
  1283. }}
  1284. >
  1285. {$i18n.t('Reset')}
  1286. </button>
  1287. </div>
  1288. </div>
  1289. <div class=" mb-2.5 flex w-full justify-between">
  1290. <div class=" self-center text-xs font-medium">
  1291. {$i18n.t('Reset Vector Storage/Knowledge')}
  1292. </div>
  1293. <div class="flex items-center relative">
  1294. <button
  1295. class="text-xs"
  1296. on:click={() => {
  1297. showResetConfirm = true;
  1298. }}
  1299. >
  1300. {$i18n.t('Reset')}
  1301. </button>
  1302. </div>
  1303. </div>
  1304. <div class=" mb-2.5 flex w-full justify-between">
  1305. <div class=" self-center text-xs font-medium">
  1306. {$i18n.t('Reindex Knowledge Base Vectors')}
  1307. </div>
  1308. <div class="flex items-center relative">
  1309. <button
  1310. class="text-xs"
  1311. on:click={() => {
  1312. showReindexConfirm = true;
  1313. }}
  1314. >
  1315. {$i18n.t('Reindex')}
  1316. </button>
  1317. </div>
  1318. </div>
  1319. </div>
  1320. </div>
  1321. </div>
  1322. <div class="flex justify-end pt-3 text-sm font-medium">
  1323. <button
  1324. 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"
  1325. type="submit"
  1326. >
  1327. {$i18n.t('Save')}
  1328. </button>
  1329. </div>
  1330. {:else}
  1331. <div class="flex items-center justify-center h-full">
  1332. <Spinner className="size-5" />
  1333. </div>
  1334. {/if}
  1335. </form>