🍳
【Next.js】 Rewritesを使うとVercel AI SDK でストリーミングができない
問題
ChatGPTのようにリアルタイムに出力をクライアントへ送信してぬるぬるさせたい。
ところが、なぜかストリーミングせず、生成が終わったタイミングで出力がすべて一度に送られてくるので、調査開始。
技術構成
フロントエンドはNext.jsでバックエンドはHonoで実装している(伏線)。
調査
フロントエンド側が問題なのか、バックエンド側が問題なのかをはっきりさせるために、それぞれで検証した。
curlコマンドでAPIを直接叩く
curlコマンドを使って、Honoサーバー側から返されるレスポンスを見てみた。
curl -i -X POST http://localhost:8080/chat
出力が順次出てきたので、期待される結果となった。バックエンド側は問題なく動作しているようである。
クライアント側
クライアント側でai-sdk
の提供する機能を使用せず、fetchからAPIを呼び出すようにしてみた。
こちらはやはりすべて一度に出力されてしまう。
原因
バックエンド側もクライアント側も問題ないとなると、Next.jsのプロキシ部分がどうも怪しそうである。
調べていると、それらしきissueを発見。
/api
宛のリクエストは、Next.jsのRewrite機能を用いて、Honoが実行しているlocalhost:8080
へフォワードするように設定していたが、これが原因だった。
Next.jsのRewriteでは、text/event-stream
ヘッダの付くストリーミングレスポンスに対応しておらず、すべてのレスポンスが終わるまでバッファリングしてから、一度に送ってしまうようである。
解決策
Next.jsのRewriteは使えないので、/api
内に自分でプロキシを実装して解決。
api/chat/route.ts
export const runtime = "edge";
export async function POST(req: Request) {
// localhost:8080へfetchし、そのレスポンスのbodyをそのまま返す
const res = await fetch("http://localhost:8080/chat", {
method: "POST",
body: await req.text(),
headers: { "Content-Type": "application/json" },
});
return new Response(res.body, {
headers: {
"Content-Type": "text/event-stream",
},
});
}
Discussion