目次
完成イメージ
バックエンド(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ができないように設定されているサービスもあるので、その場合は設定を変更してあげよう
