forked from open-webui/open-webui
		
	merged from main and added new translation keys
This commit is contained in:
		
						commit
						aa3985e879
					
				
					 13 changed files with 368 additions and 172 deletions
				
			
		|  | @ -1,9 +1,9 @@ | |||
| import { RAG_API_BASE_URL } from '$lib/constants'; | ||||
| 
 | ||||
| export const getChunkParams = async (token: string) => { | ||||
| export const getRAGConfig = async (token: string) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${RAG_API_BASE_URL}/chunk`, { | ||||
| 	const res = await fetch(`${RAG_API_BASE_URL}/config`, { | ||||
| 		method: 'GET', | ||||
| 		headers: { | ||||
| 			'Content-Type': 'application/json', | ||||
|  | @ -27,18 +27,27 @@ export const getChunkParams = async (token: string) => { | |||
| 	return res; | ||||
| }; | ||||
| 
 | ||||
| export const updateChunkParams = async (token: string, size: number, overlap: number) => { | ||||
| type ChunkConfigForm = { | ||||
| 	chunk_size: number; | ||||
| 	chunk_overlap: number; | ||||
| }; | ||||
| 
 | ||||
| type RAGConfigForm = { | ||||
| 	pdf_extract_images: boolean; | ||||
| 	chunk: ChunkConfigForm; | ||||
| }; | ||||
| 
 | ||||
| export const updateRAGConfig = async (token: string, payload: RAGConfigForm) => { | ||||
| 	let error = null; | ||||
| 
 | ||||
| 	const res = await fetch(`${RAG_API_BASE_URL}/chunk/update`, { | ||||
| 	const res = await fetch(`${RAG_API_BASE_URL}/config/update`, { | ||||
| 		method: 'POST', | ||||
| 		headers: { | ||||
| 			'Content-Type': 'application/json', | ||||
| 			Authorization: `Bearer ${token}` | ||||
| 		}, | ||||
| 		body: JSON.stringify({ | ||||
| 			chunk_size: size, | ||||
| 			chunk_overlap: overlap | ||||
| 			...payload | ||||
| 		}) | ||||
| 	}) | ||||
| 		.then(async (res) => { | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ | |||
| 	import { splitStream } from '$lib/utils'; | ||||
| 	import { onMount, getContext } from 'svelte'; | ||||
| 	import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm'; | ||||
| 	import Tooltip from '$lib/components/common/Tooltip.svelte'; | ||||
| 
 | ||||
| 	const i18n = getContext('i18n'); | ||||
| 
 | ||||
|  | @ -39,6 +40,10 @@ | |||
| 
 | ||||
| 	let OLLAMA_URLS = []; | ||||
| 	let selectedOllamaUrlIdx: string | null = null; | ||||
| 
 | ||||
| 	let updateModelId = null; | ||||
| 	let updateProgress = null; | ||||
| 
 | ||||
| 	let showExperimentalOllama = false; | ||||
| 	let ollamaVersion = ''; | ||||
| 	const MAX_PARALLEL_DOWNLOADS = 3; | ||||
|  | @ -63,6 +68,71 @@ | |||
| 
 | ||||
| 	let deleteModelTag = ''; | ||||
| 
 | ||||
| 	const updateModelsHandler = async () => { | ||||
| 		for (const model of $models.filter( | ||||
| 			(m) => | ||||
| 				m.size != null && | ||||
| 				(selectedOllamaUrlIdx === null ? true : (m?.urls ?? []).includes(selectedOllamaUrlIdx)) | ||||
| 		)) { | ||||
| 			console.log(model); | ||||
| 
 | ||||
| 			updateModelId = model.id; | ||||
| 			const res = await pullModel(localStorage.token, model.id, selectedOllamaUrlIdx).catch( | ||||
| 				(error) => { | ||||
| 					toast.error(error); | ||||
| 					return null; | ||||
| 				} | ||||
| 			); | ||||
| 
 | ||||
| 			if (res) { | ||||
| 				const reader = res.body | ||||
| 					.pipeThrough(new TextDecoderStream()) | ||||
| 					.pipeThrough(splitStream('\n')) | ||||
| 					.getReader(); | ||||
| 
 | ||||
| 				while (true) { | ||||
| 					try { | ||||
| 						const { value, done } = await reader.read(); | ||||
| 						if (done) break; | ||||
| 
 | ||||
| 						let lines = value.split('\n'); | ||||
| 
 | ||||
| 						for (const line of lines) { | ||||
| 							if (line !== '') { | ||||
| 								let data = JSON.parse(line); | ||||
| 
 | ||||
| 								console.log(data); | ||||
| 								if (data.error) { | ||||
| 									throw data.error; | ||||
| 								} | ||||
| 								if (data.detail) { | ||||
| 									throw data.detail; | ||||
| 								} | ||||
| 								if (data.status) { | ||||
| 									if (data.digest) { | ||||
| 										updateProgress = 0; | ||||
| 										if (data.completed) { | ||||
| 											updateProgress = Math.round((data.completed / data.total) * 1000) / 10; | ||||
| 										} else { | ||||
| 											updateProgress = 100; | ||||
| 										} | ||||
| 									} else { | ||||
| 										toast.success(data.status); | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} catch (error) { | ||||
| 						console.log(error); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		updateModelId = null; | ||||
| 		updateProgress = null; | ||||
| 	}; | ||||
| 
 | ||||
| 	const pullModelHandler = async () => { | ||||
| 		const sanitizedModelTag = modelTag.trim(); | ||||
| 		if (modelDownloadStatus[sanitizedModelTag]) { | ||||
|  | @ -389,7 +459,7 @@ | |||
| 			return []; | ||||
| 		}); | ||||
| 
 | ||||
| 		if (OLLAMA_URLS.length > 1) { | ||||
| 		if (OLLAMA_URLS.length > 0) { | ||||
| 			selectedOllamaUrlIdx = 0; | ||||
| 		} | ||||
| 
 | ||||
|  | @ -404,18 +474,51 @@ | |||
| 			<div class="space-y-2 pr-1.5"> | ||||
| 				<div class="text-sm font-medium">{$i18n.t('Manage Ollama Models')}</div> | ||||
| 
 | ||||
| 				{#if OLLAMA_URLS.length > 1} | ||||
| 					<div class="flex-1 pb-1"> | ||||
| 						<select | ||||
| 							class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" | ||||
| 							bind:value={selectedOllamaUrlIdx} | ||||
| 							placeholder={$i18n.t('Select an Ollama instance')} | ||||
| 						> | ||||
| 							{#each OLLAMA_URLS as url, idx} | ||||
| 								<option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option> | ||||
| 							{/each} | ||||
| 						</select> | ||||
| 				{#if OLLAMA_URLS.length > 0} | ||||
| 					<div class="flex gap-2"> | ||||
| 						<div class="flex-1 pb-1"> | ||||
| 							<select | ||||
| 								class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none" | ||||
| 								bind:value={selectedOllamaUrlIdx} | ||||
| 								placeholder="Select an Ollama instance" | ||||
| 							> | ||||
| 								{#each OLLAMA_URLS as url, idx} | ||||
| 									<option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option> | ||||
| 								{/each} | ||||
| 							</select> | ||||
| 						</div> | ||||
| 
 | ||||
| 						<div> | ||||
| 							<div class="flex w-full justify-end"> | ||||
| 								<Tooltip content="Update All Models" placement="top"> | ||||
| 									<button | ||||
| 										class="p-2.5 flex gap-2 items-center 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={() => { | ||||
| 											updateModelsHandler(); | ||||
| 										}} | ||||
| 									> | ||||
| 										<svg | ||||
| 											xmlns="http://www.w3.org/2000/svg" | ||||
| 											viewBox="0 0 16 16" | ||||
| 											fill="currentColor" | ||||
| 											class="w-4 h-4" | ||||
| 										> | ||||
| 											<path | ||||
| 												d="M7 1a.75.75 0 0 1 .75.75V6h-1.5V1.75A.75.75 0 0 1 7 1ZM6.25 6v2.94L5.03 7.72a.75.75 0 0 0-1.06 1.06l2.5 2.5a.75.75 0 0 0 1.06 0l2.5-2.5a.75.75 0 1 0-1.06-1.06L7.75 8.94V6H10a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h2.25Z" | ||||
| 											/> | ||||
| 											<path | ||||
| 												d="M4.268 14A2 2 0 0 0 6 15h6a2 2 0 0 0 2-2v-3a2 2 0 0 0-1-1.732V11a3 3 0 0 1-3 3H4.268Z" | ||||
| 											/> | ||||
| 										</svg> | ||||
| 									</button> | ||||
| 								</Tooltip> | ||||
| 							</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 
 | ||||
| 					{#if updateModelId} | ||||
| 						Updating "{updateModelId}" {updateProgress ? `(${updateProgress}%)` : ''} | ||||
| 					{/if} | ||||
| 				{/if} | ||||
| 
 | ||||
| 				<div class="space-y-2"> | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| <script lang="ts"> | ||||
| 	import { getDocs } from '$lib/apis/documents'; | ||||
| 	import { | ||||
| 		getChunkParams, | ||||
| 		getRAGConfig, | ||||
| 		updateRAGConfig, | ||||
| 		getQuerySettings, | ||||
| 		scanDocs, | ||||
| 		updateChunkParams, | ||||
| 		updateQuerySettings | ||||
| 	} from '$lib/apis/rag'; | ||||
| 	import { documents } from '$lib/stores'; | ||||
|  | @ -19,6 +19,7 @@ | |||
| 
 | ||||
| 	let chunkSize = 0; | ||||
| 	let chunkOverlap = 0; | ||||
| 	let pdfExtractImages = true; | ||||
| 
 | ||||
| 	let querySettings = { | ||||
| 		template: '', | ||||
|  | @ -37,16 +38,24 @@ | |||
| 	}; | ||||
| 
 | ||||
| 	const submitHandler = async () => { | ||||
| 		const res = await updateChunkParams(localStorage.token, chunkSize, chunkOverlap); | ||||
| 		const res = await updateRAGConfig(localStorage.token, { | ||||
| 			pdf_extract_images: pdfExtractImages, | ||||
| 			chunk: { | ||||
| 				chunk_overlap: chunkOverlap, | ||||
| 				chunk_size: chunkSize | ||||
| 			} | ||||
| 		}); | ||||
| 		querySettings = await updateQuerySettings(localStorage.token, querySettings); | ||||
| 	}; | ||||
| 
 | ||||
| 	onMount(async () => { | ||||
| 		const res = await getChunkParams(localStorage.token); | ||||
| 		const res = await getRAGConfig(localStorage.token); | ||||
| 
 | ||||
| 		if (res) { | ||||
| 			chunkSize = res.chunk_size; | ||||
| 			chunkOverlap = res.chunk_overlap; | ||||
| 			pdfExtractImages = res.pdf_extract_images; | ||||
| 
 | ||||
| 			chunkSize = res.chunk.chunk_size; | ||||
| 			chunkOverlap = res.chunk.chunk_overlap; | ||||
| 		} | ||||
| 
 | ||||
| 		querySettings = await getQuerySettings(localStorage.token); | ||||
|  | @ -163,6 +172,22 @@ | |||
| 				</div> | ||||
| 			</div> | ||||
| 
 | ||||
| 			<div> | ||||
| 				<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> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 
 | ||||
| 		<div> | ||||
| 			<div class=" text-sm font-medium">{$i18n.t('Query Params')}</div> | ||||
| 
 | ||||
| 			<div class=" flex"> | ||||
|  | @ -182,19 +207,19 @@ | |||
| 				</div> | ||||
| 
 | ||||
| 				<!-- <div class="flex w-full"> | ||||
| 					<div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div> | ||||
| 
 | ||||
| 					<div class="self-center p-3"> | ||||
| 						<input | ||||
| 							class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600" | ||||
| 							type="number" | ||||
| 							placeholder="Enter Chunk Overlap" | ||||
| 							bind:value={chunkOverlap} | ||||
| 							autocomplete="off" | ||||
| 							min="0" | ||||
| 						/> | ||||
| 					</div> | ||||
| 				</div> --> | ||||
| 						<div class=" self-center text-xs font-medium min-w-fit">Chunk Overlap</div> | ||||
| 	 | ||||
| 						<div class="self-center p-3"> | ||||
| 							<input | ||||
| 								class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600" | ||||
| 								type="number" | ||||
| 								placeholder="Enter Chunk Overlap" | ||||
| 								bind:value={chunkOverlap} | ||||
| 								autocomplete="off" | ||||
| 								min="0" | ||||
| 							/> | ||||
| 						</div> | ||||
| 					</div> --> | ||||
| 			</div> | ||||
| 
 | ||||
| 			<div> | ||||
|  |  | |||
|  | @ -236,7 +236,7 @@ | |||
| 	"Prompts": "Prompts", | ||||
| 	"Pull a model from Ollama.com": "Ein Modell von Ollama.com abrufen", | ||||
| 	"Pull Progress": "Fortschritt abrufen", | ||||
| 	"Query Params": "", | ||||
| 	"Query Params": "Query Parameter", | ||||
| 	"RAG Template": "RAG-Vorlage", | ||||
| 	"Raw Format": "Rohformat", | ||||
| 	"Record voice": "Stimme aufnehmen", | ||||
|  | @ -346,5 +346,6 @@ | |||
| 	"Write a summary in 50 words that summarizes [topic or keyword].": "Schreibe eine kurze Zusammenfassung in 50 Wörtern, die [Thema oder Schlüsselwort] zusammenfasst.", | ||||
| 	"You": "Du", | ||||
| 	"You're a helpful assistant.": "Du bist ein hilfreicher Assistent.", | ||||
| 	"You're now logged in.": "Du bist nun eingeloggt." | ||||
| 	"You're now logged in.": "Du bist nun eingeloggt.", | ||||
| 	"PDF Extract Images (OCR)": "Text von Bilder aus PDFs extrahieren (OCR)" | ||||
| } | ||||
|  |  | |||
|  | @ -236,7 +236,7 @@ | |||
| 	"Prompts": "Prompts", | ||||
| 	"Pull a model from Ollama.com": "Pull a model from Ollama.com", | ||||
| 	"Pull Progress": "Pull Progress", | ||||
| 	"Query Params": "", | ||||
| 	"Query Params": "Query Params", | ||||
| 	"RAG Template": "RAG Template", | ||||
| 	"Raw Format": "Raw Format", | ||||
| 	"Record voice": "Record voice", | ||||
|  | @ -346,5 +346,6 @@ | |||
| 	"Write a summary in 50 words that summarizes [topic or keyword].": "Write a summary in 50 words that summarizes [topic or keyword].", | ||||
| 	"You": "You", | ||||
| 	"You're a helpful assistant.": "You're a helpful assistant.", | ||||
| 	"You're now logged in.": "You're now logged in." | ||||
| 	"You're now logged in.": "You're now logged in.", | ||||
| 	"PDF Extract Images (OCR)": "PDF Extract Images (OCR)" | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jannik Streidl
						Jannik Streidl