diff --git a/backend/open_webui/routers/images.py b/backend/open_webui/routers/images.py index a15dd91498..83ecdc407e 100644 --- a/backend/open_webui/routers/images.py +++ b/backend/open_webui/routers/images.py @@ -613,6 +613,9 @@ async def image_generations( model = await get_image_model(request) + metadata['model'] = model + metadata['size'] = size + try: if image_config.IMAGE_GENERATION_ENGINE == 'openai': headers = { diff --git a/backend/open_webui/tools/builtin.py b/backend/open_webui/tools/builtin.py index 136d2001dc..ed52f779cd 100644 --- a/backend/open_webui/tools/builtin.py +++ b/backend/open_webui/tools/builtin.py @@ -388,6 +388,10 @@ async def generate_image( images = await image_generations( request=__request__, form_data=CreateImageForm(prompt=prompt), + metadata={ + 'chat_id': __chat_id__, + 'message_id': __message_id__, + }, user=user, ) @@ -456,6 +460,10 @@ async def edit_image( images = await image_edits( request=__request__, form_data=EditImageForm(prompt=prompt, image=image_urls), + metadata={ + 'chat_id': __chat_id__, + 'message_id': __message_id__, + }, user=user, ) diff --git a/src/lib/apis/chats/index.ts b/src/lib/apis/chats/index.ts index a659fd9b08..92e920536e 100644 --- a/src/lib/apis/chats/index.ts +++ b/src/lib/apis/chats/index.ts @@ -785,6 +785,19 @@ export const getChatById = async (token: string, id: string) => { return res; }; +export const chatExists = async (token: string, id: string) => { + const res = await fetch(`${WEBUI_API_BASE_URL}/chats/${id}`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + ...(token && { authorization: `Bearer ${token}` }) + } + }).catch(() => null); + + return res?.ok ?? false; +}; + export const getChatByShareId = async (token: string, share_id: string) => { let error = null; diff --git a/src/lib/apis/images/index.ts b/src/lib/apis/images/index.ts index 5d87c13a18..8877213d67 100644 --- a/src/lib/apis/images/index.ts +++ b/src/lib/apis/images/index.ts @@ -1,4 +1,16 @@ import { IMAGES_API_BASE_URL } from '$lib/constants'; +import { searchFiles } from '$lib/apis/files'; + +export const getGeneratedImages = async (token: string, skip: number = 0, limit: number = 20) => { + try { + return await searchFiles(token, 'generated-image*', skip, limit); + } catch (error) { + if (typeof error === 'string' && error.includes('No files found')) { + return []; + } + throw error; + } +}; export const getConfig = async (token: string = '') => { let error = null; diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 686613cca9..33fffb63ec 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -148,6 +148,7 @@ let messagesRef: Messages | undefined; let autoScroll = true; + let focusedMessageOnLoad = false; let isNearTop = true; let processing = ''; let messagesContainerElement: HTMLDivElement; @@ -586,7 +587,9 @@ await tick(); loading = false; noteChatDebug('embedded chat loading false'); - window.setTimeout(() => scrollToBottom(), 0); + if (!focusedMessageOnLoad) { + window.setTimeout(() => scrollToBottom(), 0); + } await tick(); @@ -2007,6 +2010,7 @@ const loadChat = async () => { noteChatDebug('loadChat start'); + focusedMessageOnLoad = false; // chatIdProp is empty for chats started from the home page (URL set via replaceState) chatId.set(chatIdProp || $chatId); noteChatDebug('loadChat set active chat id'); @@ -2149,6 +2153,35 @@ await tick(); + const focusMessageId = $page.url.searchParams.get('message'); + if (focusMessageId && history.messages[focusMessageId]) { + const focusFileId = $page.url.searchParams.get('image'); + await showMessage(history.messages[focusMessageId], false, false); + autoScroll = false; + await messagesRef?.showAllMessages(); + await tick(); + + const scrollToFocus = () => { + const messageElement = document.getElementById(`message-${focusMessageId}`); + const target = + (focusFileId && + messageElement?.querySelector(`img[src*="${CSS.escape(focusFileId)}"]`)) || + messageElement; + target?.scrollIntoView({ behavior: 'auto', block: 'center' }); + return target; + }; + + const target = scrollToFocus(); + requestAnimationFrame(() => { + scrollToFocus(); + requestAnimationFrame(() => scrollToFocus()); + }); + if (target instanceof HTMLImageElement && !target.complete) { + target.addEventListener('load', () => scrollToFocus(), { once: true }); + } + focusedMessageOnLoad = true; + } + return true; } else { console.warn('[note-chat] chat response missing chat payload', { diff --git a/src/lib/components/chat/Messages.svelte b/src/lib/components/chat/Messages.svelte index 2f4ad5fec9..23a2e00e32 100644 --- a/src/lib/components/chat/Messages.svelte +++ b/src/lib/components/chat/Messages.svelte @@ -154,6 +154,13 @@ } }; + export const showAllMessages = async () => { + if (messagesCount === null) return; + messagesCount = null; + buildMessages(); + await tick(); + }; + export const scrollToTop = async () => { messagesCount = null; buildMessages(); diff --git a/src/lib/components/common/ImagePreview.svelte b/src/lib/components/common/ImagePreview.svelte index d1186562cf..a47b3d379d 100644 --- a/src/lib/components/common/ImagePreview.svelte +++ b/src/lib/components/common/ImagePreview.svelte @@ -1,9 +1,11 @@ -
+ {$i18n.t('Image generation is not enabled')} +
++ {$i18n.t('Contact your administrator to enable image generation')} +