ChatInterface#
Open this notebook in Jupyterlite | Download this notebook from GitHub (right-click to download).
import panel as pn
from panel.chat import ChatInterface
pn.extension("perspective")
The ChatInterface
is a high-level layout, providing a user-friendly front-end interface for inputting different kinds of messages: text, images, PDFs, etc.
This layout provides front-end methods to:
Input (append) messages to the chat log.
Re-run (resend) the most recent
user
inputChatMessage
.Remove messages until the previous
user
inputChatMessage
.Clear the chat log, erasing all
ChatMessage
objects.
Since ChatInterface
inherits from ChatFeed
, it features all the capabilities of ChatFeed
; please see ChatFeed.ipynb for its backend capabilities.
Check out the panel-chat-examples docs to see applicable examples related to LangChain, OpenAI, Mistral, Llama, etc. If you have an example to demo, we’d love to add it to the panel-chat-examples gallery!
Parameters:#
Core#
widgets
(Widget | List[Widget]
): Widgets to use for the input. If not provided, defaults to[TextInput]
.user
(str
): Name of the ChatInterface user.avatar
(str | bytes | BytesIO | pn.pane.Image
): The avatar to use for the user. Can be a single character text, an emoji, or anything supported bypn.pane.Image
. If not set, uses the first character of the name.reset_on_send
(bool
): Whether to reset the widget’s value after sending a message; has no effect forTextInput
.auto_send_types
(tuple
): The widget types to automatically send when the user presses enter or clicks away from the widget. If not provided, defaults to[TextInput]
.button_properties
(Dict[Dict[str, Any]]
): Allows addition of functionality or customization of buttons by supplying a mapping from the button name to a dictionary containing theicon
,callback
, and/orpost_callback
keys. If the button names correspond to default buttons (send, rerun, undo, clear), the default icon can be updated and if acallback
key value pair is provided, the specified callback functionality runs before the existing one. For button names that don’t match existing ones, new buttons are created and must include acallback
orpost_callback
key. The provided callbacks should have a signature that accepts two positional arguments: instance (the ChatInterface instance) and event (the button click event).
Styling#
show_send
(bool
): Whether to show the send button. Default is True.show_rerun
(bool
): Whether to show the rerun button. Default is True.show_undo
(bool
): Whether to show the undo button. Default is True.show_clear
(bool
): Whether to show the clear button. Default is True.show_button_name
(bool
): Whether to show the button name. Default is True.
Properties:#
active_widget
(Widget
): The currently active widget.active
(int
): The currently active input widget tab index; -1 if there is only one widget available which is not in a tab.
ChatInterface()
Although ChatInterface
can be initialized without any arguments, it becomes much more useful, and interesting, with a callback
.
def even_or_odd(contents, user, instance):
if len(contents) % 2 == 0:
return "Even number of characters."
return "Odd number of characters."
ChatInterface(callback=even_or_odd)
You may also provide a more relevant, default user
name and avatar
.
ChatInterface(
callback=even_or_odd,
user="Asker",
avatar="?",
callback_user="Counter",
)
You can also use a different type of widget for input, like TextAreaInput
instead of TextInput
, by setting widgets
.
def count_chars(contents, user, instance):
return f"Found {len(contents)} characters."
ChatInterface(
callback=count_chars,
widgets=pn.widgets.TextAreaInput(
placeholder="Enter some text to get a count!", auto_grow=True, max_rows=3
),
)
Multiple widgets
can be set, which will be nested under a Tabs
layout.
def get_num(contents, user, instance):
if isinstance(contents, str):
num = len(contents)
else:
num = contents
return f"Got {num}."
ChatInterface(
callback=get_num,
widgets=[
pn.widgets.TextAreaInput(placeholder="Enter some text to get a count!"),
pn.widgets.IntSlider(name="Number Input", end=10)
],
)
Widgets other than TextInput
will require the user to manually click the Send
button, unless the type is specified in auto_send_types
.
ChatInterface(
callback=get_num,
widgets=[
pn.widgets.TextAreaInput(placeholder="Enter some text to get a count!"),
pn.widgets.IntSlider(name="Number Input", end=10)
],
auto_send_types=[pn.widgets.IntSlider],
)
If you include a FileInput
in the list of widgets you can enable the user to upload files.
ChatInterface(widgets=pn.widgets.FileInput(name="CSV File", accept=".csv"))
Try uploading a dataset! If you don’t have a dataset in hand, download this sample dataset, penguins.csv
.
Note, if you don’t like the default renderer, pn.pane.DataFrame
for CSVs, you can specify renderers
to use pn.pane.Perspective
; just be sure you have the "perspective"
extension added to pn.extension(...)
at the top of your file!
ChatInterface(
widgets=pn.widgets.FileInput(name="CSV File", accept=".csv"),
renderers=pn.pane.Perspective
)
If a list is provided to renderers
, will attempt to use the first renderer that does not raise an exception.
In addition, you may render the input however you’d like with a custom renderer as long as the signature accepts one argument, namely value
!
def bad_renderer(value):
raise Exception("Won't render using this...")
def custom_renderer(value):
return pn.Column(
f"Found {len(value)} rows in the CSV.",
pn.pane.Perspective(value, height=600)
)
ChatInterface(
widgets=pn.widgets.FileInput(name="CSV File", accept=".csv"),
renderers=[bad_renderer, custom_renderer]
)
If you’d like to guide the user into using one widget after another, you can set active
in the callback.
def guided_get_num(contents, user, instance):
if isinstance(contents, str):
num = len(contents)
instance.active = 1 # change to IntSlider tab
else:
num = contents
instance.active = 0 # Change to TextAreaInput tab
return f"Got {num}."
pn.chat.ChatInterface(
callback=guided_get_num,
widgets=[
pn.widgets.TextAreaInput(placeholder="Enter some text to get a count!"),
pn.widgets.IntSlider(name="Number Input", end=10)
],
)
Or, simply initialize with a single widget first, then replace with another widget in the callback.
def get_num_guided(contents, user, instance):
if isinstance(contents, str):
num = len(contents)
instance.widgets = [widgets[1]] # change to IntSlider
else:
num = contents
instance.widgets = [widgets[0]] # Change to TextAreaInput
return f"Got {num}."
widgets = [
pn.widgets.TextAreaInput(placeholder="Enter some text to get a count!"),
pn.widgets.IntSlider(name="Number Input", end=10)
]
pn.chat.ChatInterface(
callback=get_num_guided,
widgets=widgets[0],
)
The currently active widget can be accessed with the active_widget
property.
widgets = [
pn.widgets.TextAreaInput(placeholder="Enter some text to get a count!"),
pn.widgets.IntSlider(name="Number Input", end=10)
]
chat_interface = pn.chat.ChatInterface(
widgets=widgets,
)
print(chat_interface.active_widget)
TextAreaInput(css_classes=['chat-interface-input-wid...], placeholder='Enter some text t..., sizing_mode='stretch_width')
Sometimes, you may not want the widget to be reset after its contents has been sent.
To have the widgets’ value
persist, set reset_on_send=False
.
pn.chat.ChatInterface(
widgets=pn.widgets.TextAreaInput(),
reset_on_send=False,
)
If you’re not using an LLM to respond, the Rerun
button may not be practical so it can be hidden by setting show_rerun=False
.
The same can be done for other buttons as well with show_send
, show_undo
, and show_clear
.
pn.chat.ChatInterface(callback=get_num, show_rerun=False, show_undo=False)
If you want a slimmer ChatInterface
, use show_button_name=False
to hide the labels of the buttons and/ or width
to set the total width of the component.
pn.chat.ChatInterface(callback=get_num, show_button_name=False, width=400)
New buttons with custom functionality can be added to the input row through button_properties
.
def show_notice(instance, event):
instance.send("This is how you add buttons!", respond=False, user="System")
pn.chat.ChatInterface(
button_properties={"help": {"callback": show_notice, "icon": "help"}}
)
Default buttons can also be updated with custom behaviors, before using callback
and after using post_callback
.
def run_before(instance, event):
instance.send(
"This will be cleared so it won't show after clear!",
respond=False,
user="System",
)
def run_after(instance, event):
instance.send("This will show after clear!", respond=False, user="System")
pn.chat.ChatInterface(
button_properties={
"clear": {"callback": run_before, "post_callback": run_after, "icon": "help"}
}
)
Check out the panel-chat-examples docs for more examples related to LangChain, OpenAI, Mistral, Llama, etc.
Open this notebook in Jupyterlite | Download this notebook from GitHub (right-click to download).