Unverified Commit 92c98eda authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge pull request #1781 from open-webui/dev

0.1.122
parents 1092ee9c 85df019c
......@@ -193,7 +193,7 @@
<div class=" mt-2 mb-1 flex justify-center space-x-2 text-sm font-medium">
<button
id="save-edit-message-button"
class="px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded-lg"
class="px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg-lg"
on:click={() => {
editMessageConfirmHandler();
}}
......
......@@ -125,7 +125,7 @@
<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"
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
on:click={() => {
saveSettings({
options: {
......
......@@ -147,8 +147,8 @@
<option value="dark">🌑 {$i18n.t('Dark')}</option>
<option value="oled-dark">🌃 {$i18n.t('OLED Dark')}</option>
<option value="light">☀️ {$i18n.t('Light')}</option>
<option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>
<option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option>
<!-- <option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>
<option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option> -->
</select>
</div>
</div>
......
......@@ -161,7 +161,7 @@
<div class="flex justify-end pt-5 text-sm font-medium">
<button
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
type="submit"
>
{$i18n.t('Save')}
......
......@@ -158,7 +158,7 @@
<div class="flex justify-end pt-5 text-sm font-medium">
<button
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
type="submit"
>
{$i18n.t('Save')}
......
......@@ -8,7 +8,9 @@
updateQuerySettings,
resetVectorDB,
getEmbeddingConfig,
updateEmbeddingConfig
updateEmbeddingConfig,
getRerankingConfig,
updateRerankingConfig
} from '$lib/apis/rag';
import { documents, models } from '$lib/stores';
......@@ -23,11 +25,13 @@
let scanDirLoading = false;
let updateEmbeddingModelLoading = false;
let updateRerankingModelLoading = false;
let showResetConfirm = false;
let embeddingEngine = '';
let embeddingModel = '';
let rerankingModel = '';
let OpenAIKey = '';
let OpenAIUrl = '';
......@@ -38,7 +42,9 @@
let querySettings = {
template: '',
k: 4
r: 0.0,
k: 4,
hybrid: false
};
const scanHandler = async () => {
......@@ -115,6 +121,29 @@
}
};
const rerankingModelUpdateHandler = async () => {
console.log('Update reranking model attempt:', rerankingModel);
updateRerankingModelLoading = true;
const res = await updateRerankingConfig(localStorage.token, {
reranking_model: rerankingModel
}).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
});
}
}
};
const submitHandler = async () => {
const res = await updateRAGConfig(localStorage.token, {
pdf_extract_images: pdfExtractImages,
......@@ -138,6 +167,20 @@
}
};
const setRerankingConfig = async () => {
const rerankingConfig = await getRerankingConfig(localStorage.token);
if (rerankingConfig) {
rerankingModel = rerankingConfig.reranking_model;
}
};
const toggleHybridSearch = async () => {
querySettings.hybrid = !querySettings.hybrid;
querySettings = await updateQuerySettings(localStorage.token, querySettings);
};
onMount(async () => {
const res = await getRAGConfig(localStorage.token);
......@@ -149,6 +192,7 @@
}
await setEmbeddingConfig();
await setRerankingConfig();
querySettings = await getQuerySettings(localStorage.token);
});
......@@ -165,6 +209,24 @@
<div>
<div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
<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>
<div class=" flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Embedding Model Engine')}</div>
<div class="flex items-center relative">
......@@ -349,6 +411,75 @@
<hr class=" dark:border-gray-700 my-3" />
{#if querySettings.hybrid === true}
<div class=" ">
<div class=" mb-2 text-sm font-medium">{$i18n.t('Update Reranking 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 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}
<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>
<hr class=" dark:border-gray-700 my-3" />
{/if}
<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' })}
......@@ -473,6 +604,28 @@
</div>
</div>
{#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>
<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>
</div>
</div>
{/if}
<div>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('RAG Template')}</div>
<textarea
......@@ -581,7 +734,7 @@
</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"
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
type="submit"
>
{$i18n.t('Save')}
......
<script lang="ts">
export let className = 'size-4';
export let strokeWidth = '1.5';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M8.625 12a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H8.25m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0H12m4.125 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm0 0h-.375M21 12c0 4.556-4.03 8.25-9 8.25a9.764 9.764 0 0 1-2.555-.337A5.972 5.972 0 0 1 5.41 20.97a5.969 5.969 0 0 1-.474-.065 4.48 4.48 0 0 0 .978-2.025c.09-.457-.133-.901-.467-1.226C3.93 16.178 3 14.189 3 12c0-4.556 4.03-8.25 9-8.25s9 3.694 9 8.25Z"
/>
</svg>
<script lang="ts">
export let className = 'size-4';
export let strokeWidth = '1.5';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M20.25 8.511c.884.284 1.5 1.128 1.5 2.097v4.286c0 1.136-.847 2.1-1.98 2.193-.34.027-.68.052-1.02.072v3.091l-3-3c-1.354 0-2.694-.055-4.02-.163a2.115 2.115 0 0 1-.825-.242m9.345-8.334a2.126 2.126 0 0 0-.476-.095 48.64 48.64 0 0 0-8.048 0c-1.131.094-1.976 1.057-1.976 2.192v4.286c0 .837.46 1.58 1.155 1.951m9.345-8.334V6.637c0-1.621-1.152-3.026-2.76-3.235A48.455 48.455 0 0 0 11.25 3c-2.115 0-4.198.137-6.24.402-1.608.209-2.76 1.614-2.76 3.235v6.226c0 1.621 1.152 3.026 2.76 3.235.577.075 1.157.14 1.74.194V21l4.155-4.155"
/>
</svg>
......@@ -4,8 +4,6 @@
import fileSaver from 'file-saver';
const { saveAs } = fileSaver;
import { jsPDF } from 'jspdf';
import { showSettings } from '$lib/stores';
import { flyAndScale } from '$lib/utils/transitions';
......
......@@ -29,6 +29,7 @@
import ArchiveBox from '../icons/ArchiveBox.svelte';
import ArchivedChatsModal from './Sidebar/ArchivedChatsModal.svelte';
const BREAKPOINT = 1024;
let show = false;
let navElement;
......@@ -49,9 +50,7 @@
let isEditing = false;
onMount(async () => {
if (window.innerWidth > 1024) {
show = true;
}
show = window.innerWidth > BREAKPOINT;
await chats.set(await getChatList(localStorage.token));
let touchstartX = 0;
......@@ -79,12 +78,20 @@
checkDirection();
};
const onResize = () => {
if (show && window.innerWidth < BREAKPOINT) {
show = false;
}
};
document.addEventListener('touchstart', onTouchStart);
document.addEventListener('touchend', onTouchEnd);
window.addEventListener('resize', onResize);
return () => {
document.removeEventListener('touchstart', onTouchStart);
document.removeEventListener('touchend', onTouchEnd);
document.removeEventListener('resize', onResize);
};
});
......@@ -172,7 +179,7 @@
<div class="px-2 flex justify-center space-x-2">
<a
id="sidebar-new-chat-button"
class="flex-grow flex justify-between rounded-xl px-4 py-2 hover:bg-gray-200 dark:hover:bg-gray-900 transition"
class="flex-grow flex justify-between rounded-xl px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-900 transition"
href="/"
on:click={async () => {
selectedChatId = null;
......@@ -217,7 +224,7 @@
{#if $user?.role === 'admin'}
<div class="px-2 flex justify-center mt-0.5">
<a
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-200 dark:hover:bg-gray-900 transition"
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-100 dark:hover:bg-gray-900 transition"
href="/modelfiles"
on:click={() => {
selectedChatId = null;
......@@ -249,7 +256,7 @@
<div class="px-2 flex justify-center">
<a
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-200 dark:hover:bg-gray-900 transition"
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-100 dark:hover:bg-gray-900 transition"
href="/prompts"
on:click={() => {
selectedChatId = null;
......@@ -281,7 +288,7 @@
<div class="px-2 flex justify-center mb-1">
<a
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-200 dark:hover:bg-gray-900 transition"
class="flex-grow flex space-x-3 rounded-xl px-3.5 py-2 hover:bg-gray-100 dark:hover:bg-gray-900 transition"
href="/documents"
on:click={() => {
selectedChatId = null;
......@@ -328,7 +335,7 @@
<div class="mt-3">
<button
class="flex justify-center items-center space-x-1.5 px-3 py-2.5 rounded-lg text-xs bg-gray-200 hover:bg-gray-300 transition text-gray-800 font-medium w-full"
class="flex justify-center items-center space-x-1.5 px-3 py-2.5 rounded-lg text-xs bg-gray-100 hover:bg-gray-200 transition text-gray-800 font-medium w-full"
type="button"
on:click={() => {
saveSettings({
......@@ -438,7 +445,7 @@
class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId ||
chat.id === chatTitleEditId ||
chat.id === chatDeleteId
? 'bg-gray-300 dark:bg-gray-900'
? 'bg-gray-200 dark:bg-gray-900'
: chat.id === selectedChatId
? 'bg-gray-100 dark:bg-gray-950'
: 'group-hover:bg-gray-100 dark:group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
......@@ -450,7 +457,7 @@
class=" w-full flex justify-between rounded-xl px-3 py-2 {chat.id === $chatId ||
chat.id === chatTitleEditId ||
chat.id === chatDeleteId
? 'bg-gray-300 dark:bg-gray-900'
? 'bg-gray-200 dark:bg-gray-900'
: chat.id === selectedChatId
? 'bg-gray-100 dark:bg-gray-950'
: ' group-hover:bg-gray-100 dark:group-hover:bg-gray-950'} whitespace-nowrap text-ellipsis"
......@@ -475,7 +482,7 @@
class="
{chat.id === $chatId || chat.id === chatTitleEditId || chat.id === chatDeleteId
? 'from-gray-300 dark:from-gray-900'
? 'from-gray-200 dark:from-gray-900'
: chat.id === selectedChatId
? 'from-gray-100 dark:from-gray-950'
: 'invisible group-hover:visible from-gray-100 dark:from-gray-950'}
......@@ -628,7 +635,7 @@
<div class="flex flex-col">
{#if $user !== undefined}
<button
class=" flex rounded-xl py-3 px-3.5 w-full hover:bg-gray-200 dark:hover:bg-gray-900 transition"
class=" flex rounded-xl py-3 px-3.5 w-full hover:bg-gray-100 dark:hover:bg-gray-900 transition"
on:click={() => {
showDropdown = !showDropdown;
}}
......
......@@ -69,9 +69,9 @@
{#if chats.length > 0}
<div class="text-left text-sm w-full mb-4 max-h-[22rem] overflow-y-scroll">
<div class="relative overflow-x-auto">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400 table-auto">
<table class="w-full text-sm text-left text-gray-600 dark:text-gray-400 table-auto">
<thead
class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 border-gray-800"
class="text-xs text-gray-700 uppercase bg-transparent dark:text-gray-200 border-b-2 dark:border-gray-800"
>
<tr>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
......@@ -82,7 +82,7 @@
<tbody>
{#each chats as chat, idx}
<tr
class="bg-white {idx !== chats.length - 1 &&
class="bg-transparent {idx !== chats.length - 1 &&
'border-b'} dark:bg-gray-900 dark:border-gray-850 text-xs"
>
<td class="px-3 py-1 w-2/3">
......
This diff is collapsed.
......@@ -44,7 +44,7 @@
"available!": "¡disponible!",
"Back": "Vuelve atrás",
"Builder Mode": "Modo de Constructor",
"Cancel": "Cancela",
"Cancel": "Cancelar",
"Categories": "Categorías",
"Change Password": "Cambia la Contraseña",
"Chat": "Chat",
......@@ -152,6 +152,7 @@
"File Mode": "Modo de archivo",
"File not found.": "Archivo no encontrado.",
"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Se detectó suplantación de huellas: No se pueden usar las iniciales como avatar. Por defecto se utiliza la imagen de perfil predeterminada.",
"Fluidly stream large external response chunks": "Transmita con fluidez grandes fragmentos de respuesta externa",
"Focus chat input": "Enfoca la entrada del chat",
"Format your variables using square brackets like this:": "Formatee sus variables usando corchetes así:",
"From (Base Model)": "Desde (Modelo Base)",
......
......@@ -7,6 +7,10 @@
"code": "bg-BG",
"title": "Bulgarian (BG)"
},
{
"code": "bn-BD",
"title": "Banlga (বাংলা)"
},
{
"code": "ca-ES",
"title": "Catalan"
......
......@@ -120,6 +120,7 @@
"Edit Doc": "Belgeyi Düzenle",
"Edit User": "Kullanıcıyı Düzenle",
"Email": "E-posta",
"Embedding model: {{embedding_model}}": "Gömme modeli: {{embedding_model}}",
"Enable Chat History": "Sohbet Geçmişini Etkinleştir",
"Enable New Sign Ups": "Yeni Kayıtları Etkinleştir",
"Enabled": "Etkin",
......@@ -150,6 +151,8 @@
"Failed to read clipboard contents": "Pano içeriği okunamadı",
"File Mode": "Dosya Modu",
"File not found.": "Dosya bulunamadı.",
"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "Parmak izi sahteciliği tespit edildi: Avatar olarak baş harfler kullanılamıyor. Varsayılan profil resmine dönülüyor.",
"Fluidly stream large external response chunks": "Büyük harici yanıt chunklarını akıcı bir şekilde yayınlayın",
"Focus chat input": "Sohbet girişine odaklan",
"Format your variables using square brackets like this:": "Değişkenlerinizi şu şekilde kare parantezlerle biçimlendirin:",
"From (Base Model)": "(Temel Model)'den",
......@@ -193,8 +196,11 @@
"MMMM DD, YYYY": "DD MMMM YYYY",
"Model '{{modelName}}' has been successfully downloaded.": "'{{modelName}}' başarıyla indirildi.",
"Model '{{modelTag}}' is already in queue for downloading.": "'{{modelTag}}' zaten indirme sırasında.",
"Model {{embedding_model}} update complete!": "Model {{embedding_model}} güncellemesi tamamlandı!",
"Model {{embedding_model}} update failed or not required!": "Model {{embedding_model}} güncellemesi başarısız oldu veya gerekli değil!",
"Model {{modelId}} not found": "{{modelId}} bulunamadı",
"Model {{modelName}} already exists.": "{{modelName}} zaten mevcut.",
"Model filesystem path detected. Model shortname is required for update, cannot continue.": "Model dosya sistemi yolu algılandı. Güncelleme için model kısa adı gerekli, devam edilemiyor.",
"Model Name": "Model Adı",
"Model not selected": "Model seçilmedi",
"Model Tag Name": "Model Etiket Adı",
......@@ -332,7 +338,10 @@
"TTS Settings": "TTS Ayarları",
"Type Hugging Face Resolve (Download) URL": "Hugging Face Resolve (Download) URL'sini Yazın",
"Uh-oh! There was an issue connecting to {{provider}}.": "Ah! {{provider}}'a bağlanırken bir sorun oluştu.",
"Understand that updating or changing your embedding model requires reset of the vector database and re-import of all documents. You have been warned!": "Gömme modelinizi güncellemenin veya değiştirmenin, vektör veritabanının sıfırlanmasını ve tüm belgelerin yeniden içe aktarılmasını gerektirdiğini anlayın. Uyarıldın!",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Bilinmeyen Dosya Türü '{{file_type}}', ancak düz metin olarak kabul ediliyor ve işleniyor",
"Update": "Güncelleme",
"Update embedding model {{embedding_model}}": "Gömme modelini güncelle: {{embedding_model}}",
"Update password": "Parolayı Güncelle",
"Upload a GGUF model": "Bir GGUF modeli yükle",
"Upload files": "Dosyaları Yükle",
......@@ -340,6 +349,7 @@
"URL Mode": "URL Modu",
"Use '#' in the prompt input to load and select your documents.": "Belgelerinizi yüklemek ve seçmek için promptda '#' kullanın.",
"Use Gravatar": "Gravatar Kullan",
"Use Initials": "Baş Harfleri Kullan",
"user": "kullanıcı",
"User Permissions": "Kullanıcı İzinleri",
"Users": "Kullanıcılar",
......
......@@ -341,7 +341,7 @@
"Use '#' in the prompt input to load and select your documents.": "Для введення промтів до веб-сторінок (URL) або вибору документів, будь ласка, використовуйте символ '#'.",
"Use Gravatar": "Змінити аватар",
"user": "користувач",
"User Permissions": "Дозволи користувача",
"User Permissions": "Права користувача",
"Users": "Користувачі",
"Utilize": "Використовувати",
"Valid time units:": "Дійсні одиниці часу:",
......
......@@ -5,20 +5,20 @@
"(latest)": "",
"{{modelName}} is thinking...": "{{modelName}} 正在思考...",
"{{webUIName}} Backend Required": "需要 {{webUIName}} 后端",
"a user": "",
"a user": "用户",
"About": "关于",
"Account": "账户",
"Action": "操作",
"Add a model": "添加模型",
"Add a model tag name": "添加模型标签名称",
"Add a short description about what this modelfile does": "添加关于此模型文件功能的简短描述",
"Add a short title for this prompt": "为这个提示添加一个简短的标题",
"Add a tag": "",
"Add a short description about what this modelfile does": "为这个模型文件添加一段简短描述",
"Add a short title for this prompt": "为这个提示添加一个简短的标题",
"Add a tag": "添加标签",
"Add Docs": "添加文档",
"Add Files": "添加文件",
"Add message": "添加消息",
"add tags": "添加标签",
"Adjusting these settings will apply changes universally to all users.": "调整这些设置将对所有用户普遍应用更改。",
"Adjusting these settings will apply changes universally to all users.": "调整这些设置将对所有用户应用更改。",
"admin": "管理员",
"Admin Panel": "管理员面板",
"Admin Settings": "管理员设置",
......@@ -26,15 +26,15 @@
"all": "所有",
"All Users": "所有用户",
"Allow": "允许",
"Allow Chat Deletion": "允许删除聊天",
"Allow Chat Deletion": "允许删除聊天记录",
"alphanumeric characters and hyphens": "字母数字字符和连字符",
"Already have an account?": "已经有账户了吗?",
"an assistant": "",
"an assistant": "助手",
"and": "和",
"API Base URL": "API 基础 URL",
"API Key": "API 密钥",
"API RPM": "API RPM",
"are allowed - Activate this command by typing": "允许 - 通过输入激活命令",
"are allowed - Activate this command by typing": "允许 - 通过输入激活这个命令",
"Are you sure?": "你确定吗?",
"Audio": "音频",
"Auto-playback response": "自动播放回应",
......@@ -43,21 +43,21 @@
"AUTOMATIC1111 Base URL is required.": "需要 AUTOMATIC1111 基础 URL。",
"available!": "可用!",
"Back": "返回",
"Builder Mode": "构建模式",
"Builder Mode": "构建模式",
"Cancel": "取消",
"Categories": "分类",
"Change Password": "更改密码",
"Chat": "聊天",
"Chat History": "聊天历史",
"Chat History is off for this browser.": "此浏览器已关闭聊天历史。",
"Chat History is off for this browser.": "此浏览器已关闭聊天历史功能。",
"Chats": "聊天",
"Check Again": "再次检查",
"Check for updates": "检查更新",
"Checking for updates...": "正在检查更新...",
"Choose a model before saving...": "保存前选择一个模型...",
"Chunk Overlap": "块重叠",
"Chunk Params": "块参数",
"Chunk Size": "块大小",
"Chunk Overlap": "块重叠(Chunk Overlap)",
"Chunk Params": "块参数(Chunk Params)",
"Chunk Size": "块大小(Chunk Size)",
"Click here for help.": "点击这里获取帮助。",
"Click here to check other modelfiles.": "点击这里检查其他模型文件。",
"Click here to select": "点击这里选择",
......@@ -83,7 +83,7 @@
"Current Model": "当前模型",
"Current Password": "当前密码",
"Custom": "自定义",
"Customize Ollama models for a specific purpose": "为特定目的定制Ollama模型",
"Customize Ollama models for a specific purpose": "定制特定用途的Ollama模型",
"Dark": "暗色",
"Database": "数据库",
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
......@@ -106,7 +106,7 @@
"Discover a prompt": "探索提示词",
"Discover, download, and explore custom prompts": "发现、下载并探索自定义提示词",
"Discover, download, and explore model presets": "发现、下载并探索模型预设",
"Display the username instead of You in the Chat": "在聊天中显示用户名而不是“”",
"Display the username instead of You in the Chat": "在聊天中显示用户名而不是“”",
"Document": "文档",
"Document Settings": "文档设置",
"Documents": "文档",
......@@ -115,7 +115,7 @@
"Don't have an account?": "没有账户?",
"Download as a File": "下载为文件",
"Download Database": "下载数据库",
"Drop any files here to add to the conversation": "将任何文件拖到这里以添加到对话中",
"Drop any files here to add to the conversation": "拖动文件到此处以添加到对话中",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "例如 '30s','10m'。有效的时间单位是's', 'm', 'h'。",
"Edit Doc": "编辑文档",
"Edit User": "编辑用户",
......@@ -123,21 +123,21 @@
"Enable Chat History": "启用聊天历史",
"Enable New Sign Ups": "启用新注册",
"Enabled": "启用",
"Enter {{role}} message here": "",
"Enter API Key": "",
"Enter Chunk Overlap": "",
"Enter Chunk Size": "",
"Enter Image Size (e.g. 512x512)": "",
"Enter LiteLLM API Base URL (litellm_params.api_base)": "",
"Enter LiteLLM API Key (litellm_params.api_key)": "",
"Enter LiteLLM API RPM (litellm_params.rpm)": "",
"Enter LiteLLM Model (litellm_params.model)": "",
"Enter Max Tokens (litellm_params.max_tokens)": "",
"Enter model tag (e.g. {{modelTag}})": "",
"Enter Number of Steps (e.g. 50)": "",
"Enter {{role}} message here": "在此处输入 {{role}} 信息",
"Enter API Key": "输入API密匙",
"Enter Chunk Overlap": "输入块重叠(Chunk Overlap)",
"Enter Chunk Size": "输入块大小(Chunk Size)",
"Enter Image Size (e.g. 512x512)": "输入图片大小(例如 512x512)",
"Enter LiteLLM API Base URL (litellm_params.api_base)": "输入 LiteLLM API 基本 URL (litellm_params.api_base)",
"Enter LiteLLM API Key (litellm_params.api_key)": "输入 LiteLLM API 密匙 (litellm_params.api_key)",
"Enter LiteLLM API RPM (litellm_params.rpm)": "输入 LiteLLM API 速率限制 (litellm_params.rpm)",
"Enter LiteLLM Model (litellm_params.model)": "输入 LiteLLM 模型 (litellm_params.model)",
"Enter Max Tokens (litellm_params.max_tokens)": "输入模型的 Max Tokens (litellm_params.max_tokens)",
"Enter model tag (e.g. {{modelTag}})": "输入模型标签(例如{{modelTag}})",
"Enter Number of Steps (e.g. 50)": "输入步数(例如50)",
"Enter stop sequence": "输入停止序列",
"Enter Top K": "",
"Enter URL (e.g. http://127.0.0.1:7860/)": "",
"Enter Top K": "输入 Top K",
"Enter URL (e.g. http://127.0.0.1:7860/)": "输入 URL (例如 http://127.0.0.1:7860/)",
"Enter Your Email": "输入您的电子邮件",
"Enter Your Full Name": "输入您的全名",
"Enter Your Password": "输入您的密码",
......@@ -159,7 +159,7 @@
"Hello, {{name}}": "你好,{{name}}",
"Hide": "隐藏",
"Hide Additional Params": "隐藏额外参数",
"How can I help you today?": "今天我如何能帮到你?",
"How can I help you today?": "今天能帮你做什么?",
"Image Generation (Experimental)": "图像生成(实验性)",
"Image Generation Engine": "图像生成引擎",
"Image Settings": "图像设置",
......@@ -179,14 +179,14 @@
"Language": "语言",
"Light": "浅色",
"Listening...": "监听中...",
"LLMs can make mistakes. Verify important information.": "大型语言模型可能会犯错。验证重要信息。",
"LLMs can make mistakes. Verify important information.": "LLM可能会生成错误信息,请验证重要信息。",
"Made by OpenWebUI Community": "由OpenWebUI社区制作",
"Make sure to enclose them with": "确保将它们包含在内",
"Manage LiteLLM Models": "管理LiteLLM模型",
"Manage Models": "",
"Manage Models": "管理模型",
"Manage Ollama Models": "管理Ollama模型",
"Max Tokens": "最大令牌数",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可以同时下载3个模型请稍后试。",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "最多可以同时下载3个模型请稍后试。",
"Mirostat": "Mirostat",
"Mirostat Eta": "Mirostat Eta",
"Mirostat Tau": "Mirostat Tau",
......@@ -198,8 +198,8 @@
"Model Name": "模型名称",
"Model not selected": "未选择模型",
"Model Tag Name": "模型标签名称",
"Model Whitelisting": "",
"Model(s) Whitelisted": "",
"Model Whitelisting": "白名单模型",
"Model(s) Whitelisted": "模型已加入白名单",
"Modelfile": "模型文件",
"Modelfile Advanced Settings": "模型文件高级设置",
"Modelfile Content": "模型文件内容",
......@@ -207,7 +207,7 @@
"Models": "模型",
"My Documents": "我的文档",
"My Modelfiles": "我的模型文件",
"My Prompts": "我的提示",
"My Prompts": "我的提示",
"Name": "名称",
"Name Tag": "名称标签",
"Name your modelfile": "命名你的模型文件",
......@@ -221,7 +221,7 @@
"Ollama Version": "Ollama 版本",
"On": "开",
"Only": "仅",
"Only alphanumeric characters and hyphens are allowed in the command string.": "命令字符串中只允许使用字母数字字符和连字符。",
"Only alphanumeric characters and hyphens are allowed in the command string.": "命令字符串中只允许使用英文字母数字(0-9)以及连字符(-)。",
"Oops! Hold tight! Your files are still in the processing oven. We're cooking them up to perfection. Please be patient and we'll let you know once they're ready.": "哎呀!请稍等!您的文件仍在处理中。我们正在将它们做得尽善尽美,请耐心等待,一旦准备好我们会通知您。",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "哎呀!看起来 URL 无效。请仔细检查后再试一次。",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "哎呀!您正在使用不支持的方法(仅限前端)。请从后端提供 WebUI。",
......@@ -235,10 +235,10 @@
"or": "或",
"Parameters": "参数",
"Password": "密码",
"PDF Extract Images (OCR)": "",
"PDF Extract Images (OCR)": "PDF图像处理(使用OCR)",
"pending": "待定",
"Permission denied when accessing microphone: {{error}}": "访问麦克风时权限被拒绝:{{error}}",
"Playground": "游乐场",
"Playground": "Playground",
"Profile": "个人资料",
"Prompt Content": "提示词内容",
"Prompt suggestions": "提示词建议",
......@@ -255,7 +255,7 @@
"Repeat Penalty": "重复惩罚",
"Request Mode": "请求模式",
"Reset Vector Storage": "重置向量存储",
"Response AutoCopy to Clipboard": "响应自动复制到剪贴板",
"Response AutoCopy to Clipboard": "自动复制回答到剪贴板",
"Role": "角色",
"Rosé Pine": "Rosé Pine",
"Rosé Pine Dawn": "Rosé Pine Dawn",
......@@ -263,7 +263,7 @@
"Save & Create": "保存并创建",
"Save & Submit": "保存并提交",
"Save & Update": "保存并更新",
"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "直接将聊天记录保存到浏览器存储中不再受支持。请点击下面的按钮下载并删除您的聊天记录。别担心,您可以通过轻松地将聊天记录重新导入到后端",
"Saving chat logs directly to your browser's storage is no longer supported. Please take a moment to download and delete your chat logs by clicking the button below. Don't worry, you can easily re-import your chat logs to the backend through": "不再支持直接将聊天记录保存到浏览器存储中。请点击下面的按钮下载并删除您的聊天记录。别担心,您可以通过轻松地将聊天记录重新导入到后端",
"Scan": "扫描",
"Scan complete!": "扫描完成!",
"Scan for documents from {{path}}": "从 {{path}} 扫描文档",
......@@ -275,10 +275,10 @@
"Seed": "种子",
"Select a mode": "选择一个模式",
"Select a model": "选择一个模型",
"Select an Ollama instance": "",
"Select an Ollama instance": "选择一个Ollama实例",
"Send a Message": "发送消息",
"Send message": "发送消息",
"Server connection verified": "服务器连接已验证",
"Server connection verified": "已验证服务器连接",
"Set as default": "设为默认",
"Set Default Model": "设置默认模型",
"Set Image Size": "设置图片大小",
......@@ -286,7 +286,7 @@
"Set Title Auto-Generation Model": "设置标题自动生成模型",
"Set Voice": "设置声音",
"Settings": "设置",
"Settings saved successfully!": "",
"Settings saved successfully!": "设置已保存",
"Share to OpenWebUI Community": "分享到OpenWebUI社区",
"short-summary": "简短总结",
"Show": "显示",
......@@ -316,10 +316,10 @@
"Theme": "主题",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "这确保了您宝贵的对话被安全保存到后端数据库中。谢谢!",
"This setting does not sync across browsers or devices.": "此设置不会在浏览器或设备之间同步。",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:在每次替换后,在聊天输入中按Tab键可以连续更新多个变量。",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "提示:在每次替换后,在聊天输入中按Tab键可以连续更新多个变量。",
"Title": "标题",
"Title Auto-Generation": "标题自动生成",
"Title Generation Prompt": "标题生成提示",
"Title Generation Prompt": "自动生成标题的提示",
"to": "到",
"To access the available model names for downloading,": "要访问可下载的模型名称,",
"To access the GGUF models available for downloading,": "要访问可下载的GGUF模型,",
......@@ -331,9 +331,9 @@
"Trouble accessing Ollama?": "访问Ollama时遇到问题?",
"TTS Settings": "文本转语音设置",
"Type Hugging Face Resolve (Download) URL": "输入Hugging Face解析(下载)URL",
"Uh-oh! There was an issue connecting to {{provider}}.": "哦哦!连接到{{provider}}时出现问题。",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "未知文件类型'{{file_type}}',但接受并视为纯文本",
"Update password": "",
"Uh-oh! There was an issue connecting to {{provider}}.": "哎呀!连接到{{provider}}时出现问题。",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "未知文件类型'{{file_type}}',视为纯文本进行处理",
"Update password": "更新密码",
"Upload a GGUF model": "上传一个GGUF模型",
"Upload files": "上传文件",
"Upload Progress": "上传进度",
......@@ -347,17 +347,17 @@
"Valid time units:": "有效时间单位:",
"variable": "变量",
"variable to have them replaced with clipboard content.": "变量将被剪贴板内容替换。",
"Version": "",
"Version": "版本",
"Web": "网页",
"WebUI Add-ons": "WebUI 插件",
"WebUI Settings": "WebUI 设置",
"WebUI will make requests to": "",
"WebUI will make requests to": "WebUI将请求",
"What’s New in": "最新变化",
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "当历史记录被关闭时,这个浏览器上的新聊天不会出现在你任何设备的历史记录中。",
"Whisper (Local)": "私语(本地)",
"Whisper (Local)": "Whisper(本地)",
"Write a prompt suggestion (e.g. Who are you?)": "写一个提示建议(例如:你是谁?)",
"Write a summary in 50 words that summarizes [topic or keyword].": "用50个字写一个总结[主题或关键词]。",
"You": "你",
"You're a helpful assistant.": "你是一个有帮助的助手。",
"You're now logged in.": "你现在已经登录。"
"You're now logged in.": "登录。"
}
......@@ -7,11 +7,11 @@
import { goto } from '$app/navigation';
import { getOllamaModels, getOllamaVersion } from '$lib/apis/ollama';
import { getModels as _getModels } from '$lib/utils';
import { getOllamaVersion } from '$lib/apis/ollama';
import { getModelfiles } from '$lib/apis/modelfiles';
import { getPrompts } from '$lib/apis/prompts';
import { getOpenAIModels } from '$lib/apis/openai';
import { getLiteLLMModels } from '$lib/apis/litellm';
import { getDocs } from '$lib/apis/documents';
import { getAllChatTags } from '$lib/apis/chats';
......@@ -47,26 +47,7 @@
let showShortcuts = false;
const getModels = async () => {
let models = await Promise.all([
await getOllamaModels(localStorage.token).catch((error) => {
console.log(error);
return null;
}),
await getOpenAIModels(localStorage.token).catch((error) => {
console.log(error);
return null;
}),
await getLiteLLMModels(localStorage.token).catch((error) => {
console.log(error);
return null;
})
]);
models = models
.filter((models) => models)
.reduce((a, e, i, arr) => a.concat(e, ...(i < arr.length - 1 ? [{ name: 'hr' }] : [])), []);
return models;
return _getModels(localStorage.token);
};
const setOllamaVersion = async (version: string = '') => {
......@@ -195,8 +176,7 @@
});
</script>
{#if loaded}
<div class=" hidden lg:flex fixed bottom-0 right-0 px-3 py-3 z-10">
<div class=" hidden lg:flex fixed bottom-0 right-0 px-3 py-3 z-10">
<Tooltip content="Help" placement="left">
<button
id="show-shortcuts-button"
......@@ -209,11 +189,15 @@
?
</button>
</Tooltip>
</div>
</div>
<ShortcutsModal bind:show={showShortcuts} />
<ShortcutsModal bind:show={showShortcuts} />
<div class="app relative">
<div class="app relative">
<div
class=" text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-900 min-h-screen overflow-auto flex flex-row"
>
{#if loaded}
{#if !['user', 'admin'].includes($user.role)}
<div class="fixed w-full h-full flex z-50">
<div
......@@ -233,7 +217,7 @@
<div class=" mt-6 mx-auto relative group w-fit">
<button
class="relative z-20 flex px-5 py-2 rounded-full bg-white border border-gray-100 dark:border-none hover:bg-gray-100 transition font-medium text-sm"
class="relative z-20 flex px-5 py-2 rounded-full bg-white border border-gray-100 dark:border-none hover:bg-gray-100 text-gray-700 transition font-medium text-sm"
on:click={async () => {
location.href = '/';
}}
......@@ -307,16 +291,13 @@
</div>
{/if}
<div
class=" text-gray-700 dark:text-gray-100 bg-white dark:bg-gray-900 min-h-screen overflow-auto flex flex-row"
>
<Sidebar />
<SettingsModal bind:show={$showSettings} />
<ChangelogModal bind:show={$showChangelog} />
<slot />
{/if}
</div>
</div>
{/if}
</div>
<style>
.loading {
......
......@@ -351,7 +351,13 @@
model: model,
messages: messagesBody,
options: {
...($settings.options ?? {})
...($settings.options ?? {}),
stop:
$settings?.options?.stop ?? undefined
? $settings.options.stop.map((str) =>
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
)
: undefined
},
format: $settings.requestFormat ?? undefined,
keep_alive: $settings.keepAlive ?? undefined,
......@@ -532,7 +538,7 @@
console.log(model);
const res = await generateOpenAIChatCompletion(
const [res, controller] = await generateOpenAIChatCompletion(
localStorage.token,
{
model: model.id,
......@@ -576,7 +582,12 @@
})
})),
seed: $settings?.options?.seed ?? undefined,
stop: $settings?.options?.stop ?? undefined,
stop:
$settings?.options?.stop ?? undefined
? $settings?.options?.stop.map((str) =>
decodeURIComponent(JSON.parse('"' + str.replace(/\"/g, '\\"') + '"'))
)
: undefined,
temperature: $settings?.options?.temperature ?? undefined,
top_p: $settings?.options?.top_p ?? undefined,
num_ctx: $settings?.options?.num_ctx ?? undefined,
......@@ -608,6 +619,11 @@
if (done || stopResponseFlag || _chatId !== $chatId) {
responseMessage.done = true;
messages = messages;
if (stopResponseFlag) {
controller.abort('User: Stop Response');
}
break;
}
......
......@@ -5,6 +5,8 @@
import { onMount, getContext } from 'svelte';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime);
import { toast } from 'svelte-sonner';
......@@ -13,6 +15,9 @@
import EditUserModal from '$lib/components/admin/EditUserModal.svelte';
import SettingsModal from '$lib/components/admin/SettingsModal.svelte';
import Pagination from '$lib/components/common/Pagination.svelte';
import ChatBubbles from '$lib/components/icons/ChatBubbles.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import UserChatsModal from '$lib/components/admin/UserChatsModal.svelte';
const i18n = getContext('i18n');
......@@ -25,6 +30,8 @@
let page = 1;
let showSettingsModal = false;
let showUserChatsModal = false;
let showEditUserModal = false;
const updateRoleHandler = async (id, role) => {
......@@ -84,6 +91,7 @@
/>
{/key}
<UserChatsModal bind:show={showUserChatsModal} user={selectedUser} />
<SettingsModal bind:show={showSettingsModal} />
<div class="min-h-screen max-h-[100dvh] w-full flex justify-center dark:text-white">
......@@ -157,7 +165,10 @@
<th scope="col" class="px-3 py-2"> {$i18n.t('Role')} </th>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
<th scope="col" class="px-3 py-2"> {$i18n.t('Email')} </th>
<th scope="col" class="px-3 py-2"> {$i18n.t('Last Active')} </th>
<th scope="col" class="px-3 py-2"> {$i18n.t('Created at')} </th>
<th scope="col" class="px-3 py-2 text-right" />
</tr>
</thead>
......@@ -215,11 +226,28 @@
<td class=" px-3 py-2"> {user.email} </td>
<td class=" px-3 py-2">
{dayjs(user.timestamp * 1000).format($i18n.t('MMMM DD, YYYY'))}
{dayjs(user.last_active_at * 1000).fromNow()}
</td>
<td class=" px-3 py-2">
{dayjs(user.created_at * 1000).format($i18n.t('MMMM DD, YYYY'))}
</td>
<td class="px-3 py-2 text-right">
<div class="flex justify-end w-full">
<Tooltip content="Chats">
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
showUserChatsModal = !showUserChatsModal;
selectedUser = user;
}}
>
<ChatBubbles />
</button>
</Tooltip>
<Tooltip content="Edit User">
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
......@@ -242,7 +270,9 @@
/>
</svg>
</button>
</Tooltip>
<Tooltip content="Delete User">
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={async () => {
......@@ -264,6 +294,7 @@
/>
</svg>
</button>
</Tooltip>
</div>
</td>
</tr>
......
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