Skip to content

Commit 3b70cd6

Browse files
authored
Merge pull request open-webui#11257 from open-webui/dev
0.5.20
2 parents 23bb0d9 + 1173459 commit 3b70cd6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+228
-93
lines changed

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,19 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [0.5.19] - 2024-03-04
8+
## [0.5.20] - 2025-03-05
9+
10+
### Added
11+
12+
- **⚡ Toggle Code Execution On/Off**: You can now enable or disable code execution, providing more control over security, ensuring a safer and more customizable experience.
13+
14+
### Fixed
15+
16+
- **📜 Pinyin Keyboard Enter Key Now Works Properly**: Resolved an issue where the Enter key for Pinyin keyboards was not functioning as expected, ensuring seamless input for Chinese users.
17+
- **🖼️ Web Manifest Loading Issue Fixed**: Addressed inconsistencies with 'site.webmanifest', guaranteeing proper loading and representation of the app across different browsers and devices.
18+
- **📦 Non-Root Container Issue Resolved**: Fixed a critical issue where the UI failed to load correctly in non-root containers, ensuring reliable deployment in various environments.
19+
20+
## [0.5.19] - 2025-03-04
921

1022
### Added
1123

backend/open_webui/config.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,10 @@ def oidc_oauth_register(client):
593593
(FRONTEND_BUILD_DIR / "static")
594594
)
595595
target_path.parent.mkdir(parents=True, exist_ok=True)
596-
shutil.copyfile(file_path, target_path)
596+
try:
597+
shutil.copyfile(file_path, target_path)
598+
except Exception as e:
599+
logging.error(f"An error occurred: {e}")
597600

598601
frontend_favicon = FRONTEND_BUILD_DIR / "static" / "favicon.png"
599602

@@ -1377,6 +1380,11 @@ class BannerModel(BaseModel):
13771380
# Code Interpreter
13781381
####################################
13791382

1383+
ENABLE_CODE_EXECUTION = PersistentConfig(
1384+
"ENABLE_CODE_EXECUTION",
1385+
"code_execution.enable",
1386+
os.environ.get("ENABLE_CODE_EXECUTION", "True").lower() == "true",
1387+
)
13801388

13811389
CODE_EXECUTION_ENGINE = PersistentConfig(
13821390
"CODE_EXECUTION_ENGINE",
@@ -1553,7 +1561,9 @@ class BannerModel(BaseModel):
15531561
ELASTICSEARCH_PASSWORD = os.environ.get("ELASTICSEARCH_PASSWORD", None)
15541562
ELASTICSEARCH_CLOUD_ID = os.environ.get("ELASTICSEARCH_CLOUD_ID", None)
15551563
SSL_ASSERT_FINGERPRINT = os.environ.get("SSL_ASSERT_FINGERPRINT", None)
1556-
1564+
ELASTICSEARCH_INDEX_PREFIX = os.environ.get(
1565+
"ELASTICSEARCH_INDEX_PREFIX", "open_webui_collections"
1566+
)
15571567
# Pgvector
15581568
PGVECTOR_DB_URL = os.environ.get("PGVECTOR_DB_URL", DATABASE_URL)
15591569
if VECTOR_DB == "pgvector" and not PGVECTOR_DB_URL.startswith("postgres"):

backend/open_webui/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
# Direct Connections
106106
ENABLE_DIRECT_CONNECTIONS,
107107
# Code Execution
108+
ENABLE_CODE_EXECUTION,
108109
CODE_EXECUTION_ENGINE,
109110
CODE_EXECUTION_JUPYTER_URL,
110111
CODE_EXECUTION_JUPYTER_AUTH,
@@ -660,6 +661,7 @@ async def lifespan(app: FastAPI):
660661
#
661662
########################################
662663

664+
app.state.config.ENABLE_CODE_EXECUTION = ENABLE_CODE_EXECUTION
663665
app.state.config.CODE_EXECUTION_ENGINE = CODE_EXECUTION_ENGINE
664666
app.state.config.CODE_EXECUTION_JUPYTER_URL = CODE_EXECUTION_JUPYTER_URL
665667
app.state.config.CODE_EXECUTION_JUPYTER_AUTH = CODE_EXECUTION_JUPYTER_AUTH
@@ -1173,6 +1175,7 @@ async def get_app_config(request: Request):
11731175
"enable_direct_connections": app.state.config.ENABLE_DIRECT_CONNECTIONS,
11741176
"enable_channels": app.state.config.ENABLE_CHANNELS,
11751177
"enable_web_search": app.state.config.ENABLE_RAG_WEB_SEARCH,
1178+
"enable_code_execution": app.state.config.ENABLE_CODE_EXECUTION,
11761179
"enable_code_interpreter": app.state.config.ENABLE_CODE_INTERPRETER,
11771180
"enable_image_generation": app.state.config.ENABLE_IMAGE_GENERATION,
11781181
"enable_autocomplete_generation": app.state.config.ENABLE_AUTOCOMPLETE_GENERATION,

backend/open_webui/retrieval/vector/dbs/elasticsearch.py

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
ELASTICSEARCH_USERNAME,
1111
ELASTICSEARCH_PASSWORD,
1212
ELASTICSEARCH_CLOUD_ID,
13+
ELASTICSEARCH_INDEX_PREFIX,
1314
SSL_ASSERT_FINGERPRINT,
1415
)
1516

@@ -23,7 +24,7 @@ class ElasticsearchClient:
2324
"""
2425

2526
def __init__(self):
26-
self.index_prefix = "open_webui_collections"
27+
self.index_prefix = ELASTICSEARCH_INDEX_PREFIX
2728
self.client = Elasticsearch(
2829
hosts=[ELASTICSEARCH_URL],
2930
ca_certs=ELASTICSEARCH_CA_CERTS,
@@ -95,6 +96,14 @@ def _result_to_search_result(self, result) -> SearchResult:
9596
def _create_index(self, dimension: int):
9697
body = {
9798
"mappings": {
99+
"dynamic_templates": [
100+
{
101+
"strings": {
102+
"match_mapping_type": "string",
103+
"mapping": {"type": "keyword"},
104+
}
105+
}
106+
],
98107
"properties": {
99108
"collection": {"type": "keyword"},
100109
"id": {"type": "keyword"},
@@ -106,7 +115,7 @@ def _create_index(self, dimension: int):
106115
},
107116
"text": {"type": "text"},
108117
"metadata": {"type": "object"},
109-
}
118+
},
110119
}
111120
}
112121
self.client.indices.create(index=self._get_index_name(dimension), body=body)
@@ -131,12 +140,9 @@ def has_collection(self, collection_name) -> bool:
131140
except Exception as e:
132141
return None
133142

134-
# @TODO: Make this delete a collection and not an index
135-
def delete_colleciton(self, collection_name: str):
136-
# TODO: fix this to include the dimension or a * prefix
137-
# delete_collection here means delete a bunch of documents for an index.
138-
# We are simply adapting to the norms of the other DBs.
139-
self.client.indices.delete(index=self._get_collection_name(collection_name))
143+
def delete_collection(self, collection_name: str):
144+
query = {"query": {"term": {"collection": collection_name}}}
145+
self.client.delete_by_query(index=f"{self.index_prefix}*", body=query)
140146

141147
# Status: works
142148
def search(
@@ -239,34 +245,49 @@ def insert(self, collection_name: str, items: list[VectorItem]):
239245
]
240246
bulk(self.client, actions)
241247

242-
# Status: should work
248+
# Upsert documents using the update API with doc_as_upsert=True.
243249
def upsert(self, collection_name: str, items: list[VectorItem]):
244250
if not self._has_index(dimension=len(items[0]["vector"])):
245-
self._create_index(collection_name, dimension=len(items[0]["vector"]))
246-
251+
self._create_index(dimension=len(items[0]["vector"]))
247252
for batch in self._create_batches(items):
248253
actions = [
249254
{
250-
"_index": self._get_index_name(dimension=len(items[0]["vector"])),
255+
"_op_type": "update",
256+
"_index": self._get_index_name(dimension=len(item["vector"])),
251257
"_id": item["id"],
252-
"_source": {
258+
"doc": {
259+
"collection": collection_name,
253260
"vector": item["vector"],
254261
"text": item["text"],
255262
"metadata": item["metadata"],
256263
},
264+
"doc_as_upsert": True,
257265
}
258266
for item in batch
259267
]
260-
self.client.bulk(actions)
261-
262-
# TODO: This currently deletes by * which is not always supported in ElasticSearch.
263-
# Need to read a bit before changing. Also, need to delete from a specific collection
264-
def delete(self, collection_name: str, ids: list[str]):
265-
# Assuming ID is unique across collections and indexes
266-
actions = [
267-
{"delete": {"_index": f"{self.index_prefix}*", "_id": id}} for id in ids
268-
]
269-
self.client.bulk(body=actions)
268+
bulk(self.client, actions)
269+
270+
# Delete specific documents from a collection by filtering on both collection and document IDs.
271+
def delete(
272+
self,
273+
collection_name: str,
274+
ids: Optional[list[str]] = None,
275+
filter: Optional[dict] = None,
276+
):
277+
278+
query = {
279+
"query": {"bool": {"filter": [{"term": {"collection": collection_name}}]}}
280+
}
281+
# logic based on chromaDB
282+
if ids:
283+
query["query"]["bool"]["filter"].append({"terms": {"_id": ids}})
284+
elif filter:
285+
for field, value in filter.items():
286+
query["query"]["bool"]["filter"].append(
287+
{"term": {f"metadata.{field}": value}}
288+
)
289+
290+
self.client.delete_by_query(index=f"{self.index_prefix}*", body=query)
270291

271292
def reset(self):
272293
indices = self.client.indices.get(index=f"{self.index_prefix}*")

backend/open_webui/routers/configs.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ async def set_direct_connections_config(
7070
# CodeInterpreterConfig
7171
############################
7272
class CodeInterpreterConfigForm(BaseModel):
73+
ENABLE_CODE_EXECUTION: bool
7374
CODE_EXECUTION_ENGINE: str
7475
CODE_EXECUTION_JUPYTER_URL: Optional[str]
7576
CODE_EXECUTION_JUPYTER_AUTH: Optional[str]
@@ -89,6 +90,7 @@ class CodeInterpreterConfigForm(BaseModel):
8990
@router.get("/code_execution", response_model=CodeInterpreterConfigForm)
9091
async def get_code_execution_config(request: Request, user=Depends(get_admin_user)):
9192
return {
93+
"ENABLE_CODE_EXECUTION": request.app.state.config.ENABLE_CODE_EXECUTION,
9294
"CODE_EXECUTION_ENGINE": request.app.state.config.CODE_EXECUTION_ENGINE,
9395
"CODE_EXECUTION_JUPYTER_URL": request.app.state.config.CODE_EXECUTION_JUPYTER_URL,
9496
"CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,
@@ -111,6 +113,8 @@ async def set_code_execution_config(
111113
request: Request, form_data: CodeInterpreterConfigForm, user=Depends(get_admin_user)
112114
):
113115

116+
request.app.state.config.ENABLE_CODE_EXECUTION = form_data.ENABLE_CODE_EXECUTION
117+
114118
request.app.state.config.CODE_EXECUTION_ENGINE = form_data.CODE_EXECUTION_ENGINE
115119
request.app.state.config.CODE_EXECUTION_JUPYTER_URL = (
116120
form_data.CODE_EXECUTION_JUPYTER_URL
@@ -153,6 +157,7 @@ async def set_code_execution_config(
153157
)
154158

155159
return {
160+
"ENABLE_CODE_EXECUTION": request.app.state.config.ENABLE_CODE_EXECUTION,
156161
"CODE_EXECUTION_ENGINE": request.app.state.config.CODE_EXECUTION_ENGINE,
157162
"CODE_EXECUTION_JUPYTER_URL": request.app.state.config.CODE_EXECUTION_JUPYTER_URL,
158163
"CODE_EXECUTION_JUPYTER_AUTH": request.app.state.config.CODE_EXECUTION_JUPYTER_AUTH,

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "open-webui",
3-
"version": "0.5.19",
3+
"version": "0.5.20",
44
"private": true,
55
"scripts": {
66
"dev": "npm run pyodide:fetch && vite dev --host",

src/lib/components/admin/Settings/CodeExecution.svelte

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@
4545

4646
<hr class=" border-gray-100 dark:border-gray-850 my-2" />
4747

48+
<div class="mb-2.5">
49+
<div class=" flex w-full justify-between">
50+
<div class=" self-center text-xs font-medium">
51+
{$i18n.t('Enable Code Execution')}
52+
</div>
53+
54+
<Switch bind:state={config.ENABLE_CODE_EXECUTION} />
55+
</div>
56+
</div>
57+
4858
<div class="mb-2.5">
4959
<div class="flex w-full justify-between">
5060
<div class=" self-center text-xs font-medium">{$i18n.t('Code Execution Engine')}</div>

src/lib/components/chat/Chat.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1937,7 +1937,7 @@
19371937

19381938
<PaneGroup direction="horizontal" class="w-full h-full">
19391939
<Pane defaultSize={50} class="h-full flex w-full relative">
1940-
{#if $banners.length > 0 && !history.currentId && !$chatId && selectedModels.length <= 1}
1940+
{#if !history.currentId && !$chatId && selectedModels.length <= 1 && ($banners.length > 0 || ($config?.license_metadata?.type ?? null) === 'trial' || (($config?.license_metadata?.seats ?? null) !== null && $config?.user_count > $config?.license_metadata?.seats))}
19411941
<div class="absolute top-12 left-0 right-0 w-full z-30">
19421942
<div class=" flex flex-col gap-1 w-full">
19431943
{#if ($config?.license_metadata?.type ?? null) === 'trial'}

src/lib/components/chat/MessageInput.svelte

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@
8585
let loaded = false;
8686
let recording = false;
8787
88+
let isComposing = false;
89+
8890
let chatInputContainerElement;
8991
let chatInputElement;
9092
@@ -707,6 +709,8 @@
707709
console.log(res);
708710
return res;
709711
}}
712+
oncompositionstart={() => (isComposing = true)}
713+
oncompositionend={() => (isComposing = false)}
710714
on:keydown={async (e) => {
711715
e = e.detail.event;
712716

@@ -806,6 +810,10 @@
806810
navigator.msMaxTouchPoints > 0
807811
)
808812
) {
813+
if (isComposing) {
814+
return;
815+
}
816+
809817
// Uses keyCode '13' for Enter key for chinese/japanese keyboards.
810818
//
811819
// Depending on the user's settings, it will send the message
@@ -882,6 +890,8 @@
882890
class="scrollbar-hidden bg-transparent dark:text-gray-100 outline-hidden w-full pt-3 px-1 resize-none"
883891
placeholder={placeholder ? placeholder : $i18n.t('Send a Message')}
884892
bind:value={prompt}
893+
on:compositionstart={() => (isComposing = true)}
894+
on:compositionend={() => (isComposing = false)}
885895
on:keydown={async (e) => {
886896
const isCtrlPressed = e.ctrlKey || e.metaKey; // metaKey is for Cmd key on Mac
887897

@@ -983,6 +993,10 @@
983993
navigator.msMaxTouchPoints > 0
984994
)
985995
) {
996+
if (isComposing) {
997+
return;
998+
}
999+
9861000
console.log('keypress', e);
9871001
// Prevent Enter key from creating a new line
9881002
const isCtrlPressed = e.ctrlKey || e.metaKey;

0 commit comments

Comments
 (0)