Compare commits

...

12 commits

Author SHA1 Message Date
Athanasios Oikonomou
4c23243ace
Merge 86f33de9f3 into 2b1a29d44b 2025-12-09 08:56:54 +01:00
Timothy Jaeryang Baek
2b1a29d44b enh: display user groups in user preview
Some checks failed
Deploy to HuggingFace Spaces / check-secret (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Has been cancelled
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Python CI / Format Backend (push) Has been cancelled
Frontend Build / Format & Build Frontend (push) Has been cancelled
Frontend Build / Frontend Unit Tests (push) Has been cancelled
Deploy to HuggingFace Spaces / deploy (push) Has been cancelled
Create and publish Docker images with specific build args / merge-main-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-cuda126-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-ollama-images (push) Has been cancelled
Create and publish Docker images with specific build args / merge-slim-images (push) Has been cancelled
2025-12-08 12:45:52 -05:00
Timothy Jaeryang Baek
f5fbbaf060 refac: redis config log
Co-Authored-By: Jan Kessler <Ithanil@users.noreply.github.com>
2025-12-08 12:18:25 -05:00
Timothy Jaeryang Baek
ba158d378f feat: REDIS_SOCKET_CONNECT_TIMEOUT
Co-Authored-By: Jan Kessler <Ithanil@users.noreply.github.com>
2025-12-08 11:59:45 -05:00
Timothy Jaeryang Baek
b02397e460 feat: WEB_LOADER_TIMEOUT 2025-12-08 11:49:27 -05:00
Timothy Jaeryang Baek
bcd50ed8f1 refac 2025-12-08 11:30:38 -05:00
_00_
8cea0cf746
FIX: Pipeline save settings - Handle undefined valves property (#19791)
### FIX: Pipeline save settings - Handle undefined valves property

When a Pipeline valve have a null value the settings isn't saved.
The error occurs because the code tries to call `.split()` on a `null` value when saving pipeline valves.
This happens when you set a valve to "None" (null) and then click save.

This PR Fix this issue.
2025-12-08 10:09:53 -05:00
Timothy Jaeryang Baek
ce945a9334 refac
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
2025-12-07 23:56:37 -05:00
Timothy Jaeryang Baek
3c8f1cf8e5 fix: source citations user message display issue 2025-12-07 23:53:46 -05:00
Timothy Jaeryang Baek
4d4ed743ae fix: styling
Some checks are pending
Deploy to HuggingFace Spaces / check-secret (push) Waiting to run
Deploy to HuggingFace Spaces / deploy (push) Blocked by required conditions
Create and publish Docker images with specific build args / build-main-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-main-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-cuda126-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-ollama-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/amd64, ubuntu-latest) (push) Waiting to run
Create and publish Docker images with specific build args / build-slim-image (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Create and publish Docker images with specific build args / merge-main-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-cuda126-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-ollama-images (push) Blocked by required conditions
Create and publish Docker images with specific build args / merge-slim-images (push) Blocked by required conditions
Frontend Build / Format & Build Frontend (push) Waiting to run
Frontend Build / Frontend Unit Tests (push) Waiting to run
2025-12-07 18:13:28 -05:00
Timothy Jaeryang Baek
aa9c0389c3 refac 2025-12-07 18:06:06 -05:00
Athanasios Oikonomou
86f33de9f3 feat: support per-model RAG template override
Add `rag_template` as a configurable parameter in model settings, allowing each
model to define its own RAG template instead of always using the global default.
- Middleware now selects the model-specific RAG template if provided.
- Model editor UI updated to allow editing and saving `rag_template`.
- Fallback to global `RAG_TEMPLATE` remains when no override is set.
2025-09-24 22:58:10 +03:00
17 changed files with 198 additions and 89 deletions

View file

@ -635,6 +635,7 @@ OAUTH_AUDIENCE = PersistentConfig(
os.environ.get("OAUTH_AUDIENCE", ""), os.environ.get("OAUTH_AUDIENCE", ""),
) )
def load_oauth_providers(): def load_oauth_providers():
OAUTH_PROVIDERS.clear() OAUTH_PROVIDERS.clear()
if GOOGLE_CLIENT_ID.value and GOOGLE_CLIENT_SECRET.value: if GOOGLE_CLIENT_ID.value and GOOGLE_CLIENT_SECRET.value:
@ -2999,6 +3000,12 @@ WEB_LOADER_CONCURRENT_REQUESTS = PersistentConfig(
int(os.getenv("WEB_LOADER_CONCURRENT_REQUESTS", "10")), 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 = PersistentConfig(
"ENABLE_WEB_LOADER_SSL_VERIFICATION", "ENABLE_WEB_LOADER_SSL_VERIFICATION",

View file

@ -395,6 +395,13 @@ try:
except ValueError: except ValueError:
REDIS_SENTINEL_MAX_RETRY_COUNT = 2 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 # UVICORN WORKERS
#################################### ####################################
@ -620,9 +627,16 @@ ENABLE_WEBSOCKET_SUPPORT = (
WEBSOCKET_MANAGER = os.environ.get("WEBSOCKET_MANAGER", "") WEBSOCKET_MANAGER = os.environ.get("WEBSOCKET_MANAGER", "")
WEBSOCKET_REDIS_OPTIONS = os.environ.get("WEBSOCKET_REDIS_OPTIONS", "") WEBSOCKET_REDIS_OPTIONS = os.environ.get("WEBSOCKET_REDIS_OPTIONS", "")
if WEBSOCKET_REDIS_OPTIONS == "": if WEBSOCKET_REDIS_OPTIONS == "":
log.debug("No WEBSOCKET_REDIS_OPTIONS provided, defaulting to None") if REDIS_SOCKET_CONNECT_TIMEOUT:
WEBSOCKET_REDIS_OPTIONS = None 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
else: else:
try: try:
WEBSOCKET_REDIS_OPTIONS = json.loads(WEBSOCKET_REDIS_OPTIONS) WEBSOCKET_REDIS_OPTIONS = json.loads(WEBSOCKET_REDIS_OPTIONS)

View file

@ -208,6 +208,7 @@ from open_webui.config import (
FIRECRAWL_API_KEY, FIRECRAWL_API_KEY,
WEB_LOADER_ENGINE, WEB_LOADER_ENGINE,
WEB_LOADER_CONCURRENT_REQUESTS, WEB_LOADER_CONCURRENT_REQUESTS,
WEB_LOADER_TIMEOUT,
WHISPER_MODEL, WHISPER_MODEL,
WHISPER_VAD_FILTER, WHISPER_VAD_FILTER,
WHISPER_LANGUAGE, WHISPER_LANGUAGE,
@ -922,6 +923,7 @@ 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_ENGINE = WEB_LOADER_ENGINE
app.state.config.WEB_LOADER_CONCURRENT_REQUESTS = WEB_LOADER_CONCURRENT_REQUESTS 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.WEB_SEARCH_TRUST_ENV = WEB_SEARCH_TRUST_ENV
app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = ( app.state.config.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL = (

View file

@ -33,6 +33,7 @@ from open_webui.config import (
PLAYWRIGHT_WS_URL, PLAYWRIGHT_WS_URL,
PLAYWRIGHT_TIMEOUT, PLAYWRIGHT_TIMEOUT,
WEB_LOADER_ENGINE, WEB_LOADER_ENGINE,
WEB_LOADER_TIMEOUT,
FIRECRAWL_API_BASE_URL, FIRECRAWL_API_BASE_URL,
FIRECRAWL_API_KEY, FIRECRAWL_API_KEY,
TAVILY_API_KEY, TAVILY_API_KEY,
@ -674,6 +675,20 @@ def get_web_loader(
if WEB_LOADER_ENGINE.value == "" or WEB_LOADER_ENGINE.value == "safe_web": if WEB_LOADER_ENGINE.value == "" or WEB_LOADER_ENGINE.value == "safe_web":
WebLoaderClass = SafeWebBaseLoader 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": if WEB_LOADER_ENGINE.value == "playwright":
WebLoaderClass = SafePlaywrightURLLoader WebLoaderClass = SafePlaywrightURLLoader
web_loader_args["playwright_timeout"] = PLAYWRIGHT_TIMEOUT.value web_loader_args["playwright_timeout"] = PLAYWRIGHT_TIMEOUT.value

View file

@ -536,6 +536,7 @@ async def get_rag_config(request: Request, user=Depends(get_admin_user)):
"SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID, "SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID,
"SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK, "SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK,
"WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE, "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, "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_WS_URL": request.app.state.config.PLAYWRIGHT_WS_URL,
"PLAYWRIGHT_TIMEOUT": request.app.state.config.PLAYWRIGHT_TIMEOUT, "PLAYWRIGHT_TIMEOUT": request.app.state.config.PLAYWRIGHT_TIMEOUT,
@ -594,6 +595,7 @@ class WebConfig(BaseModel):
SOUGOU_API_SID: Optional[str] = None SOUGOU_API_SID: Optional[str] = None
SOUGOU_API_SK: Optional[str] = None SOUGOU_API_SK: Optional[str] = None
WEB_LOADER_ENGINE: Optional[str] = None WEB_LOADER_ENGINE: Optional[str] = None
WEB_LOADER_TIMEOUT: Optional[str] = None
ENABLE_WEB_LOADER_SSL_VERIFICATION: Optional[bool] = None ENABLE_WEB_LOADER_SSL_VERIFICATION: Optional[bool] = None
PLAYWRIGHT_WS_URL: Optional[str] = None PLAYWRIGHT_WS_URL: Optional[str] = None
PLAYWRIGHT_TIMEOUT: Optional[int] = None PLAYWRIGHT_TIMEOUT: Optional[int] = None
@ -1071,6 +1073,8 @@ async def update_rag_config(
# Web loader settings # Web loader settings
request.app.state.config.WEB_LOADER_ENGINE = form_data.web.WEB_LOADER_ENGINE 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 = ( request.app.state.config.ENABLE_WEB_LOADER_SSL_VERIFICATION = (
form_data.web.ENABLE_WEB_LOADER_SSL_VERIFICATION form_data.web.ENABLE_WEB_LOADER_SSL_VERIFICATION
) )
@ -1206,6 +1210,7 @@ async def update_rag_config(
"SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID, "SOUGOU_API_SID": request.app.state.config.SOUGOU_API_SID,
"SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK, "SOUGOU_API_SK": request.app.state.config.SOUGOU_API_SK,
"WEB_LOADER_ENGINE": request.app.state.config.WEB_LOADER_ENGINE, "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, "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_WS_URL": request.app.state.config.PLAYWRIGHT_WS_URL,
"PLAYWRIGHT_TIMEOUT": request.app.state.config.PLAYWRIGHT_TIMEOUT, "PLAYWRIGHT_TIMEOUT": request.app.state.config.PLAYWRIGHT_TIMEOUT,

View file

@ -391,6 +391,7 @@ async def update_user_info_by_session_user(
class UserActiveResponse(UserStatus): class UserActiveResponse(UserStatus):
name: str name: str
profile_image_url: Optional[str] = None profile_image_url: Optional[str] = None
groups: Optional[list] = []
is_active: bool is_active: bool
model_config = ConfigDict(extra="allow") model_config = ConfigDict(extra="allow")
@ -412,11 +413,12 @@ async def get_user_by_id(user_id: str, user=Depends(get_verified_user)):
) )
user = Users.get_user_by_id(user_id) user = Users.get_user_by_id(user_id)
if user: if user:
groups = Groups.get_groups_by_member_id(user_id)
return UserActiveResponse( return UserActiveResponse(
**{ **{
**user.model_dump(), **user.model_dump(),
"groups": [{"id": group.id, "name": group.name} for group in groups],
"is_active": Users.is_user_active(user_id), "is_active": Users.is_user_active(user_id),
} }
) )

View file

@ -716,17 +716,18 @@ async def chat_web_search_handler(
return form_data return form_data
def get_last_images(message_list): def get_images_from_messages(message_list):
images = [] images = []
for message in reversed(message_list): for message in reversed(message_list):
images_flag = False
message_images = []
for file in message.get("files", []): for file in message.get("files", []):
if file.get("type") == "image": if file.get("type") == "image":
images.append(file.get("url")) message_images.append(file.get("url"))
images_flag = True
if images_flag: if message_images:
break images.append(message_images)
return images return images
@ -780,7 +781,16 @@ async def chat_image_generation_handler(
user_message = get_last_user_message(message_list) user_message = get_last_user_message(message_list)
prompt = user_message prompt = user_message
input_images = get_last_images(message_list) 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)
system_message_content = "" system_message_content = ""
@ -1540,10 +1550,15 @@ async def process_chat_payload(request, form_data, user, metadata, model):
if prompt is None: if prompt is None:
raise Exception("No user message found") raise Exception("No user message found")
model_rag_template = (
model.get("info", {}).get("params", {}).get("rag_template", "")
or request.app.state.config.RAG_TEMPLATE
)
if context_string != "": if context_string != "":
form_data["messages"] = add_or_update_user_message( form_data["messages"] = add_or_update_user_message(
rag_template( rag_template(
request.app.state.config.RAG_TEMPLATE, model_rag_template,
context_string, context_string,
prompt, prompt,
), ),

View file

@ -7,6 +7,7 @@ import redis
from open_webui.env import ( from open_webui.env import (
REDIS_CLUSTER, REDIS_CLUSTER,
REDIS_SOCKET_CONNECT_TIMEOUT,
REDIS_SENTINEL_HOSTS, REDIS_SENTINEL_HOSTS,
REDIS_SENTINEL_MAX_RETRY_COUNT, REDIS_SENTINEL_MAX_RETRY_COUNT,
REDIS_SENTINEL_PORT, REDIS_SENTINEL_PORT,
@ -162,6 +163,7 @@ def get_redis_connection(
username=redis_config["username"], username=redis_config["username"],
password=redis_config["password"], password=redis_config["password"],
decode_responses=decode_responses, decode_responses=decode_responses,
socket_connect_timeout=REDIS_SOCKET_CONNECT_TIMEOUT,
) )
connection = SentinelRedisProxy( connection = SentinelRedisProxy(
sentinel, sentinel,
@ -188,6 +190,7 @@ def get_redis_connection(
username=redis_config["username"], username=redis_config["username"],
password=redis_config["password"], password=redis_config["password"],
decode_responses=decode_responses, decode_responses=decode_responses,
socket_connect_timeout=REDIS_SOCKET_CONNECT_TIMEOUT,
) )
connection = SentinelRedisProxy( connection = SentinelRedisProxy(
sentinel, sentinel,

View file

@ -47,7 +47,7 @@
if (pipeline && (pipeline?.valves ?? false)) { if (pipeline && (pipeline?.valves ?? false)) {
for (const property in valves_spec.properties) { for (const property in valves_spec.properties) {
if (valves_spec.properties[property]?.type === 'array') { 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());
} }
} }

View file

@ -767,6 +767,19 @@
</div> </div>
{#if webConfig.WEB_LOADER_ENGINE === '' || webConfig.WEB_LOADER_ENGINE === 'safe_web'} {#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=" mb-2.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium"> <div class=" self-center text-xs font-medium">
{$i18n.t('Verify SSL Certificate')} {$i18n.t('Verify SSL Certificate')}

View file

@ -102,6 +102,18 @@
</div> </div>
{/if} {/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} {#if $_user?.id !== user.id}
<hr class="border-gray-100/50 dark:border-gray-800/50 my-2.5" /> <hr class="border-gray-100/50 dark:border-gray-800/50 my-2.5" />

View file

@ -75,12 +75,11 @@
`<sup class="footnote-ref footnote-ref-text">${token.escapedText}</sup>` `<sup class="footnote-ref footnote-ref-text">${token.escapedText}</sup>`
) || ''} ) || ''}
{:else if token.type === 'citation'} {:else if token.type === 'citation'}
<SourceToken {id} {token} {sourceIds} onClick={onSourceClick} /> {#if (sourceIds ?? []).length > 0}
<!-- {#if token.ids && token.ids.length > 0} <SourceToken {id} {token} {sourceIds} onClick={onSourceClick} />
{#each token.ids as sourceId} {:else}
<Source id={sourceId - 1} title={sourceIds[sourceId - 1]} onClick={onSourceClick} /> <TextToken {token} {done} />
{/each} {/if}
{/if} -->
{:else if token.type === 'text'} {:else if token.type === 'text'}
<TextToken {token} {done} /> <TextToken {token} {done} />
{/if} {/if}

View file

@ -39,37 +39,43 @@
}; };
</script> </script>
{#if (token?.ids ?? []).length == 1} {sourceIds}
<Source id={token.ids[0] - 1} title={sourceIds[token.ids[0] - 1]} {onClick} />
{:else} {#if sourceIds}
<LinkPreview.Root openDelay={0} bind:open={openPreview}> {#if (token?.ids ?? []).length == 1}
<LinkPreview.Trigger> <Source id={token.ids[0] - 1} title={sourceIds[token.ids[0] - 1]} {onClick} />
<button {:else}
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" <LinkPreview.Root openDelay={0} bind:open={openPreview}>
on:click={() => { <LinkPreview.Trigger>
openPreview = !openPreview; <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}
> >
<span class="line-clamp-1"> <div class="bg-gray-50 dark:bg-gray-850 rounded-xl p-1 cursor-pointer">
{getDisplayTitle(formattedTitle(decodeString(sourceIds[token.ids[0] - 1])))} {#each token.ids as sourceId}
<span class="dark:text-white/50 text-black/50">+{(token?.ids ?? []).length - 1}</span> <div class="">
</span> <Source id={sourceId - 1} title={sourceIds[sourceId - 1]} {onClick} />
</button> </div>
</LinkPreview.Trigger> {/each}
<LinkPreview.Content </div>
class="z-[999]" </LinkPreview.Content>
align="start" </LinkPreview.Root>
strategy="fixed" {/if}
sideOffset={6} {:else}
el={containerElement} <span>{token.raw}</span>
>
<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}

View file

@ -1460,37 +1460,35 @@
{/if} {/if}
{/if} {/if}
{#if isLastMessage} {#each model?.actions ?? [] as action}
{#each model?.actions ?? [] as action} <Tooltip content={action.name} placement="bottom">
<Tooltip content={action.name} placement="bottom"> <button
<button type="button"
type="button" aria-label={action.name}
aria-label={action.name} class="{isLastMessage || ($settings?.highContrastMode ?? false)
class="{isLastMessage || ($settings?.highContrastMode ?? false) ? 'visible'
? '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"
: '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={() => {
on:click={() => { actionMessage(action.id, message);
actionMessage(action.id, message); }}
}} >
> {#if action?.icon}
{#if action?.icon} <div class="size-4">
<div class="size-4"> <img
<img src={action.icon}
src={action.icon} class="w-4 h-4 {action.icon.includes('svg')
class="w-4 h-4 {action.icon.includes('svg') ? 'dark:invert-[80%]'
? 'dark:invert-[80%]' : ''}"
: ''}" style="fill: currentColor;"
style="fill: currentColor;" alt={action.name}
alt={action.name} />
/> </div>
</div> {:else}
{:else} <Sparkles strokeWidth="2.1" className="size-4" />
<Sparkles strokeWidth="2.1" className="size-4" /> {/if}
{/if} </button>
</button> </Tooltip>
</Tooltip> {/each}
{/each}
{/if}
{/if} {/if}
{/if} {/if}
{/if} {/if}

View file

@ -67,6 +67,7 @@
} }
let system = ''; let system = '';
let rag_template = '';
let info = { let info = {
id: '', id: '',
base_model_id: null, base_model_id: null,
@ -78,12 +79,14 @@
tags: [] tags: []
}, },
params: { params: {
system: '' system: '',
rag_template: ''
} }
}; };
let params = { let params: {
system: '' system: '';
rag_template: '';
}; };
let knowledge = []; let knowledge = [];
@ -207,6 +210,7 @@
} }
info.params.system = system.trim() === '' ? null : system; info.params.system = system.trim() === '' ? null : system;
info.params.rag_template = rag_template.trim() === '' ? null : rag_template;
info.params.stop = params.stop ? params.stop.split(',').filter((s) => s.trim()) : null; info.params.stop = params.stop ? params.stop.split(',').filter((s) => s.trim()) : null;
Object.keys(info.params).forEach((key) => { Object.keys(info.params).forEach((key) => {
if (info.params[key] === '' || info.params[key] === null) { if (info.params[key] === '' || info.params[key] === null) {
@ -254,6 +258,7 @@
} }
system = model?.params?.system ?? ''; system = model?.params?.system ?? '';
rag_template = model?.params?.rag_template ?? '';
params = { ...params, ...model?.params }; params = { ...params, ...model?.params };
params.stop = params?.stop params.stop = params?.stop
@ -521,15 +526,15 @@
</div> </div>
</div> </div>
<div> <div class="shrink-0">
<button <button
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" 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"
type="button" type="button"
on:click={() => { on:click={() => {
showAccessControlModal = true; showAccessControlModal = true;
}} }}
> >
<LockClosed strokeWidth="2.5" className="size-3.5" /> <LockClosed strokeWidth="2.5" className="size-3.5 shrink-0" />
<div class="text-sm font-medium shrink-0"> <div class="text-sm font-medium shrink-0">
{$i18n.t('Access')} {$i18n.t('Access')}
@ -644,6 +649,18 @@
</div> </div>
</div> </div>
<div class="my-1">
<div class=" text-xs font-semibold mb-2">{$i18n.t('RAG Template')}</div>
<div>
<Textarea
placeholder={$i18n.t(
'Leave empty to use the default prompt, or enter a custom prompt'
)}
bind:value={rag_template}
/>
</div>
</div>
<div class="flex w-full justify-between"> <div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium"> <div class=" self-center text-xs font-medium">
{$i18n.t('Advanced Params')} {$i18n.t('Advanced Params')}

View file

@ -64,7 +64,7 @@
onMount(async () => { onMount(async () => {
window.addEventListener('message', async (event) => { window.addEventListener('message', async (event) => {
if ( if (
!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:5173'].includes( !['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:9999'].includes(
event.origin event.origin
) )
) { ) {

View file

@ -34,8 +34,9 @@
onMount(async () => { onMount(async () => {
window.addEventListener('message', async (event) => { window.addEventListener('message', async (event) => {
console.log(event);
if ( if (
!['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:5173'].includes( !['https://openwebui.com', 'https://www.openwebui.com', 'http://localhost:9999'].includes(
event.origin event.origin
) )
) )