mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-13 04:45:19 +00:00
Compare commits
1 commit
4c23243ace
...
f26d0494ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f26d0494ca |
17 changed files with 85 additions and 172 deletions
|
|
@ -635,7 +635,6 @@ OAUTH_AUDIENCE = PersistentConfig(
|
|||
os.environ.get("OAUTH_AUDIENCE", ""),
|
||||
)
|
||||
|
||||
|
||||
def load_oauth_providers():
|
||||
OAUTH_PROVIDERS.clear()
|
||||
if GOOGLE_CLIENT_ID.value and GOOGLE_CLIENT_SECRET.value:
|
||||
|
|
@ -3000,12 +2999,6 @@ WEB_LOADER_CONCURRENT_REQUESTS = PersistentConfig(
|
|||
int(os.getenv("WEB_LOADER_CONCURRENT_REQUESTS", "10")),
|
||||
)
|
||||
|
||||
WEB_LOADER_TIMEOUT = PersistentConfig(
|
||||
"WEB_LOADER_TIMEOUT",
|
||||
"rag.web.loader.timeout",
|
||||
os.getenv("WEB_LOADER_TIMEOUT", ""),
|
||||
)
|
||||
|
||||
|
||||
ENABLE_WEB_LOADER_SSL_VERIFICATION = PersistentConfig(
|
||||
"ENABLE_WEB_LOADER_SSL_VERIFICATION",
|
||||
|
|
|
|||
|
|
@ -395,13 +395,6 @@ try:
|
|||
except ValueError:
|
||||
REDIS_SENTINEL_MAX_RETRY_COUNT = 2
|
||||
|
||||
|
||||
REDIS_SOCKET_CONNECT_TIMEOUT = os.environ.get("REDIS_SOCKET_CONNECT_TIMEOUT", "")
|
||||
try:
|
||||
REDIS_SOCKET_CONNECT_TIMEOUT = float(REDIS_SOCKET_CONNECT_TIMEOUT)
|
||||
except ValueError:
|
||||
REDIS_SOCKET_CONNECT_TIMEOUT = None
|
||||
|
||||
####################################
|
||||
# UVICORN WORKERS
|
||||
####################################
|
||||
|
|
@ -627,16 +620,9 @@ ENABLE_WEBSOCKET_SUPPORT = (
|
|||
WEBSOCKET_MANAGER = os.environ.get("WEBSOCKET_MANAGER", "")
|
||||
|
||||
WEBSOCKET_REDIS_OPTIONS = os.environ.get("WEBSOCKET_REDIS_OPTIONS", "")
|
||||
|
||||
|
||||
if WEBSOCKET_REDIS_OPTIONS == "":
|
||||
if REDIS_SOCKET_CONNECT_TIMEOUT:
|
||||
WEBSOCKET_REDIS_OPTIONS = {
|
||||
"socket_connect_timeout": REDIS_SOCKET_CONNECT_TIMEOUT
|
||||
}
|
||||
else:
|
||||
log.debug("No WEBSOCKET_REDIS_OPTIONS provided, defaulting to None")
|
||||
WEBSOCKET_REDIS_OPTIONS = None
|
||||
log.debug("No WEBSOCKET_REDIS_OPTIONS provided, defaulting to None")
|
||||
WEBSOCKET_REDIS_OPTIONS = None
|
||||
else:
|
||||
try:
|
||||
WEBSOCKET_REDIS_OPTIONS = json.loads(WEBSOCKET_REDIS_OPTIONS)
|
||||
|
|
|
|||
|
|
@ -208,7 +208,6 @@ from open_webui.config import (
|
|||
FIRECRAWL_API_KEY,
|
||||
WEB_LOADER_ENGINE,
|
||||
WEB_LOADER_CONCURRENT_REQUESTS,
|
||||
WEB_LOADER_TIMEOUT,
|
||||
WHISPER_MODEL,
|
||||
WHISPER_VAD_FILTER,
|
||||
WHISPER_LANGUAGE,
|
||||
|
|
@ -923,7 +922,6 @@ app.state.config.WEB_SEARCH_CONCURRENT_REQUESTS = WEB_SEARCH_CONCURRENT_REQUESTS
|
|||
|
||||
app.state.config.WEB_LOADER_ENGINE = WEB_LOADER_ENGINE
|
||||
app.state.config.WEB_LOADER_CONCURRENT_REQUESTS = WEB_LOADER_CONCURRENT_REQUESTS
|
||||
app.state.config.WEB_LOADER_TIMEOUT = WEB_LOADER_TIMEOUT
|
||||
|
||||
app.state.config.WEB_SEARCH_TRUST_ENV = WEB_SEARCH_TRUST_ENV
|
||||
app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = (
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ from open_webui.config import (
|
|||
PLAYWRIGHT_WS_URL,
|
||||
PLAYWRIGHT_TIMEOUT,
|
||||
WEB_LOADER_ENGINE,
|
||||
WEB_LOADER_TIMEOUT,
|
||||
FIRECRAWL_API_BASE_URL,
|
||||
FIRECRAWL_API_KEY,
|
||||
TAVILY_API_KEY,
|
||||
|
|
@ -675,20 +674,6 @@ def get_web_loader(
|
|||
|
||||
if WEB_LOADER_ENGINE.value == "" or WEB_LOADER_ENGINE.value == "safe_web":
|
||||
WebLoaderClass = SafeWebBaseLoader
|
||||
|
||||
request_kwargs = {}
|
||||
if WEB_LOADER_TIMEOUT.value:
|
||||
try:
|
||||
timeout_value = float(WEB_LOADER_TIMEOUT.value)
|
||||
except ValueError:
|
||||
timeout_value = None
|
||||
|
||||
if timeout_value:
|
||||
request_kwargs["timeout"] = timeout_value
|
||||
|
||||
if request_kwargs:
|
||||
web_loader_args["requests_kwargs"] = request_kwargs
|
||||
|
||||
if WEB_LOADER_ENGINE.value == "playwright":
|
||||
WebLoaderClass = SafePlaywrightURLLoader
|
||||
web_loader_args["playwright_timeout"] = PLAYWRIGHT_TIMEOUT.value
|
||||
|
|
|
|||
|
|
@ -536,7 +536,6 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
|
|||
"SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID,
|
||||
"SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK,
|
||||
"WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE,
|
||||
"WEB_LOADER_TIMEOUT": request.app.state.config.WEB_LOADER_TIMEOUT,
|
||||
"ENABLE_WEB_LOADER_SSL_VERIFICATION": request.app.state.config.ENABLE_WEB_LOADER_SSL_VERIFICATION,
|
||||
"PLAYWRIGHT_WS_URL": request.app.state.config.PLAYWRIGHT_WS_URL,
|
||||
"PLAYWRIGHT_TIMEOUT": request.app.state.config.PLAYWRIGHT_TIMEOUT,
|
||||
|
|
@ -595,7 +594,6 @@ class WebConfig(BaseModel):
|
|||
SOUGOU_API_SID: Optional[str] = None
|
||||
SOUGOU_API_SK: Optional[str] = None
|
||||
WEB_LOADER_ENGINE: Optional[str] = None
|
||||
WEB_LOADER_TIMEOUT: Optional[str] = None
|
||||
ENABLE_WEB_LOADER_SSL_VERIFICATION: Optional[bool] = None
|
||||
PLAYWRIGHT_WS_URL: Optional[str] = None
|
||||
PLAYWRIGHT_TIMEOUT: Optional[int] = None
|
||||
|
|
@ -1073,8 +1071,6 @@ async def update_rag_config(
|
|||
|
||||
# Web loader settings
|
||||
request.app.state.config.WEB_LOADER_ENGINE = form_data.web.WEB_LOADER_ENGINE
|
||||
request.app.state.config.WEB_LOADER_TIMEOUT = form_data.web.WEB_LOADER_TIMEOUT
|
||||
|
||||
request.app.state.config.ENABLE_WEB_LOADER_SSL_VERIFICATION = (
|
||||
form_data.web.ENABLE_WEB_LOADER_SSL_VERIFICATION
|
||||
)
|
||||
|
|
@ -1210,7 +1206,6 @@ async def update_rag_config(
|
|||
"SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID,
|
||||
"SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK,
|
||||
"WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE,
|
||||
"WEB_LOADER_TIMEOUT": request.app.state.config.WEB_LOADER_TIMEOUT,
|
||||
"ENABLE_WEB_LOADER_SSL_VERIFICATION": request.app.state.config.ENABLE_WEB_LOADER_SSL_VERIFICATION,
|
||||
"PLAYWRIGHT_WS_URL": request.app.state.config.PLAYWRIGHT_WS_URL,
|
||||
"PLAYWRIGHT_TIMEOUT": request.app.state.config.PLAYWRIGHT_TIMEOUT,
|
||||
|
|
|
|||
|
|
@ -391,7 +391,6 @@ async def update_user_info_by_session_user(
|
|||
class UserActiveResponse(UserStatus):
|
||||
name: str
|
||||
profile_image_url: Optional[str] = None
|
||||
groups: Optional[list] = []
|
||||
|
||||
is_active: bool
|
||||
model_config = ConfigDict(extra="allow")
|
||||
|
|
@ -413,12 +412,11 @@ async def get_user_by_id(user_id: str, user=Depends(get_verified_user)):
|
|||
)
|
||||
|
||||
user = Users.get_user_by_id(user_id)
|
||||
|
||||
if user:
|
||||
groups = Groups.get_groups_by_member_id(user_id)
|
||||
return UserActiveResponse(
|
||||
**{
|
||||
**user.model_dump(),
|
||||
"groups": [{"id": group.id, "name": group.name} for group in groups],
|
||||
"is_active": Users.is_user_active(user_id),
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -716,18 +716,17 @@ async def chat_web_search_handler(
|
|||
return form_data
|
||||
|
||||
|
||||
def get_images_from_messages(message_list):
|
||||
def get_last_images(message_list):
|
||||
images = []
|
||||
|
||||
for message in reversed(message_list):
|
||||
|
||||
message_images = []
|
||||
images_flag = False
|
||||
for file in message.get("files", []):
|
||||
if file.get("type") == "image":
|
||||
message_images.append(file.get("url"))
|
||||
images.append(file.get("url"))
|
||||
images_flag = True
|
||||
|
||||
if message_images:
|
||||
images.append(message_images)
|
||||
if images_flag:
|
||||
break
|
||||
|
||||
return images
|
||||
|
||||
|
|
@ -781,16 +780,7 @@ async def chat_image_generation_handler(
|
|||
user_message = get_last_user_message(message_list)
|
||||
|
||||
prompt = user_message
|
||||
message_images = get_images_from_messages(message_list)
|
||||
|
||||
# Limit to first 2 sets of images
|
||||
# We may want to change this in the future to allow more images
|
||||
input_images = []
|
||||
for idx, images in enumerate(message_images):
|
||||
if idx >= 2:
|
||||
break
|
||||
for image in images:
|
||||
input_images.append(image)
|
||||
input_images = get_last_images(message_list)
|
||||
|
||||
system_message_content = ""
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import redis
|
|||
|
||||
from open_webui.env import (
|
||||
REDIS_CLUSTER,
|
||||
REDIS_SOCKET_CONNECT_TIMEOUT,
|
||||
REDIS_SENTINEL_HOSTS,
|
||||
REDIS_SENTINEL_MAX_RETRY_COUNT,
|
||||
REDIS_SENTINEL_PORT,
|
||||
|
|
@ -163,7 +162,6 @@ def get_redis_connection(
|
|||
username=redis_config["username"],
|
||||
password=redis_config["password"],
|
||||
decode_responses=decode_responses,
|
||||
socket_connect_timeout=REDIS_SOCKET_CONNECT_TIMEOUT,
|
||||
)
|
||||
connection = SentinelRedisProxy(
|
||||
sentinel,
|
||||
|
|
@ -190,7 +188,6 @@ def get_redis_connection(
|
|||
username=redis_config["username"],
|
||||
password=redis_config["password"],
|
||||
decode_responses=decode_responses,
|
||||
socket_connect_timeout=REDIS_SOCKET_CONNECT_TIMEOUT,
|
||||
)
|
||||
connection = SentinelRedisProxy(
|
||||
sentinel,
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
if (pipeline && (pipeline?.valves ?? false)) {
|
||||
for (const property in valves_spec.properties) {
|
||||
if (valves_spec.properties[property]?.type === 'array') {
|
||||
valves[property] = (valves[property] ?? '').split(',').map((v) => v.trim());
|
||||
valves[property] = valves[property].split(',').map((v) => v.trim());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -767,19 +767,6 @@
|
|||
</div>
|
||||
|
||||
{#if webConfig.WEB_LOADER_ENGINE === '' || webConfig.WEB_LOADER_ENGINE === 'safe_web'}
|
||||
<div class=" mb-2.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Timeout')}
|
||||
</div>
|
||||
<div class="flex items-center relative">
|
||||
<input
|
||||
class="flex-1 w-full text-sm bg-transparent outline-hidden"
|
||||
placeholder={$i18n.t('Timeout')}
|
||||
bind:value={webConfig.WEB_LOADER_TIMEOUT}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" mb-2.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">
|
||||
{$i18n.t('Verify SSL Certificate')}
|
||||
|
|
|
|||
|
|
@ -102,18 +102,6 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
{#if (user?.groups ?? []).length > 0}
|
||||
<div class="mx-3.5 mt-2 flex gap-0.5">
|
||||
{#each user.groups as group}
|
||||
<div
|
||||
class="px-1.5 py-0.5 rounded-lg bg-gray-50 dark:text-white dark:bg-gray-900/50 text-black transition text-xs"
|
||||
>
|
||||
{group.name}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if $_user?.id !== user.id}
|
||||
<hr class="border-gray-100/50 dark:border-gray-800/50 my-2.5" />
|
||||
|
||||
|
|
|
|||
|
|
@ -75,11 +75,12 @@
|
|||
`<sup class="footnote-ref footnote-ref-text">${token.escapedText}</sup>`
|
||||
) || ''}
|
||||
{:else if token.type === 'citation'}
|
||||
{#if (sourceIds ?? []).length > 0}
|
||||
<SourceToken {id} {token} {sourceIds} onClick={onSourceClick} />
|
||||
{:else}
|
||||
<TextToken {token} {done} />
|
||||
{/if}
|
||||
<SourceToken {id} {token} {sourceIds} onClick={onSourceClick} />
|
||||
<!-- {#if token.ids && token.ids.length > 0}
|
||||
{#each token.ids as sourceId}
|
||||
<Source id={sourceId - 1} title={sourceIds[sourceId - 1]} onClick={onSourceClick} />
|
||||
{/each}
|
||||
{/if} -->
|
||||
{:else if token.type === 'text'}
|
||||
<TextToken {token} {done} />
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -39,43 +39,37 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
{sourceIds}
|
||||
|
||||
{#if sourceIds}
|
||||
{#if (token?.ids ?? []).length == 1}
|
||||
<Source id={token.ids[0] - 1} title={sourceIds[token.ids[0] - 1]} {onClick} />
|
||||
{:else}
|
||||
<LinkPreview.Root openDelay={0} bind:open={openPreview}>
|
||||
<LinkPreview.Trigger>
|
||||
<button
|
||||
class="text-[10px] w-fit translate-y-[2px] px-2 py-0.5 dark:bg-white/5 dark:text-white/80 dark:hover:text-white bg-gray-50 text-black/80 hover:text-black transition rounded-xl"
|
||||
on:click={() => {
|
||||
openPreview = !openPreview;
|
||||
}}
|
||||
>
|
||||
<span class="line-clamp-1">
|
||||
{getDisplayTitle(formattedTitle(decodeString(sourceIds[token.ids[0] - 1])))}
|
||||
<span class="dark:text-white/50 text-black/50">+{(token?.ids ?? []).length - 1}</span>
|
||||
</span>
|
||||
</button>
|
||||
</LinkPreview.Trigger>
|
||||
<LinkPreview.Content
|
||||
class="z-[999]"
|
||||
align="start"
|
||||
strategy="fixed"
|
||||
sideOffset={6}
|
||||
el={containerElement}
|
||||
>
|
||||
<div class="bg-gray-50 dark:bg-gray-850 rounded-xl p-1 cursor-pointer">
|
||||
{#each token.ids as sourceId}
|
||||
<div class="">
|
||||
<Source id={sourceId - 1} title={sourceIds[sourceId - 1]} {onClick} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</LinkPreview.Content>
|
||||
</LinkPreview.Root>
|
||||
{/if}
|
||||
{#if (token?.ids ?? []).length == 1}
|
||||
<Source id={token.ids[0] - 1} title={sourceIds[token.ids[0] - 1]} {onClick} />
|
||||
{:else}
|
||||
<span>{token.raw}</span>
|
||||
<LinkPreview.Root openDelay={0} bind:open={openPreview}>
|
||||
<LinkPreview.Trigger>
|
||||
<button
|
||||
class="text-[10px] w-fit translate-y-[2px] px-2 py-0.5 dark:bg-white/5 dark:text-white/80 dark:hover:text-white bg-gray-50 text-black/80 hover:text-black transition rounded-xl"
|
||||
on:click={() => {
|
||||
openPreview = !openPreview;
|
||||
}}
|
||||
>
|
||||
<span class="line-clamp-1">
|
||||
{getDisplayTitle(formattedTitle(decodeString(sourceIds[token.ids[0] - 1])))}
|
||||
<span class="dark:text-white/50 text-black/50">+{(token?.ids ?? []).length - 1}</span>
|
||||
</span>
|
||||
</button>
|
||||
</LinkPreview.Trigger>
|
||||
<LinkPreview.Content
|
||||
class="z-[999]"
|
||||
align="start"
|
||||
strategy="fixed"
|
||||
sideOffset={6}
|
||||
el={containerElement}
|
||||
>
|
||||
<div class="bg-gray-50 dark:bg-gray-850 rounded-xl p-1 cursor-pointer">
|
||||
{#each token.ids as sourceId}
|
||||
<div class="">
|
||||
<Source id={sourceId - 1} title={sourceIds[sourceId - 1]} {onClick} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</LinkPreview.Content>
|
||||
</LinkPreview.Root>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1460,35 +1460,37 @@
|
|||
{/if}
|
||||
{/if}
|
||||
|
||||
{#each model?.actions ?? [] as action}
|
||||
<Tooltip content={action.name} placement="bottom">
|
||||
<button
|
||||
type="button"
|
||||
aria-label={action.name}
|
||||
class="{isLastMessage || ($settings?.highContrastMode ?? false)
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
actionMessage(action.id, message);
|
||||
}}
|
||||
>
|
||||
{#if action?.icon}
|
||||
<div class="size-4">
|
||||
<img
|
||||
src={action.icon}
|
||||
class="w-4 h-4 {action.icon.includes('svg')
|
||||
? 'dark:invert-[80%]'
|
||||
: ''}"
|
||||
style="fill: currentColor;"
|
||||
alt={action.name}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<Sparkles strokeWidth="2.1" className="size-4" />
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/each}
|
||||
{#if isLastMessage}
|
||||
{#each model?.actions ?? [] as action}
|
||||
<Tooltip content={action.name} placement="bottom">
|
||||
<button
|
||||
type="button"
|
||||
aria-label={action.name}
|
||||
class="{isLastMessage || ($settings?.highContrastMode ?? false)
|
||||
? 'visible'
|
||||
: 'invisible group-hover:visible'} p-1.5 hover:bg-black/5 dark:hover:bg-white/5 rounded-lg dark:hover:text-white hover:text-black transition"
|
||||
on:click={() => {
|
||||
actionMessage(action.id, message);
|
||||
}}
|
||||
>
|
||||
{#if action?.icon}
|
||||
<div class="size-4">
|
||||
<img
|
||||
src={action.icon}
|
||||
class="w-4 h-4 {action.icon.includes('svg')
|
||||
? 'dark:invert-[80%]'
|
||||
: ''}"
|
||||
style="fill: currentColor;"
|
||||
alt={action.name}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<Sparkles strokeWidth="2.1" className="size-4" />
|
||||
{/if}
|
||||
</button>
|
||||
</Tooltip>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -526,15 +526,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="shrink-0">
|
||||
<div>
|
||||
<button
|
||||
class="bg-gray-50 shrink-0 hover:bg-gray-100 text-black dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition px-2 py-1 rounded-full flex gap-1 items-center"
|
||||
class="bg-gray-50 hover:bg-gray-100 text-black dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition px-2 py-1 rounded-full flex gap-1 items-center"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showAccessControlModal = true;
|
||||
}}
|
||||
>
|
||||
<LockClosed strokeWidth="2.5" className="size-3.5 shrink-0" />
|
||||
<LockClosed strokeWidth="2.5" className="size-3.5" />
|
||||
|
||||
<div class="text-sm font-medium shrink-0">
|
||||
{$i18n.t('Access')}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
onMount(async () => {
|
||||
window.addEventListener('message', async (event) => {
|
||||
if (
|
||||
!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:9999'].includes(
|
||||
!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:5173'].includes(
|
||||
event.origin
|
||||
)
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -34,9 +34,8 @@
|
|||
|
||||
onMount(async () => {
|
||||
window.addEventListener('message', async (event) => {
|
||||
console.log(event);
|
||||
if (
|
||||
!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:9999'].includes(
|
||||
!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:5173'].includes(
|
||||
event.origin
|
||||
)
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue