Streamlit restful app

Thanks @raphaeljolivet for the code and @janaka for reinforcing the solution.

The endpoint works perfectly. However, I am trying to extend a little bit the functionality and I have some troubles.

My app is a chat app that receives the assistant responses via API. When I’m trying to access the streamlit context to add a new message in the conversation I receive the following error logged 3 times in the console: 2024-01-16 18:27:33.407 Thread 'MainThread': missing ScriptRunContext

class HelloHandler(RequestHandler):
    def get(self):
        with st.chat_message("assistant"):
            st.markdown("Hello World")
        self.write({'message': 'hello world'})

Do you have any feedback for this?

Here’s an example I created combining Streamlit and Flask so the application can receive webhooks from stripe: 🌶️ Streamlit x Flask x Stripe Example (with user authentication)

@laurentiupiciu

I don’t quite understand what you are trying to achieve? Why are you calling Streamlit components?

@janaka

Let’s say for simplicity that each time a request is received in the REST API endpoint, some message should be displayed on the screen, and thus streamlit primitives should be called.

Which screen? The API might be called by any http client, including one running on a headless computer.

sorry, but generally that doesn’t make any sense. An API is for code to interact with. If you want to render UI/HTML then that a page. Fine/legit if that’s actually what you want and taking this approach to have more control over page requests like routing and other middleware. If that is what you want to do, to start with your handler class might need to inherit from a different class to make life easier returning HTML.

Re using ST components to render that HTML likely isn’t going to work given dropping down to this layer you are side stepping all the ST framework. Remember ST uses iframes and websockets to handle components. The framework is stateful to that extend.

if what you are try to do is have callback endpoint or similar that side effect the app and the UI renders things based on those then change the logic in the endpoint to update a datastore (in memory like session state, a database etc.) Then adjust your page UI logic to render based on the data source. If you want the UI to live update you might need some extra logic (or if you are lucky you might get it for free because of how ST works, I don’t know)

I’m not an ST expert by a long shot so might be wrong.

one more reply one this, maybe I’ll manage to make you understand my need. Let’s take the following example for what you can do using a classic framework (HTML+CSS+JS+some backend+external API):

  1. User triggers a job in the UI
  2. Backend sends to an external API the job (request payload containing a webhook_url). The job might take several minutes, so the API is designed asynchronous: returns a task_id but instead of backend checking the API from time to time, the API itself uses the webhook_url to post the job result once it’s done
  3. Backend receives the job result from the external API
  4. Backend REST API endpoint uses websockets to communicate with Frontend to display some info to the user.

That’s the “Why”

Starting from that, I need a similar behaviour in Streamlit:
0. Streamlit exposes a REST API endpoint (Tornado)

  1. User sends message
  2. Streamlit calls the external API (request payload containing webhook_url as the REST API endpoint). The job is posted by the external API
  3. Backend receives the job result from the external API
  4. And now… I am trying to mimic the web socket solution in Streamlit … by using directly the Streamlit context.

Makes sense?

Hey,
yes, I understand what you want to achieve. It’s basically what I imagined in the last paragraph, in my previous reply.

My opinion is still that the approach in your solution either will not work or will be brittle.

IMHO the better approach is to decouple via a store of some sort. Getting the webhook handler to update session state attached to UI components is one thing you can try.

All the best.