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

Allow st.chat_input to be inside a container (i.e. have it above other elements) #7296

Closed
2 tasks done
WTIgraysonm opened this issue Sep 8, 2023 · 18 comments · Fixed by #7896
Closed
2 tasks done
Labels
feature:st.chat_input status:likely Will probably implement but no timeline yet type:enhancement Requests for feature enhancements or new features

Comments

@WTIgraysonm
Copy link

Checklist

  • I have searched the existing issues for similar feature requests.
  • I added a descriptive title and summary to this issue.

Summary

Please allow us to be able to position the st.chat_input in different parts of the screen. Such as being part of a st.container or other layouts (columns, expanders). This will allow us to be able to position other elements below or around the chat_input and it won't be locked to the bottom of the main screen.

Why?

I want to be able to have a chat element at the top of a page, with a form underneath it as a feedback form. And currently the only way to do this is by injecting javascript code to move elements by id. And this gets harry very quickly trying to make sure this looks good on all devices (plus we have to run the javascript after the elements are created causing the initial load of the page to sometimes glitch).

Here's an example of what I'd like to be able to produce:
image
Allowing the st.messages to be a scrollable window, but the st.chat_input and st.form to be static on the bottom of the page.

How?

Don't lock st.chat_input to the bottom of the screen, but to the bottom of whatever container (or layer element) it's inside of. This way when we render st.container(s) after it those elements are actually blow them on the page.

Additional Context

Here's how I'm currently getting around this. (And I know this could be prettier):

from streamlit.components.v1 import html

# Rearange the Form to be under the chat_input and set the margines so that the response text is above the chat, and form, and there's a buffer between the chat_input and form
html(
    '''
    <script>
        window.onload = function() {
            console.log("on Window Load")
            function checkForElements() {
                console.log("start checkForElements")
                let formExists = false;
                let chatInputExists = false;
                let chatResponseExists = false;

                // Check if form_element exists
                const form_element = window.parent.document.querySelector("[data-testid='stForm']");
                if (form_element) {
                    console.log("Element 1 exists:", form_element);
                    formExists = true;
                }

                // Check if chat_input_element exists
                const chat_input_element = window.parent.document.querySelector(".stChatFloatingInputContainer");
                if (chat_input_element) {
                    console.log("Element 2 exists:", chat_input_element);
                    chatInputExists = true;
                }

                // Check if chat_response_element exists
                const chat_response_element = window.parent.document.querySelector("[data-testid='stVerticalBlock']");
                if (chat_response_element) {
                    console.log("Element 3 exists:", chat_response_element);
                    chatResponseExists = true;
                }

                // If both elements exist, perform your actions
                if (formExists && chatInputExists && chatResponseExists) {
                    console.log("All elements are now created. Move the form below the chat_input. And adjust css.");
                    // Append the child div to the parent div
                    chat_input_element.appendChild(form_element);
                    // Add margin to top of form
                    form_element.style.marginTop = '20px';
                    // Add padding to bottom of chat_response_element
                    chat_response_element.style.paddingBottom = '180px';
                }  else {
                    // Elements do not exist, wait for a moment and check again
                    setTimeout(checkForElements, 100); // Check again after 1 mili-second
                }
            }
            checkForElements();
        }
    </script>
    '''
)
@WTIgraysonm WTIgraysonm added the type:enhancement Requests for feature enhancements or new features label Sep 8, 2023
@github-actions
Copy link

github-actions bot commented Sep 8, 2023

To help Streamlit prioritize this feature, react with a 👍 (thumbs up emoji) to the initial post.

Your vote helps us identify which enhancements matter most to our users.

Visits

@jrieke
Copy link
Collaborator

jrieke commented Sep 11, 2023

Yes! We are thinking about this and will probably start working on it in the next few months. No exact plan yet of how we'll make this work but will update here when we have a good idea.

@jrieke jrieke added the status:likely Will probably implement but no timeline yet label Sep 11, 2023
@mritonia
Copy link

Seems like this should be labeled as a bug based on the code comments I saw in the code (see screenshot of the code comments from line 301 of ./streamlit/elements/widgets/chat.py). Do you have an plans to address this soon? Or, do you have a workaround for this?
Screenshot 2023-09-21 at 12 16 30 PM

@manuel-soria
Copy link

@jrieke checking in to see if you have an estimate date for this, or know whether it's not prioritized at the moment. Thank you!

@sfc-gh-jrieke
Copy link
Contributor

We're just about to start work on this. Don't have a concrete release date yet but should come out within the next ~3 months!

@manuel-soria
Copy link

thanks!

@Amonsalvek
Copy link

That's great! Just encountered this problem aswell, so it'll help a lot.
Thanks for the update and thanks @WTIgraysonm for the workaround, will definitely give it a try.

@LukasMasuch
Copy link
Collaborator

LukasMasuch commented Jan 22, 2024

We have implemented support for using chat input inside containers. With the 1.31 release, whenever chat_input is nested in any other layout containers (columns, container, expander, ..) it will show the chat input inline (behaves like any other widget). E.g.:

with st.container():
    st.chat_input("Whats up?")

Or:

with st.expander("Chat inside expander"):
    st.chat_input("Whats up?")

This will display the chat_input inline instead of pinned to the bottom.

@jrieke
Copy link
Collaborator

jrieke commented Jan 22, 2024

To add on to that: we'll probably also introduce a parameter on st.chat_input to manually switch between showing it at the bottom and inline. So you can even show it inline in the main area of the app, without having to use an additional container. But we first want to wait until we figure out a slightly related feature, so we're not using conflicting naming schemes.

@bmeekers
Copy link

Just curious if there's a planned release date for the 1.31 update? I'm currently developing a chatbot similar to OP and the ability to put chat_input into a container would greatly clean up the UI. Thanks!

@LukasMasuch
Copy link
Collaborator

@bmeekers The release date is sometime next week.

@xixiaoguai727
Copy link

1.31

Really need the 1.31❤😭❤

@NahuelCostaCortez
Copy link

Hi! Just trying the 1.31 release and although chat_input works inside containers/tabs etc it displays the messages below the chat_input:

Captura de pantalla 2024-02-08 214744

It´d be nice if it behaved like in a regular page.

@LukasMasuch
Copy link
Collaborator

LukasMasuch commented Feb 8, 2024

@NahuelCostaCortez with the inline mode, it behaves like any other regular widget, e.g. st.text_input. You need to use placeholder containers to place elements above the chat_input. here is an example:

import streamlit as st

with st.container():
    # Create a placeholder container that holds all the messages:
    messages = st.container(height=300)
    if prompt := st.chat_input("Say something"):
        messages.chat_message("user").write(prompt)
        messages.chat_message("assistant").write(f"Echo: {prompt}")

@NahuelCostaCortez
Copy link

Thank you for the prompt reply. This works. However, when adding user-assistant interface the messages appear below the icon:

image

This is the code:

import streamlit as st


tab1, tab2 = st.tabs(["Tab1", "Tab2"])

with tab1:
    with st.container():
        messages = st.container(height=300)
        prompt = st.chat_input("Say something")
        if prompt:
            with messages.chat_message("assistant"):
                messages.write(f"User has sent the following prompt: {prompt}")

with tab2:
    st.write("Tab2")

Is there any way to solve this?

Thank you in advance.

@LukasMasuch
Copy link
Collaborator

Instead of messages.write you need to use st.write in this case. Otherwise it's adding the text the messages container, I instead of the chat message one.

@NahuelCostaCortez
Copy link

Yeah, you´re right. Thanks for your help!

@sfc-gh-jesmith
Copy link

sfc-gh-jesmith commented Feb 9, 2024

Hey all, just wanted to update 1.31.0 is officially out! Check out the docs to learn how you can put chat features in containers, the side bar, tabs, expanders, the bottom of the page and more.

Thanks again for your input and contributions, helping this feature become part of Streamlit.🥳 

Looking forward to seeing all the new ways you’ll feature st.chat_message and st.chat_input in your apps. Be sure to share on the forum so the community can see and learn from you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature:st.chat_input status:likely Will probably implement but no timeline yet type:enhancement Requests for feature enhancements or new features
Projects
None yet
Development

Successfully merging a pull request may close this issue.