LangChain.js: подключение к OpenAI-совместимому API в Node.js
Как направить ChatOpenAI из LangChain.js на шлюз Zukko: одна настройка baseURL, потоковый вывод, вызов инструментов и проверка расхода.
4 мин · · разработка · API-ключи
Если вы пишете на LangChain.js и хотите звать через него не только модели OpenAI, отдельный пакет под каждого поставщика не нужен. Класс ChatOpenAI умеет работать с любым сервером, который понимает формат Chat Completions, а адрес сервера задаётся одним полем. Ниже разберём, как подключить LangChain.js к шлюзу Zukko в Node.js: от установки до потокового вывода и вызова инструментов.
Что понадобится
- Node.js 18 или новее и менеджер пакетов npm.
- Ключ вида
fetch_...: создаётся в кабинете, полностью показывается один раз. - Положительный баланс. Пополнение от $1: СБП (платёжная страница добавляет комиссию 8,5% сверху), криптовалюта или карты MasterCard.
Установка пакетов:
npm install @langchain/openai @langchain/core zod
export ZUKKO_API_KEY="fetch_ваш_ключ"Пакет @langchain/openai содержит ChatOpenAI, @langchain/core нужен для сообщений и инструментов, zod описывает схемы аргументов.
LangChain.js: базовое подключение
В документации LangChain.js адрес сервера меняется через поле configuration: оно принимает те же параметры, что и официальный OpenAI SDK. Для Zukko адрес заканчивается на /v1, потому что это клиент семейства OpenAI-совместимых.
import { ChatOpenAI } from "@langchain/openai";
const llm = new ChatOpenAI({
model: "claude-sonnet-5-5",
apiKey: process.env.ZUKKO_API_KEY,
configuration: {
baseURL: "https://api.zukko.pro/v1",
},
});
const answer = await llm.invoke([
{ role: "system", content: "Отвечай коротко и по делу." },
{ role: "user", content: "Чем Promise.all отличается от Promise.allSettled?" },
]);
console.log(answer.content);Сохраните код как chat.ts в проекте с ES-модулями ("type": "module" в package.json) и запустите привычным способом, например npx tsx chat.ts.
Если ваша версия пакета не принимает apiKey в конструкторе, задайте переменную окружения OPENAI_API_KEY с тем же значением. Для базового адреса тоже есть переменная окружения OPENAI_BASE_URL, которую читает нижележащий SDK. Но явный configuration.baseURL понятнее при чтении кода и не зависит от окружения сервера.
Модель можно менять на любую из каталога: gpt-6.1-sol, deepseek-v4.1-flash, glm-5.3-flash, claude-opus-5-5. Актуальный список и цены отдаёт GET https://api.zukko.pro/v1/models, он же показан на странице «Модели» в кабинете.
Стриминг
Потоковый вывод в LangChain.js не требует отдельных настроек: метод stream() возвращает асинхронный итератор.
const stream = await llm.stream("Объясни замыкания в JavaScript в трёх предложениях");
for await (const chunk of stream) {
process.stdout.write(String(chunk.content));
}Шлюз в конце потока присылает служебный чанк с usage и пустым списком choices. LangChain это учитывает сам. Если же вы разбираете поток руками через официальный SDK, перед обращением к chunk.choices[0] проверяйте, что массив не пуст.
Инструменты (tool calling)
Чтобы модель могла вызывать ваш код, опишите функцию через tool и привяжите её методом bindTools.
import { tool } from "@langchain/core/tools";
import { HumanMessage, ToolMessage } from "@langchain/core/messages";
import * as z from "zod";
const getWeather = tool(
async ({ city }) => `В городе ${city} +12, небольшой дождь`,
{
name: "get_weather",
description: "Возвращает текущую погоду в городе",
schema: z.object({ city: z.string().describe("Название города") }),
},
);
const withTools = llm.bindTools([getWeather]);Здесь цикл написан вручную, чтобы было видно, что происходит: модель возвращает не текст, а запрос на вызов, вы выполняете функцию и отдаёте результат обратно. Когда понадобятся несколько шагов подряд, ту же логику берут на себя агентные обёртки LangChain; про них и про сам подход есть заметка о tool use. Как именно разные модели справляются с вызовами инструментов, зависит от модели, поэтому проверьте на своих задачах.
Сколько это стоит
Цены на 18 октября 2026, $ за 1M токенов. Для claude-sonnet-5-5: вход 0.4, выход 2. Допустим, агент делает три вызова модели по 2 000 токенов входа и 300 токенов выхода:
- вход: 6 000 × 0.4 / 1 000 000 = $0.0024;
- выход: 900 × 2 / 1 000 000 = $0.0018;
- итого $0.0042 за прогон.
Важная оговорка про кэш. Для Claude скидку за кэш промптов шлюз применяет только в Anthropic-формате (/v1/messages). В OpenAI-формате, которым пользуется ChatOpenAI, весь вход идёт по цене входа. Если у вас длинный повторяющийся контекст и модель Claude, посмотрите сравнение форматов. У GPT-моделей чтение кэша видно в обоих форматах.
Без LangChain: официальный openai SDK
Если фреймворк вам не нужен, тот же адрес работает с обычным пакетом openai:
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.zukko.pro/v1",
apiKey: process.env.ZUKKO_API_KEY,
});
const r = await client.chat.completions.create({
model: "gpt-6.1-sol",
messages: [{ role: "user", content: "Привет" }],
});
console.log(r.choices[0].message.content);Подробнее про Node.js SDK: полный пример. Общий разбор настройки LangChain на Python и JS есть в статье о подключении LangChain к шлюзу.
Типичные ошибки
- 404
model_not_found: имя модели написано не так, как в/v1/models. - 401: ключ скопирован с пробелом или передан не в то поле.
- 402: на балансе не хватает средств.
- 403
model_not_allowed_for_key: для ключа в кабинете ограничен список моделей. - 429: исчерпан дневной или месячный лимит ключа либо ограничение у провайдера модели.
- Запросы уходят не туда: проверьте, что
baseURLвнутриconfiguration, а не на верхнем уровне конструктора.
Если вы хотите безопасно пробовать эксперименты, заведите отдельный ключ с дневным лимитом в $ и списком разрешённых моделей.
Частые вопросы
Нужен ли отдельный пакет для Claude в LangChain.js?
Для вызова через Zukko нет: Claude доступен в OpenAI-совместимом формате, поэтому достаточно ChatOpenAI с нужным baseURL. Отдельный Anthropic-класс имеет смысл, если вам нужен именно Anthropic-формат и скидка за кэш; тогда базовый адрес без /v1.
Куда подставить baseURL, если я использую переменные окружения?
Можно задать OPENAI_BASE_URL=https://api.zukko.pro/v1 и OPENAI_API_KEY=fetch_.... Но явная настройка в configuration надёжнее, особенно в серверных окружениях, где переменные могут отличаться.
Работает ли это из России?
Для оплаты из России есть три способа: СБП, карты MasterCard и криптовалюта. Код приложения при этом не меняется.
Как не потратить лишнего во время отладки?
Создайте отдельный ключ с дневным лимитом, начните с дешёвой модели (deepseek-v4.1-flash или glm-5.3-flash) и следите за расходом в разделе «Использование» кабинета.
Чтобы попробовать, создайте ключ в кабинете, подставьте его в пример выше и запустите первый запрос.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Свой прокси к нейросети на Node.js: не показывать API-ключ во фронтендеНебольшой сервер на Express между вашим фронтендом и API нейросети: ключ остаётся на сервере, а модели, длина запроса и частота ограничены.
- DeepSeek API на Python: запросы, стриминг, JSON и обработка ошибокГотовый код для DeepSeek V4.1 Flash и V4 Pro на OpenAI SDK: запрос, стриминг, разбор JSON и аккуратная обработка ошибок.
- Gemini API на Python: запросы через OpenAI SDK и OpenAI-форматРабочий пример Gemini API на Python без отдельной библиотеки: меняем base_url в OpenAI SDK, добавляем стриминг и JSON и сравниваем с google-genai.
- Anthropic SDK для Python: base_url, ключ и первый запрос через шлюзОфициальный пакет anthropic для Python нужно только направить на другой адрес. Разбираем base_url, ключ, стриминг, подсчёт токенов, кэш и ошибки.