Руководство
Как использовать gpt-6.1-terra через HiAPI API: curl, Python и рабочий запрос
Что на самом деле нужно для одного запроса к gpt-6.1-terra
Каждый вызов gpt-6.1-terra в HiAPI сводится к четырём вещам: URL, заголовок авторизации, идентификатор модели и тело запроса. Если сделать это правильно, всё остальное в статье — лишь обвязка.
Эндпоинт
Все вызовы моделей выполняются через один базовый URL в HiAPI. Переключать хост для каждой модели не нужно — именно название модели в теле запроса определяет маршрутизацию. Направьте клиент на базовый URL HiAPI и сохраните его в переменной, чтобы менять в одном месте.
Заголовок авторизации
Авторизация передаётся как bearer-токен в заголовке Authorization. Этот токен — API-ключ, выданный в вашем аккаунте HiAPI. Никогда не встраивайте его в исходный файл; читайте из переменной окружения (например, HIAPI_API_KEY), чтобы один и тот же код работал локально, в CI и в продакшене.
Идентификатор модели
В теле запроса модель нужно указать явно: "model": "gpt-6.1-terra". Строка чувствительна к регистру и должна совпадать точно. Неверное или обрезанное имя модели — одна из самых частых причин отклонения запроса, поэтому держите его как константу в коде, а не как строку, набранную на скорую руку.
Поля тела запроса
Минимальный запрос содержит модель и входное содержимое — подсказку или сообщения, которые должна обработать модель. Поверх этого ядра можно добавлять любые необязательные параметры, поддерживаемые моделью (подсказки формата вывода, управление длиной и т.д.), но начинайте с минимального тела, дающего валидный ответ. Минимальное тело проще отлаживать, чем полностью снаряжённое, и оно помогает понять, где произошёл сбой — в теле или в обвязке.
Один структурный момент стоит усвоить пораньше: одни ответы возвращаются встроенными, другие — как задачи, которые нужно опрашивать. Как их различить, мы расскажем дальше, но уже сейчас проектируйте слой запросов так, чтобы обработка ответов могла переключаться между двумя режимами без переписывания.
Рабочий curl-запрос, разобранный построчно
Ниже полный пример вызова — экспортируйте ключ и вставляйте прямо в терминал. Он использует базовый URL HiAPI и минимальное JSON-тело.
curl -sS -X POST "$HIAPI_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6.1-terra",
"messages": [
{"role": "user", "content": "Кратко опиши круговорот воды в трёх предложениях."}
]
}'
Что делает каждый флаг
-X POST задаёт HTTP-метод. Вызовы моделей — это POST, поскольку они несут тело запроса.
-sS заставляет curl скрывать прогресс, но показывать реальные ошибки. Без -S неудачный запрос может выглядеть просто тишиной.
-H "Authorization: Bearer $HIAPI_API_KEY" добавляет ваши учётные данные. Префикс Bearer и следующий за ним одиночный пробел — часть схемы авторизации; пропуск любого из них приводит к ошибке авторизации, которая выглядит никак не связанной с заголовком.
-H "Content-Type: application/json" сообщает серверу, что тело — JSON. Некоторые шлюзы терпят отсутствие content type, но полагаться на это не стоит.
-d '...' — это тело запроса. Одинарные кавычки не дают shell подставлять значения в JSON. Если хотите использовать файл, замените -d '...' на -d @payload.json и держите JSON в файле, который можно линтить и диффить.
Две привычки, экономящие время
Во-первых, при сбое добавляйте -i, чтобы напечатать строку статуса и заголовки ответа. Ответ с кодом 200, но странным телом читается совсем иначе, чем 401 или 422, и эту разницу хочется видеть сразу. Во-вторых, прогоняйте вывод через JSON-форматтер (curl ... | python -m json.tool), чтобы вложенные поля ответа были читаемы. Оба флага безвредны, и при настройке вызова вы будете пользоваться ими часто.
Перенос вызова в Python
Python-версия должна сохранять точную семантику curl-вызова: тот же URL, те же заголовки, то же тело, тот же разбор. Использование стандартной библиотеки оставляет число зависимостей нулевым и делает перенос очевидным.
import json
import os
import urllib.request
HIAPI_BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
HIAPI_API_KEY = os.environ["HIAPI_API_KEY"]
def call_terra(prompt: str, model: str = "gpt-6.1-terra") -> dict:
body = {
"model": model,
"messages": [{"role": "user", "content": prompt}],
}
req = urllib.request.Request(
f"{HIAPI_BASE_URL}/v1/chat/completions",
data=json.dumps(body).encode("utf-8"),
headers={
"Authorization": f"Bearer {HIAPI_API_KEY}",
"Content-Type": "application/json",
},
method="POST",
)
with urllib.request.urlopen(req, timeout=120) as resp:
return json.loads(resp.read().decode("utf-8"))
result = call_terra("Кратко опиши круговорот воды в трёх предложениях.")
print(json.dumps(result, indent=2))
Замечания о переносе
Заголовки строятся один раз внутри объекта запроса и соответствуют двум флагам -H. Тело — Python-dict, сериализуемый через json.dumps, что программно эквивалентно curl-телу в одинарных кавычках — и заодно устраняет проблемы с кавычками в Windows shell.
Функция принимает модель как параметр со значением по умолчанию, а не повторяет литерал повсюду. Так у имени модели единственный источник истины, и при этом его можно осознанно переопределить.
Обработка ошибок здесь намеренно опущена, чтобы нормальный путь оставался читаемым. В реальном коде оберните вызов urlopen и перехватывайте HTTP-исключения, читая .read() из объекта ошибки, чтобы увидеть сообщение сервера. Таймаут задан явно; бесконечное блокирование в итоге подвесит worker.
Если вы предпочитаете httpx или requests, соответствие один к одному: requests.post(url, headers=..., json=body, timeout=120). Параметр json= сериализует dict за вас. Семантика не меняется — в этом и суть: curl-вызов — это контракт, а Python лишь его вторая форма представления.
Выбор между одиночным вызовом и асинхронной задачей
Ждать подходит не для каждого запроса. Решение зависит от длительности работы и от того, может ли вызывающая сторона позволить себе блокировку.
Когда подходит одиночный вызов
Короткие интерактивные запросы — суммаризация, классификация, небольшая переработка текста — должны быть обычными синхронными вызовами. Ответ приходит встроенным, вы его разбираете и возвращаете. Это простейший путь в коде и самый простой для рассуждений в тестах. Если пользователь ждёт в UI, синхронность — ожидаемая форма.
Когда стоит рассматривать задачу
Когда работа идёт долго или порождает много артефактов, а обрыв соединения означает дорогостоящую переделку, вызов следует рассматривать как асинхронный. В таких случаях вы отправляете запрос, получаете идентификатор задачи и опрашиваете, пока она не достигнет терминального состояния. Сама отправка возвращается быстро, поэтому ваш worker или HTTP-обработчик не занят весь процесс.
Практическое правило: если вы ловите себя на том, что задаёте аномально длинный клиентский таймаут, чтобы «вызов прошёл», это сигнал перейти к задачам. Таймаут — не механизм планирования.
Сделайте переключение дешёвым
Не раздваивайте кодовую базу на два несвязанных клиента. Оберните оба пути в одну функцию, возвращающую либо мгновенный результат, либо дескриптор задачи, и пусть вызывающая сторона запрашивает нужный режим. Так построение запроса — модель, авторизация, тело — остаётся общим, а различается только стратегия ожидания. Это также означает единообразную отладку: при небольшом теле запроса вы можете воспроизвести асинхронный сбой синхронно.
Будьте честны со своей нагрузкой, а не угадывайте поведение платформы. Если ваши запросы обычно завершаются за несколько секунд, оставайтесь синхронными и пропустите сложность. Асинхронный путь ценен для действительно медленных запросов, а не для всех.
Опрос завершения без долбёжки API
Цикл опроса имеет три обязанности: ждать достаточно долго, чтобы быть вежливым, в конце концов остановиться и ясно представить терминальное состояние. Вот компактная версия.
import time
def poll_until_done(fetch_status, job_id, interval=3.0, timeout=600.0):
deadline = time.monotonic() + timeout
while True:
status = fetch_status(job_id)
state = status.get("status")
if state in ("succeeded", "failed", "cancelled"):
return status
if time.monotonic() > deadline:
raise TimeoutError(f"job {job_id} not finished after {timeout}s")
time.sleep(interval)
Почему такая структура
fetch_status внедряется, поэтому один и тот же цикл работает и при опросе через HTTP, и при опросе внутренней очереди. Это же делает тривиальным тестирование с фейковым объектом, который сначала дважды возвращает running, а затем succeeded.
Проверка состояния — явный белый список терминальных состояний, а не проверка одной строки "done". Терминальные состояния разные — успех, ошибка, отмена — и вы хотите остановиться на любом из них, а не крутиться до таймаута, потому что искали только одно.
Дедлайн использует time.monotonic(), чтобы корректировки системных часов случайно не удлиняли и не укорачивали ожидание.
Выбор интервала опроса
Начинайте с нескольких секунд и сопротивляйтесь соблазну опрашивать в плотном цикле. Субсекундные интервалы тратят бюджет запросов и увеличивают нагрузку, но не ускоряют выполнение задачи. Если ожидаете долгие задачи, рассмотрите умеренный backoff — например, увеличивайте интервал после каждой проверки до потолка. Потолок держите скромным; цель — сократить число запросов, а не создать многосекундные слепые зоны.
Таймаут не опционален
Всегда задавайте таймаут — щедрый относительно работы, но конечный. При срабатывании не возвращайте молча пустой результат. Бросьте исключение, залогируйте идентификатор задачи и дайте вызывающей стороне решить, повторять ли попытку — задача, на которой опрос истёк, может всё ещё выполняться, а слепая повторная отправка может привести к дублированию работы.
Скачивание и хранение выходных данных
Если ответ содержит ссылки на артефакты — URL, идентификаторы файлов или ключи хранилища — считайте их эфемерными. Они могут истечь, быть ограничены вашим аккаунтом и не пережить повторную попытку. Долговечная копия — та, которую вы сохранили сами.
Схема «получить и сохранить»
Когда задача завершается, прочитайте ссылку на артефакт из терминального состояния, скачайте один раз и запишите в место, которое контролируете: локальный путь для скриптов, бакет объектного хранилища для всего, что близко к продакшену. Записывайте идентификатор задачи вместе с сохранённым файлом, чтобы можно было проследить артефакт до породившего его запроса.
def save_output(fetch_bytes, artifact_url, dest_path):
data = fetch_bytes(artifact_url)
with open(dest_path, "wb") as f:
f.write(data)
return dest_path
Практики против тихой потери данных
Пишите во временный путь и переименовывайте при успехе, чтобы неудачное скачивание никогда не оставляло наполовину записанный файл, выглядящий валидным. Если ответ раскрывает размер в байтах или контрольную сумму, проверяйте их. Для больших файлов пишите потоком на диск, а не загружайте весь объём в память.
Также сохраняйте сырой ответ финального вызова статуса. JSON хранить дёшево, и когда кто-то спросит, почему файл выглядит неправильно, документ состояния с именем модели, отметкой времени и id задачи — самый быстрый способ восстановить ход событий.
Наконец, разделите получение и обработку. Сначала скачайте, сохраните, затем преобразуйте. Если вы парсите по мере скачивания, кратковременная сетевая ошибка выглядит как баг форматирования, и вы потратите полдня на решение не той проблемы.
Отладка типовых сбоев
Большинство сбоев попадает в четыре категории. У каждой есть быстрый изолирующий тест.
Ошибки авторизации
Симптом: 401 или 403 ещё до того, как что-либо в теле запроса станет важным. Проверьте, что ключ действительно загружен — печатайте, задана ли переменная окружения, а не её значение. Проверьте префикс Bearer и пробел после него. Убедитесь, что имя заголовка написано правильно и прокси его не срезает. Если один и тот же ключ работает в curl, но не в вашем приложении, значит приложение строит заголовок иначе; сравните буквальный dict заголовков с флагами curl.
Некорректный формат тела запроса
Симптом: 400 или 422 с сообщением о теле. Валидируйте JSON перед отправкой — json.dumps сериализует почти что угодно, включая dict, который вы не собирались строить. Проверьте висячие запятые, скопированные из конфигурационных файлов, значения, переданные списком вместо строки, и отсутствующие обязательные поля. Сократите до минимального тела из первого раздела и добавляйте поля по одному, пока не сломается.
Неверное имя модели
Симптом: отказ, похожий на ошибку запроса или сообщение о ненайденной модели. Строка модели должна быть ровно gpt-6.1-terra. Частые причины: устаревшее значение по умолчанию в хелпере, переменная окружения с другим значением или опечатка из-за повторного ввода вместо копирования. Держите её как константу модуля и грепайте дубликаты.
Таймаут опроса
Симптом: ваш цикл завершается по таймауту, а задача всё ещё жива. Сначала убедитесь, что опрашиваемый идентификатор задачи — тот самый, что вы отправили; распечатать оба при отправке — простейшая проверка. Затем посмотрите на список терминальных состояний: состояние, которого вы не ожидали, заставит цикл крутиться вечно. Наконец, проверьте, соответствуют ли ваш интервал и дедлайн реальной длительности задачи; дедлайн, настроенный под короткие запросы, всегда сработает на длинных. При таймауте логируйте последнее состояние — так вы узнаете, в каком состоянии была задача, когда вы сдались.
Разбирайтесь с ними по порядку — авторизация, тело, модель, опрос — потому что каждый слой маскирует лежащий под ним. Сломанный заголовок заставит каждое тело запроса выглядеть неправильным.
Похожие статьи
Все статьи →-
Руководство
Как вызвать hailuo-2.3-fast/image-to-video через HiAPI API: практика с curl и Python
Первый рабочий запрос к быстрому и недорогому уровню генерации видео по изображению — от ID модели до загрузки MP4
-
Руководство
Как вызывать hailuo-2.3/text-to-video через API HiAPI: curl, Python и рабочие примеры запросов
-
Руководство
Использование Kandinsky 6.0 Pro для преобразования текста в видео на HiAPI: руководство по curl и Python
Практическая демонстрация с упором на запросы: построение вызова от промпта до видеоклипа, отправка запроса, опрос статуса завершения и скачивание результата