4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ChatGPTのstreamをつかったアプリを作るポイント (FastAPI & Svelte)

Posted at

はじめに

OpenAIのChatGPT APIをつかったチャットとかを作っていると、公式みたいにチャットの文字をストリーミングつかって徐々に出したいっていうことあると思います。短い文章ならまだいいとして、特に長い文章だと、応答が遅くなるだけでストレスですものね。

というわけで、ChatCompletionのstreamオプションをtrueにした際の実装の注意点を書き残したいと思ます。ちなみにサーバサイドはFastAPIで、フロントエンドはSvelteです。

サーバサイド(FastAPI)

ChatGPTのstreamオプションをtrueにする

ChatGPTをコールする際に、stream=Trueを指定します。engineのところ、AzureOpenAIの場合のコードになっています。素のOpenAIの場合はmodel="gpt-3.5-turbo"ですね。

    response = openai.ChatCompletion.create(
        engine=settings.AOAI_MODEL, # engine = "deployment_name".
        messages=messages,
        temperature=temperature,
        stream = True,
    )

ChatGPTのresponseをStreamingResonseで返す

stream=Trueにするとresponseのなかがchunk構成になっています。公式の内部構造を参考に、ここから応答の文字列をストリームで返します。以下みたいな感じですね。これでサーバからは応答文字列がストリームで返ってきます。

from fastapi.responses import StreamingResponse

def chatgpt_stream(response):
    for chunk in response:
        if chunk is not None:
            content = chunk["choices"][0]["delta"].get("content")
            if content is not None:
                yield content

@router.post("/test")
def message(messages: List[Message]):

    response = call_chatgpt([m.dict() for m in messages])

    return StreamingResponse(
        chatgpt_stream(response),media_type="text/event-stream"
    )

フロントエンド(Svelte)

フロントエンドでストリームを受け取る

Svelte側ではストリームで受け取った内容を、取得でき次第更新していきます。今回作りが配列を表示している関係で配列=配列とかやってます(けど、もう少しうまくできそうではありますが)
ざっくり以下みたいなコードです。TextDecoderをつかって、文字をとって、表示文字を更新する感じです。

      const response = await fetch("/simple/stream", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify(send_chats)
      });

      const latest_chat = {"role":"assistant","content":""}
      chats = [...chats, latest_chat]

      if(response.body){

        const decoder = new TextDecoder();
        const reader = response.body.getReader();

        while(true){
          const {done, value} = await reader.read();
          if(done){
            break;
          }
          const text = decoder.decode(value);
          latest_chat.content += text;
          chats = chats
        }

      }

おわりに

FastAPIとSvelteを使っている場合に、ChatGPTのレスポンスをどうストリーム化するかのポイントを紹介しました。ただ、個人的には別にぐるぐるローダーが回っていてもそんなに気にならないので、ここはもうUIどうするか次第ですけどね。
あと、Azure OpenAIの方はフィルターの設定次第では固まりの粒度が大きくなるとかあるみたいなので、これまた検証しないといけないです。

ひとまず文字がにょろにょろ出るようにはなりましたということで、以上です。

4
4
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
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?