Gradio Python: чат с нейросетью со стримингом за час

Собираем веб-интерфейс чата на Gradio: ответ приходит потоком, можно выбрать модель и системный промпт, а под ответом видна стоимость в долларах.

5 мин · · разработка · автоматизация

Нужен веб-интерфейс для общения с моделью, но не хочется писать фронтенд? Gradio Python решает это одним классом: gr.ChatInterface рисует окно чата, историю и кнопку остановки. Мы подключим к нему OpenAI-совместимый шлюз, включим стриминг, добавим выбор модели и покажем под каждым ответом число токенов и цену. Получится чат-бот на основе GPT, Claude или DeepSeek, который можно запустить за час.

Что нужно заранее

Нужны Python (подходящую версию смотрите в документации Gradio) и ключ fetch_... из кабинета. Установка:

bash
pip install --upgrade gradio openai
export ZUKKO_API_KEY="fetch_ваш_ключ"

Версию Gradio берите свежую. Примеры ниже следуют документации gradio.app: функция чата получает message и history, а history это список словарей с ключами role и content.

Самый короткий вариант: gr.load_chat

В документации Gradio есть однострочник для любого OpenAI-совместимого сервера (там он показан на примере Ollama). С шлюзом Zukko это выглядит так:

bash
import os
import gradio as gr

gr.load_chat(
    "https://api.zukko.pro/v1/",
    model="deepseek-v4.1-flash",
    token=os.environ["ZUKKO_API_KEY"],
).launch()

Этого хватит, чтобы проверить ключ и модель. Но выбор модели, системный промпт и учёт токенов здесь не настроить, поэтому дальше пишем свою функцию чата. Тот же приём работает и с локальным сервером LM Studio: подставьте его адрес из настроек программы (локальный сервер LM Studio тоже отдаёт OpenAI-совместимый API), и получится LM Studio на Python с готовым интерфейсом.

Gradio Python: чат со стримингом

Стриминг в Gradio делается через yield: каждый yield заменяет предыдущий вариант ответа. Параметры вроде модели и системного промпта передаются через additional_inputs: Gradio отрисует их под чатом и добавит в вызов функции после message и history.

bash
import os
import gradio as gr
from openai import OpenAI

client = OpenAI(
    base_url="https://api.zukko.pro/v1",
    api_key=os.environ["ZUKKO_API_KEY"],
)

PRICES = {  # $ за 1M токенов (вход, выход), на 18 октября 2026
    "claude-sonnet-5-5": (0.4, 2.0),
    "gpt-6.1-sol": (0.27, 1.32),
    "deepseek-v4.1-flash": (0.013, 0.038),
    "glm-5.3-flash": (0.04, 0.04),

Запустите python app.py и откройте адрес из консоли. Разберём важные места.

Почему проверка if chunk.choices

В стриминге OpenAI-формата Zukko всегда присылает последний чанк с usage и пустым списком choices. Без проверки chunk.choices[0] упадёт с IndexError. Тот же чанк мы используем, чтобы показать токены и цену.

Зачем stream.close()

Пока ответ идёт, кнопка «Отправить» превращается в «Стоп». Если пользователь останавливает генерацию, Gradio прерывает функцию-генератор, а finally закрывает соединение. Так соединение со шлюзом не остаётся открытым после остановки, и цикл не дочитывает ответ, который никто не увидит.

История и обрезка

Модель не помнит предыдущих реплик, поэтому история отправляется заново с каждым запросом, и вход растёт с каждым ходом. Константа MAX_HISTORY ограничивает его. Строку со статистикой мы вырезаем из истории по маркеру, чтобы не отправлять её модели.

Сколько стоит один ответ

Формула: (входные токены × цена входа + выходные токены × цена выхода) / 1 000 000. Пусть запрос содержит 2 000 входных токенов вместе с историей, а ответ 500 токенов. Цены на 18 октября 2026:

МодельВход / выход, $ за 1MРасчётОдин ответ1 000 ответов
claude-sonnet-5-50.4 / 22000 × 0.4 + 500 × 2 = 1800$0.0018$1.80
gpt-6.1-sol0.27 / 1.32540 + 660 = 1200$0.0012$1.20
glm-5.3-flash0.04 / 0.0480 + 20 = 100$0.0001$0.10
deepseek-v4.1-flash0.013 / 0.03826 + 19 = 45$0.000045$0.045

В колонке расчёта указаны миллионные доли доллара. При работе с Claude через OpenAI-формат (/v1/chat/completions) весь вход идёт по цене входа, скидка за кэш там не применяется. Для чата с короткой историей это некритично. Список и цены моделей всегда можно получить через GET /v1/models.

Выбор модели и качество

Мы не утверждаем, какая модель в этом списке отвечает лучше. Ориентиры из таблицы Zukko: индекс силы Artificial Analysis у claude-sonnet-5-5 48, у gpt-6.1-sol 52, у glm-5.3-flash 42, у deepseek-v4.1-flash 39. Выпадающий список в интерфейсе как раз нужен, чтобы сравнить модели на своих вопросах и посмотреть на цену рядом.

Безопасность перед публикацией

  • Не вшивайте ключ в код: используйте переменную окружения, как выше.
  • Заведите для чата отдельный ключ с дневным лимитом в долларах и списком разрешённых моделей (allowed_models). Если ссылку на чат увидят посторонние, потратить они смогут только этот лимит. Как задаются эти поля, описано в разделе о лимитах ключа.
  • Для закрытого доступа в demo.launch() есть параметр auth с парой логин/пароль, а публичную ссылку share=True лучше не включать на ключе с большим лимитом.

Схожий чат на другом фреймворке разобран в статье про Streamlit.

Частые вопросы

Gradio сам считает токены?

Нет. Токены берём из последнего чанка стриминга: usage.prompt_tokens и usage.completion_tokens. Расходы также видны в разделе «Использование» кабинета.

Почему в истории content иногда список?

В свежих версиях Gradio содержимое сообщения может быть списком частей (текст, файлы). Функция as_text склеивает текстовые части и игнорирует остальное.

Можно ли подключить картинки?

Для этого включают multimodal=True и передают изображения в модель, которая принимает картинки на вход. Из четырёх моделей выше картинки в таблице Zukko поддерживает только claude-sonnet-5-5 и gpt-6.1-sol. Формат вложений сверяйте с документацией Gradio и справочником по chat/completions.

Можно ли запустить то же самое с локальной моделью?

Да, достаточно сменить base_url и модель на адрес локального сервера, например Ollama или LM Studio. Код чата не изменится.

Чтобы запустить чат у себя, создайте ключ в кабинете, поставьте на него дневной лимит и вставьте в переменную ZUKKO_API_KEY. Другие примеры на Python собраны в справочнике.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.