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 でラップして返すアーキテクチャだった。

しかしこの設計には問題があった。

現在の 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 への移行で確認すべき項目をまとめる。


まとめ

Vercel AI SDK で旧 API が動かなくなる主な原因は、ストリームヘルパーの廃止とプロバイダーパッケージの分離だ。

  1. StreamingTextResponse / OpenAIStream が廃止 → streamText().toDataStreamResponse() に移行
  2. プロバイダーが別パッケージ化 → npm install @ai-sdk/openai でインストール
  3. Tool Call の形式が変わった → tool() ヘルパー + Zod スキーマで定義
  4. useChat と組み合わせる場合 → サーバー側は toDataStreamResponse() を使う

新 API はプロバイダーの切り替えがモデル名を変えるだけで済むなど、実際に使い始めると旧 API より書きやすい。移行コストはかかるが、一度やり切ると管理がシンプルになる。

技術ブログの記事一覧へ