General.svelte 22.5 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
		}).catch(async (error) => {
			toast.error(error);
			await setRerankingConfig();
			return null;
		});
		updateRerankingModelLoading = false;

		if (res) {
			console.log('rerankingModelUpdateHandler:', res);
			if (res.status === true) {
Steven Kreitzer's avatar
Steven Kreitzer committed
140
141
142
143
144
145
146
147
148
				if (rerankingModel === '') {
					toast.success($i18n.t('Reranking model disabled', res), {
						duration: 1000 * 10
					});
				} else {
					toast.success($i18n.t('Reranking model set to "{{reranking_model}}"', res), {
						duration: 1000 * 10
					});
				}
Steven Kreitzer's avatar
Steven Kreitzer committed
149
150
151
152
			}
		}
	};

Timothy J. Baek's avatar
Timothy J. Baek committed
153
	const submitHandler = async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
154
155
156
157
158
159
160
		const res = await updateRAGConfig(localStorage.token, {
			pdf_extract_images: pdfExtractImages,
			chunk: {
				chunk_overlap: chunkOverlap,
				chunk_size: chunkSize
			}
		});
161
		querySettings = await updateQuerySettings(localStorage.token, querySettings);
Timothy J. Baek's avatar
Timothy J. Baek committed
162
163
	};

164
165
166
167
168
169
170
	const setEmbeddingConfig = async () => {
		const embeddingConfig = await getEmbeddingConfig(localStorage.token);

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

171
172
			OpenAIKey = embeddingConfig.openai_config.key;
			OpenAIUrl = embeddingConfig.openai_config.url;
173
174
175
		}
	};

Steven Kreitzer's avatar
Steven Kreitzer committed
176
177
178
179
180
181
182
183
	const setRerankingConfig = async () => {
		const rerankingConfig = await getRerankingConfig(localStorage.token);

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

Steven Kreitzer's avatar
Steven Kreitzer committed
184
185
186
187
188
189
	const toggleHybridSearch = async () => {
		querySettings.hybrid = !querySettings.hybrid;

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

Timothy J. Baek's avatar
Timothy J. Baek committed
190
	onMount(async () => {
Timothy J. Baek's avatar
Timothy J. Baek committed
191
		const res = await getRAGConfig(localStorage.token);
Timothy J. Baek's avatar
Timothy J. Baek committed
192
193

		if (res) {
Timothy J. Baek's avatar
Timothy J. Baek committed
194
195
196
197
			pdfExtractImages = res.pdf_extract_images;

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

200
		await setEmbeddingConfig();
Steven Kreitzer's avatar
Steven Kreitzer committed
201
		await setRerankingConfig();
202

203
		querySettings = await getQuerySettings(localStorage.token);
Timothy J. Baek's avatar
Timothy J. Baek committed
204
	});
205
206
207
208
209
</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
210
		submitHandler();
211
212
213
		saveHandler();
	}}
>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
214
	<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
215
		<div>
216
			<div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
217

Steven Kreitzer's avatar
Steven Kreitzer committed
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
			<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>

236
			<div class=" flex w-full justify-between">
Timothy J. Baek's avatar
Timothy J. Baek committed
237
				<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Model Engine')}</div>
238
239
240
241
				<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
242
						placeholder="Select an embedding model engine"
243
244
245
246
247
248
						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
249
						}}
250
					>
251
						<option value="">{$i18n.t('Default (SentenceTransformers)')}</option>
252
						<option value="ollama">{$i18n.t('Ollama')}</option>
253
						<option value="openai">{$i18n.t('OpenAI')}</option>
254
					</select>
255
				</div>
256
			</div>
257
258
259
260
261
262

			{#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')}
263
						bind:value={OpenAIUrl}
264
265
266
267
268
269
						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')}
270
						bind:value={OpenAIKey}
271
272
273
274
						required
					/>
				</div>
			{/if}
275
		</div>
276

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

281
				{#if embeddingEngine === 'ollama'}
Timothy J. Baek's avatar
Timothy J. Baek committed
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
343
344
345
346
347
348
					<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>
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
403
404
405
406
407
408
				{: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>
409
					</div>
410
411
				{/if}

Timothy J. Baek's avatar
Timothy J. Baek committed
412
413
414
415
416
417
				<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>

418
419
				<hr class=" dark:border-gray-700 my-3" />

Steven Kreitzer's avatar
Steven Kreitzer committed
420
421
422
				{#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
423

Steven Kreitzer's avatar
Steven Kreitzer committed
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
462
463
464
465
466
467
						<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
468
469
									<svg
										xmlns="http://www.w3.org/2000/svg"
Steven Kreitzer's avatar
Steven Kreitzer committed
470
471
472
										viewBox="0 0 16 16"
										fill="currentColor"
										class="w-4 h-4"
Steven Kreitzer's avatar
Steven Kreitzer committed
473
									>
Steven Kreitzer's avatar
Steven Kreitzer committed
474
475
476
477
478
479
480
481
482
483
										<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
484
485
					</div>

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

489
490
491
492
493
				<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>

494
					<button
495
496
497
						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'
							: ''}"
498
						on:click={() => {
499
500
							scanHandler();
							console.log('check');
501
						}}
502
503
						type="button"
						disabled={scanDirLoading}
504
					>
505
506
507
508
						<div class="self-center font-medium">{$i18n.t('Scan')}</div>

						{#if scanDirLoading}
							<div class="ml-3 self-center">
509
								<svg
510
									class=" w-3 h-3"
511
512
513
514
515
516
517
									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;
518
										}
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
										@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
535
				</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
536

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

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

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
542
543
544
					<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>
545

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
546
547
							<div class="self-center p-3">
								<input
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
548
									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
549
550
551
552
553
554
555
556
									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
557

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
558
559
560
561
						<div class="flex w-full">
							<div class=" self-center text-xs font-medium min-w-fit">
								{$i18n.t('Chunk Overlap')}
							</div>
562

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
563
564
							<div class="self-center p-3">
								<input
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
565
									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
566
567
568
569
570
571
572
573
574
									type="number"
									placeholder={$i18n.t('Enter Chunk Overlap')}
									bind:value={chunkOverlap}
									autocomplete="off"
									min="0"
								/>
							</div>
						</div>
					</div>
575

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
576
					<div class="pr-2">
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
577
578
579
580
581
582
583
584
585
586
587
						<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>
588
589
590
					</div>
				</div>

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

Steven Kreitzer's avatar
Steven Kreitzer committed
593
				<div class=" ">
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
594
595
596
597
					<div class=" text-sm font-medium">{$i18n.t('Query Params')}</div>

					<div class=" flex">
						<div class="  flex w-full justify-between">
Steven Kreitzer's avatar
Steven Kreitzer committed
598
							<div class="self-center text-xs font-medium min-w-fit">{$i18n.t('Top K')}</div>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
599
600
601

							<div class="self-center p-3">
								<input
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
602
									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
603
604
605
606
607
608
609
610
									type="number"
									placeholder={$i18n.t('Enter Top K')}
									bind:value={querySettings.k}
									autocomplete="off"
									min="0"
								/>
							</div>
						</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
611

Steven Kreitzer's avatar
Steven Kreitzer committed
612
613
614
615
						{#if querySettings.hybrid === true}
							<div class="flex w-full">
								<div class=" self-center text-xs font-medium min-w-fit">
									{$i18n.t('Minimum Score')}
Steven Kreitzer's avatar
Steven Kreitzer committed
616
								</div>
617

Steven Kreitzer's avatar
Steven Kreitzer committed
618
619
620
621
622
								<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"
Steven Kreitzer's avatar
Steven Kreitzer committed
623
										placeholder={$i18n.t('Enter Score')}
Steven Kreitzer's avatar
Steven Kreitzer committed
624
625
626
										bind:value={querySettings.r}
										autocomplete="off"
										min="0.0"
Steven Kreitzer's avatar
Steven Kreitzer committed
627
										title={$i18n.t('The score should be a value between 0.0 (0%) and 1.0 (100%).')}
Steven Kreitzer's avatar
Steven Kreitzer committed
628
629
									/>
								</div>
630
							</div>
Steven Kreitzer's avatar
Steven Kreitzer committed
631
632
633
634
635
636
637
638
						{/if}
					</div>

					{#if querySettings.hybrid === true}
						<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
							{$i18n.t(
								'Note: If you set a minimum score, the search will only return documents with a score greater than or equal to the minimum score.'
							)}
639
						</div>
Steven Kreitzer's avatar
Steven Kreitzer committed
640
641

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

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
644
645
646
647
					<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
648
							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
649
							rows="4"
Timothy J. Baek's avatar
Timothy J. Baek committed
650
						/>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
651
					</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
652
653
				</div>

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
				{#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
672

Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
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
709
710
711
712
713
714
715
716
717
718
719
720
721
						<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
722
					<button
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
723
						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
724
						on:click={() => {
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
725
							showResetConfirm = true;
Timothy J. Baek's avatar
Timothy J. Baek committed
726
727
						}}
					>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
						<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
743
					</button>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
744
				{/if}
Timothy J. Baek's avatar
Timothy J. Baek committed
745
			</div>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
746
		</div>
Timothy J. Baek's avatar
Timothy J. Baek committed
747
	</div>
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
748
749
	<div class="flex justify-end pt-3 text-sm font-medium">
		<button
Timothy J. Baek's avatar
Timothy J. Baek committed
750
			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
751
752
753
754
755
			type="submit"
		>
			{$i18n.t('Save')}
		</button>
	</div>
756
</form>