Unverified Commit 2fa94956 authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge pull request #1209 from open-webui/dev

0.1.114
parents d865b9fe 8e52ba8b
...@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file. ...@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.114] - 2024-03-20
### Added
- **🔗 Webhook Integration**: Now you can subscribe to new user sign-up events via webhook. Simply navigate to the admin panel > admin settings > webhook URL.
- **🛡️ Enhanced Model Filtering**: Alongside Ollama, OpenAI proxy model whitelisting, we've added model filtering functionality for LiteLLM proxy.
- **🌍 Expanded Language Support**: Spanish, Catalan, and Vietnamese languages are now available, with improvements made to others.
### Fixed
- **🔧 Input Field Spelling**: Resolved issue with spelling mistakes in input fields.
- **🖊️ Light Mode Styling**: Fixed styling issue with light mode in document adding.
### Changed
- **🔄 Language Sorting**: Languages are now sorted alphabetically by their code for improved organization.
## [0.1.113] - 2024-03-18 ## [0.1.113] - 2024-03-18
### Added ### Added
......
from litellm.proxy.proxy_server import ProxyConfig, initialize from litellm.proxy.proxy_server import ProxyConfig, initialize
from litellm.proxy.proxy_server import app from litellm.proxy.proxy_server import app
from fastapi import FastAPI, Request, Depends, status from fastapi import FastAPI, Request, Depends, status, Response
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.responses import StreamingResponse
import json
from utils.utils import get_http_authorization_cred, get_current_user from utils.utils import get_http_authorization_cred, get_current_user
from config import ENV from config import ENV
from config import (
MODEL_FILTER_ENABLED,
MODEL_FILTER_LIST,
)
proxy_config = ProxyConfig() proxy_config = ProxyConfig()
...@@ -26,16 +38,58 @@ async def on_startup(): ...@@ -26,16 +38,58 @@ async def on_startup():
await startup() await startup()
app.state.MODEL_FILTER_ENABLED = MODEL_FILTER_ENABLED
app.state.MODEL_FILTER_LIST = MODEL_FILTER_LIST
@app.middleware("http") @app.middleware("http")
async def auth_middleware(request: Request, call_next): async def auth_middleware(request: Request, call_next):
auth_header = request.headers.get("Authorization", "") auth_header = request.headers.get("Authorization", "")
request.state.user = None
if ENV != "dev":
try: try:
user = get_current_user(get_http_authorization_cred(auth_header)) user = get_current_user(get_http_authorization_cred(auth_header))
print(user) print(user)
request.state.user = user
except Exception as e: except Exception as e:
return JSONResponse(status_code=400, content={"detail": str(e)}) return JSONResponse(status_code=400, content={"detail": str(e)})
response = await call_next(request) response = await call_next(request)
return response return response
class ModifyModelsResponseMiddleware(BaseHTTPMiddleware):
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
) -> Response:
response = await call_next(request)
user = request.state.user
if "/models" in request.url.path:
if isinstance(response, StreamingResponse):
# Read the content of the streaming response
body = b""
async for chunk in response.body_iterator:
body += chunk
data = json.loads(body.decode("utf-8"))
if app.state.MODEL_FILTER_ENABLED:
if user and user.role == "user":
data["data"] = list(
filter(
lambda model: model["id"]
in app.state.MODEL_FILTER_LIST,
data["data"],
)
)
# Modified Flag
data["modified"] = True
return JSONResponse(content=data)
return response
app.add_middleware(ModifyModelsResponseMiddleware)
...@@ -709,7 +709,7 @@ class GenerateChatCompletionForm(BaseModel): ...@@ -709,7 +709,7 @@ class GenerateChatCompletionForm(BaseModel):
format: Optional[str] = None format: Optional[str] = None
options: Optional[dict] = None options: Optional[dict] = None
template: Optional[str] = None template: Optional[str] = None
stream: Optional[bool] = True stream: Optional[bool] = None
keep_alive: Optional[Union[int, str]] = None keep_alive: Optional[Union[int, str]] = None
......
...@@ -19,6 +19,7 @@ from config import ( ...@@ -19,6 +19,7 @@ from config import (
DEFAULT_USER_ROLE, DEFAULT_USER_ROLE,
ENABLE_SIGNUP, ENABLE_SIGNUP,
USER_PERMISSIONS, USER_PERMISSIONS,
WEBHOOK_URL,
) )
app = FastAPI() app = FastAPI()
...@@ -32,6 +33,7 @@ app.state.DEFAULT_MODELS = DEFAULT_MODELS ...@@ -32,6 +33,7 @@ app.state.DEFAULT_MODELS = DEFAULT_MODELS
app.state.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS app.state.DEFAULT_PROMPT_SUGGESTIONS = DEFAULT_PROMPT_SUGGESTIONS
app.state.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE app.state.DEFAULT_USER_ROLE = DEFAULT_USER_ROLE
app.state.USER_PERMISSIONS = USER_PERMISSIONS app.state.USER_PERMISSIONS = USER_PERMISSIONS
app.state.WEBHOOK_URL = WEBHOOK_URL
app.add_middleware( app.add_middleware(
......
...@@ -27,7 +27,8 @@ from utils.utils import ( ...@@ -27,7 +27,8 @@ from utils.utils import (
create_token, create_token,
) )
from utils.misc import parse_duration, validate_email_format from utils.misc import parse_duration, validate_email_format
from constants import ERROR_MESSAGES from utils.webhook import post_webhook
from constants import ERROR_MESSAGES, WEBHOOK_MESSAGES
router = APIRouter() router = APIRouter()
...@@ -155,6 +156,17 @@ async def signup(request: Request, form_data: SignupForm): ...@@ -155,6 +156,17 @@ async def signup(request: Request, form_data: SignupForm):
) )
# response.set_cookie(key='token', value=token, httponly=True) # response.set_cookie(key='token', value=token, httponly=True)
if request.app.state.WEBHOOK_URL:
post_webhook(
request.app.state.WEBHOOK_URL,
WEBHOOK_MESSAGES.USER_SIGNUP(user.name),
{
"action": "signup",
"message": WEBHOOK_MESSAGES.USER_SIGNUP(user.name),
"user": user.model_dump_json(exclude_none=True),
},
)
return { return {
"token": token, "token": token,
"token_type": "Bearer", "token_type": "Bearer",
......
...@@ -290,13 +290,19 @@ DEFAULT_PROMPT_SUGGESTIONS = ( ...@@ -290,13 +290,19 @@ DEFAULT_PROMPT_SUGGESTIONS = (
DEFAULT_USER_ROLE = os.getenv("DEFAULT_USER_ROLE", "pending") DEFAULT_USER_ROLE = os.getenv("DEFAULT_USER_ROLE", "pending")
USER_PERMISSIONS = {"chat": {"deletion": True}}
USER_PERMISSIONS_CHAT_DELETION = (
os.environ.get("USER_PERMISSIONS_CHAT_DELETION", "True").lower() == "true"
)
USER_PERMISSIONS = {"chat": {"deletion": USER_PERMISSIONS_CHAT_DELETION}}
MODEL_FILTER_ENABLED = os.environ.get("MODEL_FILTER_ENABLED", False) MODEL_FILTER_ENABLED = os.environ.get("MODEL_FILTER_ENABLED", "False").lower() == "true"
MODEL_FILTER_LIST = os.environ.get("MODEL_FILTER_LIST", "") MODEL_FILTER_LIST = os.environ.get("MODEL_FILTER_LIST", "")
MODEL_FILTER_LIST = [model.strip() for model in MODEL_FILTER_LIST.split(";")] MODEL_FILTER_LIST = [model.strip() for model in MODEL_FILTER_LIST.split(";")]
WEBHOOK_URL = os.environ.get("WEBHOOK_URL", "")
#################################### ####################################
# WEBUI_VERSION # WEBUI_VERSION
......
...@@ -5,6 +5,13 @@ class MESSAGES(str, Enum): ...@@ -5,6 +5,13 @@ class MESSAGES(str, Enum):
DEFAULT = lambda msg="": f"{msg if msg else ''}" DEFAULT = lambda msg="": f"{msg if msg else ''}"
class WEBHOOK_MESSAGES(str, Enum):
DEFAULT = lambda msg="": f"{msg if msg else ''}"
USER_SIGNUP = lambda username="": (
f"New user signed up: {username}" if username else "New user signed up"
)
class ERROR_MESSAGES(str, Enum): class ERROR_MESSAGES(str, Enum):
def __str__(self) -> str: def __str__(self) -> str:
return super().__str__() return super().__str__()
...@@ -46,7 +53,7 @@ class ERROR_MESSAGES(str, Enum): ...@@ -46,7 +53,7 @@ class ERROR_MESSAGES(str, Enum):
PANDOC_NOT_INSTALLED = "Pandoc is not installed on the server. Please contact your administrator for assistance." PANDOC_NOT_INSTALLED = "Pandoc is not installed on the server. Please contact your administrator for assistance."
INCORRECT_FORMAT = ( INCORRECT_FORMAT = (
lambda err="": f"Invalid format. Please use the correct format{err if err else ''}" lambda err="": f"Invalid format. Please use the correct format{err}"
) )
RATE_LIMIT_EXCEEDED = "API rate limit exceeded" RATE_LIMIT_EXCEEDED = "API rate limit exceeded"
......
{ {
"version": "0.0.1", "version": 0,
"ui": { "ui": {
"prompt_suggestions": [ "prompt_suggestions": [
{ {
......
...@@ -38,6 +38,7 @@ from config import ( ...@@ -38,6 +38,7 @@ from config import (
FRONTEND_BUILD_DIR, FRONTEND_BUILD_DIR,
MODEL_FILTER_ENABLED, MODEL_FILTER_ENABLED,
MODEL_FILTER_LIST, MODEL_FILTER_LIST,
WEBHOOK_URL,
) )
from constants import ERROR_MESSAGES from constants import ERROR_MESSAGES
...@@ -58,6 +59,9 @@ app = FastAPI(docs_url="/docs" if ENV == "dev" else None, redoc_url=None) ...@@ -58,6 +59,9 @@ app = FastAPI(docs_url="/docs" if ENV == "dev" else None, redoc_url=None)
app.state.MODEL_FILTER_ENABLED = MODEL_FILTER_ENABLED app.state.MODEL_FILTER_ENABLED = MODEL_FILTER_ENABLED
app.state.MODEL_FILTER_LIST = MODEL_FILTER_LIST app.state.MODEL_FILTER_LIST = MODEL_FILTER_LIST
app.state.WEBHOOK_URL = WEBHOOK_URL
origins = ["*"] origins = ["*"]
...@@ -178,7 +182,7 @@ class ModelFilterConfigForm(BaseModel): ...@@ -178,7 +182,7 @@ class ModelFilterConfigForm(BaseModel):
@app.post("/api/config/model/filter") @app.post("/api/config/model/filter")
async def get_model_filter_config( async def update_model_filter_config(
form_data: ModelFilterConfigForm, user=Depends(get_admin_user) form_data: ModelFilterConfigForm, user=Depends(get_admin_user)
): ):
...@@ -197,6 +201,28 @@ async def get_model_filter_config( ...@@ -197,6 +201,28 @@ async def get_model_filter_config(
} }
@app.get("/api/webhook")
async def get_webhook_url(user=Depends(get_admin_user)):
return {
"url": app.state.WEBHOOK_URL,
}
class UrlForm(BaseModel):
url: str
@app.post("/api/webhook")
async def update_webhook_url(form_data: UrlForm, user=Depends(get_admin_user)):
app.state.WEBHOOK_URL = form_data.url
webui_app.state.WEBHOOK_URL = app.state.WEBHOOK_URL
return {
"url": app.state.WEBHOOK_URL,
}
@app.get("/api/version") @app.get("/api/version")
async def get_app_config(): async def get_app_config():
......
import requests
def post_webhook(url: str, message: str, event_data: dict) -> bool:
try:
payload = {}
if "https://hooks.slack.com" in url:
payload["text"] = message
elif "https://discord.com/api/webhooks" in url:
payload["content"] = message
else:
payload = {**event_data}
r = requests.post(url, json=payload)
r.raise_for_status()
return True
except Exception as e:
print(e)
return False
{ {
"name": "open-webui", "name": "open-webui",
"version": "0.1.113", "version": "0.1.114",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev --host", "dev": "vite dev --host",
......
...@@ -139,3 +139,60 @@ export const updateModelFilterConfig = async ( ...@@ -139,3 +139,60 @@ export const updateModelFilterConfig = async (
return res; return res;
}; };
export const getWebhookUrl = async (token: string) => {
let error = null;
const res = await fetch(`${WEBUI_BASE_URL}/api/webhook`, {
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;
return null;
});
if (error) {
throw error;
}
return res.url;
};
export const updateWebhookUrl = async (token: string, url: string) => {
let error = null;
const res = await fetch(`${WEBUI_BASE_URL}/api/webhook`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
url: url
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err;
return null;
});
if (error) {
throw error;
}
return res.url;
};
<script lang="ts"> <script lang="ts">
import { getWebhookUrl, updateWebhookUrl } from '$lib/apis';
import { import {
getDefaultUserRole, getDefaultUserRole,
getJWTExpiresDuration, getJWTExpiresDuration,
...@@ -16,6 +17,8 @@ ...@@ -16,6 +17,8 @@
let defaultUserRole = 'pending'; let defaultUserRole = 'pending';
let JWTExpiresIn = ''; let JWTExpiresIn = '';
let webhookUrl = '';
const toggleSignUpEnabled = async () => { const toggleSignUpEnabled = async () => {
signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token); signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token);
}; };
...@@ -28,18 +31,23 @@ ...@@ -28,18 +31,23 @@
JWTExpiresIn = await updateJWTExpiresDuration(localStorage.token, duration); JWTExpiresIn = await updateJWTExpiresDuration(localStorage.token, duration);
}; };
const updateWebhookUrlHandler = async () => {
webhookUrl = await updateWebhookUrl(localStorage.token, webhookUrl);
};
onMount(async () => { onMount(async () => {
signUpEnabled = await getSignUpEnabledStatus(localStorage.token); signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
defaultUserRole = await getDefaultUserRole(localStorage.token); defaultUserRole = await getDefaultUserRole(localStorage.token);
JWTExpiresIn = await getJWTExpiresDuration(localStorage.token); JWTExpiresIn = await getJWTExpiresDuration(localStorage.token);
webhookUrl = await getWebhookUrl(localStorage.token);
}); });
</script> </script>
<form <form
class="flex flex-col h-full justify-between space-y-3 text-sm" class="flex flex-col h-full justify-between space-y-3 text-sm"
on:submit|preventDefault={() => { on:submit|preventDefault={() => {
// console.log('submit');
updateJWTExpiresDurationHandler(JWTExpiresIn); updateJWTExpiresDurationHandler(JWTExpiresIn);
updateWebhookUrlHandler();
saveHandler(); saveHandler();
}} }}
> >
...@@ -108,6 +116,23 @@ ...@@ -108,6 +116,23 @@
<hr class=" dark:border-gray-700 my-3" /> <hr class=" dark:border-gray-700 my-3" />
<div class=" w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Webhook URL')}</div>
</div>
<div class="flex mt-2 space-x-2">
<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="text"
placeholder={`https://example.com/webhook`}
bind:value={webhookUrl}
/>
</div>
</div>
<hr class=" dark:border-gray-700 my-3" />
<div class=" w-full justify-between"> <div class=" w-full justify-between">
<div class="flex w-full justify-between"> <div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('JWT Expiration')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('JWT Expiration')}</div>
......
...@@ -673,7 +673,7 @@ ...@@ -673,7 +673,7 @@
? chatInputPlaceholder ? chatInputPlaceholder
: isRecording : isRecording
? $i18n.t('Listening...') ? $i18n.t('Listening...')
: $i18n.t('Send a Messsage')} : $i18n.t('Send a Message')}
bind:value={prompt} bind:value={prompt}
on:keypress={(e) => { on:keypress={(e) => {
if (e.keyCode == 13 && !e.shiftKey) { if (e.keyCode == 13 && !e.shiftKey) {
......
...@@ -422,7 +422,7 @@ ...@@ -422,7 +422,7 @@
class=" flex justify-start space-x-1 overflow-x-auto buttons text-gray-700 dark:text-gray-500" class=" flex justify-start space-x-1 overflow-x-auto buttons text-gray-700 dark:text-gray-500"
> >
{#if siblings.length > 1} {#if siblings.length > 1}
<div class="flex self-center min-w-fit"> <div class="flex self-center min-w-fit -mt-1">
<button <button
class="self-center dark:hover:text-white hover:text-black transition" class="self-center dark:hover:text-white hover:text-black transition"
on:click={() => { on:click={() => {
......
...@@ -203,7 +203,7 @@ ...@@ -203,7 +203,7 @@
<div class=" flex justify-start space-x-1 text-gray-700 dark:text-gray-500"> <div class=" flex justify-start space-x-1 text-gray-700 dark:text-gray-500">
{#if siblings.length > 1} {#if siblings.length > 1}
<div class="flex self-center"> <div class="flex self-center -mt-1">
<button <button
class="self-center dark:hover:text-white hover:text-black transition" class="self-center dark:hover:text-white hover:text-black transition"
on:click={() => { on:click={() => {
......
...@@ -138,7 +138,7 @@ ...@@ -138,7 +138,7 @@
/> />
<button <button
class="w-full text-sm font-medium py-3 bg-gray-850 hover:bg-gray-800 text-center rounded-xl" class="w-full text-sm font-medium py-3 bg-gray-100 hover:bg-gray-200 dark:bg-gray-850 dark:hover:bg-gray-800 text-center rounded-xl"
type="button" type="button"
on:click={() => { on:click={() => {
uploadDocInputElement.click(); uploadDocInputElement.click();
......
{
"'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.": "'s', 'm', 'h', 'd', 'w' or '-1' per no caduca mai.",
"(Beta)": "(Beta)",
"(e.g. `sh webui.sh --api`)": "(p. ex. `sh webui.sh --api`)",
"(latest)": "(últim)",
"{{modelName}} is thinking...": "{{modelName}} està pensant...",
"{{webUIName}} Backend Required": "Es requereix Backend de {{webUIName}}",
"a user": "un usuari",
"About": "Sobre",
"Account": "Compte",
"Action": "Acció",
"Add a model": "Afegeix un model",
"Add a model tag name": "Afegeix un nom d'etiqueta de model",
"Add a short description about what this modelfile does": "Afegeix una descripció curta del que fa aquest arxiu de model",
"Add a short title for this prompt": "Afegeix un títol curt per aquest prompt",
"Add a tag": "Afegeix una etiqueta",
"Add Docs": "Afegeix Documents",
"Add Files": "Afegeix Arxius",
"Add message": "Afegeix missatge",
"add tags": "afegeix etiquetes",
"Adjusting these settings will apply changes universally to all users.": "Ajustar aquests paràmetres aplicarà canvis de manera universal a tots els usuaris.",
"admin": "administrador",
"Admin Panel": "Panell d'Administració",
"Admin Settings": "Configuració d'Administració",
"Advanced Parameters": "Paràmetres Avançats",
"all": "tots",
"All Users": "Tots els Usuaris",
"Allow": "Permet",
"Allow Chat Deletion": "Permet la Supressió del Xat",
"alphanumeric characters and hyphens": "caràcters alfanumèrics i guions",
"Already have an account?": "Ja tens un compte?",
"an assistant": "un assistent",
"and": "i",
"API Base URL": "URL Base de l'API",
"API Key": "Clau de l'API",
"API RPM": "RPM de l'API",
"are allowed - Activate this command by typing": "estan permesos - Activa aquesta comanda escrivint",
"Are you sure?": "Estàs segur?",
"Audio": "Àudio",
"Auto-playback response": "Resposta de reproducció automàtica",
"Auto-send input after 3 sec.": "Enviar entrada automàticament després de 3 segons",
"AUTOMATIC1111 Base URL": "URL Base AUTOMATIC1111",
"AUTOMATIC1111 Base URL is required.": "Es requereix l'URL Base AUTOMATIC1111.",
"available!": "disponible!",
"Back": "Enrere",
"Builder Mode": "Mode Constructor",
"Cancel": "Cancel·la",
"Categories": "Categories",
"Change Password": "Canvia la Contrasenya",
"Chat": "Xat",
"Chat History": "Històric del Xat",
"Chat History is off for this browser.": "L'historial de xat està desactivat per a aquest navegador.",
"Chats": "Xats",
"Check Again": "Comprova-ho de Nou",
"Check for updates": "Comprova si hi ha actualitzacions",
"Checking for updates...": "Comprovant actualitzacions...",
"Choose a model before saving...": "Tria un model abans de guardar...",
"Chunk Overlap": "Solapament de Blocs",
"Chunk Params": "Paràmetres de Blocs",
"Chunk Size": "Mida del Bloc",
"Click here for help.": "Fes clic aquí per ajuda.",
"Click here to check other modelfiles.": "Fes clic aquí per comprovar altres fitxers de model.",
"Click here to select": "Fes clic aquí per seleccionar",
"Click here to select documents.": "Fes clic aquí per seleccionar documents.",
"click here.": "fes clic aquí.",
"Click on the user role button to change a user's role.": "Fes clic al botó de rol d'usuari per canviar el rol d'un usuari.",
"Close": "Tanca",
"Collection": "Col·lecció",
"Command": "Comanda",
"Confirm Password": "Confirma la Contrasenya",
"Connections": "Connexions",
"Content": "Contingut",
"Context Length": "Longitud del Context",
"Conversation Mode": "Mode de Conversa",
"Copy last code block": "Copia l'últim bloc de codi",
"Copy last response": "Copia l'última resposta",
"Copying to clipboard was successful!": "La còpia al porta-retalls ha estat exitosa!",
"Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title':": "Crea una frase concisa de 3-5 paraules com a capçalera per a la següent consulta, seguint estrictament el límit de 3-5 paraules i evitant l'ús de la paraula 'títol':",
"Create a modelfile": "Crea un fitxer de model",
"Create Account": "Crea un Compte",
"Created at": "Creat el",
"Created by": "Creat per",
"Current Model": "Model Actual",
"Current Password": "Contrasenya Actual",
"Custom": "Personalitzat",
"Customize Ollama models for a specific purpose": "Personalitza els models Ollama per a un propòsit específic",
"Dark": "Fosc",
"Database": "Base de Dades",
"DD/MM/YYYY HH:mm": "DD/MM/YYYY HH:mm",
"Default": "Per defecte",
"Default (Automatic1111)": "Per defecte (Automatic1111)",
"Default (Web API)": "Per defecte (Web API)",
"Default model updated": "Model per defecte actualitzat",
"Default Prompt Suggestions": "Suggeriments de Prompt Per Defecte",
"Default User Role": "Rol d'Usuari Per Defecte",
"delete": "esborra",
"Delete a model": "Esborra un model",
"Delete chat": "Esborra xat",
"Delete Chats": "Esborra Xats",
"Deleted {{deleteModelTag}}": "Esborrat {{deleteModelTag}}",
"Deleted {tagName}": "Esborrat {tagName}",
"Description": "Descripció",
"Desktop Notifications": "Notificacions d'Escriptori",
"Disabled": "Desactivat",
"Discover a modelfile": "Descobreix un fitxer de model",
"Discover a prompt": "Descobreix un prompt",
"Discover, download, and explore custom prompts": "Descobreix, descarrega i explora prompts personalitzats",
"Discover, download, and explore model presets": "Descobreix, descarrega i explora presets de models",
"Display the username instead of You in the Chat": "Mostra el nom d'usuari en lloc de 'Tu' al Xat",
"Document": "Document",
"Document Settings": "Configuració de Documents",
"Documents": "Documents",
"does not make any external connections, and your data stays securely on your locally hosted server.": "no realitza connexions externes, i les teves dades romanen segures al teu servidor allotjat localment.",
"Don't Allow": "No Permetre",
"Don't have an account?": "No tens un compte?",
"Download as a File": "Descarrega com a Arxiu",
"Download Database": "Descarrega Base de Dades",
"Drop any files here to add to the conversation": "Deixa qualsevol arxiu aquí per afegir-lo a la conversa",
"e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.": "p. ex. '30s','10m'. Les unitats de temps vàlides són 's', 'm', 'h'.",
"Edit Doc": "Edita Document",
"Edit User": "Edita Usuari",
"Email": "Correu electrònic",
"Enable Chat History": "Activa Historial de Xat",
"Enable New Sign Ups": "Permet Noves Inscripcions",
"Enabled": "Activat",
"Enter {{role}} message here": "Introdueix aquí el missatge de {{role}}",
"Enter API Key": "Introdueix la Clau API",
"Enter Chunk Overlap": "Introdueix el Solapament de Blocs",
"Enter Chunk Size": "Introdueix la Mida del Bloc",
"Enter Image Size (e.g. 512x512)": "Introdueix la Mida de la Imatge (p. ex. 512x512)",
"Enter LiteLLM API Base URL (litellm_params.api_base)": "Introdueix l'URL Base de LiteLLM API (litellm_params.api_base)",
"Enter LiteLLM API Key (litellm_params.api_key)": "Introdueix la Clau de LiteLLM API (litellm_params.api_key)",
"Enter LiteLLM API RPM (litellm_params.rpm)": "Introdueix RPM de LiteLLM API (litellm_params.rpm)",
"Enter LiteLLM Model (litellm_params.model)": "Introdueix el Model de LiteLLM (litellm_params.model)",
"Enter Max Tokens (litellm_params.max_tokens)": "Introdueix el Màxim de Tokens (litellm_params.max_tokens)",
"Enter model tag (e.g. {{modelTag}})": "Introdueix l'etiqueta del model (p. ex. {{modelTag}})",
"Enter Number of Steps (e.g. 50)": "Introdueix el Nombre de Passos (p. ex. 50)",
"Enter stop sequence": "Introdueix la seqüència de parada",
"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 Your Email": "Introdueix el Teu Correu Electrònic",
"Enter Your Full Name": "Introdueix el Teu Nom Complet",
"Enter Your Password": "Introdueix la Teva Contrasenya",
"Experimental": "Experimental",
"Export All Chats (All Users)": "Exporta Tots els Xats (Tots els Usuaris)",
"Export Chats": "Exporta Xats",
"Export Documents Mapping": "Exporta el Mapatge de Documents",
"Export Modelfiles": "Exporta Fitxers de Model",
"Export Prompts": "Exporta Prompts",
"Failed to read clipboard contents": "No s'ha pogut llegir el contingut del porta-retalls",
"File Mode": "Mode Arxiu",
"File not found.": "Arxiu no trobat.",
"Focus chat input": "Enfoca l'entrada del xat",
"Format your variables using square brackets like this:": "Formata les teves variables utilitzant claudàtors així:",
"From (Base Model)": "Des de (Model Base)",
"Full Screen Mode": "Mode de Pantalla Completa",
"General": "General",
"General Settings": "Configuració General",
"Hello, {{name}}": "Hola, {{name}}",
"Hide": "Amaga",
"Hide Additional Params": "Amaga Paràmetres Addicionals",
"How can I help you today?": "Com et puc ajudar avui?",
"Image Generation (Experimental)": "Generació d'Imatges (Experimental)",
"Image Generation Engine": "Motor de Generació d'Imatges",
"Image Settings": "Configuració d'Imatges",
"Images": "Imatges",
"Import Chats": "Importa Xats",
"Import Documents Mapping": "Importa el Mapa de Documents",
"Import Modelfiles": "Importa Fitxers de Model",
"Import Prompts": "Importa Prompts",
"Include `--api` flag when running stable-diffusion-webui": "Inclou la bandera `--api` quan executis stable-diffusion-webui",
"Interface": "Interfície",
"join our Discord for help.": "uneix-te al nostre Discord per ajuda.",
"JSON": "JSON",
"JWT Expiration": "Expiració de JWT",
"JWT Token": "Token JWT",
"Keep Alive": "Mantén Actiu",
"Keyboard shortcuts": "Dreceres de Teclat",
"Language": "Idioma",
"Light": "Clar",
"Listening...": "Escoltant...",
"LLMs can make mistakes. Verify important information.": "Els LLMs poden cometre errors. Verifica la informació important.",
"Made by OpenWebUI Community": "Creat per la Comunitat OpenWebUI",
"Make sure to enclose them with": "Assegura't d'envoltar-los amb",
"Manage LiteLLM Models": "Gestiona Models LiteLLM",
"Manage Models": "Gestiona Models",
"Manage Ollama Models": "Gestiona Models Ollama",
"Max Tokens": "Màxim de Tokens",
"Maximum of 3 models can be downloaded simultaneously. Please try again later.": "Es poden descarregar un màxim de 3 models simultàniament. Si us plau, prova-ho més tard.",
"Mirostat": "Mirostat",
"Mirostat Eta": "Eta de Mirostat",
"Mirostat Tau": "Tau de Mirostat",
"MMMM DD, YYYY": "DD de MMMM, YYYY",
"Model '{{modelName}}' has been successfully downloaded.": "El model '{{modelName}}' s'ha descarregat amb èxit.",
"Model '{{modelTag}}' is already in queue for downloading.": "El model '{{modelTag}}' ja està en cua per ser descarregat.",
"Model {{modelId}} not found": "Model {{modelId}} no trobat",
"Model {{modelName}} already exists.": "El model {{modelName}} ja existeix.",
"Model Name": "Nom del Model",
"Model not selected": "Model no seleccionat",
"Model Tag Name": "Nom de l'Etiqueta del Model",
"Model Whitelisting": "Llista Blanca de Models",
"Model(s) Whitelisted": "Model(s) a la Llista Blanca",
"Modelfile": "Fitxer de Model",
"Modelfile Advanced Settings": "Configuració Avançada de Fitxers de Model",
"Modelfile Content": "Contingut del Fitxer de Model",
"Modelfiles": "Fitxers de Model",
"Models": "Models",
"My Documents": "Els Meus Documents",
"My Modelfiles": "Els Meus Fitxers de Model",
"My Prompts": "Els Meus Prompts",
"Name": "Nom",
"Name Tag": "Etiqueta de Nom",
"Name your modelfile": "Nomena el teu fitxer de model",
"New Chat": "Xat Nou",
"New Password": "Nova Contrasenya",
"Not sure what to add?": "No estàs segur del que afegir?",
"Not sure what to write? Switch to": "No estàs segur del que escriure? Canvia a",
"Off": "Desactivat",
"Okay, Let's Go!": "D'acord, Anem!",
"Ollama Base URL": "URL Base d'Ollama",
"Ollama Version": "Versió d'Ollama",
"On": "Activat",
"Only": "Només",
"Only alphanumeric characters and hyphens are allowed in the command string.": "Només es permeten caràcters alfanumèrics i guions en la cadena de comandes.",
"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.": "Ui! Aguanta! Els teus fitxers encara estan en el forn de processament. Els estem cuinant a la perfecció. Si us plau, tingues paciència i t'avisarem quan estiguin llestos.",
"Oops! Looks like the URL is invalid. Please double-check and try again.": "Ui! Sembla que l'URL és invàlida. Si us plau, revisa-ho i prova de nou.",
"Oops! You're using an unsupported method (frontend only). Please serve the WebUI from the backend.": "Ui! Estàs utilitzant un mètode no suportat (només frontend). Si us plau, serveix la WebUI des del backend.",
"Open": "Obre",
"Open AI": "Open AI",
"Open AI (Dall-E)": "Open AI (Dall-E)",
"Open new chat": "Obre un nou xat",
"OpenAI API": "API d'OpenAI",
"OpenAI API Key": "Clau API d'OpenAI",
"OpenAI API Key is required.": "Es requereix la Clau API d'OpenAI.",
"or": "o",
"Parameters": "Paràmetres",
"Password": "Contrasenya",
"PDF Extract Images (OCR)": "Extreu Imatges de PDF (OCR)",
"pending": "pendent",
"Permission denied when accessing microphone: {{error}}": "Permís denegat en accedir al micròfon: {{error}}",
"Playground": "Zona de Jocs",
"Profile": "Perfil",
"Prompt Content": "Contingut del Prompt",
"Prompt suggestions": "Suggeriments de Prompt",
"Prompts": "Prompts",
"Pull a model from Ollama.com": "Treu un model d'Ollama.com",
"Pull Progress": "Progrés de Tracció",
"Query Params": "Paràmetres de Consulta",
"RAG Template": "Plantilla RAG",
"Raw Format": "Format Brut",
"Record voice": "Enregistra veu",
"Redirecting you to OpenWebUI Community": "Redirigint-te a la Comunitat OpenWebUI",
"Release Notes": "Notes de la Versió",
"Repeat Last N": "Repeteix Últim N",
"Repeat Penalty": "Penalització de Repetició",
"Request Mode": "Mode de Sol·licitud",
"Reset Vector Storage": "Reinicia l'Emmagatzematge de Vectors",
"Response AutoCopy to Clipboard": "Resposta AutoCopiar al Portapapers",
"Role": "Rol",
"Rosé Pine": "Rosé Pine",
"Rosé Pine Dawn": "Albada Rosé Pine",
"Save": "Guarda",
"Save & Create": "Guarda i Crea",
"Save & Submit": "Guarda i Envia",
"Save & Update": "Guarda i Actualitza",
"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": "Guardar registres de xat directament a l'emmagatzematge del teu navegador ja no és suportat. Si us plau, pren un moment per descarregar i eliminar els teus registres de xat fent clic al botó de sota. No et preocupis, pots reimportar fàcilment els teus registres de xat al backend a través de",
"Scan": "Escaneja",
"Scan complete!": "Escaneig completat!",
"Scan for documents from {{path}}": "Escaneja documents des de {{path}}",
"Search": "Cerca",
"Search Documents": "Cerca Documents",
"Search Prompts": "Cerca Prompts",
"See readme.md for instructions": "Consulta el readme.md per a instruccions",
"See what's new": "Veure novetats",
"Seed": "Llavor",
"Select a mode": "Selecciona un mode",
"Select a model": "Selecciona un model",
"Select an Ollama instance": "Selecciona una instància d'Ollama",
"Send a Message": "Envia un Missatge",
"Send message": "Envia missatge",
"Server connection verified": "Connexió al servidor verificada",
"Set as default": "Estableix com a predeterminat",
"Set Default Model": "Estableix Model Predeterminat",
"Set Image Size": "Estableix Mida de la Imatge",
"Set Steps": "Estableix Passos",
"Set Title Auto-Generation Model": "Estableix Model d'Auto-Generació de Títol",
"Set Voice": "Estableix Veu",
"Settings": "Configuracions",
"Settings saved successfully!": "Configuracions guardades amb èxit!",
"Share to OpenWebUI Community": "Comparteix amb la Comunitat OpenWebUI",
"short-summary": "resum curt",
"Show": "Mostra",
"Show Additional Params": "Mostra Paràmetres Addicionals",
"Show shortcuts": "Mostra dreceres",
"sidebar": "barra lateral",
"Sign in": "Inicia sessió",
"Sign Out": "Tanca sessió",
"Sign up": "Registra't",
"Speech recognition error: {{error}}": "Error de reconeixement de veu: {{error}}",
"Speech-to-Text Engine": "Motor de Veu a Text",
"SpeechRecognition API is not supported in this browser.": "L'API de Reconèixer Veu no és compatible amb aquest navegador.",
"Stop Sequence": "Atura Seqüència",
"STT Settings": "Configuracions STT",
"Submit": "Envia",
"Success": "Èxit",
"Successfully updated.": "Actualitzat amb èxit.",
"Sync All": "Sincronitza Tot",
"System": "Sistema",
"System Prompt": "Prompt del Sistema",
"Tags": "Etiquetes",
"Temperature": "Temperatura",
"Template": "Plantilla",
"Text Completion": "Completació de Text",
"Text-to-Speech Engine": "Motor de Text a Veu",
"Tfs Z": "Tfs Z",
"Theme": "Tema",
"This ensures that your valuable conversations are securely saved to your backend database. Thank you!": "Això assegura que les teves converses valuoses queden segurament guardades a la teva base de dades backend. Gràcies!",
"This setting does not sync across browsers or devices.": "Aquesta configuració no es sincronitza entre navegadors ni dispositius.",
"Tip: Update multiple variable slots consecutively by pressing the tab key in the chat input after each replacement.": "Consell: Actualitza diversos espais de variables consecutivament prement la tecla de tabulació en l'entrada del xat després de cada reemplaçament.",
"Title": "Títol",
"Title Auto-Generation": "Auto-Generació de Títol",
"Title Generation Prompt": "Prompt de Generació de Títol",
"to": "a",
"To access the available model names for downloading,": "Per accedir als noms dels models disponibles per descarregar,",
"To access the GGUF models available for downloading,": "Per accedir als models GGUF disponibles per descarregar,",
"to chat input.": "a l'entrada del xat.",
"Toggle settings": "Commuta configuracions",
"Toggle sidebar": "Commuta barra lateral",
"Top K": "Top K",
"Top P": "Top P",
"Trouble accessing Ollama?": "Problemes accedint a Ollama?",
"TTS Settings": "Configuracions TTS",
"Type Hugging Face Resolve (Download) URL": "Escriu URL de Resolució (Descàrrega) de Hugging Face",
"Uh-oh! There was an issue connecting to {{provider}}.": "Uf! Hi va haver un problema connectant-se a {{provider}}.",
"Unknown File Type '{{file_type}}', but accepting and treating as plain text": "Tipus d'Arxiu Desconegut '{{file_type}}', però acceptant i tractant com a text pla",
"Update password": "Actualitza contrasenya",
"Upload a GGUF model": "Puja un model GGUF",
"Upload files": "Puja arxius",
"Upload Progress": "Progrés de Càrrega",
"URL Mode": "Mode URL",
"Use '#' in the prompt input to load and select your documents.": "Utilitza '#' a l'entrada del prompt per carregar i seleccionar els teus documents.",
"Use Gravatar": "Utilitza Gravatar",
"user": "usuari",
"User Permissions": "Permisos d'Usuari",
"Users": "Usuaris",
"Utilize": "Utilitza",
"Valid time units:": "Unitats de temps vàlides:",
"variable": "variable",
"variable to have them replaced with clipboard content.": "variable per tenir-les reemplaçades amb el contingut del porta-retalls.",
"Version": "Versió",
"Web": "Web",
"WebUI Add-ons": "Complements de WebUI",
"WebUI Settings": "Configuració de WebUI",
"WebUI will make requests to": "WebUI farà peticions a",
"What’s New in": "Què hi ha de Nou en",
"When history is turned off, new chats on this browser won't appear in your history on any of your devices.": "Quan l'historial està desactivat, els nous xats en aquest navegador no apareixeran en el teu historial en cap dels teus dispositius.",
"Whisper (Local)": "Whisper (Local)",
"Write a prompt suggestion (e.g. Who are you?)": "Escriu una suggerència de prompt (p. ex. Qui ets tu?)",
"Write a summary in 50 words that summarizes [topic or keyword].": "Escriu un resum en 50 paraules que resumeixi [tema o paraula clau].",
"You": "Tu",
"You're a helpful assistant.": "Ets un assistent útil.",
"You're now logged in.": "Ara estàs connectat."
}
\ No newline at end of file
...@@ -276,7 +276,7 @@ ...@@ -276,7 +276,7 @@
"Select a mode": "", "Select a mode": "",
"Select a model": "Ein Modell auswählen", "Select a model": "Ein Modell auswählen",
"Select an Ollama instance": "", "Select an Ollama instance": "",
"Send a Messsage": "Eine Nachricht senden", "Send a Message": "Eine Nachricht senden",
"Send message": "Nachricht senden", "Send message": "Nachricht senden",
"Server connection verified": "Serververbindung überprüft", "Server connection verified": "Serververbindung überprüft",
"Set as default": "Als Standard festlegen", "Set as default": "Als Standard festlegen",
......
...@@ -276,7 +276,7 @@ ...@@ -276,7 +276,7 @@
"Select a mode": "", "Select a mode": "",
"Select a model": "Select a model", "Select a model": "Select a model",
"Select an Ollama instance": "", "Select an Ollama instance": "",
"Send a Messsage": "Send a Messsage", "Send a Message": "Send a Message",
"Send message": "Send message", "Send message": "Send message",
"Server connection verified": "Server connection verified", "Server connection verified": "Server connection verified",
"Set as default": "Set as default", "Set as default": "Set as default",
......
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