Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Panel contents in preparation for Pycon #9

Merged
merged 4 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
397 changes: 397 additions & 0 deletions 04-panel-intro.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,397 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Intro to HoloViz"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"HoloViz is a suite of high-level Python tools that are designed to work together to make visualizing data a breeze, from conducting exploratory data analysis to deploying complex dashboards.\n",
"\n",
"The core HoloViz projects are as follows:\n",
"\n",
"- [Panel](https://panel.holoviz.org): Create interactive dashboards in Jupyter notebooks or standalone apps\n",
"- [hvPlot](https://hvplot.holoviz.org): Quickly and interactively explore data with a familiar API\n",
"- [HoloViews](https://holoviews.org): Interactive plotting experience\n",
"- [GeoViews](http://geoviews.org): Geographic extension of HoloViews\n",
"- [Datashader](https://datashader.org): Render big data images in a browser\n",
"- [Lumen](https://lumen.holoviz.org/): Construct no-code dashboards from simple YAML specifications\n",
"- [Colorcet](https://colorcet.holoviz.org/): Plot with perceptually based colormaps\n",
"- [Param](https://param.holoviz.org): Declaratively code in Python"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## What is Panel\n",
"\n",
"Today, the focus is on Panel.\n",
"\n",
"Panel packs many pre-built frontend components that are **usable with Python**.\n",
"\n",
"That means you can convert your static Python scripts into interactive ones--**no Javascript necessary**!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import panel as pn\n",
"pn.extension()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Basic Panel Tutorial\n",
"\n",
"Let's start out building an interactive app that allows the user to print a custom message.\n",
"\n",
"Currently it's hard coded to `\"Hello World\"`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(\"Hello World!\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Widget\n",
"\n",
"We can give the user more control by introducing a `TextInput` widget."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"message_input = pn.widgets.TextInput(value=\"Hello World!\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Interactivity\n",
"\n",
"Then, we can `pn.bind` the widget's `param.value` to the callback, `echo_message`, which simply echos the input value on change.\n",
"\n",
"Note: it's important to prefix `value` with `param`--without it, there will be no updates!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def echo_message(message):\n",
" return f\"<i>{message}</i>\"\n",
"\n",
"message_ref = pn.bind(echo_message, message=message_input.param.value)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Layout\n",
"\n",
"Next, create a simple layout to see the results.\n",
"\n",
"Try typing unique in the widget to see the message update!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pn.Column(message_input, message_ref)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Recap\n",
"\n",
"To recap, we:\n",
"\n",
"1. instantiated a widget (`TextInput`).\n",
"2. defined a function `echo_message`\n",
"3. bounded the function to the widget's *param* value\n",
"4. laid out the the widget and the bound reference\n",
"\n",
"![recap](images/recap.png)\n",
"\n",
"Here's all the code cells collected into one!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import panel as pn\n",
"pn.extension()\n",
"\n",
"message_input = pn.widgets.TextInput(value=\"Hello World!\")\n",
"\n",
"def echo_message(message):\n",
" return f\"<i>{message}</i>\"\n",
"\n",
"message_ref = pn.bind(echo_message, message=message_input.param.value)\n",
"\n",
"pn.Column(message_input, message_ref)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Challenge\n",
"\n",
"Doing this repeatedly is key to creating more complex apps with Panel, so let's do a quick exercise.\n",
"\n",
"Your goal is to create a widget that will toggle the message to upper case if activated by filling out the ellipses (`...`)!\n",
"\n",
"Hint: check out the [Component gallery](https://panel.holoviz.org/reference/index.html) to see what widgets are available to accomplish this goal (one of them starts with a `T`, but there are multiple solutions!)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import panel as pn\n",
"pn.extension()\n",
"\n",
"message_input = pn.widgets.TextInput(value=\"Hello World!\")\n",
"toggle_upper = ... # Fill this out\n",
"\n",
"def echo_message(message, toggle_upper):\n",
" ... # Fill this out\n",
" return f\"<i>{message}</i>\"\n",
"\n",
"message_ref = pn.bind(echo_message, message=message_input.param.value, toggle_upper=...) # Fill this out\n",
"\n",
"pn.Column(message_input, message_ref)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Congrats on building an interactive Panel app! 🎉"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Introducing Panel ChatInterface"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, introducing `pn.chat.ChatInterface`, which is a component that packages all the steps you just learned to provide convenient features for developing a Chat UI with LLMs!\n",
"\n",
"### Widget\n",
"\n",
"Try typing a message and pressing enter to send!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"chat = pn.chat.ChatInterface()\n",
"chat"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You might have noticed that it echoes the message you entered, but it doesn't reply... not fun (yet).\n",
"\n",
"### Interactivity\n",
"\n",
"To make it reply, all we have to do is set a `callback`, like `pn.bind`, but with a caveat: it needs these three arguments: `contents`, `user`, and `instance`.\n",
"\n",
"Now when you try sending a message in the chat interface, it will be echoed back in italics!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def echo_message(contents: str, user: str, instance: pn.chat.ChatInterface):\n",
" return f\"<i>{contents}</i>\"\n",
"\n",
"chat.callback = echo_message"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Streaming\n",
"\n",
"You might have seen services, like OpenAI and Mistral, stream tokens as they arrive.\n",
"\n",
"We can simulate streaming tokens by looping through the contents of the user's input, concatenating the characters to the final message, and `yield`ing it in italics.\n",
"\n",
"Since there's no serious computation, it'll run too fast for us to perceive streaming--thus `time.sleep`.\n",
"\n",
"Here's the latest code collected into one (and also `callback` within instantation)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"import panel as pn\n",
"pn.extension()\n",
"\n",
"def stream_echo_message(contents: str, user: str, instance: pn.chat.ChatInterface):\n",
" message = \"\"\n",
" for char in contents:\n",
" time.sleep(0.1) # to simulate a serious computation\n",
" message += char\n",
" yield f\"<i>{message}</i>\"\n",
"\n",
"chat = pn.chat.ChatInterface(callback=stream_echo_message)\n",
"chat"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### More Interactivity\n",
"\n",
"`pn.chat.ChatInterface` can be used with other widgets too!\n",
"\n",
"Here, we include a `pn.widgets.FloatSlider` to control how long to wait between each character streamed."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"import panel as pn\n",
"pn.extension()\n",
"\n",
"def stream_echo_message(contents: str, user: str, instance: pn.chat.ChatInterface):\n",
" message = \"\"\n",
" for char in contents:\n",
" time.sleep(slider.value) # to simulate a serious computation\n",
" message += char\n",
" yield f\"<i>{message}</i>\"\n",
"\n",
"slider = pn.widgets.FloatSlider(start=0.01, value=0.5, name=\"Sleep (s)\", align=\"center\")\n",
"chat = pn.chat.ChatInterface(callback=stream_echo_message, min_height=350)\n",
"\n",
"pn.Column(slider, chat)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Other Inputs\n",
"\n",
"`pn.chat.ChatInterface` also supports multi-modal inputs, like images, videos, PDFs, and more!"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from io import BytesIO\n",
"import panel as pn\n",
"\n",
"pn.extension()\n",
"\n",
"def display_info(contents: BytesIO, user: str, instance: pn.chat.ChatInterface):\n",
" size = len(contents.getvalue())\n",
" return f\"Size of input: {size / (1024 * 1024):.2f} MB\"\n",
"\n",
"file_input = pn.widgets.FileInput(accept=\".jpeg,.png,.gif,.mp4,.pdf\")\n",
"chat = pn.chat.ChatInterface(widgets=[file_input], callback=display_info)\n",
"chat"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That's it for a crash course on Panel! These techniques, used repeatedly, will allow you to build increasingly complex web apps with just Python.\n",
"\n",
"To learn more about `pn.chat.ChatInterface`, click [here](https://panel.holoviz.org/reference/chat/ChatInterface.html). It inherits from `pn.chat.ChatFeed`, so check that out [here](https://panel.holoviz.org/reference/chat/ChatFeed.html) too!\n",
"\n",
"For more tutorials delving into Panel in general, click [here](https://panel.holoviz.org/tutorials/index.html) or check out the app gallery [here](https://panel.holoviz.org/gallery/index.html).\n",
"\n",
"There is also a HoloViz Discourse if you want to ask questions [here](https://discourse.holoviz.org/)."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "ahuang@anaconda.com-ahuang@anaconda.com-ragna-panel-presentations",
"language": "python",
"name": "conda-env-ahuang_anaconda.com-ahuang_anaconda.com-ragna-panel-presentations-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 4
}