{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "import time\n",
    "import panel as pn\n",
    "\n",
    "pn.extension()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `ChatStep` opens up the possibility to display / hide intermediate steps prior to the final result.\n",
    "\n",
    "Check out the [panel-chat-examples](https://holoviz-topics.github.io/panel-chat-examples/) docs to see applicable examples related to [LangChain](https://python.langchain.com/docs/get_started/introduction), [OpenAI](https://openai.com/blog/chatgpt), [Mistral](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&ved=2ahUKEwjZtP35yvSBAxU00wIHHerUDZAQFnoECBEQAQ&url=https%3A%2F%2Fdocs.mistral.ai%2F&usg=AOvVaw2qpx09O_zOzSksgjBKiJY_&opi=89978449), [Llama](https://ai.meta.com/llama/), etc. If you have an example to demo, we'd love to add it to the panel-chat-examples gallery!\n",
    "\n",
    "#### Parameters:\n",
    "\n",
    "##### Core\n",
    "\n",
    "* **`collapsed_on_success`** (`bool`): Whether to collapse the card on completion. Defaults to `True`.\n",
    "* **`context_exception`** (`str`): How to handle exceptions raised upon exiting the context manager. If \"raise\", the exception will be raised. If \"summary\", a summary will be sent to the chat step. If \"verbose\", the full traceback will be sent to the chat step. If \"ignore\", the exception will be ignored.\n",
    "* **`success_title`** (`str`): Title to display when status is success; if not provided and `collapsed_on_success` uses the last object's string. Defaults to `None`.\n",
    "* **`default_title`** (`str`): The default title to display if the other title params are unset. Defaults to an empty string `\"\"`.\n",
    "* **`failed_title`** (`str`): Title to display when status is failed. Defaults to `None`.\n",
    "* **`margin`** (`tuple`): Allows creating additional space around the component. May be specified as a two-tuple of the form (vertical, horizontal) or a four-tuple (top, right, bottom, left). Defaults to `(5, 5, 5, 10)`.\n",
    "* **``objects``** (list): The list of objects to display in the `ChatStep`, which will be formatted like a `Column`. Should not generally be modified directly except when replaced in its entirety.\n",
    "* **`pending_title`** (`str`): Title to display when status is pending. Defaults to `None`.\n",
    "* **`running_title`** (`str`): Title to display when status is running. Defaults to `None`.\n",
    "* **`status`** (`str`): The status of the chat step. Must be one of [\"pending\", \"running\", \"success\", \"failed\"]. Defaults to `\"pending\"`.\n",
    "\n",
    "##### Styling\n",
    "\n",
    "* **`collapsed`** (`bool`): Whether the contents of the `ChatStep` are collapsed. Defaults to `False`.\n",
    "* **`default_badges`** (`dict`): Mapping from status to default status badge. Defaults to `DEFAULT_STATUS_BADGES`; keys must be one of 'pending', 'running', 'success', 'failed'.\n",
    "\n",
    "#### Methods\n",
    "\n",
    "##### Core\n",
    "\n",
    "* **`stream`**: Stream a token to the last available string-like object.\n",
    "* **`stream_title`**: Stream a token to the title header.\n",
    "* **`serialize`**: Format the object to a string.\n",
    "\n",
    "___"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Basics"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`ChatStep` can be initialized without any arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step = pn.chat.ChatStep()\n",
    "chat_step"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Append `Markdown` objects to the chat step by calling `stream`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step.stream(\"Just thinking...\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Calling it again will concatenate the text to the last available `Markdown` pane."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step.stream(\" about `ChatStep`!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternatively, setting `replace=True` will override the existing message."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step.stream(\"`ChatStep` can do a lot of things!\", replace=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's possible to also append any objects, like images."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step.append(pn.pane.Image(\"https://assets.holoviz.org/panel/samples/png_sample.png\", width=50, height=50))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Calling `stream` afterwards will append a new `Markdown` pane below it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step.stream(\"Like it can support images too! Above is a picture of dices.\")\n",
    "print(chat_step.objects)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To convert the objects into a string, call `serialize`, or simply use `str()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step.serialize()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Badges\n",
    "\n",
    "The default avatars are `BooleanStatus`, but can be changed by providing `default_badges`. The values can be emojis, images, text, or Panel objects."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step = pn.chat.ChatStep(\n",
    "    default_badges={\n",
    "        \"pending\": \"🤔\",\n",
    "        \"running\": \"🏃\",\n",
    "        \"success\": \"🎉\",\n",
    "        \"failed\": \"😞\",\n",
    "    },\n",
    "    status=\"success\",\n",
    ")\n",
    "chat_step"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Status"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To show that the step is processing, you can set the status to `running` and provide a `running_title`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step = pn.chat.ChatStep(status=\"running\", running_title=\"Processing this step...\")\n",
    "chat_step.stream(\"Pretending to do something.\")\n",
    "chat_step"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Upon completion, set the status to `success` and it'll automatically collapse the contents."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step.status = \"success\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To have the title update on success, either provide the `success_title` or `default_title`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step = pn.chat.ChatStep(status=\"running\", running_title=\"Processing this step...\", success_title=\"Pretend job done!\")\n",
    "chat_step.stream(\"Pretending to do something.\")\n",
    "chat_step.status = \"success\"\n",
    "chat_step"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To streamline this process, `ChatStep` may be used as a context manager.\n",
    "\n",
    "Upon entry, the status will be changed from the default `pending` to `running`.\n",
    "\n",
    "Exiting will change the status from `running` to `completed` if successful."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step = pn.chat.ChatStep(running_title=\"Processing this step...\", success_title=\"Pretend job done!\")\n",
    "with chat_step:\n",
    "    chat_step.stream(\"Pretending to do something.\")\n",
    "\n",
    "chat_step"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "However, if an exception occurs, the status will be set to \"failed\" and the error message will be displayed on the title."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step = pn.chat.ChatStep(running_title=\"Processing this step...\", success_title=\"Pretend job done!\")\n",
    "chat_step"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    with chat_step:\n",
    "        chat_step.stream(\"Breaking something\")\n",
    "        raise RuntimeError(\"Just demoing!\")\n",
    "except RuntimeError as e:\n",
    "    print(\"Comment this try/except to see what happens!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The title can also be streamed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step = pn.chat.ChatStep()\n",
    "chat_step"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step.status = \"running\"\n",
    "for char in \"It's streaming a title!\":\n",
    "    time.sleep(0.02)\n",
    "    chat_step.stream_title(char)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It can be used with any `status`, like setting `status=\"pending\"`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step = pn.chat.ChatStep()\n",
    "chat_step"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "for char in \"I'm deciding on a title... how about this one?\":\n",
    "    time.sleep(0.01)\n",
    "    chat_step.stream_title(char, status=\"pending\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The title can also be completely replaced--equivalent to setting `chat_step.pending_title = \"Nevermind!`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "chat_step.stream_title(\"Nevermind!\", replace=True, status=\"pending\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Be sure to check out [ChatFeed](ChatFeed.ipynb) to see it works with a `ChatFeed` or `ChatInterface`!"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
