Vercel AI SDK で StreamingTextResponse・OpenAIStream が動かない時の原因と対処法:新 API への移行ガイド【ai パッケージ】
公開日:2026-07-10/カテゴリ:AI・Next.js・TypeScript/AnchorUp 技術ブログ
まず確認:Vercel AI SDK で動かない時の症状と対策(結論)
Vercel AI SDK(ai パッケージ)を最新版にアップグレードすると、旧 API を使っているコードが一斉に壊れる。主な症状と対策を先にまとめる。
| 症状 | 原因 | 対策 |
|---|---|---|
'StreamingTextResponse' is not exported from 'ai' |
StreamingTextResponse が廃止 |
streamText().toDataStreamResponse() に変更 |
'OpenAIStream' is not exported from 'ai' |
ストリームヘルパーが廃止 | @ai-sdk/openai + streamText() を使う |
useChat でテキストが表示されない |
Data Stream プロトコル不一致 | サーバー側を toDataStreamResponse() に統一 |
Cannot find module '@ai-sdk/openai' |
プロバイダーパッケージ未インストール | npm install @ai-sdk/openai を実行 |
| Tool Call の型エラー・実行されない | tool() ヘルパーへの API 変更 |
tool() + Zod スキーマで再定義する |
generateObject が型エラー |
Structured Output の schema 形式変更 | schema: z.object(...) で渡す |
なぜ旧 API が廃止されたのか
Vercel AI SDK は当初、OpenAI の Node.js SDK から ReadableStream を受け取り StreamingTextResponse でラップして返すアーキテクチャだった。
しかしこの設計には問題があった。
- プロバイダーごとに別々のヘルパーが必要(
OpenAIStream、AnthropicStreamなど) - ストリームの形式がバラバラでクライアント側が扱いにくい
- Tool Call / Structured Output への対応が複雑になっていった
現在の AI SDK は streamText()・generateText()・generateObject() などのコア関数と、プロバイダーパッケージ(@ai-sdk/openai 等)を組み合わせる設計に一本化された。旧ヘルパーはこの過程で廃止された。
エラー1: StreamingTextResponse is not exported from 'ai'
最も多いエラー。旧 API でよく見た StreamingTextResponse が削除された。
SyntaxError: The requested module 'ai' does not provide an export named 'StreamingTextResponse'
旧コード(動かなくなった)
// ❌ app/api/chat/route.ts(旧スタイル)
import OpenAI from 'openai'
import { OpenAIStream, StreamingTextResponse } from 'ai' // ← エラー
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY })
export async function POST(req: Request) {
const { messages } = await req.json()
const response = await openai.chat.completions.create({
model: 'gpt-4o',
stream: true,
messages,
})
const stream = OpenAIStream(response) // ← エラー
return new StreamingTextResponse(stream) // ← エラー
}
新コード(正しい書き方)
# まず @ai-sdk/openai をインストールする
npm install @ai-sdk/openai
// ✅ app/api/chat/route.ts(新スタイル)
import { openai } from '@ai-sdk/openai' // ← 専用プロバイダーパッケージ
import { streamText } from 'ai'
export async function POST(req: Request) {
const { messages } = await req.json()
const result = streamText({
model: openai('gpt-4o'),
messages,
})
return result.toDataStreamResponse() // ← StreamingTextResponse の代わり
}
クライアント側の useChat はほぼそのまま使える。
// ✅ components/chat.tsx(変更なし)
'use client'
import { useChat } from 'ai/react'
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/chat',
})
return (
<div>
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role === 'user' ? 'あなた' : 'AI'}:</strong>
{m.content}
</div>
))}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
placeholder="メッセージを入力..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>送信</button>
</form>
</div>
)
}
エラー2: useChat でストリームが途切れる・テキストが表示されない
サーバー側を新 API に移行したのに、クライアント側でテキストが正しく表示されない場合がある。
原因: toTextStreamResponse() と toDataStreamResponse() の違い。
| メソッド | 用途 | useChat との組み合わせ |
|---|---|---|
toDataStreamResponse() |
Tool Call・メタ情報を含む Data Stream | useChat と組み合わせる場合はこちら |
toTextStreamResponse() |
テキストのみの plain text stream | カスタム実装やシンプルなストリームに使う |
useChat は Data Stream プロトコルを期待しているため、toDataStreamResponse() を使う必要がある。
// ❌ useChat と組み合わせると動かないことがある
return result.toTextStreamResponse()
// ✅ useChat には必ずこちら
return result.toDataStreamResponse()
また useChat のデフォルト API エンドポイントは /api/chat だが、変更する場合は明示的に指定する。
const { messages, ... } = useChat({
api: '/api/my-chat-endpoint', // デフォルトは /api/chat
})
エラー3: プロバイダーパッケージが見つからない
Error: Cannot find module '@ai-sdk/openai'
Vercel AI SDK では、モデルプロバイダーが別パッケージになっている。使うプロバイダーに応じて個別にインストールが必要。
npm install @ai-sdk/openai # OpenAI / Azure OpenAI
npm install @ai-sdk/anthropic # Anthropic Claude
npm install @ai-sdk/google # Google Gemini
npm install @ai-sdk/mistral # Mistral AI
npm install @ai-sdk/cohere # Cohere
npm install @ai-sdk/amazon-bedrock # AWS Bedrock
各プロバイダーの基本的な使い方。
// OpenAI
import { openai } from '@ai-sdk/openai'
const model = openai('gpt-4o')
// Anthropic
import { anthropic } from '@ai-sdk/anthropic'
const model = anthropic('claude-sonnet-4-5')
// Google Gemini
import { google } from '@ai-sdk/google'
const model = google('gemini-2.0-flash')
環境変数はプロバイダーごとに異なるが、多くはデフォルト名をそのまま読んでくれる。
# .env.local
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_GENERATIVE_AI_API_KEY=AIza...
エラー4: Tool Call が動かない・型エラーになる
旧 API では OpenAI SDK の functions / tools 形式をそのまま渡していたが、新 API では tool() ヘルパーと Zod を使う。
旧コード(動かなくなった)
// ❌ 旧スタイル: OpenAI SDK の tools 形式を直接渡していた
const response = await openai.chat.completions.create({
model: 'gpt-4o',
messages,
tools: [{
type: 'function',
function: {
name: 'getWeather',
description: '現在の天気を取得する',
parameters: {
type: 'object',
properties: {
location: { type: 'string', description: '都市名' },
},
required: ['location'],
},
},
}],
})
新コード(正しい書き方)
// ✅ 新スタイル: tool() ヘルパー + Zod スキーマ
import { openai } from '@ai-sdk/openai'
import { streamText, tool } from 'ai'
import { z } from 'zod'
export async function POST(req: Request) {
const { messages } = await req.json()
const result = streamText({
model: openai('gpt-4o'),
messages,
tools: {
getWeather: tool({
description: '現在の天気を取得する',
parameters: z.object({
location: z.string().describe('都市名(例: 東京)'),
}),
execute: async ({ location }) => {
// 実際の天気 API 呼び出し(例)
return { location, temperature: 22, condition: '晴れ' }
},
}),
},
})
return result.toDataStreamResponse()
}
Tool Call の結果をクライアント側で表示する場合は useChat の toolInvocations を使う。
// ✅ Tool Call の結果をクライアントで表示
'use client'
import { useChat } from 'ai/react'
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit } = useChat({
api: '/api/chat',
})
return (
<div>
{messages.map((m) => (
<div key={m.id}>
<strong>{m.role}:</strong>
{m.content}
{/* Tool Call の結果を表示 */}
{m.toolInvocations?.map((tool) => (
<div key={tool.toolCallId} style={{ background: '#f0f0f0', margin: '4px 0' }}>
<code>{tool.toolName}</code>:{' '}
{'result' in tool ? JSON.stringify(tool.result) : '実行中...'}
</div>
))}
</div>
))}
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit">送信</button>
</form>
</div>
)
}
エラー5: generateObject で Structured Output が動かない
generateObject を使うと JSON 形式で構造化された出力を得られる。型推論も効く。
// ✅ generateObject で構造化出力
import { openai } from '@ai-sdk/openai'
import { generateObject } from 'ai'
import { z } from 'zod'
const schema = z.object({
title: z.string().describe('記事タイトル'),
summary: z.string().describe('3文以内の要約'),
tags: z.array(z.string()).describe('関連タグ(最大5個)'),
})
const { object } = await generateObject({
model: openai('gpt-4o'),
schema,
prompt: '以下の文章を要約してください: ...',
})
// object は schema の型として推論される
console.log(object.title) // string
console.log(object.tags) // string[]
よくあるミス: schema に Zod オブジェクト以外を渡すとエラーになる。
// ❌ NG: プリミティブ型は直接使えない
const { object } = await generateObject({
model: openai('gpt-4o'),
schema: z.string(), // ← z.object() にする必要がある
prompt: '...',
})
// ✅ OK: z.object() でラップする
const { object } = await generateObject({
model: openai('gpt-4o'),
schema: z.object({ result: z.string() }),
prompt: '...',
})
generateText と streamText の使い分け
| 関数 | 用途 | レスポンス |
|---|---|---|
generateText() |
一括生成(チャット以外のバックエンド処理など) | { text, usage, finishReason } |
streamText() |
ストリーミング(チャット UI など) | toDataStreamResponse() で返す |
generateObject() |
構造化 JSON 出力 | { object } |
streamObject() |
構造化 JSON のストリーミング | toTextStreamResponse() など |
// generateText: 処理完了まで待ってからレスポンス
const { text } = await generateText({
model: openai('gpt-4o'),
prompt: '東京の観光スポットを3つ教えてください',
})
console.log(text)
// streamText: ストリーミングでレスポンス
const result = streamText({
model: openai('gpt-4o'),
prompt: '東京の観光スポットを3つ教えてください',
})
return result.toDataStreamResponse()
移行チェックリスト
旧 API から新 API への移行で確認すべき項目をまとめる。
- [ ]
StreamingTextResponseをresult.toDataStreamResponse()に変更した - [ ]
OpenAIStream・AnthropicStream等の旧ヘルパーを削除した - [ ]
@ai-sdk/openai(または使用中のプロバイダー)をインストールした - [ ]
import OpenAI from 'openai'をimport { openai } from '@ai-sdk/openai'に変更した - [ ] Tool Call を
tool()+ Zod スキーマ形式に書き直した - [ ]
useChatのエンドポイントがtoDataStreamResponse()を返しているか確認した - [ ] 環境変数名がプロバイダーのデフォルト(
OPENAI_API_KEY等)と一致しているか確認した
まとめ
Vercel AI SDK で旧 API が動かなくなる主な原因は、ストリームヘルパーの廃止とプロバイダーパッケージの分離だ。
StreamingTextResponse/OpenAIStreamが廃止 →streamText().toDataStreamResponse()に移行- プロバイダーが別パッケージ化 →
npm install @ai-sdk/openaiでインストール - Tool Call の形式が変わった →
tool()ヘルパー + Zod スキーマで定義 useChatと組み合わせる場合 → サーバー側はtoDataStreamResponse()を使う
新 API はプロバイダーの切り替えがモデル名を変えるだけで済むなど、実際に使い始めると旧 API より書きやすい。移行コストはかかるが、一度やり切ると管理がシンプルになる。