概要
- 問題: vercel/ai を使ってストリーミング応答を扱う際、reasoning summary が受け取れない
- 結論: stream を UI 側にマージする writer.merge(...) を先に呼び、あとで stream.consumeStream() を呼ぶ(または consumeStream を呼ばない)OpenAI Responses API / Azure の両方で同様に動作します
{
"dependencies": {
"@ai-sdk/azure": "2.0.32",
"@ai-sdk/openai": "2.0.32",
"ai": "^5.0.15"
}
}
背景
- 元の issue: https://github.com/vercel/ai/issues/6569
- 切り分けの要点は、ストリームの消費(consumeStream)をいつ呼ぶかによって、reasoning summary を含むメタデータが正常に届くかどうかが変わる、ということでした。検証の結果、consumeStream を writer.merge の後に移動する(あるいはコメントアウトする)と動作しました(なんでや
再現コード(失敗例)
- 以下は "reasoning summary が届かない" パターン
const stream = createUIMessageStream({
originalMessages: messages,
execute: async ({ writer }) => {
const stream = streamText({
model: myProvider.languageModel(metadata.selectedChatModel),
messages: convertToModelMessages(messages),
providerOptions: {
openai: {
previousResponseId: 'previousResponseId' in providerMeta ? providerMeta.previousResponseId : undefined,
reasoningSummary: 'detailed',
reasoningEffort: 'medium',
textVerbosity: 'medium',
} satisfies OpenAIResponsesProviderOptions,
},
});
stream.consumeStream(); // ← ここで先に consume してしまう(失敗パターン)
writer.merge(stream.toUIMessageStream({ sendReasoning: true }));
},
onFinish: async ({ responseMessage }) => {
await handleChatOnFinish(responseMessage);
},
});
return createUIMessageStreamResponse({ stream });
成功例(修正版)
- consumeStream を writer.merge の後に移動した例。これで reasoning summary を受け取れました
const stream = createUIMessageStream({
originalMessages: messages,
execute: async ({ writer }) => {
const stream = streamText({
model: myProvider.languageModel(metadata.selectedChatModel),
messages: convertToModelMessages(messages),
providerOptions: {
openai: {
previousResponseId: 'previousResponseId' in providerMeta ? providerMeta.previousResponseId : undefined,
reasoningSummary: 'detailed',
reasoningEffort: 'medium',
textVerbosity: 'medium',
} satisfies OpenAIResponsesProviderOptions,
},
});
writer.merge(stream.toUIMessageStream({ sendReasoning: true }));
stream.consumeStream(); // ← consume を後で呼ぶ(成功パターン)
},
onFinish: async ({ responseMessage }) => {
await handleChatOnFinish(responseMessage);
},
});
return createUIMessageStreamResponse({ stream });
解
- consumeStream を完全にコメントアウトしても成功しました(UI 側のマージだけで reasoning が届く)
vercel ai sdk v5 を扱っている情報がまだ結構少ないのと、かつ createUIMessageStream
を使ってるケースは更に少なくてハマりましたー