Unverified Commit 4900ac51 authored by Timothy Jaeryang Baek's avatar Timothy Jaeryang Baek Committed by GitHub
Browse files

Merge pull request #3536 from open-webui/dev

0.3.7
parents 24b638fc 58d8dd94
......@@ -4,6 +4,7 @@ updates:
directory: '/backend'
schedule:
interval: weekly
target-branch: 'dev'
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
......
......@@ -5,6 +5,24 @@ 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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.7] - 2024-06-29
### Added
- **🌐 Enhanced Internationalization (i18n)**: Newly introduced Indonesian translation, and updated translations for Turkish, Chinese, and Catalan languages to improve user accessibility.
### Fixed
- **🕵️‍♂️ Browser Language Detection**: Corrected the issue where the application was not properly detecting and adapting to the browser's language settings.
- **🔐 OIDC Admin Role Assignment**: Fixed a bug where the admin role was not being assigned to the first user who signed up via OpenID Connect (OIDC).
- **💬 Chat/Completions Endpoint**: Resolved an issue where the chat/completions endpoint was non-functional when the stream option was set to False.
- **🚫 'WEBUI_AUTH' Configuration**: Addressed the problem where setting 'WEBUI_AUTH' to False was not being applied correctly.
### Changed
- **📦 Dependency Update**: Upgraded 'authlib' from version 1.3.0 to 1.3.1 to ensure better security and performance enhancements.
## [0.3.6] - 2024-06-27
### Added
......
......@@ -617,6 +617,8 @@ class ChatCompletionMiddleware(BaseHTTPMiddleware):
return StreamingResponse(
self.ollama_stream_wrapper(response.body_iterator, data_items),
)
return response
else:
return response
......@@ -1944,6 +1946,11 @@ async def oauth_callback(provider: str, request: Request, response: Response):
picture_url = ""
if not picture_url:
picture_url = "/user.png"
role = (
"admin"
if Users.get_num_users() == 0
else webui_app.state.config.DEFAULT_USER_ROLE
)
user = Auths.insert_new_auth(
email=email,
password=get_password_hash(
......@@ -1951,7 +1958,7 @@ async def oauth_callback(provider: str, request: Request, response: Response):
), # Random password, not used
name=user_data.get("name", "User"),
profile_image_url=picture_url,
role=webui_app.state.config.DEFAULT_USER_ROLE,
role=role,
oauth_sub=provider_sub,
)
......@@ -1978,7 +1985,7 @@ async def oauth_callback(provider: str, request: Request, response: Response):
# Set the cookie token
response.set_cookie(
key="token",
value=token,
value=jwt_token,
httponly=True, # Ensures the cookie is not accessible via JavaScript
)
......
......@@ -58,7 +58,7 @@ rank-bm25==0.2.2
faster-whisper==1.0.2
PyJWT[crypto]==2.8.0
authlib==1.3.0
authlib==1.3.1
black==24.4.2
langfuse==2.33.0
......
{
"name": "open-webui",
"version": "0.3.6",
"version": "0.3.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open-webui",
"version": "0.3.6",
"version": "0.3.7",
"dependencies": {
"@codemirror/lang-javascript": "^6.2.2",
"@codemirror/lang-python": "^6.1.6",
......
{
"name": "open-webui",
"version": "0.3.6",
"version": "0.3.7",
"private": true,
"scripts": {
"dev": "npm run pyodide:fetch && vite dev --host",
......
......@@ -59,7 +59,7 @@ dependencies = [
"faster-whisper==1.0.2",
"PyJWT[crypto]==2.8.0",
"authlib==1.3.0",
"authlib==1.3.1",
"black==24.4.2",
"langfuse==2.33.0",
......
......@@ -27,61 +27,73 @@
}
let codeEditor;
let boilerplate = `from pydantic import BaseModel
let boilerplate = `"""
title: Example Filter
author: open-webui
author_url: https://github.com/open-webui
funding_url: https://github.com/open-webui
version: 0.1
"""
from pydantic import BaseModel, Field
from typing import Optional
class Filter:
class Valves(BaseModel):
max_turns: int = 4
priority: int = Field(
default=0, description="Priority level for the filter operations."
)
max_turns: int = Field(
default=8, description="Maximum allowable conversation turns for a user."
)
pass
class UserValves(BaseModel):
max_turns: int = Field(
default=4, description="Maximum allowable conversation turns for a user."
)
pass
def __init__(self):
# Indicates custom file handling logic. This flag helps disengage default routines in favor of custom
# implementations, informing the WebUI to defer file-related operations to designated methods within this class.
# Alternatively, you can remove the files directly from the body in from the inlet hook
self.file_handler = True
# self.file_handler = True
# Initialize 'valves' with specific configurations. Using 'Valves' instance helps encapsulate settings,
# which ensures settings are managed cohesively and not confused with operational flags like 'file_handler'.
self.valves = self.Valves(**{"max_turns": 2})
self.valves = self.Valves()
pass
def inlet(self, body: dict, user: Optional[dict] = None) -> dict:
def inlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
# Modify the request body or validate it before processing by the chat completion API.
# This function is the pre-processor for the API where various checks on the input can be performed.
# It can also modify the request before sending it to the API.
print(f"inlet:{__name__}")
print(f"inlet:body:{body}")
print(f"inlet:user:{user}")
print(f"inlet:user:{__user__}")
if user.get("role", "admin") in ["user", "admin"]:
if __user__.get("role", "admin") in ["user", "admin"]:
messages = body.get("messages", [])
if len(messages) > self.valves.max_turns:
max_turns = min(__user__["valves"].max_turns, self.valves.max_turns)
if len(messages) > max_turns:
raise Exception(
f"Conversation turn limit exceeded. Max turns: {self.valves.max_turns}"
f"Conversation turn limit exceeded. Max turns: {max_turns}"
)
return body
def outlet(self, body: dict, user: Optional[dict] = None) -> dict:
def outlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
# Modify or analyze the response body after processing by the API.
# This function is the post-processor for the API, which can be used to modify the response
# or perform additional checks and analytics.
print(f"outlet:{__name__}")
print(f"outlet:body:{body}")
print(f"outlet:user:{user}")
messages = [
{
**message,
"content": f"{message['content']} - @@Modified from Filter Outlet",
}
for message in body.get("messages", [])
]
return {"messages": messages}
print(f"outlet:user:{__user__}")
return body
`;
const _boilerplate = `from pydantic import BaseModel
......
This diff is collapsed.
This diff is collapsed.
......@@ -63,6 +63,10 @@
"code": "hr-HR",
"title": "Croatian (Hrvatski)"
},
{
"code": "id-ID",
"title": "Indonesian (Bahasa Indonesia)"
},
{
"code": "it-IT",
"title": "Italian (Italiano)"
......
This diff is collapsed.
......@@ -261,8 +261,8 @@
"File": "文件",
"File Mode": "文件模式",
"File not found.": "文件未找到。",
"Filter is now globally disabled": "",
"Filter is now globally enabled": "",
"Filter is now globally disabled": "过滤器已全局禁用",
"Filter is now globally enabled": "过滤器已全局启用",
"Filters": "过滤器",
"Fingerprint spoofing detected: Unable to use initials as avatar. Defaulting to default profile image.": "检测到指纹伪造:无法使用姓名缩写作为头像。默认使用默认个人形象。",
"Fluidly stream large external response chunks": "流畅地传输外部大型响应块数据",
......@@ -281,7 +281,7 @@
"Generate Image": "生成图像",
"Generating search query": "生成搜索查询",
"Generation Info": "生成信息",
"Global": "",
"Global": "全局",
"Good Response": "点赞此回答",
"Google PSE API Key": "Google PSE API 密钥",
"Google PSE Engine Id": "Google PSE 引擎 ID",
......
......@@ -746,3 +746,10 @@ export const extractFrontmatter = (content) => {
return frontmatter;
};
// Function to determine the best matching language
export const bestMatchingLanguage = (supportedLanguages, preferredLanguages, defaultLocale) => {
const languages = supportedLanguages.map((lang) => lang.code);
const match = preferredLanguages.find((lang) => languages.includes(lang));
return match || defaultLocale;
};
......@@ -31,6 +31,7 @@
import { WEBUI_BASE_URL, WEBUI_HOSTNAME } from '$lib/constants';
import i18n, { initI18n, getLanguages } from '$lib/i18n';
import { bestMatchingLanguage } from '$lib/utils';
setContext('i18n', i18n);
......@@ -92,12 +93,11 @@
// so `/error` can show something that's not `undefined`.
const languages = await getLanguages();
const browserLanguages = navigator.languages
? navigator.languages
: [navigator.language || navigator.userLanguage];
const browserLanguage = navigator.languages
? navigator.languages[0]
: navigator.language || navigator.userLanguage;
initI18n(languages.includes(browserLanguage) ? browserLanguage : backendConfig?.default_locale);
initI18n(bestMatchingLanguage(languages, browserLanguages, backendConfig.default_locale));
if (backendConfig) {
// Save Backend Status to Store
......
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