ストリーミングチャット UI
ストリーミング UI は、トークンが到着するたびに表示されるため即時感があります。パターンは共通: サーバルートが ai& にストリームを開き、SSE のボディをそのままブラウザに流し、クライアントが delta を描画します。
サーバルート (Next.js App Router)
Section titled “サーバルート (Next.js App Router)”import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://api.aiand.com/v1", apiKey: process.env.AIAND_API_KEY!,});
export async function POST(req: Request) { const { messages } = await req.json();
const stream = await client.chat.completions.create({ model: "deepseek-ai/deepseek-v4-flash", messages, stream: true, stream_options: { include_usage: true }, });
const encoder = new TextEncoder(); const body = new ReadableStream({ async start(controller) { for await (const chunk of stream) { const delta = chunk.choices[0]?.delta?.content ?? ""; if (delta) controller.enqueue(encoder.encode(delta)); } controller.close(); }, });
return new Response(body, { headers: { "Content-Type": "text/plain; charset=utf-8" }, });}クライアントコンポーネント
Section titled “クライアントコンポーネント”"use client";import { useState } from "react";
export default function Chat() { const [input, setInput] = useState(""); const [output, setOutput] = useState("");
async function send() { setOutput(""); const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages: [{ role: "user", content: input }] }), }); const reader = res.body!.getReader(); const decoder = new TextDecoder(); while (true) { const { value, done } = await reader.read(); if (done) break; setOutput((prev) => prev + decoder.decode(value)); } }
return ( <div> <input value={input} onChange={(e) => setInput(e.target.value)} /> <button onClick={send}>Send</button> <pre>{output}</pre> </div> );}コストとリクエスト ID の取得
Section titled “コストとリクエスト ID の取得”Node SDK の for await ループはコンテンツチャンクのみを iterate し、metrics trailer イベントを直接公開しません。コストとリクエスト ID を取得するには:
- リクエスト ID はレスポンスヘッダ
X-Request-IDから読む。 - コストはリクエストヘッダ
X-Aiand-Metrics: trueを付与した上で、fetch+ SSE パーサでdata: [DONE]の後のevent: metricsを読む — ペイロードにはトークン数・cost・currencyが含まれます。ストリーミングイベント を参照。
- アップストリームのレスポンスボディは OpenAI の形式と完全に一致するため、ベース URL を切り替えるだけで既存パーサがそのまま動作します。
- API キーをクライアントコードに埋め込まず、必ずサーバ経由で呼び出します。
- Vercel AI SDK の
useChat連携については Vercel AI SDK を参照。