2026-04-15
DeepSeek V4 в Node.js: TypeScript, стриминг, tool use
DeepSeek V4 совместим с OpenAI, поэтому в Node.js достаточно пакета openai из npm. Гид проходит в TypeScript: установка, первый вызов, стриминг через Next.js route handler, tool use с валидацией через Zod и контроль стоимости tokens.
1. Установка и окружение
Официальный openai принимает baseURL — указываете DeepSeek. Ключ только в .env, не коммитьте его и не экспонируйте в браузер.
npm install openai zod
echo "DEEPSEEK_API_KEY=sk-..." >> .env.local2. Первый вызов в TypeScript
Клиент создаётся один раз и переиспользуется. baseURL на эндпоинт DeepSeek, model — deepseek-chat.
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: "https://api.deepseek.com/v1",
});
export async function ask(question: string): Promise<string> {
const res = await client.chat.completions.create({
model: "deepseek-chat",
messages: [
{ role: "system", content: "You are a concise technical assistant." },
{ role: "user", content: question },
],
temperature: 0.2,
});
return res.choices[0].message.content ?? "";
}3. Стриминг в Next.js route handler
Для чат-UI с tokens в реальном времени проще всего обернуть async iterable от SDK в ReadableStream и вернуть его как Response.
// app/api/chat/route.ts
import OpenAI from "openai";
export const runtime = "nodejs";
const client = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: "https://api.deepseek.com/v1",
});
export async function POST(req: Request) {
const { messages } = await req.json();
const stream = await client.chat.completions.create({
model: "deepseek-chat",
messages,
stream: 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" },
});
}4. Валидация аргументов tool use через Zod
Tool use в V4 достаточно стабилен для боевых агентов, но модель иногда галлюцинирует аргументы. Всегда парсите их Zod-схемой перед выполнением.
import { z } from "zod";
const GetWeatherArgs = z.object({ city: z.string().min(1) });
const tools = [{
type: "function" as const,
function: {
name: "get_weather",
description: "Get the current weather for a city",
parameters: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
},
}];
const res = await client.chat.completions.create({
model: "deepseek-chat",
messages: [{ role: "user", content: "Weather in Tokyo?" }],
tools,
});
const call = res.choices[0].message.tool_calls?.[0];
if (call?.function.name === "get_weather") {
const args = GetWeatherArgs.parse(JSON.parse(call.function.arguments));
// … execute tool and feed result back as a { role: "tool" } message …
}5. Контроль стоимости в Node.js
Входные и выходные tokens раздельно, выход ~2×. system prompt и история — всё входные tokens, неконтролируемые диалоги быстро раздувают счёт.
Ставьте maxTokens, суммируйте старые ходы, кэшируйте RAG-чанки. На объёме — /pricing со скидочными официальными ключами.
FAQ
Можно ли звонить из браузера?
Нет, ключ утечёт. Только через Node.js сервер, serverless или Next.js route handler.
Bun / Deno / Cloudflare Workers?
Работают. SDK openai поставляется в ESM и требует лишь fetch. Для длинных стримов предпочтителен Node.js.
Совместим с LangChain / Vercel AI SDK?
Да — любая библиотека с OpenAI-совместимым baseURL работает с DeepSeek.
Где дешёвые ключи?
В /pricing — официальные ключи со скидкой.
Если у вас уже Node.js / Next.js, переход на DeepSeek V4 — это буквально две строки: baseURL и model. Промпты, инструменты и ретраи остаются, а расходы на tokens падают на порядок.