Forms disappear into chat_message

Hello,

I am trying to do my first project in streamlit with a chatbot locally, and I need that under certain conditions a form is opened within the chat to generate a “support ticket” where the user enters their name, email and description of the incident and when doing so Click on the submit button, the internal logic fires a request to an external endpoint to create the ticket on another platform, and a message appears in the form confirming the successful creation of the ticket.

However, with my current code, after pressing the “submitted = st.form_submit_button(“Submit”)” button within the “popover” function, the form disappears from the chat and the request cannot be sent to the external API.

This is the code I was using.

import requests
import streamlit as st
import time

st.session_state["ticket"] = False

st.set_page_config(
    page_title="Smart Agent",
)

# Initialize chat history
if "messages" not in st.session_state:
    st.session_state.messages = []

# Display chat messages from history on app rerun
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# Function to fetch topics from the API
def fetch_topics():
    headers = {
        "admin": "demo",
        "channel": "WEB",
    }
    response = requests.get("URL", headers=headers)
    if response.status_code == 200:
        topics_data = response.json()
        topic_names = [topic["topic_name"] for topic in topics_data]
        return topic_names
    else:
        # Handle error: display message or use default topics
        st.error("Error al obtener los tópicos")

# Accept user input
if message := st.chat_input("Escribe una pregunta"):
    st.session_state.messages.append({"role": "user", "content": message})
    with st.chat_message("user"):
        st.markdown(message)

def popover():
    with st.expander("Crear nuevo ticket"):
        with st.form("tickets",clear_on_submit=True):
            st.markdown("**Nuevo ticket**")
            name = st.text_input("Nombre")
            email = st.text_input("Correo")
            descripcion = st.text_area("Descripción")
            submitted = st.form_submit_button("Enviar")
            if submitted:
                body = {
                    "name": name,
                    "email": email,
                    "description": descripcion,
                }

                response_pop = requests.post(
                        "URL",
                        json=body,
                )

                if response_pop.status_code == 200:
                    with st.spinner("Ticket Creado exitosamente!"):
                        time.sleep(5)
                else:
                  st.error("Error al crear el ticket: " + str(response_pop.status_code))
                st.session_state["ticket"] = False 

# Función para enviar la solicitud POST
def send_message(message, topic, language):
    message= {
        "message": message,
        "language": language,
        "topic": "demo"+topic,
        "summary": "false",
        "chat_history": [],
    }
    response = requests.post(
        "URL",
        json=message,
    )

    if response.status_code == 200:
        with st.chat_message("assistant"):
            data_json = response.json()
            message_text = data_json["message"]
            nuevo_ticket = data_json["create_ticket"]
            st.write(message_text)
            st.session_state.messages.append({"role": "assistant", "content": message_text})
            if nuevo_ticket:
                st.session_state["ticket"] = True
                popover()

# Sidebar
sidebar = st.sidebar
with st.sidebar:
    st.image("logoSAv3.png")


# Fetch topics before displaying the selection box
topics_name = fetch_topics()

# Topic selection with user-friendly labels
topic = sidebar.selectbox("Seleccione un tópico", topics_name)

# Language selection
language = sidebar.selectbox("Seleccione un idioma", ["Spanish", "English"])

# Trigger on chat input
prompt= st.chat_input
if prompt:
    send_message(message, topic, language)

# Reset button for chat history
reset_button = st.sidebar.button("Reiniciar Conversación")
if reset_button:
  # Clear chat history
  st.session_state.messages = []
  st.experimental_rerun()

Hi @Franky_Rivero, and welcome to our community! :hugs:

I’ve yet to run your code, but does the issue with the form disappearing still occur if you exclude the popover() function / when the form rendering happens outside the chat component?

Thanks,
Charly

Hi @Charly_Wargnier,
Yes, this issue continues to occur and I have not been able to find a solution. thank you for your support

Ok, thank you.

Is your app deployed anywhere, and if so, do you have a GitHub repo we could have a look at?

Best,
Charly

yes, this is de link: https://webapp-tguspzbdw3nw5xavysc6qp.streamlit.app/

the code:

import requests
import streamlit as st
import time

ticket = False

st.set_page_config(
    page_title="test",
)

# Initialize chat history
if "messages" not in st.session_state:
    st.session_state["messages"] = [{"role": "assistant", "content": "Hello!"}]
    st.session_state["ticket"] = False

# Display chat messages from history on app rerun
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# Function to fetch topics from the API
def fetch_topics():
    headers = {
        "admin": "demo",
        "channel": "WEB",
    }
    response = requests.get(URL, headers=headers)
    if response.status_code == 200:
        topics_data = response.json()
        topic_names = [topic["topic_name"] for topic in topics_data]
        return topic_names
    else:
        # Handle error: display message or use default topics
        st.error("Error al obtener los tópicos")

# Accept user input
if message := st.chat_input("Escribe una pregunta"):
    st.session_state.messages.append({"role": "user", "content": message})
    with st.chat_message("user"):
        st.markdown(message)

def popover():
       with st.form("tickets",clear_on_submit=False):
            st.markdown("**Nuevo ticket**")
            name = st.text_input("Nombre")
            email = st.text_input("Correo")
            descripcion = st.text_area("Descripción")
            submitted = st.form_submit_button("Enviar")

#            if submitted:
#                body = {
#                    "name": name,
#                    "email": email,
#                    "description": descripcion,
#                }

#                response_pop = requests.post(
#                        URL,
#                        json=body,
#                )#
#
#                if response_pop.status_code == 200:
#                    with st.spinner("Ticket Creado exitosamente!"):
#                        time.sleep(5)
#                else:
#                  st.error("Error al crear el ticket: " + str(response_pop.status_code))
#                st.session_state["ticket"] = False 

# Función para enviar la solicitud POST
def send_message(message, topic, language):
    message= {
        "message": message,
        "language": language,
        "topic": "demo#"+topic,
        "summary": "false",
        "chat_history": [],
    }
    response = requests.post(
        URL,
        json=message,
    )

    if response.status_code == 200:
        with st.chat_message("assistant"):
            data_json = response.json()
            message_text = data_json["message"]
            nuevo_ticket = data_json["create_ticket"]
            st.write(message_text)
            st.session_state.messages.append({"role": "assistant", "content": message_text})
            st.session_state["ticket"] = True
            popover()
#            if ticket:
#                st.write("Crea un nuevo ticket en el siguiente formulario")
#                popover()

# Sidebar
sidebar = st.sidebar



# Fetch topics before displaying the selection box
topics_name = fetch_topics()

# Topic selection with user-friendly labels
topic = sidebar.selectbox("Seleccione un tópico", topics_name)

# Language selection
language = sidebar.selectbox("Seleccione un idioma", ["Spanish", "English"])

# Trigger on chat input
prompt= st.chat_input
if prompt:
    send_message(message, topic, language)

# Reset button for chat history
reset_button = st.sidebar.button("Reiniciar Conversación")
if reset_button:
  # Clear chat history
  st.session_state.messages = []
  st.experimental_rerun()

Thank you,
Franky

Did you ever figure this out? Facing the same issue

I am seeing the same issue as well.

What about using @st.dialog?
Here’s the demo

2024-10-23 3.19.19 p.m.

"""
- This chatbot replies with the reversed input message
- If you send the word ticket, it'll prompt you to submit a form
"""
import streamlit as st

# Initialize chat history
if "messages" not in st.session_state:
    st.session_state.messages = []

if "show_ticket_form" not in st.session_state:
    st.session_state.show_ticket_form = False


st.title("Simple Chatbot Application")

# Container for chat messages
messages = st.container()

# Chat input widget
if prompt := st.chat_input("Say something"):
    # Append user message to chat history
    st.session_state.messages.append({"role": "user", "content": prompt})

    with messages:
        for msg in st.session_state.messages:
            st.chat_message(msg["role"]).write(msg["content"])
        
        # Generate assistant response
        if prompt == "ticket":
            st.session_state.show_ticket_form = True

        assistant_response = prompt[::-1]

        # Append assistant message to chat history
        st.session_state.messages.append({"role": "assistant", "content": assistant_response})
        st.chat_message("assistant").write(assistant_response)


@st.dialog("Submit a Ticket")
def ticket_form():
    with st.form("ticket_form"):
        st.write("Please fill in the ticket details below:")
        ticket_detail = st.text_input("Ticket Detail", "")
        
        submitted = st.form_submit_button("Submit")

        if submitted:
            st.session_state.show_ticket_form = False
            st.success(f"Ticket submitted successfully! {ticket_detail}")


if st.session_state.show_ticket_form:
    ticket_form()

I m having same kind of error, have you figured it out?

Any luck…

A general piece of advice (though feel free to create a new debug topic with your specifics):

If you want to include a form within a chat message, you will most likely find it easiest to define a callback function on that form. In the original post of this thread, the form is displayed in the same rerun that processes a user submission. That means that as soon as the form is submitted, it no longer exists! It requires that “new” chat input to even show up.

  1. User submits something to chat → triggers a rerun
  2. The send_message function executes and if it gets a bad response, a form displays.
  3. The user fills out and submits the form → triggers a rerun
  4. On this second rerun, there is no new chat submission, so the send_message function doesn’t execute, the form doesn’t display, and there is no chance to ever see submitted==True.

This is effectively the same as nested buttons.

If the form used a callback and all widgets in the form had keys, the callback function could handle the form-submitted data at the beginning of that second rerun.