サイトアイコン アマチュア無線局JS2IIU

【Streamlit】st.write_stream()でデータストリームを表示する

こんにちは、JS2IIUです。
Streamlitで動的なデータを扱う際に便利なst.write_stream()について解説します。この関数は、ジェネレータ、イテラブル、またはストリームのようなシーケンスをアプリにストリーミング表示することができます。リアルタイムのデータ更新を表示するのに最適です。今回もよろしくお願いします。

シグネチャ

Python
st.write_stream(data, *, key=None)

サンプルプログラム

1. 無限にカウントアップする数字を表示

Python
import streamlit as st
import time

def counter():
  i = 0
  while True:
    yield i
    i += 1
    time.sleep(1)

st.write_stream(counter())

解説:

  1. counter()関数は無限に数字を生成するジェネレータです。yieldキーワードで数字を1つずつ返します。
  2. time.sleep(1)で1秒ごとに数字を生成します。
  3. st.write_stream(counter())でジェネレータをStreamlitアプリにストリーミング表示します。

2. ファイルから1行ずつ読み込んで表示

Python
import urllib.request

import streamlit as st
import time

def read_file(file_path):
    with open(file_path, 'r') as f:
        for line in f:
            for word in line.split():
                yield word + " "
                time.sleep(0.05)  # 表示間隔を調整

def read_web_file(url):
    with urllib.request.urlopen(url=url) as f:
        lines = f.read().decode('cp932').split('\n')
        for line in lines:
            for word in line.split():
                yield word + " "
                time.sleep(0.05)  # 表示間隔を調整

# タイトル
st.title("テキストファイルのストリーミング表示")

# ファイルの内容をストリーミング表示
st.write_stream(read_web_file('https://raw.githubusercontent.com/JS2IIU-MH/Streamlit_sample_data/refs/heads/main/data/lipsum.txt'))

解説:

  1. read_file()関数はファイルから1行ずつ読み込むジェネレータです。
  2. read_web_file()関数はWEB上のファイルを読みこみ、1行ずつ読みこむジェネレータです。
  3. st.write_stream(read_file('data.txt'))data.txtファイルの内容を1行ずつストリーミング表示します。
  4. WEB上のテキストファイルはこちらを使いました。
Streamlit_sample_data/data/lipsum.txt at main · JS2IIU-MH/Streamlit_sample_data
Sample data for blog post about Streamlit. Contribute to JS2IIU-MH/Streamlit_sample_data development by creating an account on GitHub.

3. 外部APIからデータを取得して表示

Python
import streamlit as st
import requests
import time

def fetch_data(url):
  while True:
    response = requests.get(url)
    data = response.json()
    yield data
    time.sleep(5)

st.write_stream(fetch_data('https://api.example.com/data'))

解説:

  1. fetch_data()関数は外部APIからデータを定期的に取得するジェネレータです。
  2. requests.get(url)でAPIにリクエストを送信し、response.json()でJSON形式のレスポンスをパースします。
  3. time.sleep(5)で5秒ごとにAPIからデータを取得します。
  4. st.write_stream(fetch_data('https://api.example.com/data'))でAPIから取得したデータをストリーミング表示します。

4. GPT-o4のチャットを表示する

Python
import streamlit as st
from openai import OpenAI

# OpenAIクライアントを生成
client = OpenAI(api_key="YOUR API KEY HERE")  # ここにあなたのAPIキーを入力してください

# セッションステートを初期化
if "messages" not in st.session_state:
    st.session_state.messages = []

# メッセージ履歴を表示
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# ユーザーからの入力を受け取る
if prompt := st.chat_input("What is up?"):
    # ユーザーのメッセージを履歴に追加
    st.session_state.messages.append({"role": "user", "content": prompt})
    # チャットメッセージとして表示
    with st.chat_message("user"):
        st.markdown(prompt)

    # OpenAI APIを使用して応答を生成
    def generate_response():
        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {"role": "system", "content": "You are a helpful assistant."},
                *st.session_state.messages,
            ]
        )
        response_content = response.choices[0].message.content

        # AIの応答を逐次返すジェネレーター
        for chunk in response_content.split():
            yield chunk + " "  # 各単語にスペースを追加して出力
            st.time.sleep(0.1)  # 擬似ストリーミング効果のための遅延

    # AI応答をストリーミング表示
    with st.chat_message("assistant"):
        st.write_stream(generate_response)

    # 最終的な応答内容をセッションステートに追加
    full_response = "".join(generate_response())
    st.session_state.messages.append({"role": "assistant", "content": full_response})

コード解説

ライブラリのインポート

Python
import streamlit as st
from openai import OpenAI

OpenAIクライアントの生成

Python
client = OpenAI(api_key="YOUR API KEY HERE")  # ここにあなたのAPIキーを入力してください

セッションステートの初期化

Python
if "messages" not in st.session_state:
    st.session_state.messages = []

メッセージ履歴の表示

Python
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

ユーザーの入力を受け取る

Python
if prompt := st.chat_input("What is up?"):

ユーザーのメッセージを追加

Python
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
    st.markdown(prompt)

OpenAI APIによる応答生成

Python
def generate_response():
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            *st.session_state.messages,
        ]
    )
    response_content = response.choices[0].message.content

    # AIの応答を逐次返すジェネレーター
    for chunk in response_content.split():
        yield chunk + " "  # 各単語にスペースを追加して出力
        st.time.sleep(0.1)  # 擬似ストリーミング効果のための遅延

AIの応答をストリーミング表示

Python
with st.chat_message("assistant"):
    st.write_stream(generate_response)

応答を履歴に追加

Python
full_response = "".join(generate_response())
st.session_state.messages.append({"role": "assistant", "content": full_response})

動作の全体概要

  1. ユーザーがメッセージを入力すると、入力内容が履歴に保存され、画面に表示されます。
  2. OpenAI APIを使用してAI応答を取得。
  3. AIの応答をst.write_streamで逐次表示。
  4. 応答の履歴を保存して、チャットの継続性を維持。

このプログラムは、ユーザー入力とAIの応答をリアルタイムにやり取りするシンプルなチャットアプリケーションの実装例です。st.write_streamを利用して、より直感的でインタラクティブなUIを実現しています。

参考になるWEBサイト

注記: st.write_stream()は、Streamlitのバージョン1.3.0以降で利用可能です。もし古いバージョンを使用している場合は、アップグレードしてください。

最後に、書籍のPRです。

最新のOpenAIのチャットAPIの使い方もしっかりと解説されている良書です。2024年11月初版発行、「LangChainとLangGraphによるRAG・AIエージェント[実践]入門」西見、吉田、大嶋著。

最後まで読んでいただきありがとうございます。73

モバイルバージョンを終了