Add allow_file_downloads param to allow downloading image/video/audio media in chatbot (#9905)

* add allow_file_downloads param

* add download btn for markdown images

* add changeset

* tweak

* fix test

* remove param

* revert param removal

* fix show logic

* rename show_download_button to allow_file_downloads

* change default to True

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
This commit is contained in:
Hannah 2024-11-15 23:50:32 +00:00 committed by GitHub
parent da6f191554
commit 08f4b8b000
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 63 additions and 63 deletions

View File

@ -0,0 +1,6 @@
---
"@gradio/chatbot": patch
"gradio": patch
---
fix:Add `allow_file_downloads` param to allow downloading image/video/audio media in chatbot

View File

@ -184,6 +184,7 @@ class Chatbot(Component):
placeholder: str | None = None,
examples: list[ExampleMessage] | None = None,
show_copy_all_button=False,
allow_file_downloads=True,
):
"""
Parameters:
@ -218,6 +219,7 @@ class Chatbot(Component):
placeholder: a placeholder message to display in the chatbot when it is empty. Centered vertically and horizontally in the Chatbot. Supports Markdown and HTML. If None, no placeholder is displayed.
examples: A list of example messages to display in the chatbot before any user/assistant messages are shown. Each example should be a dictionary with an optional "text" key representing the message that should be populated in the Chatbot when clicked, an optional "files" key, whose value should be a list of files to populate in the Chatbot, an optional "icon" key, whose value should be a filepath or URL to an image to display in the example box, and an optional "display_text" key, whose value should be the text to display in the example box. If "display_text" is not provided, the value of "text" will be displayed.
show_copy_all_button: If True, will show a copy all button that copies all chatbot messages to the clipboard.
allow_file_downloads: If True, will show a download button for chatbot messages that contain media. Defaults to True.
"""
if type is None:
warnings.warn(
@ -259,6 +261,7 @@ class Chatbot(Component):
self.line_breaks = line_breaks
self.layout = layout
self.show_copy_all_button = show_copy_all_button
self.allow_file_downloads = allow_file_downloads
super().__init__(
label=label,
every=every,

View File

@ -58,7 +58,7 @@
<Story
name="Chatbot with math disabled, small height"
args={{ latex_delimiters: [], height: 200 }}
args={{ latex_delimiters: [], height: 200, show_copy_button: false }}
/>
<Story
@ -85,24 +85,10 @@
/>
<Story
name="Chatbot with copy button"
args={{
show_copy_button: true
}}
/>
<Story
name="Chatbot with chat bubble full width disabled"
args={{
bubble_full_width: false
}}
/>
<Story
name="Chatbot with panel layout enabled"
name="Chatbot with chat bubble full width disabled and copy button"
args={{
bubble_full_width: false,
layout: "panel"
show_copy_button: true
}}
/>

View File

@ -79,6 +79,7 @@
export let placeholder: string | null = null;
export let examples: ExampleMessage[] | null = null;
export let theme_mode: "system" | "light" | "dark";
export let allow_file_downloads = true;
</script>
<Block
@ -157,6 +158,7 @@
load_component={gradio.load_component}
msg_format={type}
root={gradio.root}
{allow_file_downloads}
/>
</div>
</Block>

View File

@ -13,7 +13,6 @@
export let show_retry: boolean;
export let show_undo: boolean;
export let show_copy_button: boolean;
export let show: boolean;
export let message: NormalisedMessage | NormalisedMessage[];
export let position: "right" | "left";
export let avatar: FileData | null;
@ -48,7 +47,7 @@
message.content.value?.url;
</script>
{#if show}
{#if show_copy || show_retry || show_undo || likeable}
<div
class="message-buttons-{position} {layout} message-buttons {avatar !==
null && 'with-avatar'}"
@ -57,14 +56,6 @@
{#if show_copy}
<Copy value={message_text} />
{/if}
{#if show_download && !Array.isArray(message) && is_component_message(message)}
<DownloadLink
href={message?.content?.value.url}
download={message.content.value.orig_name || "image"}
>
<IconButton Icon={DownloadIcon} />
</DownloadLink>
{/if}
{#if show_retry}
<IconButton
Icon={Retry}

View File

@ -10,6 +10,7 @@
import type { NormalisedMessage } from "../types";
import { copy } from "@gradio/utils";
import Message from "./Message.svelte";
import { DownloadLink } from "@gradio/wasm/svelte";
import { dequal } from "dequal/lite";
import {
@ -22,7 +23,13 @@
} from "svelte";
import { Image } from "@gradio/image/shared";
import { Clear, Trash, Community, ScrollDownArrow } from "@gradio/icons";
import {
Clear,
Trash,
Community,
ScrollDownArrow,
Download
} from "@gradio/icons";
import { IconButtonWrapper, IconButton } from "@gradio/atoms";
import type { SelectData, LikeData } from "@gradio/utils";
import type { ExampleMessage } from "../types";
@ -40,6 +47,7 @@
export let _fetch: typeof fetch;
export let load_component: Gradio["load_component"];
export let allow_file_downloads: boolean;
let _components: Record<string, ComponentType<SvelteComponent>> = {};
@ -292,6 +300,14 @@
on:click={() => (is_image_preview_open = false)}
label={"Clear"}
/>
{#if allow_file_downloads}
<DownloadLink
href={image_preview_source}
download={image_preview_source_alt || "image"}
>
<IconButton Icon={Download} label={"Download"} />
</DownloadLink>
{/if}
</IconButtonWrapper>
</div>
{/if}
@ -326,6 +342,7 @@
{show_copy_button}
handle_action={(selected) => handle_like(i, messages[0], selected)}
scroll={is_browser ? scroll : () => {}}
{allow_file_downloads}
/>
{/each}
{#if pending_message}

View File

@ -8,6 +8,7 @@
export let i18n;
export let upload;
export let _fetch;
export let allow_file_downloads: boolean;
</script>
{#if type === "gallery"}
@ -45,7 +46,7 @@
label=""
waveform_settings={{}}
waveform_options={{}}
show_download_button={false}
show_download_button={allow_file_downloads}
on:load
/>
{:else if type === "video"}
@ -57,7 +58,7 @@
show_share_button={true}
{i18n}
{upload}
show_download_button={false}
show_download_button={allow_file_downloads}
on:load
>
<track kind="captions" />
@ -68,7 +69,7 @@
{value}
show_label={false}
label="chatbot-image"
show_download_button={false}
show_download_button={allow_file_downloads}
on:load
{i18n}
/>

View File

@ -44,6 +44,7 @@
export let msg_format: "tuples" | "messages";
export let handle_action: (selected: string | null) => void;
export let scroll: () => void;
export let allow_file_downloads: boolean;
function handle_select(i: number, message: NormalisedMessage): void {
dispatch("select", {
@ -71,7 +72,6 @@
}
type ButtonPanelProps = {
show: boolean;
handle_action: (selected: string | null) => void;
likeable: boolean;
show_retry: boolean;
@ -86,7 +86,6 @@
let button_panel_props: ButtonPanelProps;
$: button_panel_props = {
show: show_like || show_retry || show_undo || show_copy_button,
handle_action,
likeable: show_like,
show_retry,
@ -147,11 +146,23 @@
aria-label={role + "'s message: " + get_message_label_data(message)}
>
{#if message.type === "text"}
{#if message.metadata.title}
<MessageBox
title={message.metadata.title}
expanded={is_last_bot_message([message], value)}
>
<div class="message-content">
{#if message.metadata.title}
<MessageBox
title={message.metadata.title}
expanded={is_last_bot_message([message], value)}
>
<Markdown
message={message.content}
{latex_delimiters}
{sanitize_html}
{render_markdown}
{line_breaks}
on:load={scroll}
{root}
/>
</MessageBox>
{:else}
<Markdown
message={message.content}
{latex_delimiters}
@ -161,18 +172,8 @@
on:load={scroll}
{root}
/>
</MessageBox>
{:else}
<Markdown
message={message.content}
{latex_delimiters}
{sanitize_html}
{render_markdown}
{line_breaks}
on:load={scroll}
{root}
/>
{/if}
{/if}
</div>
{:else if message.type === "component" && message.content.component in _components}
<Component
{target}
@ -185,6 +186,7 @@
{upload}
{_fetch}
on:load={() => scroll()}
{allow_file_downloads}
/>
{:else if message.type === "component" && message.content.component === "file"}
<a
@ -316,7 +318,6 @@
box-shadow: var(--shadow-drop);
align-self: flex-start;
text-align: right;
padding: var(--spacing-sm) var(--spacing-xl);
border-color: var(--border-color-accent-subdued);
background-color: var(--color-accent-soft);
}
@ -330,7 +331,6 @@
box-shadow: var(--shadow-drop);
align-self: flex-start;
text-align: right;
padding: var(--spacing-sm) var(--spacing-xl);
}
.panel .user :global(*) {
@ -417,17 +417,6 @@
overflow-wrap: break-word;
}
.user {
border-width: 1px;
border-radius: var(--radius-md);
align-self: flex-start;
border-bottom-right-radius: 0;
box-shadow: var(--shadow-drop);
text-align: right;
padding: var(--spacing-sm) var(--spacing-xl);
border-color: var(--border-color-accent-subdued);
background-color: var(--color-accent-soft);
}
@media (max-width: 480px) {
.user-row.bubble {
align-self: flex-end;
@ -441,6 +430,10 @@
}
}
.message-content {
padding: var(--spacing-sm) var(--spacing-xl);
}
.avatar-container {
align-self: flex-start;
position: relative;

View File

@ -51,6 +51,7 @@ class TestChatbot:
"_selectable": False,
"_retryable": False,
"_undoable": False,
"allow_file_downloads": True,
"key": None,
"type": "tuples",
"latex_delimiters": [{"display": True, "left": "$$", "right": "$$"}],