Skip to main content

A chat box and some helpful tools used to build chatbot app with streamlit

Project description

Attention!

Since version 1.24.0 streamlit provides official elements to build conversational apps.

The new elements are more flexible, extensible and better supported, I would suggest to use them.

However, streamlit>=1.23 requires protobuf>=4 when some package requires protobuf<=3. In this condition you can use this package(<1.0.0) with streamlit<=1.22 as alternative. They are all simple to render text messages.

This package(>=1.0.0) will focus on wrapper of official chat elements to make chat with LLMs more convenient.

Chatbox component for streamlit

A Streamlit component to show chat messages. It's basiclly a wrapper of streamlit officeial elements including the chat elemnts.

  • demo

  • demo agent

Features

  • support streaming output.
  • support markdown/image/video/audio messages, and all streamlit elements could be supported by customized OutputElement.
  • output multiple messages at once, and make them collapsable.
  • maintain session state context bound to chat conversation
  • export & import chat histories

This make it easy to chat with langchain LLMs in streamlit.

Goto webui of langchain-chatchat to see the actual application.

Install

just pip install -U streamlit-chatbox

Usage examples

import streamlit as st
from streamlit_chatbox import *
import time
import simplejson as json


llm = FakeLLM()
chat_box = ChatBox(
    use_rich_markdown=True, # use streamlit-markdown
    user_theme="green", # see streamlit_markdown.st_markdown for all available themes
    assistant_theme="blue",
)
chat_box.use_chat_name("chat1") # add a chat conversatoin

def on_chat_change():
    chat_box.use_chat_name(st.session_state["chat_name"])
    chat_box.context_to_session() # restore widget values to st.session_state when chat name changed


with st.sidebar:
    st.subheader('start to chat using streamlit')
    chat_name = st.selectbox("Chat Session:", ["default", "chat1"], key="chat_name", on_change=on_chat_change)
    chat_box.use_chat_name(chat_name)
    streaming = st.checkbox('streaming', key="streaming")
    in_expander = st.checkbox('show messages in expander', key="in_expander")
    show_history = st.checkbox('show session state', key="show_history")
    chat_box.context_from_session(exclude=["chat_name"]) # save widget values to chat context

    st.divider()

    btns = st.container()

    file = st.file_uploader(
        "chat history json",
        type=["json"]
    )

    if st.button("Load Json") and file:
        data = json.load(file)
        chat_box.from_dict(data)


chat_box.init_session()
chat_box.output_messages()

def on_feedback(
    feedback,
    chat_history_id: str = "",
    history_index: int = -1,
):
    reason = feedback["text"]
    score_int = chat_box.set_feedback(feedback=feedback, history_index=history_index) # convert emoji to integer
    # do something
    st.session_state["need_rerun"] = True


feedback_kwargs = {
    "feedback_type": "thumbs",
    "optional_text_label": "wellcome to feedback",
}

if query := st.chat_input('input your question here'):
    chat_box.user_say(query)
    if streaming:
        generator = llm.chat_stream(query)
        elements = chat_box.ai_say(
            [
                # you can use string for Markdown output if no other parameters provided
                Markdown("thinking", in_expander=in_expander,
                         expanded=True, title="answer"),
                Markdown("", in_expander=in_expander, title="references"),
            ]
        )
        time.sleep(1)
        text = ""
        for x, docs in generator:
            text += x
            chat_box.update_msg(text, element_index=0, streaming=True)
        # update the element without focus
        chat_box.update_msg(text, element_index=0, streaming=False, state="complete")
        chat_box.update_msg("\n\n".join(docs), element_index=1, streaming=False, state="complete")
        chat_history_id = "some id"
        chat_box.show_feedback(**feedback_kwargs,
                                key=chat_history_id,
                                on_submit=on_feedback,
                                kwargs={"chat_history_id": chat_history_id, "history_index": len(chat_box.history) - 1})
    else:
        text, docs = llm.chat(query)
        chat_box.ai_say(
            [
                Markdown(text, in_expander=in_expander,
                         expanded=True, title="answer"),
                Markdown("\n\n".join(docs), in_expander=in_expander,
                         title="references"),
            ]
        )

cols = st.columns(2)
if cols[0].button('show me the multimedia'):
    chat_box.ai_say(Image(
        'https://tse4-mm.cn.bing.net/th/id/OIP-C.cy76ifbr2oQPMEs2H82D-QHaEv?w=284&h=181&c=7&r=0&o=5&dpr=1.5&pid=1.7'))
    time.sleep(0.5)
    chat_box.ai_say(
        Video('https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'))
    time.sleep(0.5)
    chat_box.ai_say(
        Audio('https://sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4'))

if cols[1].button('run agent'):
    chat_box.user_say('run agent')
    agent = FakeAgent()
    text = ""

    # streaming:
    chat_box.ai_say() # generate a blank placeholder to render messages
    for d in agent.run_stream():
        if d["type"] == "complete":
            chat_box.update_msg(expanded=False, state="complete")
            chat_box.insert_msg(d["llm_output"])
            break

        if d["status"] == 1:
            chat_box.update_msg(expanded=False, state="complete")
            text = ""
            chat_box.insert_msg(Markdown(text, title=d["text"], in_expander=True, expanded=True))
        elif d["status"] == 2:
            text += d["llm_output"]
            chat_box.update_msg(text, streaming=True)
        else:
            chat_box.update_msg(text, streaming=False)

btns.download_button(
    "Export Markdown",
    "".join(chat_box.export2md()),
    file_name=f"chat_history.md",
    mime="text/markdown",
)

btns.download_button(
    "Export Json",
    chat_box.to_json(),
    file_name="chat_history.json",
    mime="text/json",
)

if btns.button("clear history"):
    chat_box.init_session(clear=True)
    st.experimental_rerun()


if show_history:
    st.write(st.session_state)

Todos

  • wrapper of official chat elements

  • input messages: (this depends on the official st.chat_input improvement by #7069)

    • TEXT
    • IMAGE
      • file upload
      • paste from clipboard(streamlit_bokeh_events)
    • VIDEO
      • file upload
    • AUDIO
      • file upload
      • audio-recorder-streamlit
  • output message types:

    • Text/Markdown/Image/Audio/Video
    • any other output types supported by streamlit
  • improve output performance

    • streaming output message
    • show message in expander
    • rich output message using streamlit-markdown
    • feedback by user
  • export & import chat history

    • export to markdown
    • export to json
    • import json
  • support output of langchain' Agent.

  • conext bound to chat

changelog

v1.1.13

  • add Json output element
  • can choose to use streamlit-markdown instead of st.markdown. currently need streamlit==1.37.1 when streaming
  • user can register custom output method with ChatBox.register_output_method. This is useful to use thirdparty components:
    from streamlit_chatbox import *
    from streamlit_markdown import st_hack_markdown
    
    ChatBox.register_output_method("st_markdown", st_hack_markdown)
    cb = ChatBox()
    cb.user_say(OutputElement("user defined output method", output_method="st_markdown", theme_color="blue", mermaid_theme_CSS=""))
    

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

streamlit_chatbox-1.1.13.post1-py3-none-any.whl (15.2 kB view details)

Uploaded Python 3

File details

Details for the file streamlit_chatbox-1.1.13.post1-py3-none-any.whl.

File metadata

File hashes

Hashes for streamlit_chatbox-1.1.13.post1-py3-none-any.whl
Algorithm Hash digest
SHA256 8fc7b64af998b7ad3437f6eb49c41ae34b699dac4cc57c5d9a9e76142b948299
MD5 74fcecd2bb4ed6408580672e3e0044b8
BLAKE2b-256 0b49c64000202715ffd570819e8db068c9e7a793d663deba867cf16706b4d823

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page