レンタルサーバでWebアプリ作り:ChatGPTでテケテケ表示 Part2
ロリポップレンタルサーバではできることが限られていたので、ホスティングサービスを使ってWebアプリを公開するまでの道のりを記録します。
データ分析を仕事としているのでパソコンやITのことは多少は詳しいですが、インフラまわりは全くの素人です。そんな人が見て参考になる情報をまとめていきたいと思います。
背景
前回、SocketIOを使ってGPTの回答をテケテケ表示させました。
前回記事:https://note.com/cryptoscore/n/n83ef1cef9232
実はもっとシンプルなやり方(Event Source) があるので、それでも実装してみようと思います。
ゴール=EventSourceでテケテケさせる
EventoSourceというものを使うと、SocketIOよりもシンプルにストリーム表示できます。ただ、簡易版のようで表示はカクカクします。いっぺんにドバっと表示され、雑な感じです。
フォルダ構成
app.pyとindex.htmlだけの修正になります。それ以外は前回のものと全く同じです。内容は前回の記事をご覧ください。
test_digitalocean/
├ requirements.txt
├ .env
├ Pipfile ※pipが自動作成
├ Pipfile.lock ※pipが自動作成
├ Procfile
├ gunicorn_config.py
├ app.py
├ templates/
│ ┗index.html
app.py
EventSourceの弱点は、HTML側からPython側に値がPOSTできないことだそうです。GPT先生もそこがこんがらがるようで、なかなか良い回答が得られませんでした。
というわけで、いったんHTML側からPOSTしたものをPython側のpost_question()で質問を受け取り、その後で on_start_gptquestion() が回ります。また、HTMLへ情報を流すのに「yield」という関数を使います。それ以外はほぼ前回のと同じです。
from flask import Flask, render_template, request, jsonify, Response
from openai import OpenAI
import threading
from collections import deque
from dotenv import load_dotenv
import time
# 環境変数を読み込む
load_dotenv()
client = OpenAI()
token_queue = deque() #GPTの回答がどしどし入る箱
question = ''
app = Flask(__name__)
def thread_gptquestion(text):
global token_queue
token_queue = deque() #初期化
stream = client.chat.completions.create(
# model="gpt-3.5-turbo",
model="gpt-4-1106-preview",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": text},
],
stream=True,
)
#token_queueにどしどしためていく(非同期で)
for chunk in stream:
if chunk.choices[0].delta.content is not None:
token_queue.append(chunk.choices[0].delta.content)
token_queue.append('[[end]]') #終了サイン
def start_gptquestion(text):
global token_queue, question
thread = threading.Thread(target=thread_gptquestion, args=(text,))
thread.start()
msg = ''
while True :
if token_queue :
token = token_queue.popleft()
token = token.replace('\n', '<BR>')
if token == '[[end]]' :
print('-------------end----------------')
break
msg += token
yield f"data:{msg}\n\n"
else :
time.sleep(0.1) #キューに何もなくなったところで0.1秒待つ
time.sleep(0.1) #最後に0.1秒待つ(これがないとブラウザ側で最後の表示ができない)
yield "data:[[end]]\n\n"
time.sleep(0.1)
@app.route('/')
def index():
return render_template('index_eventsource.html')
@app.route('/post_question', methods=['POST'])
def post_question():
global question
question = str(request.data)
return '', 204 # HTTPステータスコード204は「No Content」を意味します
@app.route('/on_start_gptquestion')
def on_start_gptquestion():
global question
return Response(start_gptquestion(question), mimetype='text/event-stream')
if __name__ == '__main__':
app.run(debug=True)
index.html
startButton_es()関数が肝です。「204レスポンスが返ってきたら on_start_gptquestion を動かす」というような待ち定義 xhr を最初に作っておいて(onreadystatechange)、 post_question につないで質問テキストを 投げ込みます。python側で post_question が204を返すので、それを待って on_start_gptquestion が動き出します。
最初に `on_start_gptquestion` の記述があって、後のほうに `post_question` があるのでなんかこんがらがる。。
<!DOCTYPE html>
<html>
<head>
<title>Flask App</title>
<script>
function startButton_es() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 204) {
var source = new EventSource('/on_start_gptquestion');
source.onmessage = function(event) {
if(event.data === '[[end]]'){
source.close();
} else {
var logElement = document.getElementById('gptanswer');
logElement.textContent = event.data;
}
};
}
};
xhr.open("POST", "/post_question", true);
var text = document.getElementById('textInput').value;
xhr.send(text);
}
</script>
</head>
<body>
<input type="text" id="textInput" placeholder="Enter question to GPT">
<button onclick="startButton_es()">Submit</button>
<div id="gptanswer"></div>
</body>
</html>
これらをDigitalOceanに上げて、冒頭の画像のようなカクカクテケテケが表示されたら、成功です。
最後まで見ていただきありがとうございました!
SocketIOだと通信量が増えてしまうらしく、その改善策としてEventSourceでやってみましたが、雑感がちょっと過ぎるか。。私は何かしらの問題が起きるまでSocketIOでやろうと思いました。
DigitalOceanのアカウント登録
(200ドル分チケット付き)
以下は紹介リンクですが、ここから手続きを進めてもらえると200ドル分の無料チケット(有効期間2ヶ月)がもらえるようですので、ぜひご活用ください。
※ 2023/12/29時点
紹介リンク : https://m.do.co/c/a8b31ed34b75
サポート問い合わせ先
DigitalOceanのサポート問い合わせリンクがなかなか見つからないので、リンクを載せておきます。
https://cloudsupport.digitalocean.com/s/
場所は、トップページの右下にある「Ask a question」に行き、そのページの一番下(欄外っぽいところ)にひっそりと「Support」というリンクがあります(Contact内)。そのページの一番最後に「Contact Support」ボタンがあります。
この記事が気に入ったらサポートをしてみませんか?