Unverified Commit 78284e49 authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge pull request #1488 from open-webui/dev

0.1.118
parents 331fe04d 64a6db4b
{{- if not .Values.ollama.externalHost }}
apiVersion: v1
kind: Service
metadata:
......@@ -19,3 +20,4 @@ spec:
port: {{ .port }}
targetPort: http
{{- end }}
{{- end }}
{{- if not .Values.ollama.externalHost }}
apiVersion: apps/v1
kind: StatefulSet
metadata:
......@@ -94,3 +95,4 @@ spec:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- end }}
{{- end }}
......@@ -17,7 +17,9 @@ spec:
resources:
requests:
storage: {{ .Values.webui.persistence.size }}
{{- if .Values.webui.persistence.storageClass }}
storageClassName: {{ .Values.webui.persistence.storageClass }}
{{- end }}
{{- with .Values.webui.persistence.selector }}
selector:
{{- toYaml . | nindent 4 }}
......
nameOverride: ""
ollama:
externalHost: ""
annotations: {}
podAnnotations: {}
replicaCount: 1
......
{
"name": "open-webui",
"version": "0.1.117",
"version": "0.1.118",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open-webui",
"version": "0.1.117",
"version": "0.1.118",
"dependencies": {
"@sveltejs/adapter-node": "^1.3.1",
"async": "^3.2.5",
......@@ -5688,9 +5688,9 @@
}
},
"node_modules/undici": {
"version": "5.28.3",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz",
"integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==",
"version": "5.28.4",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
"integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
......
{
"name": "open-webui",
"version": "0.1.117",
"version": "0.1.118",
"private": true,
"scripts": {
"dev": "vite dev --host",
......
......@@ -58,7 +58,12 @@ export const userSignIn = async (email: string, password: string) => {
return res;
};
export const userSignUp = async (name: string, email: string, password: string) => {
export const userSignUp = async (
name: string,
email: string,
password: string,
profile_image_url: string
) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/auths/signup`, {
......@@ -69,7 +74,8 @@ export const userSignUp = async (name: string, email: string, password: string)
body: JSON.stringify({
name: name,
email: email,
password: password
password: password,
profile_image_url: profile_image_url
})
})
.then(async (res) => {
......
......@@ -345,3 +345,64 @@ export const resetVectorDB = async (token: string) => {
return res;
};
export const getEmbeddingModel = async (token: string) => {
let error = null;
const res = await fetch(`${RAG_API_BASE_URL}/embedding/model`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
type EmbeddingModelUpdateForm = {
embedding_model: string;
};
export const updateEmbeddingModel = async (token: string, payload: EmbeddingModelUpdateForm) => {
let error = null;
const res = await fetch(`${RAG_API_BASE_URL}/embedding/model/update`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
...payload
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err.detail;
return null;
});
if (error) {
throw error;
}
return res;
};
......@@ -295,6 +295,13 @@
const dropZone = document.querySelector('body');
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
console.log('Escape');
dragged = false;
}
};
const onDragOver = (e) => {
e.preventDefault();
dragged = true;
......@@ -350,11 +357,15 @@
dragged = false;
};
window.addEventListener('keydown', handleKeyDown);
dropZone?.addEventListener('dragover', onDragOver);
dropZone?.addEventListener('drop', onDrop);
dropZone?.addEventListener('dragleave', onDragLeave);
return () => {
window.removeEventListener('keydown', handleKeyDown);
dropZone?.removeEventListener('dragover', onDragOver);
dropZone?.removeEventListener('drop', onDrop);
dropZone?.removeEventListener('dragleave', onDragLeave);
......
......@@ -107,12 +107,8 @@
await sendPrompt(userPrompt, userMessageId, chatId);
};
const confirmEditResponseMessage = async (messageId, content) => {
history.messages[messageId].originalContent = history.messages[messageId].content;
history.messages[messageId].content = content;
const updateChatMessages = async () => {
await tick();
await updateChatById(localStorage.token, chatId, {
messages: messages,
history: history
......@@ -121,15 +117,20 @@
await chats.set(await getChatList(localStorage.token));
};
const confirmEditResponseMessage = async (messageId, content) => {
history.messages[messageId].originalContent = history.messages[messageId].content;
history.messages[messageId].content = content;
await updateChatMessages();
};
const rateMessage = async (messageId, rating) => {
history.messages[messageId].rating = rating;
await tick();
await updateChatById(localStorage.token, chatId, {
messages: messages,
history: history
});
history.messages[messageId].annotation = {
...history.messages[messageId].annotation,
rating: rating
};
await chats.set(await getChatList(localStorage.token));
await updateChatMessages();
};
const showPreviousMessage = async (message) => {
......@@ -338,6 +339,7 @@
siblings={history.messages[message.parentId]?.childrenIds ?? []}
isLastMessage={messageIdx + 1 === messages.length}
{readOnly}
{updateChatMessages}
{confirmEditResponseMessage}
{showPreviousMessage}
{showNextMessage}
......
<script lang="ts">
import { toast } from 'svelte-sonner';
import { createEventDispatcher, onMount } from 'svelte';
const dispatch = createEventDispatcher();
export let show = false;
export let message;
const LIKE_REASONS = [
`Accurate information`,
`Followed instructions perfectly`,
`Showcased creativity`,
`Positive attitude`,
`Attention to detail`,
`Thorough explanation`,
`Other`
];
const DISLIKE_REASONS = [
`Don't like the style`,
`Not factually correct`,
`Didn't fully follow instructions`,
`Refused when it shouldn't have`,
`Being Lazy`,
`Other`
];
let reasons = [];
let selectedReason = null;
let comment = '';
$: if (message.annotation.rating === 1) {
reasons = LIKE_REASONS;
} else if (message.annotation.rating === -1) {
reasons = DISLIKE_REASONS;
}
onMount(() => {
selectedReason = message.annotation.reason;
comment = message.annotation.comment;
});
const submitHandler = () => {
console.log('submitHandler');
message.annotation.reason = selectedReason;
message.annotation.comment = comment;
dispatch('submit');
toast.success('Thanks for your feedback!');
show = false;
};
</script>
<div class=" my-2.5 rounded-xl px-4 py-3 border dark:border-gray-850">
<div class="flex justify-between items-center">
<div class=" text-sm">Tell us more:</div>
<button
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-4"
>
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
</button>
</div>
{#if reasons.length > 0}
<div class="flex flex-wrap gap-2 text-sm mt-2.5">
{#each reasons as reason}
<button
class="px-3.5 py-1 border dark:border-gray-850 dark:hover:bg-gray-850 {selectedReason ===
reason
? 'dark:bg-gray-800'
: ''} transition rounded-lg"
on:click={() => {
selectedReason = reason;
}}
>
{reason}
</button>
{/each}
</div>
{/if}
<div class="mt-2">
<textarea
bind:value={comment}
class="w-full text-sm px-1 py-2 bg-transparent outline-none resize-none rounded-xl"
placeholder="Feel free to add specific details"
rows="2"
/>
</div>
<div class="mt-2 flex justify-end">
<button
class=" bg-emerald-700 text-white text-sm font-medium rounded-lg px-3.5 py-1.5"
on:click={() => {
submitHandler();
}}
>
Submit
</button>
</div>
</div>
......@@ -30,6 +30,7 @@
import Image from '$lib/components/common/Image.svelte';
import { WEBUI_BASE_URL } from '$lib/constants';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import RateComment from './RateComment.svelte';
export let modelfiles = [];
export let message;
......@@ -39,6 +40,7 @@
export let readOnly = false;
export let updateChatMessages: Function;
export let confirmEditResponseMessage: Function;
export let showPreviousMessage: Function;
export let showNextMessage: Function;
......@@ -60,6 +62,8 @@
let loadingSpeech = false;
let generatingImage = false;
let showRateComment = false;
$: tokens = marked.lexer(sanitizeResponseContent(message.content));
const renderer = new marked.Renderer();
......@@ -536,11 +540,13 @@
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === 1
: 'invisible group-hover:visible'} p-1 rounded {message?.annotation
?.rating === 1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, 1);
showRateComment = true;
}}
>
<svg
......@@ -563,11 +569,13 @@
<button
class="{isLastMessage
? 'visible'
: 'invisible group-hover:visible'} p-1 rounded {message.rating === -1
: 'invisible group-hover:visible'} p-1 rounded {message?.annotation
?.rating === -1
? 'bg-gray-100 dark:bg-gray-800'
: ''} dark:hover:text-white hover:text-black transition"
on:click={() => {
rateMessage(message.id, -1);
showRateComment = true;
}}
>
<svg
......@@ -824,6 +832,16 @@
{/if}
</div>
{/if}
{#if showRateComment}
<RateComment
bind:show={showRateComment}
bind:message
on:submit={() => {
updateChatMessages();
}}
/>
{/if}
</div>
{/if}
</div>
......
......@@ -7,6 +7,7 @@
import UpdatePassword from './Account/UpdatePassword.svelte';
import { getGravatarUrl } from '$lib/apis/utils';
import { generateInitialsImage, canvasPixelTest } from '$lib/utils';
import { copyToClipboard } from '$lib/utils';
import Plus from '$lib/components/icons/Plus.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
......@@ -18,6 +19,8 @@
let profileImageUrl = '';
let name = '';
let showAPIKeys = false;
let showJWTToken = false;
let JWTTokenCopied = false;
......@@ -28,6 +31,12 @@
let profileImageInputElement: HTMLInputElement;
const submitHandler = async () => {
if (name !== $user.name) {
if (profileImageUrl === generateInitialsImage($user.name) || profileImageUrl === '') {
profileImageUrl = generateInitialsImage(name);
}
}
const updatedUser = await updateUserProfile(localStorage.token, name, profileImageUrl).catch(
(error) => {
toast.error(error);
......@@ -125,11 +134,12 @@
}}
/>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Profile')}</div>
<div class="space-y-1">
<!-- <div class=" text-sm font-medium">{$i18n.t('Account')}</div> -->
<div class="flex space-x-5">
<div class="flex flex-col">
<div class="self-center">
<div class="self-center mt-2">
<button
class="relative rounded-full dark:bg-gray-700"
type="button"
......@@ -138,9 +148,9 @@
}}
>
<img
src={profileImageUrl !== '' ? profileImageUrl : '/user.png'}
src={profileImageUrl !== '' ? profileImageUrl : generateInitialsImage(name)}
alt="profile"
class=" rounded-full w-16 h-16 object-cover"
class=" rounded-full size-16 object-cover"
/>
<div
......@@ -161,23 +171,56 @@
</div>
</button>
</div>
</div>
<div class="flex-1 flex flex-col self-center gap-0.5">
<div class=" mb-0.5 text-sm font-medium">{$i18n.t('Profile Image')}</div>
<div>
<button
class=" text-xs text-center text-gray-800 dark:text-gray-400 rounded-full px-4 py-0.5 bg-gray-100 dark:bg-gray-850"
on:click={async () => {
if (canvasPixelTest()) {
profileImageUrl = generateInitialsImage(name);
} else {
toast.info(
$i18n.t(
'Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.'
),
{
duration: 1000 * 10
}
);
}
}}>{$i18n.t('Use Initials')}</button
>
<button
class=" text-xs text-gray-600"
class=" text-xs text-center text-gray-800 dark:text-gray-400 rounded-full px-4 py-0.5 bg-gray-100 dark:bg-gray-850"
on:click={async () => {
const url = await getGravatarUrl($user.email);
profileImageUrl = url;
}}>{$i18n.t('Use Gravatar')}</button
>
<button
class=" text-xs text-center text-gray-800 dark:text-gray-400 rounded-lg px-2 py-1"
on:click={async () => {
profileImageUrl = '/user.png';
}}>{$i18n.t('Remove')}</button
>
</div>
</div>
</div>
<div class="flex-1">
<div class="pt-0.5">
<div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name')}</div>
<div class=" mb-1 text-xs font-medium">{$i18n.t('Name')}</div>
<div class="flex-1">
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none"
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
bind:value={name}
required
......@@ -187,11 +230,24 @@
</div>
</div>
<hr class=" dark:border-gray-700 my-4" />
<div class="py-0.5">
<UpdatePassword />
</div>
<hr class=" dark:border-gray-700 my-4" />
<div class="flex justify-between items-center text-sm">
<div class=" font-medium">{$i18n.t('API keys')}</div>
<button
class=" text-xs font-medium text-gray-500"
type="button"
on:click={() => {
showAPIKeys = !showAPIKeys;
}}>{showAPIKeys ? $i18n.t('Hide') : $i18n.t('Show')}</button
>
</div>
{#if showAPIKeys}
<div class="flex flex-col gap-4">
<div class="justify-between w-full">
<div class="flex justify-between w-full">
......@@ -201,14 +257,14 @@
<div class="flex mt-2">
<div class="flex w-full">
<input
class="w-full rounded-l-lg py-1.5 pl-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-800 outline-none"
class="w-full rounded-l-lg py-1.5 pl-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-none"
type={showJWTToken ? 'text' : 'password'}
value={localStorage.token}
disabled
/>
<button
class="px-2 transition rounded-r-lg bg-white dark:bg-gray-800"
class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
on:click={() => {
showJWTToken = !showJWTToken;
}}
......@@ -248,7 +304,7 @@
</div>
<button
class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-800 transition rounded-lg"
class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-850 transition rounded-lg"
on:click={() => {
copyToClipboard(localStorage.token);
JWTTokenCopied = true;
......@@ -301,14 +357,14 @@
{#if APIKey}
<div class="flex w-full">
<input
class="w-full rounded-l-lg py-1.5 pl-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-800 outline-none"
class="w-full rounded-l-lg py-1.5 pl-4 text-sm bg-white dark:text-gray-300 dark:bg-gray-850 outline-none"
type={showAPIKey ? 'text' : 'password'}
value={APIKey}
disabled
/>
<button
class="px-2 transition rounded-r-lg bg-white dark:bg-gray-800"
class="px-2 transition rounded-r-lg bg-white dark:bg-gray-850"
on:click={() => {
showAPIKey = !showAPIKey;
}}
......@@ -348,7 +404,7 @@
</div>
<button
class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-800 transition rounded-lg"
class="ml-1.5 px-1.5 py-1 dark:hover:bg-gray-850 transition rounded-lg"
on:click={() => {
copyToClipboard(APIKey);
APIKeyCopied = true;
......@@ -393,7 +449,7 @@
<Tooltip content="Create new key">
<button
class=" px-1.5 py-1 dark:hover:bg-gray-800transition rounded-lg"
class=" px-1.5 py-1 dark:hover:bg-gray-850transition rounded-lg"
on:click={() => {
createAPIKeyHandler();
}}
......@@ -416,7 +472,7 @@
</Tooltip>
{:else}
<button
class="flex gap-1.5 items-center font-medium px-3.5 py-1.5 rounded-lg bg-gray-100/70 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800 transition"
class="flex gap-1.5 items-center font-medium px-3.5 py-1.5 rounded-lg bg-gray-100/70 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-850 transition"
on:click={() => {
createAPIKeyHandler();
}}
......@@ -429,6 +485,7 @@
</div>
</div>
</div>
{/if}
</div>
<div class="flex justify-end pt-3 text-sm font-medium">
......
......@@ -185,7 +185,7 @@
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Desktop Notifications')}</div>
<div class=" self-center text-xs font-medium">{$i18n.t('Notifications')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
......
......@@ -7,6 +7,7 @@
export let show = true;
export let size = 'md';
let modalElement = null;
let mounted = false;
const sizeToWidth = (size) => {
......@@ -19,14 +20,23 @@
}
};
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
console.log('Escape');
show = false;
}
};
onMount(() => {
mounted = true;
});
$: if (mounted) {
if (show) {
window.addEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'hidden';
} else {
window.removeEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'unset';
}
}
......@@ -36,6 +46,7 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
bind:this={modalElement}
class=" fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full min-h-screen h-screen flex justify-center z-50 overflow-hidden overscroll-contain"
in:fade={{ duration: 10 }}
on:click={() => {
......
......@@ -6,18 +6,23 @@
getQuerySettings,
scanDocs,
updateQuerySettings,
resetVectorDB
resetVectorDB,
getEmbeddingModel,
updateEmbeddingModel
} from '$lib/apis/rag';
import { documents } from '$lib/stores';
import { onMount, getContext } from 'svelte';
import { toast } from 'svelte-sonner';
import Tooltip from '$lib/components/common/Tooltip.svelte';
const i18n = getContext('i18n');
export let saveHandler: Function;
let loading = false;
let scanDirLoading = false;
let updateEmbeddingModelLoading = false;
let showResetConfirm = false;
......@@ -30,10 +35,12 @@
k: 4
};
let embeddingModel = '';
const scanHandler = async () => {
loading = true;
scanDirLoading = true;
const res = await scanDocs(localStorage.token);
loading = false;
scanDirLoading = false;
if (res) {
await documents.set(await getDocs(localStorage.token));
......@@ -41,6 +48,38 @@
}
};
const embeddingModelUpdateHandler = async () => {
if (embeddingModel.split('/').length - 1 > 1) {
toast.error(
$i18n.t(
'Model filesystem path detected. Model shortname is required for update, cannot continue.'
)
);
return;
}
console.log('Update embedding model attempt:', embeddingModel);
updateEmbeddingModelLoading = true;
const res = await updateEmbeddingModel(localStorage.token, {
embedding_model: embeddingModel
}).catch(async (error) => {
toast.error(error);
embeddingModel = (await getEmbeddingModel(localStorage.token)).embedding_model;
return null;
});
updateEmbeddingModelLoading = false;
if (res) {
console.log('embeddingModelUpdateHandler:', res);
if (res.status === true) {
toast.success($i18n.t('Model {{embedding_model}} update complete!', res), {
duration: 1000 * 10
});
}
}
};
const submitHandler = async () => {
const res = await updateRAGConfig(localStorage.token, {
pdf_extract_images: pdfExtractImages,
......@@ -62,6 +101,8 @@
chunkOverlap = res.chunk.chunk_overlap;
}
embeddingModel = (await getEmbeddingModel(localStorage.token)).embedding_model;
querySettings = await getQuerySettings(localStorage.token);
});
</script>
......@@ -73,7 +114,7 @@
saveHandler();
}}
>
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-[22rem]">
<div>
<div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
......@@ -83,7 +124,7 @@
</div>
<button
class=" self-center text-xs p-1 px-3 bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 rounded flex flex-row space-x-1 items-center {loading
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'
: ''}"
on:click={() => {
......@@ -91,24 +132,11 @@
console.log('check');
}}
type="button"
disabled={loading}
disabled={scanDirLoading}
>
<div class="self-center font-medium">{$i18n.t('Scan')}</div>
<!-- <svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-3 h-3"
>
<path
fill-rule="evenodd"
d="M13.836 2.477a.75.75 0 0 1 .75.75v3.182a.75.75 0 0 1-.75.75h-3.182a.75.75 0 0 1 0-1.5h1.37l-.84-.841a4.5 4.5 0 0 0-7.08.932.75.75 0 0 1-1.3-.75 6 6 0 0 1 9.44-1.242l.842.84V3.227a.75.75 0 0 1 .75-.75Zm-.911 7.5A.75.75 0 0 1 13.199 11a6 6 0 0 1-9.44 1.241l-.84-.84v1.371a.75.75 0 0 1-1.5 0V9.591a.75.75 0 0 1 .75-.75H5.35a.75.75 0 0 1 0 1.5H3.98l.841.841a4.5 4.5 0 0 0 7.08-.932.75.75 0 0 1 1.025-.273Z"
clip-rule="evenodd"
/>
</svg> -->
{#if loading}
{#if scanDirLoading}
<div class="ml-3 self-center">
<svg
class=" w-3 h-3"
......@@ -141,6 +169,78 @@
<hr class=" dark:border-gray-700" />
<div class="space-y-2">
<div>
<div class=" mb-2 text-sm font-medium">{$i18n.t('Update Embedding Model')}</div>
<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>
</div>
<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>
<hr class=" dark:border-gray-700 my-3" />
<div class=" ">
<div class=" text-sm font-medium">{$i18n.t('Chunk Params')}</div>
......@@ -150,7 +250,7 @@
<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"
class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="number"
placeholder={$i18n.t('Enter Chunk Size')}
bind:value={chunkSize}
......@@ -161,11 +261,13 @@
</div>
<div class="flex w-full">
<div class=" self-center text-xs font-medium min-w-fit">{$i18n.t('Chunk Overlap')}</div>
<div class=" self-center text-xs font-medium min-w-fit">
{$i18n.t('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"
class="w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="number"
placeholder={$i18n.t('Enter Chunk Overlap')}
bind:value={chunkOverlap}
......@@ -176,7 +278,7 @@
</div>
</div>
<div>
<div class="pr-2">
<div class="flex justify-between items-center text-xs">
<div class=" text-xs font-medium">{$i18n.t('PDF Extract Images (OCR)')}</div>
......@@ -191,6 +293,8 @@
</div>
</div>
<hr class=" dark:border-gray-700 my-3" />
<div>
<div class=" text-sm font-medium">{$i18n.t('Query Params')}</div>
......@@ -200,7 +304,7 @@
<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"
class=" w-full rounded-lg py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="number"
placeholder={$i18n.t('Enter Top K')}
bind:value={querySettings.k}
......@@ -209,34 +313,19 @@
/>
</div>
</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>
<div>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
<textarea
bind:value={querySettings.template}
class="w-full rounded p-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
class="w-full rounded-lg px-4 py-3 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
rows="4"
/>
</div>
</div>
<hr class=" dark:border-gray-700" />
<hr class=" dark:border-gray-700 my-3" />
{#if showResetConfirm}
<div class="flex justify-between rounded-md items-center py-2 px-3.5 w-full transition">
......@@ -330,7 +419,8 @@
</button>
{/if}
</div>
</div>
</div>
<div class="flex justify-end pt-3 text-sm font-medium">
<button
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
......
......@@ -100,7 +100,7 @@
"Deleted {{deleteModelTag}}": "Изтрито {{deleteModelTag}}",
"Deleted {tagName}": "Изтрито {tagName}",
"Description": "Описание",
"Desktop Notifications": "Десктоп Известия",
"Notifications": "Десктоп Известия",
"Disabled": "Деактивиран",
"Discover a modelfile": "Откриване на модфайл",
"Discover a prompt": "Откриване на промпт",
......
......@@ -100,7 +100,7 @@
"Deleted {{deleteModelTag}}": "Esborrat {{deleteModelTag}}",
"Deleted {tagName}": "Esborrat {tagName}",
"Description": "Descripció",
"Desktop Notifications": "Notificacions d'Escriptori",
"Notifications": "Notificacions d'Escriptori",
"Disabled": "Desactivat",
"Discover a modelfile": "Descobreix un fitxer de model",
"Discover a prompt": "Descobreix un prompt",
......
......@@ -100,7 +100,7 @@
"Deleted {{deleteModelTag}}": "{{deleteModelTag}} gelöscht",
"Deleted {tagName}": "{tagName} gelöscht",
"Description": "Beschreibung",
"Desktop Notifications": "Desktop-Benachrichtigungen",
"Notifications": "Desktop-Benachrichtigungen",
"Disabled": "Deaktiviert",
"Discover a modelfile": "Eine Modelfiles entdecken",
"Discover a prompt": "Einen Prompt entdecken",
......
{
"analyze": "analyse",
"analyzed": "analysed",
"analyzes": "analyses",
"apologize": "apologise",
"apologized": "apologised",
"apologizes": "apologises",
"apologizing": "apologising",
"canceled": "cancelled",
"canceling": "cancelling",
"capitalize": "capitalise",
"capitalized": "capitalised",
"capitalizes": "capitalises",
"center": "centre",
"centered": "centred",
"color": "colour",
"colorize": "colourise",
"customize": "customise",
"customizes": "customises",
"defense": "defence",
"dialog": "dialogue",
"emphasize": "emphasise",
"emphasized": "emphasised",
"emphasizes": "emphasises",
"favor": "favour",
"favorable": "favourable",
"favorite": "favourite",
"favoritism": "favouritism",
"labor": "labour",
"labored": "laboured",
"laboring": "labouring",
"maximize": "maximise",
"maximizes": "maximises",
"minimize": "minimise",
"minimizes": "minimises",
"neighbor": "neighbour",
"neighborhood": "neighbourhood",
"offense": "offence",
"organize": "organise",
"organizes": "organises",
"personalize": "personalise",
"personalizes": "personalises",
"program": "programme",
"programmed": "programmed",
"programs": "programmes",
"quantization": "quantisation",
"quantize": "quantise",
"randomize": "randomise",
"randomizes": "randomises",
"realize": "realise",
"realizes": "realises",
"recognize": "recognise",
"recognizes": "recognises",
"summarize": "summarise",
"summarizes": "summarises",
"theater": "theatre",
"theaters": "theatres",
"toward": "towards",
"traveled": "travelled",
"traveler": "traveller",
"traveling": "travelling",
"utilize": "utilise",
"utilizes": "utilises"
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment