This commit is contained in:
Timothy Jaeryang Baek 2026-10-29 16:44:26 +09:00
parent 1b92d98484
commit ef29fb12f1
5 changed files with 28 additions and 26 deletions

View file

@ -3150,7 +3150,7 @@ async def display_file(
:param path: Terminal file path, or file_id or /api/v1/files/<file_id>/content for a chat or knowledge base file
:param page: For PDF, DOCX, and PPTX files, open the preview at this 1-based page or slide number
:param inline: Show the file inline in chat instead of opening the file viewer
:param inline: Show terminal files inline instead of opening the file viewer; uploaded files always display inline
:return: JSON with the file's path, name, content type, and display state
"""
if __request__ is None:
@ -3182,7 +3182,7 @@ async def display_file(
'type': 'file',
'source': 'open_webui',
'exists': True,
'displayed': inline,
'displayed': True,
'path': path,
'full_path': path,
'name': file_meta.get('name') or file.filename,

View file

@ -973,8 +973,8 @@ async def terminal_event_handler(
path = result.get('path') or tool_function_params.get('path', '')
if not path:
return
# Only emit if the file actually exists
if result.get('exists') is False:
# Only open the viewer for existing files that are not displayed inline.
if result.get('exists') is False or result.get('displayed') is True:
return
page = tool_function_params.get('page')
@ -982,7 +982,6 @@ async def terminal_event_handler(
{
'type': f'terminal:{tool_function_name}',
'data': {
**result,
'path': path,
**({'page': page} if page else {}),
},

View file

@ -136,7 +136,6 @@
import NotificationToast from '../NotificationToast.svelte';
import Spinner from '../common/Spinner.svelte';
import Modal from '../common/Modal.svelte';
import ToolOutputFile from './Messages/ToolOutputFile.svelte';
import { isEmbedWindow } from '../common/FullHeightIframe.svelte';
import Tooltip from '../common/Tooltip.svelte';
import Sidebar from '../icons/Sidebar.svelte';
@ -523,8 +522,6 @@
let params = {};
let chatVariables = {};
let showChatVariablesModal = false;
let showDisplayedFile = false;
let displayedFile: Record<string, unknown> | null = null;
let loadedChatIdProp = '';
let currentDraftKey = '';
@ -1298,13 +1295,6 @@
};
const terminalEventHandler = (type: string, data: any, message: any) => {
if (type === 'terminal:display_file' && data?.source === 'open_webui') {
if ($settings?.terminalFileDisplay !== 'inline') {
displayedFile = data;
showDisplayedFile = true;
}
return;
}
const attachment = history.messages[message?.parentId]?.files?.find(
(file: any) => file.type === 'filesystem'
);
@ -5062,17 +5052,6 @@
<audio id="audioElement" style="display: none;"></audio>
<Modal bind:show={showDisplayedFile} size="xl">
<button
class="float-right p-2"
aria-label={$i18n.t('Close')}
on:click={() => (showDisplayedFile = false)}
>
<XMark className="size-4" />
</button>
<ToolOutputFile item={displayedFile} chatId={$chatId} />
</Modal>
{#if getChatVariablesForm(selectedModelIds, chatVariables, $models).conflicts.length > 0}
<Modal bind:show={showChatVariablesModal} size="md">
<div>

View file

@ -1,5 +1,7 @@
<script lang="ts">
import { getContext } from 'svelte';
import Collapsible from '$lib/components/common/Collapsible.svelte';
import Image from '$lib/components/common/Image.svelte';
import ToolCallDisplay from '$lib/components/common/ToolCallDisplay.svelte';
import ToolOutputFile from './ToolOutputFile.svelte';
import { resolveChatMessageToolCall } from '$lib/apis/chats';
@ -38,6 +40,7 @@
export let onPreview: any = () => {};
export let onToolCallResolved: any = () => {};
const i18n = getContext<typeof import('$lib/i18n').default>('i18n');
const getDetailTitle = (detailToken: OutputDetailToken): any => detailToken.summary;
const getDetailAttributes = (detailToken: OutputDetailToken): any => detailToken.attributes;
let resolvingCallId = '';
@ -100,6 +103,8 @@
{:else}
<div class="whitespace-pre-wrap text-[0.9375rem]">{displayItem.text}</div>
{/if}
{:else if displayItem.type === 'image'}
<Image src={displayItem.src} alt={$i18n.t('Generated Image')} />
{:else if displayItem.type === 'detail_group'}
<ConsecutiveDetailsGroup
id={`${id}-${displayItem.id}`}

View file

@ -50,6 +50,11 @@ export type OutputDisplayItem =
id: string;
text: string;
}
| {
type: 'image';
id: string;
src: string;
}
| {
type: 'detail_single';
id: string;
@ -438,6 +443,20 @@ export function buildOutputDisplayItems(output: OutputItem[] = []): OutputDispla
return;
}
if (item.type === 'image_generation_call') {
if (typeof item.result === 'string' && item.result) {
flushDetails();
displayItems.push({
type: 'image',
id: item.id ?? `image-${index}`,
src: item.result.startsWith('data:')
? item.result
: `data:image/${item.output_format || 'png'};base64,${item.result}`
});
}
return;
}
const fallbackText = getMessageText(item);
if (fallbackText.trim()) {
flushDetails();