RU ▾

Пользовательский base_url в OpenAI Python SDK: настройка и устранение неполадок

Кабель перемещён с одного сокета сервера на другой, демонстрируя переключение базового URL для SDK

SDK OpenAI позволяют переопределить эндпоинт одним аргументом. На этой странице показана точная настройка кастомного базового URL для OpenAI Python SDK, а также эквивалент для Node, потоковая передача и ошибки, которые приводят к большинству неудачных первых вызовов.

Последнее обновление

Итоги

  • Установите base_url в https://api.apicompatible.com/v1 и передайте ваш ключ как api_key; ничего в вашем коде менять не нужно.
  • Идентификатор модели — uncensored. Оставление старого имени модели — самая частая причина сбоя первого вызова.
  • Потоковая передача работает с stream=True, и каждый потоковый запрос занимает один из ваших слотов параллельности до завершения.
  • Переменные окружения позволяют переключать эндпоинты без изменения кода.

Краткий ответ

Создайте клиент с двумя аргументами, base_url и api_key, и продолжайте использовать те же методы, которые вы уже знаете. Базовый URL для этого API — https://api.apicompatible.com/v1, идентификатор модели — uncensored, а аутентификация представляет собой стандартный Bearer-токен, который SDK формирует из вашего ключа.

from openai import OpenAI

client = OpenAI(
    base_url="https://api.apicompatible.com/v1",
    api_key="YOUR_API_KEY",
)

resp = client.chat.completions.create(
    model="uncensored",
    messages=[
        {"role": "system", "content": "You are a concise assistant."},
        {"role": "user", "content": "Name three uses for a hash map."},
    ],
)

print(resp.choices[0].message.content)
print(resp.usage)

Это всё. SDK добавляет к базовому URL /chat/completions, поэтому запрос попадает на POST /v1/chat/completions. Если у вас ещё нет ключа, на странице API-ключ описаны регистрация и пробный баланс.

Установка базового URL с помощью переменных окружения

Официальный SDK Python считывает OPENAI_API_KEY и OPENAI_BASE_URL из окружения, если вы не передаёте их в коде. Это полезно, когда одно и то же приложение работает с разными эндпоинтами в среде разработки и на продакшене, или когда сторонний инструмент создаёт клиент за вас и не даёт возможности редактировать аргументы конструктора.

export OPENAI_BASE_URL="https://api.apicompatible.com/v1"
export OPENAI_API_KEY="YOUR_API_KEY"

python - <<'EOF'
from openai import OpenAI

client = OpenAI()
r = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Say hello in five words."}],
)
print(r.choices[0].message.content)
EOF

Явные аргументы имеют приоритет над переменными окружения. Если запрос идёт на неверный хост, проверьте, не создаёт ли какая-то другая часть вашего кода клиент со своим base_url, и загружается ли файл .env до того, как ваш скрипт прочитает переменные.

Храните ключ вне системы контроля версий. Ключ в переменной окружения или менеджере секретов легко заменить; ключ в закоммиченном файле — нет.

То же изменение в Node

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

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.apicompatible.com/v1",
  apiKey: process.env.API_KEY,
});

const completion = await client.chat.completions.create({
  model: "uncensored",
  messages: [{ role: "user", content: "Give me a one-line commit message for a typo fix." }],
});

console.log(completion.choices[0].message.content);

Для проверки через чистый HTTP без SDK этот вызов curl отправляет тот же запрос и удобен для разделения проблем SDK и проблем с ключом:

curl https://api.apicompatible.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"uncensored","messages":[{"role":"user","content":"ping"}]}'

Потоковая передача, асинхронность и повторные попытки

Потоковая передача использует серверные события, а SDK преобразует их в итератор. Передайте stream=True и считывайте дельты по мере их поступления. Данные об использовании поступают в конце потока, поэтому учитывайте чанки, не содержащие текста.

from openai import OpenAI

client = OpenAI(
    base_url="https://api.apicompatible.com/v1",
    api_key="YOUR_API_KEY",
)

stream = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Explain idempotency in two sentences."}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
print()

Асинхронный клиент принимает те же два аргумента. Замените OpenAI на AsyncOpenAI, добавьте await и используйте async for для потоков.

Важны два лимита, когда вы выполняете более одного запроса одновременно. Полный ключ позволяет 8 параллельных запросов и 300 запросов в минуту, а пробный ключ — 2 и 60 соответственно. Потоковый запрос удерживает слот параллельности до завершения потока, поэтому застрявший потребитель, который не считывает поток, тратит слот. При превышении лимита вы получаете HTTP 429 с кодом ошибки rate_limited или concurrency. SDK по умолчанию повторяет неудачные запросы несколько раз, и это можно изменить с помощью параметра max_retries в клиенте. Заголовок Retry-After: 5 при ответе 429 означает временную перегрузку, поэтому правильный ответ — подождать пять секунд.

Таймауты, повторное использование клиента и отладка отправленных данных

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

from openai import OpenAI

client = OpenAI(
    base_url="https://api.apicompatible.com/v1",
    api_key="YOUR_API_KEY",
    timeout=60.0,
    max_retries=2,
)

Длинные генерации могут занимать много времени, поэтому очень короткий таймаут приводит к ошибкам, похожим на сбои сервиса. Выберите таймаут, соответствующий самому долгому ожидаемому ответу, и увеличьте его для больших объёмов вывода.

Когда вы не можете определить, какой URL или ключ использовался для запроса, включите отладочный лог SDK. В последних версиях Python-пакета читается переменная окружения OPENAI_LOG, а её установка в значение debug выводит строку запроса и статус ответа. Проверьте текущую документацию SDK для точного поведения в вашей версии и удалите переменную после этого, так как логи могут содержать конфиденциальные данные. Выведенный хост должен быть api.apicompatible.com. Если отображается что-то другое, другой экземпляр клиента или переменная окружения переопределяет вашу настройку.

Последняя привычка, экономящая время: храните базовый URL, ключ и идентификатор модели в одном модуле конфигурации. Когда все три параметра находятся в одном месте, переключение эндпоинтов становится одним редактированием и одной проверкой.

Ошибки, ломающие первый вызов

Большинство неудачных первых вызовов вызваны коротким списком причин. Пройдитесь по нему по порядку, прежде чем подозревать сервис.

В базовом URL отсутствует /v1 или есть лишний путь

SDK добавляет только имя эндпоинта, поэтому базовый URL должен заканчиваться на /v1. Использование https://api.apicompatible.com без сегмента версии или вставка полного пути /v1/chat/completions обычно приводит к ошибке «не найдено», так как конечный URL получается неверным. Выведите client.base_url, если не уверены, какой URL разрешил SDK.

Идентификатор модели всё ещё — имя от другого провайдера

Это API обслуживает одну модель, и её идентификатор — uncensored. Оставшееся имя модели от предыдущей настройки — частая причина ошибок, и его легко пропустить, когда имя находится в файле конфигурации, а не в коде. Ищите в проекте старую строку.

Ключ получен из неправильного места

Ключ от другого провайдера здесь не сработает, и наоборот. Если вы установите и OPENAI_API_KEY, и явный api_key, будет использовано явное значение. Проверьте, какой именно ключ дошёл до клиента.

Вы вызываете эндпоинт, который этот API не предлагает

API работает только с текстом. Он предоставляет /v1/chat/completions, /v1/responses и /v1/models. Нет эндпоинтов для изображений, аудио или эмбеддингов, нет дообучения, поэтому пути кода, использующие их, должны оставаться у оригинального провайдера. Анализ стоимости и компромиссов охватывает, когда такое разделение оправдано.

Тело запроса слишком велико

Максимальный размер тела запроса — 8 МБ. Длинные контексты в порядке, до 100 000 токенов всего для промпта и завершения, но base64-блоки или большие вставленные файлы могут превысить лимит тела раньше.

Проверка соединения и вашего баланса

Перед отладкой кода приложения вызовите GET /v1/models. Чистый список означает, что ключ и URL верны. Каждый ответ также содержит заголовок X-Balance-USD, который позволяет отслеживать расходы без отдельного вызова. В Python SDK используйте with_raw_response для чтения заголовков:

from openai import OpenAI

client = OpenAI(
    base_url="https://api.apicompatible.com/v1",
    api_key="YOUR_API_KEY",
)

raw = client.chat.completions.with_raw_response.create(
    model="uncensored",
    messages=[{"role": "user", "content": "ping"}],
)

print(raw.headers.get("X-Balance-USD"))
print(raw.headers.get("X-Request-Id"))
completion = raw.parse()
print(completion.choices[0].message.content)

Сохраняйте значение X-Request-Id при обращении в поддержку, так как оно идентифицирует точный запрос. Неудачные или отклонённые запросы не тарифицируются, поэтому плохой вызов ничего не стоит. Тарификация — $0.15 за 1M входных токенов и $0.50 за 1M выходных токенов из предоплаченного баланса; на странице цен есть детали, а в документации перечислены все параметры.

Распространённые вопросы

Нужен ли специальный SDK для использования пользовательского base_url?

Нет. Стандартные пакеты OpenAI для Python и Node принимают параметр base URL. Вы меняете этот параметр и ключ, сохраняя при этом существующие вызовы.

Это base_url или baseURL?

Python использует base_url, а Node.js использует baseURL. Оба принимают одинаковое значение, https://api.apicompatible.com/v1.

Какое имя модели я должен отправить?

Укажите uncensored в качестве модели. Этот API использует одну модель, поэтому выбирать не из чего.

Почему я получаю HTTP 429, выполняя всего несколько запросов?

Пробный ключ позволяет выполнять 2 параллельных запроса, а открытые потоки учитываются в этом лимите до их завершения. Завершайте потоки или перейдите на полный ключ для 8 параллельных запросов.

Получите ключ за секунды

Зарегистрируйтесь, скопируйте токен и обновите URL эндпоинта. Это вся конфигурация.

Создать аккаунт