目次
完成イメージ
バックエンド(FastAPI)の実装
フロントエンド(Next.js)の実装
成果物
完成イメージ
ChatGPTのようにレスポンスを1文字ずつ表示する仕組みを作りたい。
バックエンドの実装
- app/routers/chatai.py ファイルを作成し、streamレスポンスを返すプログラムを書く。
以下のファイルを作っただけではもちろん動かないので、main.pyでインスタンスを書いてあげよう。
import openai
from fastapi import APIRouter, Request
from fastapi.responses import StreamingResponse
router = APIRouter()
def chatgpt_streamer(msg: str):
openai.api_key = "ここにはあなたのopenaiのapiキーを書いてください"
result = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": msg}],
stream=True,
)
for chunk in result:
if chunk is not None:
content = chunk["choices"][0]["delta"].get("content")
if content is not None:
yield content
@router.post("/chatgpt")
async def chatgpt_stream(request: Request):
# request.bodyはjson形式でリクエストが投げられることを想定
# 例) {"message": "100文字の文章を書いて"}
msg = await request.body()
return StreamingResponse(
chatgpt_streamer(msg=msg.decode()), media_type="text/event-stream"
)
フロントエンドの実装
サンプルではconsole.logで結果を表示しているが、本来はuseStateを使って文字列を画面に表示することになる。
const response = await fetch(`/chatgpt`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: "100文字の文章を書いて" }),
});
const data = response.body;
const reader = data.getReader();
const decoder = new TextDecoder();
let done = false;
let result = "";
while (!done) {
const { value, done: doneReading } = await reader.read();
done = doneReading;
const chunkValue = decoder.decode(value);
result += chunkValue;
console.log(result + (done ? "" : "▊"));
}
成果物
ロードバランサ(BIGIPなど)を使っているとstreamができないように設定されているサービスもあるので、その場合は設定を変更してあげよう