General.svelte 22.1 KB
Newer Older
1
2
<script lang="ts">
	import { getDocs } from '$lib/apis/documents';
3
	import {
Timothy J. Baek's avatar
Timothy J. Baek committed
4
5
		getRAGConfig,
		updateRAGConfig,
6
		getQuerySettings,
7
		scanDocs,
Timothy J. Baek's avatar
Timothy J. Baek committed
8
		updateQuerySettings,
9
		resetVectorDB,
Timothy J. Baek's avatar
Timothy J. Baek committed
10
		getEmbeddingConfig,
Steven Kreitzer's avatar
Steven Kreitzer committed
11
12
13
		updateEmbeddingConfig,
		getRerankingConfig,
		updateRerankingConfig
14
	} from '$lib/apis/rag';
Timothy J. Baek's avatar
Timothy J. Baek committed
15

Timothy J. Baek's avatar
Timothy J. Baek committed
16
	import { documents, models } from '$lib/stores';
17
	import { onMount, getContext } from 'svelte';
Jannik Streidl's avatar
Jannik Streidl committed
18
	import { toast } from 'svelte-sonner';
19

20
21
	import Tooltip from '$lib/components/common/Tooltip.svelte';

22
23
	const i18n = getContext('i18n');

24
	export let saveHandler: Function;
Timothy J. Baek's avatar
Timothy J. Baek committed
25

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
26
27
	let scanDirLoading = false;
	let updateEmbeddingModelLoading = false;
Steven Kreitzer's avatar
Steven Kreitzer committed
28
	let updateRerankingModelLoading = false;
Timothy J. Baek's avatar
Timothy J. Baek committed
29

Timothy J. Baek's avatar
Timothy J. Baek committed
30
31
	let showResetConfirm = false;

32
	let embeddingEngine = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
33
	let embeddingModel = '';
Steven Kreitzer's avatar
Steven Kreitzer committed
34
	let rerankingModel = '';
Timothy J. Baek's avatar
Timothy J. Baek committed
35

36
37
	let OpenAIKey = '';
	let OpenAIUrl = '';
38

Timothy J. Baek's avatar
Timothy J. Baek committed
39
40
	let chunkSize = 0;
	let chunkOverlap = 0;
Timothy J. Baek's avatar
Timothy J. Baek committed
41
	let pdfExtractImages = true;
Timothy J. Baek's avatar
Timothy J. Baek committed
42

43
44
	let querySettings = {
		template: '',
45
		r: 0.0,
Steven Kreitzer's avatar
Steven Kreitzer committed
46
47
		k: 4,
		hybrid: false
48
	};
49

50
	const scanHandler = async () => {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
51
		scanDirLoading = true;
52
		const res = await scanDocs(localStorage.token);
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
53
		scanDirLoading = false;
54
55
56

		if (res) {
			await documents.set(await getDocs(localStorage.token));
57
			toast.success($i18n.t('Scan complete!'));
58
59
60
		}
	};

61
	const embeddingModelUpdateHandler = async () => {
62
63
64
65
66
67
68
69
70
		if (embeddingEngine === '' && embeddingModel.split('/').length - 1 > 1) {
			toast.error(
				$i18n.t(
					'Model filesystem path detected. Model shortname is required for update, cannot continue.'
				)
			);
			return;
		}
		if (embeddingEngine === 'ollama' && embeddingModel === '') {
Timothy J. Baek's avatar
Timothy J. Baek committed
71
72
73
74
75
76
77
78
			toast.error(
				$i18n.t(
					'Model filesystem path detected. Model shortname is required for update, cannot continue.'
				)
			);
			return;
		}

79
		if (embeddingEngine === 'openai' && embeddingModel === '') {
Self Denial's avatar
Self Denial committed
80
81
82
83
84
			toast.error(
				$i18n.t(
					'Model filesystem path detected. Model shortname is required for update, cannot continue.'
				)
			);
85
86
87
			return;
		}

88
		if ((embeddingEngine === 'openai' && OpenAIKey === '') || OpenAIUrl === '') {
89
90
91
92
			toast.error($i18n.t('OpenAI URL/Key required.'));
			return;
		}

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
93
		console.log('Update embedding model attempt:', embeddingModel);
94

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
95
		updateEmbeddingModelLoading = true;
Timothy J. Baek's avatar
Timothy J. Baek committed
96
97
		const res = await updateEmbeddingConfig(localStorage.token, {
			embedding_engine: embeddingEngine,
98
99
100
101
			embedding_model: embeddingModel,
			...(embeddingEngine === 'openai'
				? {
						openai_config: {
102
103
							key: OpenAIKey,
							url: OpenAIUrl
104
105
106
						}
				  }
				: {})
Timothy J. Baek's avatar
Timothy J. Baek committed
107
		}).catch(async (error) => {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
108
			toast.error(error);
109
			await setEmbeddingConfig();
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
110
111
			return null;
		});
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
112
		updateEmbeddingModelLoading = false;
113
114
115

		if (res) {
			console.log('embeddingModelUpdateHandler:', res);
116
			if (res.status === true) {
117
				toast.success($i18n.t('Embedding model set to "{{embedding_model}}"', res), {
Self Denial's avatar
Self Denial committed
118
119
					duration: 1000 * 10
				});
120
121
122
123
			}
		}
	};

Steven Kreitzer's avatar
Steven Kreitzer committed
124
125
126
127
128
	const rerankingModelUpdateHandler = async () => {
		console.log('Update reranking model attempt:', rerankingModel);

		updateRerankingModelLoading = true;
		const res = await updateRerankingConfig(localStorage.token, {
129
			reranking_model: rerankingModel
Steven Kreitzer's avatar
Steven Kreitzer committed
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
		}).catch(async (error) => {
			toast.error(error);
			await setRerankingConfig();
			return null;
		});
		updateRerankingModelLoading = false;

		if (res) {
			console.log('rerankingModelUpdateHandler:', res);
			if (res.status === true) {
				toast.success($i18n.t('Reranking model set to "{{reranking_model}}"', res), {
					duration: 1000 * 10
				});
			}
		}
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
147
	const submitHandler = async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
148
149
150
151
152
153
154
		const res = await updateRAGConfig(localStorage.token, {
			pdf_extract_images: pdfExtractImages,
			chunk: {
				chunk_overlap: chunkOverlap,
				chunk_size: chunkSize
			}
		});
155
		querySettings = await updateQuerySettings(localStorage.token, querySettings);
Timothy J. Baek's avatar
Timothy J. Baek committed
156
157
	};

158
159
160
161
162
163
164
	const setEmbeddingConfig = async () => {
		const embeddingConfig = await getEmbeddingConfig(localStorage.token);

		if (embeddingConfig) {
			embeddingEngine = embeddingConfig.embedding_engine;
			embeddingModel = embeddingConfig.embedding_model;

165
166
			OpenAIKey = embeddingConfig.openai_config.key;
			OpenAIUrl = embeddingConfig.openai_config.url;
167
168
169
		}
	};

Steven Kreitzer's avatar
Steven Kreitzer committed
170
171
172
173
174
175
176
177
	const setRerankingConfig = async () => {
		const rerankingConfig = await getRerankingConfig(localStorage.token);

		if (rerankingConfig) {
			rerankingModel = rerankingConfig.reranking_model;
		}
	};

Steven Kreitzer's avatar
Steven Kreitzer committed
178
179
180
181
182
183
	const toggleHybridSearch = async () => {
		querySettings.hybrid = !querySettings.hybrid;

		querySettings = await updateQuerySettings(localStorage.token, querySettings);
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
184
	onMount(async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
185
		const res = await getRAGConfig(localStorage.token);
Timothy J. Baek's avatar
Timothy J. Baek committed
186
187

		if (res) {
Timothy J. Baek's avatar
Timothy J. Baek committed
188
189
190
191
			pdfExtractImages = res.pdf_extract_images;

			chunkSize = res.chunk.chunk_size;
			chunkOverlap = res.chunk.chunk_overlap;
Timothy J. Baek's avatar
Timothy J. Baek committed
192
		}
193

194
		await setEmbeddingConfig();
Steven Kreitzer's avatar
Steven Kreitzer committed
195
		await setRerankingConfig();
196

197
		querySettings = await getQuerySettings(localStorage.token);
Timothy J. Baek's avatar
Timothy J. Baek committed
198
	});
199
200
201
202
203
</script>

<form
	class="flex flex-col h-full justify-between space-y-3 text-sm"
	on:submit|preventDefault={() => {
Timothy J. Baek's avatar
Timothy J. Baek committed
204
		submitHandler();
205
206
207
		saveHandler();
	}}
>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
208
	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
209
		<div>
210
			<div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
211

Steven Kreitzer's avatar
Steven Kreitzer committed
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
			<div class=" flex w-full justify-between">
				<div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>

				<button
					class="p-1 px-3 text-xs flex rounded transition"
					on:click={() => {
						toggleHybridSearch();
					}}
					type="button"
				>
					{#if querySettings.hybrid === true}
						<span class="ml-2 self-center">{$i18n.t('On')}</span>
					{:else}
						<span class="ml-2 self-center">{$i18n.t('Off')}</span>
					{/if}
				</button>
			</div>

230
			<div class=" flex w-full justify-between">
Timothy J. Baek's avatar
Timothy J. Baek committed
231
				<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Model Engine')}</div>
232
233
234
235
				<div class="flex items-center relative">
					<select
						class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
						bind:value={embeddingEngine}
Timothy J. Baek's avatar
Timothy J. Baek committed
236
						placeholder="Select an embedding model engine"
237
238
239
240
241
242
						on:change={(e) => {
							if (e.target.value === 'ollama') {
								embeddingModel = '';
							} else if (e.target.value === 'openai') {
								embeddingModel = 'text-embedding-3-small';
							}
Timothy J. Baek's avatar
Timothy J. Baek committed
243
						}}
244
					>
245
						<option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
246
						<option value="ollama">{$i18n.t('Ollama')}</option>
247
						<option value="openai">{$i18n.t('OpenAI')}</option>
248
					</select>
249
				</div>
250
			</div>
251
252
253
254
255
256

			{#if embeddingEngine === 'openai'}
				<div class="mt-1 flex gap-2">
					<input
						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
						placeholder={$i18n.t('API Base URL')}
257
						bind:value={OpenAIUrl}
258
259
260
261
262
263
						required
					/>

					<input
						class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
						placeholder={$i18n.t('API Key')}
264
						bind:value={OpenAIKey}
265
266
267
268
						required
					/>
				</div>
			{/if}
269
		</div>
270

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
271
272
		<div class="space-y-2">
			<div>
Timothy J. Baek's avatar
Timothy J. Baek committed
273
274
				<div class=" mb-2 text-sm font-medium">{$i18n.t('Update Embedding Model')}</div>

275
				{#if embeddingEngine === 'ollama'}
Timothy J. Baek's avatar
Timothy J. Baek committed
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
					<div class="flex w-full">
						<div class="flex-1 mr-2">
							<select
								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
								bind:value={embeddingModel}
								placeholder={$i18n.t('Select a model')}
								required
							>
								{#if !embeddingModel}
									<option value="" disabled selected>{$i18n.t('Select a model')}</option>
								{/if}
								{#each $models.filter((m) => m.id && !m.external) as model}
									<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
										>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option
									>
								{/each}
							</select>
						</div>
						<button
							class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
							on:click={() => {
								embeddingModelUpdateHandler();
							}}
							disabled={updateEmbeddingModelLoading}
						>
							{#if updateEmbeddingModelLoading}
								<div class="self-center">
									<svg
										class=" w-4 h-4"
										viewBox="0 0 24 24"
										fill="currentColor"
										xmlns="http://www.w3.org/2000/svg"
										><style>
											.spinner_ajPY {
												transform-origin: center;
												animation: spinner_AtaB 0.75s infinite linear;
											}
											@keyframes spinner_AtaB {
												100% {
													transform: rotate(360deg);
												}
											}
										</style><path
											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"
											opacity=".25"
										/><path
											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"
											class="spinner_ajPY"
										/></svg
									>
								</div>
							{:else}
								<svg
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 16 16"
									fill="currentColor"
									class="w-4 h-4"
								>
									<path
										fill-rule="evenodd"
										d="M12.416 3.376a.75.75 0 0 1 .208 1.04l-5 7.5a.75.75 0 0 1-1.154.114l-3-3a.75.75 0 0 1 1.06-1.06l2.353 2.353 4.493-6.74a.75.75 0 0 1 1.04-.207Z"
										clip-rule="evenodd"
									/>
								</svg>
							{/if}
						</button>
					</div>
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
				{:else}
					<div class="flex w-full">
						<div class="flex-1 mr-2">
							<input
								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
								placeholder={$i18n.t('Update embedding model (e.g. {{model}})', {
									model: embeddingModel.slice(-40)
								})}
								bind:value={embeddingModel}
							/>
						</div>
						<button
							class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
							on:click={() => {
								embeddingModelUpdateHandler();
							}}
							disabled={updateEmbeddingModelLoading}
						>
							{#if updateEmbeddingModelLoading}
								<div class="self-center">
									<svg
										class=" w-4 h-4"
										viewBox="0 0 24 24"
										fill="currentColor"
										xmlns="http://www.w3.org/2000/svg"
										><style>
											.spinner_ajPY {
												transform-origin: center;
												animation: spinner_AtaB 0.75s infinite linear;
											}
											@keyframes spinner_AtaB {
												100% {
													transform: rotate(360deg);
												}
											}
										</style><path
											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"
											opacity=".25"
										/><path
											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"
											class="spinner_ajPY"
										/></svg
									>
								</div>
							{:else}
								<svg
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 16 16"
									fill="currentColor"
									class="w-4 h-4"
								>
									<path
										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"
									/>
									<path
										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"
									/>
								</svg>
							{/if}
						</button>
403
					</div>
404
405
				{/if}

Timothy J. Baek's avatar
Timothy J. Baek committed
406
407
408
409
410
411
				<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
					{$i18n.t(
						'Warning: If you update or change your embedding model, you will need to re-import all documents.'
					)}
				</div>

412
413
				<hr class=" dark:border-gray-700 my-3" />

Steven Kreitzer's avatar
Steven Kreitzer committed
414
415
416
				{#if querySettings.hybrid === true}
					<div class=" ">
						<div class=" mb-2 text-sm font-medium">{$i18n.t('Update Reranking Model')}</div>
Steven Kreitzer's avatar
Steven Kreitzer committed
417

Steven Kreitzer's avatar
Steven Kreitzer committed
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
						<div class="flex w-full">
							<div class="flex-1 mr-2">
								<input
									class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
									placeholder={$i18n.t('Update reranking model (e.g. {{model}})', {
										model: rerankingModel.slice(-40)
									})}
									bind:value={rerankingModel}
								/>
							</div>
							<button
								class="px-2.5 bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded-lg transition"
								on:click={() => {
									rerankingModelUpdateHandler();
								}}
								disabled={updateRerankingModelLoading}
							>
								{#if updateRerankingModelLoading}
									<div class="self-center">
										<svg
											class=" w-4 h-4"
											viewBox="0 0 24 24"
											fill="currentColor"
											xmlns="http://www.w3.org/2000/svg"
											><style>
												.spinner_ajPY {
													transform-origin: center;
													animation: spinner_AtaB 0.75s infinite linear;
												}
												@keyframes spinner_AtaB {
													100% {
														transform: rotate(360deg);
													}
												}
											</style><path
												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"
												opacity=".25"
											/><path
												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"
												class="spinner_ajPY"
											/></svg
										>
									</div>
								{:else}
Steven Kreitzer's avatar
Steven Kreitzer committed
462
463
									<svg
										xmlns="http://www.w3.org/2000/svg"
Steven Kreitzer's avatar
Steven Kreitzer committed
464
465
466
										viewBox="0 0 16 16"
										fill="currentColor"
										class="w-4 h-4"
Steven Kreitzer's avatar
Steven Kreitzer committed
467
									>
Steven Kreitzer's avatar
Steven Kreitzer committed
468
469
470
471
472
473
474
475
476
477
										<path
											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"
										/>
										<path
											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"
										/>
									</svg>
								{/if}
							</button>
						</div>
Steven Kreitzer's avatar
Steven Kreitzer committed
478
479
					</div>

Steven Kreitzer's avatar
Steven Kreitzer committed
480
481
					<hr class=" dark:border-gray-700 my-3" />
				{/if}
Steven Kreitzer's avatar
Steven Kreitzer committed
482

483
484
485
486
487
				<div class="  flex w-full justify-between">
					<div class=" self-center text-xs font-medium">
						{$i18n.t('Scan for documents from {{path}}', { path: '/data/docs' })}
					</div>

488
					<button
489
490
491
						class=" self-center text-xs p-1 px-3 bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded-lg flex flex-row space-x-1 items-center {scanDirLoading
							? ' cursor-not-allowed'
							: ''}"
492
						on:click={() => {
493
494
							scanHandler();
							console.log('check');
495
						}}
496
497
						type="button"
						disabled={scanDirLoading}
498
					>
499
500
501
502
						<div class="self-center font-medium">{$i18n.t('Scan')}</div>

						{#if scanDirLoading}
							<div class="ml-3 self-center">
503
								<svg
504
									class=" w-3 h-3"
505
506
507
508
509
510
511
									viewBox="0 0 24 24"
									fill="currentColor"
									xmlns="http://www.w3.org/2000/svg"
									><style>
										.spinner_ajPY {
											transform-origin: center;
											animation: spinner_AtaB 0.75s infinite linear;
512
										}
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
										@keyframes spinner_AtaB {
											100% {
												transform: rotate(360deg);
											}
										}
									</style><path
										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"
										opacity=".25"
									/><path
										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"
										class="spinner_ajPY"
									/></svg
								>
							</div>
						{/if}
					</button>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
529
				</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
530

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
531
				<hr class=" dark:border-gray-700 my-3" />
Timothy J. Baek's avatar
Timothy J. Baek committed
532

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
533
534
				<div class=" ">
					<div class=" text-sm font-medium">{$i18n.t('Chunk Params')}</div>
535

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
536
537
538
					<div class=" flex">
						<div class="  flex w-full justify-between">
							<div class="self-center text-xs font-medium min-w-fit">{$i18n.t('Chunk Size')}</div>
539

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
540
541
							<div class="self-center p-3">
								<input
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
542
									class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
543
544
545
546
547
548
549
550
									type="number"
									placeholder={$i18n.t('Enter Chunk Size')}
									bind:value={chunkSize}
									autocomplete="off"
									min="0"
								/>
							</div>
						</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
551

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
552
553
554
555
						<div class="flex w-full">
							<div class=" self-center text-xs font-medium min-w-fit">
								{$i18n.t('Chunk Overlap')}
							</div>
556

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
557
558
							<div class="self-center p-3">
								<input
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
559
									class="w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
560
561
562
563
564
565
566
567
568
									type="number"
									placeholder={$i18n.t('Enter Chunk Overlap')}
									bind:value={chunkOverlap}
									autocomplete="off"
									min="0"
								/>
							</div>
						</div>
					</div>
569

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
570
					<div class="pr-2">
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
571
572
573
574
575
576
577
578
579
580
581
						<div class="flex justify-between items-center text-xs">
							<div class=" text-xs font-medium">{$i18n.t('PDF Extract Images (OCR)')}</div>

							<button
								class=" text-xs font-medium text-gray-500"
								type="button"
								on:click={() => {
									pdfExtractImages = !pdfExtractImages;
								}}>{pdfExtractImages ? $i18n.t('On') : $i18n.t('Off')}</button
							>
						</div>
582
583
584
					</div>
				</div>

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
585
586
				<hr class=" dark:border-gray-700 my-3" />

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
587
588
589
590
591
592
593
594
595
				<div>
					<div class=" text-sm font-medium">{$i18n.t('Query Params')}</div>

					<div class=" flex">
						<div class="  flex w-full justify-between">
							<div class="self-center text-xs font-medium flex-1">{$i18n.t('Top K')}</div>

							<div class="self-center p-3">
								<input
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
596
									class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
597
598
599
600
601
602
603
604
605
									type="number"
									placeholder={$i18n.t('Enter Top K')}
									bind:value={querySettings.k}
									autocomplete="off"
									min="0"
								/>
							</div>
						</div>
					</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
606

Steven Kreitzer's avatar
Steven Kreitzer committed
607
608
609
610
611
612
					{#if querySettings.hybrid === true}
						<div class=" flex">
							<div class="  flex w-full justify-between">
								<div class="self-center text-xs font-medium flex-1">
									{$i18n.t('Relevance Threshold')}
								</div>
613

Steven Kreitzer's avatar
Steven Kreitzer committed
614
615
616
617
618
619
620
621
622
623
624
								<div class="self-center p-3">
									<input
										class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
										type="number"
										step="0.01"
										placeholder={$i18n.t('Enter Relevance Threshold')}
										bind:value={querySettings.r}
										autocomplete="off"
										min="0.0"
									/>
								</div>
625
626
							</div>
						</div>
Steven Kreitzer's avatar
Steven Kreitzer committed
627
					{/if}
628

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
629
630
631
632
					<div>
						<div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
						<textarea
							bind:value={querySettings.template}
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
633
							class="w-full rounded-lg px-4 py-3 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
634
							rows="4"
Timothy J. Baek's avatar
Timothy J. Baek committed
635
						/>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
636
					</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
637
638
				</div>

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
639
				<hr class=" dark:border-gray-700 my-3" />
Timothy J. Baek's avatar
Timothy J. Baek committed
640

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
				{#if showResetConfirm}
					<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
						<div class="flex items-center space-x-3">
							<svg
								xmlns="http://www.w3.org/2000/svg"
								viewBox="0 0 16 16"
								fill="currentColor"
								class="w-4 h-4"
							>
								<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" />
								<path
									fill-rule="evenodd"
									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"
									clip-rule="evenodd"
								/>
							</svg>
							<span>{$i18n.t('Are you sure?')}</span>
						</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
659

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
						<div class="flex space-x-1.5 items-center">
							<button
								class="hover:text-white transition"
								on:click={() => {
									const res = resetVectorDB(localStorage.token).catch((error) => {
										toast.error(error);
										return null;
									});

									if (res) {
										toast.success($i18n.t('Success'));
									}

									showResetConfirm = false;
								}}
							>
								<svg
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 20 20"
									fill="currentColor"
									class="w-4 h-4"
								>
									<path
										fill-rule="evenodd"
										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"
										clip-rule="evenodd"
									/>
								</svg>
							</button>
							<button
								class="hover:text-white transition"
								on:click={() => {
									showResetConfirm = false;
								}}
							>
								<svg
									xmlns="http://www.w3.org/2000/svg"
									viewBox="0 0 20 20"
									fill="currentColor"
									class="w-4 h-4"
								>
									<path
										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"
									/>
								</svg>
							</button>
						</div>
					</div>
				{:else}
Timothy J. Baek's avatar
Timothy J. Baek committed
709
					<button
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
710
						class=" flex rounded-md py-2 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
Timothy J. Baek's avatar
Timothy J. Baek committed
711
						on:click={() => {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
712
							showResetConfirm = true;
Timothy J. Baek's avatar
Timothy J. Baek committed
713
714
						}}
					>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
						<div class=" self-center mr-3">
							<svg
								xmlns="http://www.w3.org/2000/svg"
								viewBox="0 0 16 16"
								fill="currentColor"
								class="w-4 h-4"
							>
								<path
									fill-rule="evenodd"
									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"
									clip-rule="evenodd"
								/>
							</svg>
						</div>
						<div class=" self-center text-sm font-medium">{$i18n.t('Reset Vector Storage')}</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
730
					</button>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
731
				{/if}
Timothy J. Baek's avatar
Timothy J. Baek committed
732
			</div>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
733
		</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
734
	</div>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
735
736
	<div class="flex justify-end pt-3 text-sm font-medium">
		<button
Timothy J. Baek's avatar
Timothy J. Baek committed
737
			class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
738
739
740
741
742
			type="submit"
		>
			{$i18n.t('Save')}
		</button>
	</div>
743
</form>