Назад в блог

API и SDKИнструкция

LangChain ChatOpenAI с другим base_url: CheapTokens на Python

Как направить ChatOpenAI из LangChain на CheapTokens: base_url, ключ и id модели на Python, а ещё invoke, стриминг и токены за вызов.

Автор: CheapTokens TeamОпубликовано Обновлено 4 мин чтения

Код на Python: ChatOpenAI с base_url на API CheapTokens
Содержание
  1. Какой id модели писать в model
  2. base_url через переменные окружения
  3. Стриминг ответа
  4. Токены за каждый вызов
  5. Сколько стоит пайплайн LangChain за день
  6. Что покрывает это руководство

У вас есть цепочка LangChain на ChatOpenAI, и её нужно запустить на другом эндпоинте, ничего не переписывая. Это можно: передайте три аргумента, base_url, api_key и model, остальной код остаётся как есть. CheapTokens работает по протоколу OpenAI Chat Completions по адресу https://api.cheaptokens.dev/v1, поэтому ChatOpenAI подключается к нему напрямую.

Шаги ниже сверены с документацией LangChain по ChatOpenAI на 27 октября 2026 года. У страницы нет даты и версии, поэтому конкретный релиз langchain-openai я не называю: ставьте последний.

Шаг за шагом

Подключаем LangChain за три шага

Установка

bash
pip install -U langchain-openai

Ключ

Храните ключ в переменной окружения, а не в файле:

bash
export CHEAPTOKENS_API_KEY="your-key"

Вызов модели

python
import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)

msg = llm.invoke("Explain what a token is in one sentence.")
print(msg.content)

invoke() возвращает сообщение, а .content — это текст ответа. Вот и всё подключение. Ключ и пополнение баланса — в документации CheapTokens.

Четыре шага: base_url, api_key, полный id модели, чтение usage_metadata
Четыре вещи: base_url, api_key, полный id модели, затем usage

Какой id модели писать в model#

Пишите полный id, с префиксом разработчика. Короткое имя вроде deepseek-v4-flash вы видите в таблицах цен, а API принимает deepseek-ai/DeepSeek-V4-Flash-0731. Id из списка моделей CheapTokens:

  • deepseek-ai/DeepSeek-V4-Flash-0731: контекст 380K, до 32 768 токенов в одном ответе. Хороший выбор по умолчанию для цепочек с длинными документами и длинными ответами.
  • zai-org/GLM-5.3-Flash: контекст 390K, до 8 192 токенов в ответе.
  • MiniMaxAI/MiniMax-M2.7: контекст 180K, до 8 192 токенов в ответе.

Переключение — правка одной строки, ведь модель это просто строка:

python
llm = ChatOpenAI(
    model="zai-org/GLM-5.3-Flash",
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
)

Баланс один на все три модели, так что одну и ту же цепочку можно прогнать на каждой без новых настроек. Актуальные цены — на странице цен: у MiniMax-M2.7 вход $0.05, выход $0.15 за 1M токенов.

base_url через переменные окружения#

Цепочка вызывает ChatOpenAI, который отправляет запросы на api.cheaptokens.dev/v1
Куда уходит запрос: цепочка, ChatOpenAI, API CheapTokens

Если код трогать не хочется, LangChain читает адрес из окружения. Порядок приоритета — по документации:

  1. аргумент base_url (или openai_api_base) в коде;
  2. OPENAI_API_BASE, её читает LangChain;
  3. OPENAI_BASE_URL, её читает SDK openai.

Быстрая проверка

Вы задали base_url в коде и ещё экспортировали OPENAI_BASE_URL с другим адресом. Какой адрес возьмёт ChatOpenAI?

Показать ответ

Ответ: Аргумент base_url из кода

Явный аргумент приоритетнее всего, затем OPENAI_API_BASE, затем OPENAI_BASE_URL. Забытая переменная в терминале не перенаправит вызов, где base_url задан явно.

Из той же страницы документации ещё одно следствие: если задана OPENAI_BASE_URL, LangChain по умолчанию выключает stream_usage. Это важно в следующих двух разделах.

Стриминг ответа#

Для интерфейса нужен текст по мере появления. stream() отдаёт куски, у каждого есть .content:

python
for chunk in llm.stream("Write a three-line summary of how HTTP works."):
    print(chunk.content, end="", flush=True)

Для окна чата этого хватает. Если нужен ещё и подсчёт токенов в конце стрима, передайте stream_usage=True: ChatOpenAI(..., stream_usage=True). В этом руководстве токены считаются по invoke(), как в следующем разделе. Стриминг в обычном SDK OpenAI разобран в статье Стриминг ответов в Python.

Токены за каждый вызов#

У каждого сообщения от invoke() есть usage_metadata с токенами этого вызова:

python
msg = llm.invoke("Summarize this ticket in one line: my invoice shows the wrong VAT number.")
print(msg.usage_metadata)
# {'input_tokens': ..., 'output_tokens': ..., 'total_tokens': ...}

Сложите их по всему пайплайну, и получится бюджет запуска:

python
total_in = total_out = 0
for ticket in tickets:
    msg = llm.invoke(ticket)
    total_in += msg.usage_metadata["input_tokens"]
    total_out += msg.usage_metadata["output_tokens"]

Вход и выход тарифицируются отдельно, поэтому храните оба числа. Почему они разделены — в статье Входные и выходные токены, а готовый счётчик с остановкой по бюджету — в Учёт токенов агента на Python.

Сколько стоит пайплайн LangChain за день#

Допустим, у вас бот поддержки на 2 000 обращений в день. Каждый вызов отправляет обращение с промптом, около 3 000 токенов, а ответ занимает около 500. На MiniMax-M2.7 один такой вызов стоит $0.00023. Подвигайте ползунки под свои числа, сайт посчитает по текущим ценам:

Калькулятор стоимости

Один день бота поддержки

Входные токены на запрос: 3 000 · Выходные токены на запрос: 500 · Запросов в день: 2 000

МодельВ деньВ месяц
GLM-5.3-Flash$0.16$4.80
DeepSeek-V4-Flash$0.19$5.70
MiniMax-M2.7$0.45$13.50

Калькулятор показывает сумму за день: в таком вызове большая часть токенов приходится на вход, меньшая — на выход. В CheapTokens итог, вход и выход вместе, получается ниже, чем у OpenRouter, а цена одинакова в любое время суток: пиковых тарифов, под которые надо подстраиваться, нет.

Что покрывает это руководство#

В примере обычные chat completions: invoke, stream и usage_metadata. ChatOpenAI следует спецификации OpenAI, поэтому текст ответа лежит в .content. По документации LangChain, он переключается на Responses API, если используются встроенные инструменты или параметр reasoning.

Если вызов падает, причина обычно одна из трёх: в base_url нет /v1, вместо полного id модели указано короткое имя, или переменная с ключом не экспортирована в том терминале, где запускается Python. Как выглядит каждая, показано в списке частых ошибок подключения.

Следующий шаг: запустите блок из трёх шагов со своей цепочкой, а потом прогоните её же с другим id модели — как это устроено, объясняет статья про OpenAI-совместимый API.