Gradio Python: чат с нейросетью со стримингом за час
Собираем веб-интерфейс чата на Gradio: ответ приходит потоком, можно выбрать модель и системный промпт, а под ответом видна стоимость в долларах.
5 мин · · разработка · автоматизация
Нужен веб-интерфейс для общения с моделью, но не хочется писать фронтенд? Gradio Python решает это одним классом: gr.ChatInterface рисует окно чата, историю и кнопку остановки. Мы подключим к нему OpenAI-совместимый шлюз, включим стриминг, добавим выбор модели и покажем под каждым ответом число токенов и цену. Получится чат-бот на основе GPT, Claude или DeepSeek, который можно запустить за час.
Что нужно заранее
Нужны Python (подходящую версию смотрите в документации Gradio) и ключ fetch_... из кабинета. Установка:
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 это выглядит так:
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.
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-5 | 0.4 / 2 | 2000 × 0.4 + 500 × 2 = 1800 | $0.0018 | $1.80 |
gpt-6.1-sol | 0.27 / 1.32 | 540 + 660 = 1200 | $0.0012 | $1.20 |
glm-5.3-flash | 0.04 / 0.04 | 80 + 20 = 100 | $0.0001 | $0.10 |
deepseek-v4.1-flash | 0.013 / 0.038 | 26 + 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.
Похожие статьи
- Перевод субтитров нейросетью: SRT без сбитых таймкодовГлавный риск при переводе SRT нейросетью — сломанная структура файла. Показываем схему, где таймкоды не попадают к модели, и скрипт на Python с проверкой.
- Редактирование текстов нейросетью: корректура, стиль и сокращениеРазные виды редактуры требуют разных промптов. Показываем формулировки для корректуры, стиля и сокращения, как получать правки с объяснением и не терять авторский голос.
- Нейросеть для написания скриптов: Bash, PowerShell и PythonМодель быстро пишет скрипты для рутины, но запускать их вслепую нельзя. Показываем, как ставить задачу, требовать безопасный режим dry-run и проверять результат до запуска.
- Dify в Docker: установка, первичная настройка и подключение моделейСтавим Dify через Docker Compose, создаём администратора и подключаем модели Claude, GPT и DeepSeek по одному ключу. Отдельно — что делать, если контейнеры не стартуют.