LoginSignup
2
0

stliteとは

StreamlitはPython環境上で実行されているサーバにブラウザでアクセスする仕組みですが、このサーバを同じブラウザ上で動くようにしたものがstliteです。
Streamlit Creator1であるWhitphxさんが開発しています。

PythonをWebAssembly(WASM)に移植してブラウザ上で動くようにしたPyodideというPythonディストリビューションがあり、stliteはStreamlitをこのPyodideで動くようにすることでサーバレスを実現しているそうです2

ちょっとした可視化アプリを配布したい場合やWebページにStreamlitのプレイグラウンドを組み込みたい場合に便利そうですね。
例えばElectronと組み合わせればデスクトップで動くStreamlitアプリも実現できるそうです!

stliteにおけるHTTPリクエストの制限

stliteからのHTTPリクエストには残念ながら制限があって、いくつかのメジャーなライブラリとPyodideが提供している関数のみが使用できます。

OpenAIの公式ライブラリは内部的にHTTPXを使っているようなので、残念ながらstliteで使うことはできなさそうです…
実際に試すとAPITimeoutError: Request timed out.になりました。

Error

stliteからOpenAI APIを呼び出す

OpenAI APIはCURLでも呼び出せるインタフェースなので、少し不便ですがOpenAIの公式ライブラリを使わなくても呼び出すことができます。
今回はCURLコマンドを各プログラミング言語での書き方に変換してくれるcurlconverterを使ってプリミティブなHTTPリクエスト関数向けの書き方に変換してみました。
公式に書かれているCURLコマンドの例をcurlconverterに変換してもらうと以下のようになります。

converted.py
import os
import requests

headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ' + os.getenv('OPENAI_API_KEY', ''),
}

json_data = {
    'model': 'gpt-3.5-turbo',
    'messages': [
        {
            'role': 'system',
            'content': 'You are a helpful assistant.',
        },
        {
            'role': 'user',
            'content': 'Who won the world series in 2020?',
        },
        {
            'role': 'assistant',
            'content': 'The Los Angeles Dodgers won the World Series in 2020.',
        },
        {
            'role': 'user',
            'content': 'Where was it played?',
        },
    ],
}

response = requests.post('https://api.openai.com/v1/chat/completions', headers=headers, json=json_data)

今回はpyodide.http.pyfetch()を使うように手動でさらに書き換えてみました。

stlite_chat.py
import streamlit as st
from pyodide.http import pyfetch
import json

st.title("stlite OpenAI API example")

# APIキーを入力する
api_key = st.text_input("API Key", "")

# プロンプトを入力する
prompt = st.text_input("Prompt", "Hello!")

# OpenAI APIを呼び出す
async def call_openai_api(api_key, prompt, model="gpt-3.5-turbo"):
    # APIキーをヘッダに設定する
    headers = {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + api_key,
    }
    # リクエストボディを設定する
    body = {
        "model": model,
        "messages": [
            {
                "role": "user",
                "content": prompt,
            },
        ],
    }
    # APIを呼び出す。今回はpyfetch()を使う
    API_URL = "https://api.openai.com/v1/chat/completions"
    response = await pyfetch(API_URL, method="POST", headers=headers, body=json.dumps(body))
    response_json = await response.json()
    # レスポンスをパースする
    role = response_json["choices"][0]["message"]["role"]
    message = response_json["choices"][0]["message"]["content"]
    return (role, message)

# APIキーとプロンプトが入力されたらAPIを呼び出す
if api_key and prompt:
    role, message = await call_openai_api(api_key, prompt)
    with st.chat_message(role):
        st.write(message)

動きました!

Worked

注意事項

stliteはこれまでサーバで動いていたものがブラウザ上で動くため、secrets.tomlのようにユーザから見えない形で安全にAPIキーを書いておく方法がありません。
ソースコードにハードコーディングするのも一般的によくないので、今回は別ファイルにメモしたAPIキーを手動で都度コピペする形を想定して実装しています。

まとめ

stliteでHTTPリクエストする方法を覚えると、HTTPリクエストベースの外部APIが呼び出せるようになるため、かなり遊びの幅が広がるかと思います!
ぜひ活用してみてください。

追記:その他の方法

OpenAIの公式ライブラリを使うこともできそうというアドバイスをいただきました。今後試してみようと思います!

  1. Streamlitコミュニティへの貢献が世界トップクラスな方々のこと。https://streamlit.io/creators

  2. 「サーバレス」ではなく「静的サイトで動かせる」と表現した方が分かりやすいかも

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0