Browse Source

Merge pull request #3668 from open-webui/dev

dev
Timothy Jaeryang Baek 10 months ago
parent
commit
d60f06608e

+ 76 - 0
backend/apps/webui/main.py

@@ -19,8 +19,13 @@ from apps.webui.routers import (
     functions,
     functions,
 )
 )
 from apps.webui.models.functions import Functions
 from apps.webui.models.functions import Functions
+from apps.webui.models.models import Models
+
 from apps.webui.utils import load_function_module_by_id
 from apps.webui.utils import load_function_module_by_id
+
 from utils.misc import stream_message_template
 from utils.misc import stream_message_template
+from utils.task import prompt_template
+
 
 
 from config import (
 from config import (
     WEBUI_BUILD_HASH,
     WEBUI_BUILD_HASH,
@@ -186,6 +191,77 @@ async def get_pipe_models():
 
 
 
 
 async def generate_function_chat_completion(form_data, user):
 async def generate_function_chat_completion(form_data, user):
+    model_id = form_data.get("model")
+    model_info = Models.get_model_by_id(model_id)
+
+    if model_info:
+        if model_info.base_model_id:
+            form_data["model"] = model_info.base_model_id
+
+        model_info.params = model_info.params.model_dump()
+
+        if model_info.params:
+            if model_info.params.get("temperature", None) is not None:
+                form_data["temperature"] = float(model_info.params.get("temperature"))
+
+            if model_info.params.get("top_p", None):
+                form_data["top_p"] = int(model_info.params.get("top_p", None))
+
+            if model_info.params.get("max_tokens", None):
+                form_data["max_tokens"] = int(model_info.params.get("max_tokens", None))
+
+            if model_info.params.get("frequency_penalty", None):
+                form_data["frequency_penalty"] = int(
+                    model_info.params.get("frequency_penalty", None)
+                )
+
+            if model_info.params.get("seed", None):
+                form_data["seed"] = model_info.params.get("seed", None)
+
+            if model_info.params.get("stop", None):
+                form_data["stop"] = (
+                    [
+                        bytes(stop, "utf-8").decode("unicode_escape")
+                        for stop in model_info.params["stop"]
+                    ]
+                    if model_info.params.get("stop", None)
+                    else None
+                )
+
+        system = model_info.params.get("system", None)
+        if system:
+            system = prompt_template(
+                system,
+                **(
+                    {
+                        "user_name": user.name,
+                        "user_location": (
+                            user.info.get("location") if user.info else None
+                        ),
+                    }
+                    if user
+                    else {}
+                ),
+            )
+            # Check if the payload already has a system message
+            # If not, add a system message to the payload
+            if form_data.get("messages"):
+                for message in form_data["messages"]:
+                    if message.get("role") == "system":
+                        message["content"] = system + message["content"]
+                        break
+                else:
+                    form_data["messages"].insert(
+                        0,
+                        {
+                            "role": "system",
+                            "content": system,
+                        },
+                    )
+
+    else:
+        pass
+
     async def job():
     async def job():
         pipe_id = form_data["model"]
         pipe_id = form_data["model"]
         if "." in pipe_id:
         if "." in pipe_id:

+ 2 - 2
backend/apps/webui/models/functions.py

@@ -198,7 +198,7 @@ class FunctionsTable:
 
 
         try:
         try:
             user = Users.get_user_by_id(user_id)
             user = Users.get_user_by_id(user_id)
-            user_settings = user.settings.model_dump()
+            user_settings = user.settings.model_dump() if user.settings else {}
 
 
             # Check if user has "functions" and "valves" settings
             # Check if user has "functions" and "valves" settings
             if "functions" not in user_settings:
             if "functions" not in user_settings:
@@ -217,7 +217,7 @@ class FunctionsTable:
 
 
         try:
         try:
             user = Users.get_user_by_id(user_id)
             user = Users.get_user_by_id(user_id)
-            user_settings = user.settings.model_dump()
+            user_settings = user.settings.model_dump() if user.settings else {}
 
 
             # Check if user has "functions" and "valves" settings
             # Check if user has "functions" and "valves" settings
             if "functions" not in user_settings:
             if "functions" not in user_settings:

+ 2 - 2
backend/apps/webui/models/tools.py

@@ -149,7 +149,7 @@ class ToolsTable:
     ) -> Optional[dict]:
     ) -> Optional[dict]:
         try:
         try:
             user = Users.get_user_by_id(user_id)
             user = Users.get_user_by_id(user_id)
-            user_settings = user.settings.model_dump()
+            user_settings = user.settings.model_dump() if user.settings else {}
 
 
             # Check if user has "tools" and "valves" settings
             # Check if user has "tools" and "valves" settings
             if "tools" not in user_settings:
             if "tools" not in user_settings:
@@ -167,7 +167,7 @@ class ToolsTable:
     ) -> Optional[dict]:
     ) -> Optional[dict]:
         try:
         try:
             user = Users.get_user_by_id(user_id)
             user = Users.get_user_by_id(user_id)
-            user_settings = user.settings.model_dump()
+            user_settings = user.settings.model_dump() if user.settings else {}
 
 
             # Check if user has "tools" and "valves" settings
             # Check if user has "tools" and "valves" settings
             if "tools" not in user_settings:
             if "tools" not in user_settings:

+ 5 - 1
backend/main.py

@@ -999,12 +999,16 @@ async def get_all_models():
                     model["info"] = custom_model.model_dump()
                     model["info"] = custom_model.model_dump()
         else:
         else:
             owned_by = "openai"
             owned_by = "openai"
+            pipe = None
+
             for model in models:
             for model in models:
                 if (
                 if (
                     custom_model.base_model_id == model["id"]
                     custom_model.base_model_id == model["id"]
                     or custom_model.base_model_id == model["id"].split(":")[0]
                     or custom_model.base_model_id == model["id"].split(":")[0]
                 ):
                 ):
                     owned_by = model["owned_by"]
                     owned_by = model["owned_by"]
+                    if "pipe" in model:
+                        pipe = model["pipe"]
                     break
                     break
 
 
             models.append(
             models.append(
@@ -1016,11 +1020,11 @@ async def get_all_models():
                     "owned_by": owned_by,
                     "owned_by": owned_by,
                     "info": custom_model.model_dump(),
                     "info": custom_model.model_dump(),
                     "preset": True,
                     "preset": True,
+                    **({"pipe": pipe} if pipe is not None else {}),
                 }
                 }
             )
             )
 
 
     app.state.MODELS = {model["id"]: model for model in models}
     app.state.MODELS = {model["id"]: model for model in models}
-
     webui_app.state.MODELS = app.state.MODELS
     webui_app.state.MODELS = app.state.MODELS
 
 
     return models
     return models

+ 15 - 2
src/lib/components/admin/Settings.svelte

@@ -1,5 +1,5 @@
 <script>
 <script>
-	import { getContext, tick } from 'svelte';
+	import { getContext, tick, onMount } from 'svelte';
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 
 
 	import Database from './Settings/Database.svelte';
 	import Database from './Settings/Database.svelte';
@@ -21,11 +21,24 @@
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	let selectedTab = 'general';
 	let selectedTab = 'general';
+
+	onMount(() => {
+		const containerElement = document.getElementById('admin-settings-tabs-container');
+
+		if (containerElement) {
+			containerElement.addEventListener('wheel', function (event) {
+				if (event.deltaY !== 0) {
+					// Adjust horizontal scroll position based on vertical scroll
+					containerElement.scrollLeft += event.deltaY;
+				}
+			});
+		}
+	});
 </script>
 </script>
 
 
 <div class="flex flex-col lg:flex-row w-full h-full py-2 lg:space-x-4">
 <div class="flex flex-col lg:flex-row w-full h-full py-2 lg:space-x-4">
 	<div
 	<div
-		class="tabs flex flex-row overflow-x-auto space-x-1 max-w-full lg:space-x-0 lg:space-y-1 lg:flex-col lg:flex-none lg:w-44 dark:text-gray-200 text-xs text-left scrollbar-none"
+		id="admin-settings-tabs-container" class="tabs flex flex-row overflow-x-auto space-x-1 max-w-full lg:space-x-0 lg:space-y-1 lg:flex-col lg:flex-none lg:w-44 dark:text-gray-200 text-xs text-left scrollbar-none"
 	>
 	>
 		<button
 		<button
 			class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
 			class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===

+ 8 - 6
src/lib/components/admin/Settings/Models.svelte

@@ -158,12 +158,14 @@
 			return;
 			return;
 		}
 		}
 
 
-		const [res, controller] = await pullModel(localStorage.token, sanitizedModelTag, '0').catch(
-			(error) => {
-				toast.error(error);
-				return null;
-			}
-		);
+		const [res, controller] = await pullModel(
+			localStorage.token,
+			sanitizedModelTag,
+			selectedOllamaUrlIdx
+		).catch((error) => {
+			toast.error(error);
+			return null;
+		});
 
 
 		if (res) {
 		if (res) {
 			const reader = res.body
 			const reader = res.body

+ 19 - 11
src/lib/components/chat/Chat.svelte

@@ -126,21 +126,29 @@
 		})();
 		})();
 	}
 	}
 
 
-	const chatEventHandler = async (data) => {
-		if (data.chat_id === $chatId) {
+	const chatEventHandler = async (event) => {
+		if (event.chat_id === $chatId) {
 			await tick();
 			await tick();
-			console.log(data);
-			let message = history.messages[data.message_id];
+			console.log(event);
+			let message = history.messages[event.message_id];
 
 
-			const status = {
-				done: data?.data?.done ?? null,
-				description: data?.data?.status ?? null
-			};
+			const type = event?.data?.type ?? null;
+			const data = event?.data?.data ?? null;
 
 
-			if (message.statusHistory) {
-				message.statusHistory.push(status);
+			if (type === 'status') {
+				if (message.statusHistory) {
+					message.statusHistory.push(data);
+				} else {
+					message.statusHistory = [data];
+				}
+			} else if (type === 'citation') {
+				if (message.citations) {
+					message.citations.push(data);
+				} else {
+					message.citations = [data];
+				}
 			} else {
 			} else {
-				message.statusHistory = [status];
+				console.log('Unknown message type', data);
 			}
 			}
 
 
 			messages = messages;
 			messages = messages;

+ 15 - 22
src/lib/components/chat/Messages/ResponseMessage/WebSearchResults.svelte

@@ -2,32 +2,25 @@
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
 	import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
 	import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
 	import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
 	import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
-	import { Collapsible } from 'bits-ui';
-	import { slide } from 'svelte/transition';
+	import Collapsible from '$lib/components/common/Collapsible.svelte';
 
 
 	export let status = { urls: [], query: '' };
 	export let status = { urls: [], query: '' };
 	let state = false;
 	let state = false;
 </script>
 </script>
 
 
-<Collapsible.Root class="w-full space-y-1" bind:open={state}>
-	<Collapsible.Trigger>
-		<div
-			class="flex items-center gap-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
-		>
-			<slot />
-
-			{#if state}
-				<ChevronUp strokeWidth="3.5" className="size-3.5 " />
-			{:else}
-				<ChevronDown strokeWidth="3.5" className="size-3.5 " />
-			{/if}
-		</div>
-	</Collapsible.Trigger>
-
-	<Collapsible.Content
-		class=" text-sm border border-gray-300/30 dark:border-gray-700/50 rounded-xl"
-		transition={slide}
+<Collapsible bind:open={state} className="w-full space-y-1">
+	<div
+		class="flex items-center gap-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300 transition"
 	>
 	>
+		<slot />
+
+		{#if state}
+			<ChevronUp strokeWidth="3.5" className="size-3.5 " />
+		{:else}
+			<ChevronDown strokeWidth="3.5" className="size-3.5 " />
+		{/if}
+	</div>
+	<div class="text-sm border border-gray-300/30 dark:border-gray-700/50 rounded-xl" slot="content">
 		{#if status?.query}
 		{#if status?.query}
 			<a
 			<a
 				href="https://www.google.com/search?q={status.query}"
 				href="https://www.google.com/search?q={status.query}"
@@ -93,5 +86,5 @@
 				</div>
 				</div>
 			</a>
 			</a>
 		{/each}
 		{/each}
-	</Collapsible.Content>
-</Collapsible.Root>
+	</div>
+</Collapsible>

+ 35 - 4
src/lib/components/chat/SettingsModal.svelte

@@ -1,9 +1,10 @@
 <script lang="ts">
 <script lang="ts">
-	import { getContext } from 'svelte';
+	import { getContext, tick } from 'svelte';
 	import { toast } from 'svelte-sonner';
 	import { toast } from 'svelte-sonner';
 	import { models, settings, user } from '$lib/stores';
 	import { models, settings, user } from '$lib/stores';
-
+	import { updateUserSettings } from '$lib/apis/users';
 	import { getModels as _getModels } from '$lib/apis';
 	import { getModels as _getModels } from '$lib/apis';
+	import { goto } from '$app/navigation';
 
 
 	import Modal from '../common/Modal.svelte';
 	import Modal from '../common/Modal.svelte';
 	import Account from './Settings/Account.svelte';
 	import Account from './Settings/Account.svelte';
@@ -14,8 +15,6 @@
 	import Chats from './Settings/Chats.svelte';
 	import Chats from './Settings/Chats.svelte';
 	import User from '../icons/User.svelte';
 	import User from '../icons/User.svelte';
 	import Personalization from './Settings/Personalization.svelte';
 	import Personalization from './Settings/Personalization.svelte';
-	import { updateUserSettings } from '$lib/apis/users';
-	import { goto } from '$app/navigation';
 	import Valves from './Settings/Valves.svelte';
 	import Valves from './Settings/Valves.svelte';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
@@ -34,6 +33,37 @@
 	};
 	};
 
 
 	let selectedTab = 'general';
 	let selectedTab = 'general';
+
+	// Function to handle sideways scrolling
+	const scrollHandler = (event) => {
+		const settingsTabsContainer = document.getElementById('settings-tabs-container');
+		if (settingsTabsContainer) {
+			event.preventDefault(); // Prevent default vertical scrolling
+			settingsTabsContainer.scrollLeft += event.deltaY; // Scroll sideways
+		}
+	};
+
+	const addScrollListener = async () => {
+		await tick();
+		const settingsTabsContainer = document.getElementById('settings-tabs-container');
+		if (settingsTabsContainer) {
+			settingsTabsContainer.addEventListener('wheel', scrollHandler);
+		}
+	};
+
+	const removeScrollListener = async () => {
+		await tick();
+		const settingsTabsContainer = document.getElementById('settings-tabs-container');
+		if (settingsTabsContainer) {
+			settingsTabsContainer.removeEventListener('wheel', scrollHandler);
+		}
+	};
+
+	$: if (show) {
+		addScrollListener();
+	} else {
+		removeScrollListener();
+	}
 </script>
 </script>
 
 
 <Modal bind:show>
 <Modal bind:show>
@@ -61,6 +91,7 @@
 
 
 		<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
 		<div class="flex flex-col md:flex-row w-full p-4 md:space-x-4">
 			<div
 			<div
+				id="settings-tabs-container"
 				class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
 				class="tabs flex flex-row overflow-x-auto space-x-1 md:space-x-0 md:space-y-1 md:flex-col flex-1 md:flex-none md:w-40 dark:text-gray-200 text-xs text-left mb-3 md:mb-0"
 			>
 			>
 				<button
 				<button

+ 18 - 0
src/lib/components/common/Collapsible.svelte

@@ -0,0 +1,18 @@
+<script lang="ts">
+	import { slide } from 'svelte/transition';
+	import { quintOut } from 'svelte/easing';
+	export let open = false;
+	export let className = '';
+</script>
+
+<div class={className}>
+	<button on:click={() => (open = !open)}>
+		<slot />
+	</button>
+
+	{#if open}
+		<div transition:slide={{ duration: 300, easing: quintOut, axis: 'y' }}>
+			<slot name="content" />
+		</div>
+	{/if}
+</div>

+ 1 - 0
src/lib/components/layout/Sidebar.svelte

@@ -186,6 +186,7 @@
 				goto('/');
 				goto('/');
 			}
 			}
 			await chats.set(await getChatList(localStorage.token));
 			await chats.set(await getChatList(localStorage.token));
+			await pinnedChats.set(await getChatListByTagName(localStorage.token, 'pinned'));
 		}
 		}
 	};
 	};
 </script>
 </script>

+ 9 - 9
src/lib/i18n/locales/ca-ES/translation.json

@@ -126,7 +126,7 @@
 	"Connections": "Connexions",
 	"Connections": "Connexions",
 	"Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI",
 	"Contact Admin for WebUI Access": "Posat en contacte amb l'administrador per accedir a WebUI",
 	"Content": "Contingut",
 	"Content": "Contingut",
-	"Content Extraction": "",
+	"Content Extraction": "Extraccció de contingut",
 	"Context Length": "Mida del context",
 	"Context Length": "Mida del context",
 	"Continue Response": "Continuar la resposta",
 	"Continue Response": "Continuar la resposta",
 	"Continue with {{provider}}": "Continuar amb {{provider}}",
 	"Continue with {{provider}}": "Continuar amb {{provider}}",
@@ -213,7 +213,7 @@
 	"Enable Community Sharing": "Activar l'ús compartit amb la comunitat",
 	"Enable Community Sharing": "Activar l'ús compartit amb la comunitat",
 	"Enable New Sign Ups": "Permetre nous registres",
 	"Enable New Sign Ups": "Permetre nous registres",
 	"Enable Web Search": "Activar la cerca web",
 	"Enable Web Search": "Activar la cerca web",
-	"Engine": "",
+	"Engine": "Motor",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que els teus fitxers CSV inclouen 4 columnes en aquest ordre: Nom, Correu electrònic, Contrasenya, Rol.",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "Assegura't que els teus fitxers CSV inclouen 4 columnes en aquest ordre: Nom, Correu electrònic, Contrasenya, Rol.",
 	"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
 	"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
 	"Enter a detail about yourself for your LLMs to recall": "Introdueix un detall sobre tu què els teus models de llenguatge puguin recordar",
 	"Enter a detail about yourself for your LLMs to recall": "Introdueix un detall sobre tu què els teus models de llenguatge puguin recordar",
@@ -235,7 +235,7 @@
 	"Enter Serpstack API Key": "Introdueix la clau API Serpstack",
 	"Enter Serpstack API Key": "Introdueix la clau API Serpstack",
 	"Enter stop sequence": "Introdueix la seqüència de parada",
 	"Enter stop sequence": "Introdueix la seqüència de parada",
 	"Enter Tavily API Key": "Introdueix la clau API de Tavily",
 	"Enter Tavily API Key": "Introdueix la clau API de Tavily",
-	"Enter Tika Server URL": "",
+	"Enter Tika Server URL": "Introdueix l'URL del servidor Tika",
 	"Enter Top K": "Introdueix Top K",
 	"Enter Top K": "Introdueix Top K",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "Introdueix l'URL (p. ex. http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "Introdueix l'URL (p. ex. http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://localhost:11434)": "Introdueix l'URL (p. ex. http://localhost:11434)",
 	"Enter URL (e.g. http://localhost:11434)": "Introdueix l'URL (p. ex. http://localhost:11434)",
@@ -412,7 +412,7 @@
 	"Open": "Obre",
 	"Open": "Obre",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
 	"Open new chat": "Obre un xat nou",
 	"Open new chat": "Obre un xat nou",
-	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "La versió d'Open WebUI (v{{OPEN_WEBUI_VERSION}}) és inferior a la versió requerida (v{{REQUIRED_VERSION}})",
 	"OpenAI": "OpenAI",
 	"OpenAI": "OpenAI",
 	"OpenAI API": "API d'OpenAI",
 	"OpenAI API": "API d'OpenAI",
 	"OpenAI API Config": "Configuració de l'API d'OpenAI",
 	"OpenAI API Config": "Configuració de l'API d'OpenAI",
@@ -428,8 +428,8 @@
 	"Permission denied when accessing microphone": "Permís denegat en accedir al micròfon",
 	"Permission denied when accessing microphone": "Permís denegat en accedir al micròfon",
 	"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
 	"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
 	"Personalization": "Personalització",
 	"Personalization": "Personalització",
-	"Pin": "",
-	"Pinned": "",
+	"Pin": "Fixar",
+	"Pinned": "Fixat",
 	"Pipeline deleted successfully": "Pipeline eliminada correctament",
 	"Pipeline deleted successfully": "Pipeline eliminada correctament",
 	"Pipeline downloaded successfully": "Pipeline descarregada correctament",
 	"Pipeline downloaded successfully": "Pipeline descarregada correctament",
 	"Pipelines": "Pipelines",
 	"Pipelines": "Pipelines",
@@ -580,8 +580,8 @@
 	"This setting does not sync across browsers or devices.": "Aquesta preferència no es sincronitza entre navegadors ni dispositius.",
 	"This setting does not sync across browsers or devices.": "Aquesta preferència no es sincronitza entre navegadors ni dispositius.",
 	"This will delete": "Això eliminarà",
 	"This will delete": "Això eliminarà",
 	"Thorough explanation": "Explicació en detall",
 	"Thorough explanation": "Explicació en detall",
-	"Tika": "",
-	"Tika Server URL required.": "",
+	"Tika": "Tika",
+	"Tika Server URL required.": "La URL del servidor Tika és obligatòria.",
 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consell: Actualitza les diverses variables consecutivament prement la tecla de tabulació en l'entrada del xat després de cada reemplaçament.",
 	"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consell: Actualitza les diverses variables consecutivament prement la tecla de tabulació en l'entrada del xat després de cada reemplaçament.",
 	"Title": "Títol",
 	"Title": "Títol",
 	"Title (e.g. Tell me a fun fact)": "Títol (p. ex. Digues-me quelcom divertit)",
 	"Title (e.g. Tell me a fun fact)": "Títol (p. ex. Digues-me quelcom divertit)",
@@ -616,7 +616,7 @@
 	"Uh-oh! There was an issue connecting to {{provider}}.": "Oh! Hi ha hagut un problema connectant a {{provider}}.",
 	"Uh-oh! There was an issue connecting to {{provider}}.": "Oh! Hi ha hagut un problema connectant a {{provider}}.",
 	"UI": "UI",
 	"UI": "UI",
 	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "Tipus de fitxer desconegut '{{file_type}}'. Continuant amb la càrrega del fitxer.",
 	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "Tipus de fitxer desconegut '{{file_type}}'. Continuant amb la càrrega del fitxer.",
-	"Unpin": "",
+	"Unpin": "Alliberar",
 	"Update": "Actualitzar",
 	"Update": "Actualitzar",
 	"Update and Copy Link": "Actualitzar i copiar l'enllaç",
 	"Update and Copy Link": "Actualitzar i copiar l'enllaç",
 	"Update password": "Actualitzar la contrasenya",
 	"Update password": "Actualitzar la contrasenya",

+ 9 - 9
src/lib/i18n/locales/zh-CN/translation.json

@@ -126,7 +126,7 @@
 	"Connections": "外部连接",
 	"Connections": "外部连接",
 	"Contact Admin for WebUI Access": "请联系管理员以获取访问权限",
 	"Contact Admin for WebUI Access": "请联系管理员以获取访问权限",
 	"Content": "内容",
 	"Content": "内容",
-	"Content Extraction": "",
+	"Content Extraction": "内容提取",
 	"Context Length": "上下文长度",
 	"Context Length": "上下文长度",
 	"Continue Response": "继续生成",
 	"Continue Response": "继续生成",
 	"Continue with {{provider}}": "使用 {{provider}} 继续",
 	"Continue with {{provider}}": "使用 {{provider}} 继续",
@@ -213,7 +213,7 @@
 	"Enable Community Sharing": "启用分享至社区",
 	"Enable Community Sharing": "启用分享至社区",
 	"Enable New Sign Ups": "允许新用户注册",
 	"Enable New Sign Ups": "允许新用户注册",
 	"Enable Web Search": "启用网络搜索",
 	"Enable Web Search": "启用网络搜索",
-	"Engine": "",
+	"Engine": "引擎",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "确保您的 CSV 文件按以下顺序包含 4 列: 姓名、电子邮箱、密码、角色。",
 	"Ensure your CSV file includes 4 columns in this order: Name, Email, Password, Role.": "确保您的 CSV 文件按以下顺序包含 4 列: 姓名、电子邮箱、密码、角色。",
 	"Enter {{role}} message here": "在此处输入 {{role}} 信息",
 	"Enter {{role}} message here": "在此处输入 {{role}} 信息",
 	"Enter a detail about yourself for your LLMs to recall": "输入一个关于你自己的详细信息,方便你的大语言模型记住这些内容",
 	"Enter a detail about yourself for your LLMs to recall": "输入一个关于你自己的详细信息,方便你的大语言模型记住这些内容",
@@ -235,7 +235,7 @@
 	"Enter Serpstack API Key": "输入 Serpstack API 密钥",
 	"Enter Serpstack API Key": "输入 Serpstack API 密钥",
 	"Enter stop sequence": "输入停止序列 (Stop Sequence)",
 	"Enter stop sequence": "输入停止序列 (Stop Sequence)",
 	"Enter Tavily API Key": "输入 Tavily API 密钥",
 	"Enter Tavily API Key": "输入 Tavily API 密钥",
-	"Enter Tika Server URL": "",
+	"Enter Tika Server URL": "输入 Tika 服务器地址",
 	"Enter Top K": "输入 Top K",
 	"Enter Top K": "输入 Top K",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "输入地址 (例如:http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://127.0.0.1:7860/)": "输入地址 (例如:http://127.0.0.1:7860/)",
 	"Enter URL (e.g. http://localhost:11434)": "输入地址 (例如:http://localhost:11434)",
 	"Enter URL (e.g. http://localhost:11434)": "输入地址 (例如:http://localhost:11434)",
@@ -412,7 +412,7 @@
 	"Open": "打开",
 	"Open": "打开",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
 	"Open AI (Dall-E)": "Open AI (Dall-E)",
 	"Open new chat": "打开新对话",
 	"Open new chat": "打开新对话",
-	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "",
+	"Open WebUI version (v{{OPEN_WEBUI_VERSION}}) is lower than required version (v{{REQUIRED_VERSION}})": "当前 Open WebUI 版本 (v{{OPEN_WEBUI_VERSION}}) 低于所需的版本 (v{{REQUIRED_VERSION}})",
 	"OpenAI": "OpenAI",
 	"OpenAI": "OpenAI",
 	"OpenAI API": "OpenAI API",
 	"OpenAI API": "OpenAI API",
 	"OpenAI API Config": "OpenAI API 配置",
 	"OpenAI API Config": "OpenAI API 配置",
@@ -428,8 +428,8 @@
 	"Permission denied when accessing microphone": "申请麦克风权限被拒绝",
 	"Permission denied when accessing microphone": "申请麦克风权限被拒绝",
 	"Permission denied when accessing microphone: {{error}}": "申请麦克风权限被拒绝:{{error}}",
 	"Permission denied when accessing microphone: {{error}}": "申请麦克风权限被拒绝:{{error}}",
 	"Personalization": "个性化",
 	"Personalization": "个性化",
-	"Pin": "",
-	"Pinned": "",
+	"Pin": "置顶",
+	"Pinned": "已置顶",
 	"Pipeline deleted successfully": "Pipeline 删除成功",
 	"Pipeline deleted successfully": "Pipeline 删除成功",
 	"Pipeline downloaded successfully": "Pipeline 下载成功",
 	"Pipeline downloaded successfully": "Pipeline 下载成功",
 	"Pipelines": "Pipeline",
 	"Pipelines": "Pipeline",
@@ -578,8 +578,8 @@
 	"This setting does not sync across browsers or devices.": "此设置不会在浏览器或设备之间同步。",
 	"This setting does not sync across browsers or devices.": "此设置不会在浏览器或设备之间同步。",
 	"This will delete": "这将删除",
 	"This will delete": "这将删除",
 	"Thorough explanation": "解释较为详细",
 	"Thorough explanation": "解释较为详细",
-	"Tika": "",
-	"Tika Server URL required.": "",
+	"Tika": "Tika",
+	"Tika Server URL required.": "请输入 Tika 服务器地址。",
 	"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": "标题",
 	"Title (e.g. Tell me a fun fact)": "标题(例如 给我讲一个有趣的事实)",
 	"Title (e.g. Tell me a fun fact)": "标题(例如 给我讲一个有趣的事实)",
@@ -614,7 +614,7 @@
 	"Uh-oh! There was an issue connecting to {{provider}}.": "糟糕!连接到 {{provider}} 时出现问题。",
 	"Uh-oh! There was an issue connecting to {{provider}}.": "糟糕!连接到 {{provider}} 时出现问题。",
 	"UI": "界面",
 	"UI": "界面",
 	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "未知文件类型“{{file_type}}”,将无视继续上传文件。",
 	"Unknown file type '{{file_type}}'. Proceeding with the file upload anyway.": "未知文件类型“{{file_type}}”,将无视继续上传文件。",
-	"Unpin": "",
+	"Unpin": "取消置顶",
 	"Update": "更新",
 	"Update": "更新",
 	"Update and Copy Link": "更新和复制链接",
 	"Update and Copy Link": "更新和复制链接",
 	"Update password": "更新密码",
 	"Update password": "更新密码",