OpenAI gateway · Claude / Codex
Шлюз заперт в сетевом контуре за VPN и слушает только адрес veth. Панель живёт отдельным процессом и открыта в локальную сеть.
claude-code/sonnet:high └────┬───┘ └──┬─┘ └┬─┘ бэкенд модель уровень размышлений
Без суффикса модель не думает — отвечает сразу.
Суффиксы: :low, :medium, :high,
:xhigh, :max, а :none — то же,
что без суффикса. Какие уровни поддерживает конкретная модель, видно
на вкладке «Модели».
Уровень стоит в имени модели не случайно: так он попадает в журнал и в статистику. По записи «claude-code/sonnet:low» видно, как ход выполнялся на самом деле, — поля в теле запроса такой отметки не оставляют.
curl http://10.200.0.2:4110/v1/chat/completions \
-H "Authorization: Bearer ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-code/haiku",
"messages": [{"role": "user", "content": "Привет"}]
}'
from openai import OpenAI
client = OpenAI(
base_url="http://10.200.0.2:4110/v1",
api_key="ВАШ_КЛЮЧ",
)
answer = client.chat.completions.create(
model="claude-code/opus:high",
messages=[{"role": "user", "content": "Привет"}],
)
print(answer.choices[0].message.content)
# ход мысли приезжает отдельным полем
print(answer.choices[0].message.reasoning)
import OpenAI from 'openai';
const client = new OpenAI({
baseURL: 'http://10.200.0.2:4110/v1',
apiKey: 'ВАШ_КЛЮЧ',
});
const answer = await client.chat.completions.create({
model: 'claude-code/sonnet',
messages: [{ role: 'user', content: 'Привет' }],
});
console.log(answer.choices[0].message.content);
Проще всего — суффиксом в имени модели. Но есть и поля в теле, для
клиентов, которые имя модели не трогают: reasoning_effort
как у OpenAI и объект reasoning как у OpenRouter.
{
"model": "claude-code/sonnet",
"messages": [{"role": "user", "content": "Задача посложнее"}],
"reasoning": {
"effort": "high",
"max_tokens": 6000,
"enabled": true,
"exclude": false
}
}
Старшинство: суффикс в имени модели → reasoning →
reasoning_effort. Исключения два: enabled: false
и max_tokens сильнее суффикса — они задают не глубину,
а сам режим.
{
"choices": [{
"message": {
"role": "assistant",
"content": "Ответ, который видит пользователь",
"reasoning": "Ход мысли модели — показывать отдельно"
}
}],
"usage": {
"completion_tokens_details": {"reasoning_tokens": 881}
}
}
Ход мысли едет полем reasoning, а не внутри
content: иначе клиент не отличил бы рассуждения от
ответа и показал бы всё вперемешку. В потоке для них свои кадры —
delta.reasoning.
Точный потолок задать нельзя. Нынешние модели думают
adaptive и фиксированный бюджет игнорируют: запрос с
max_tokens: 6000 давал ноль токенов на размышления, тогда
как :max — 755. Поэтому число переводится в уровень
усилия: до 2000 — low, до 8000 — medium, до 16000 — high, дальше max.
Модель сама решает, думать ли: на лёгком вопросе она честно
ответит нулём токенов даже при :max. Уровень задаёт
потолок глубины, а не обязанность.
У Codex размышления полностью не выключаются: низший работающий
уровень — low, и без суффикса используется он, а текст
рассуждений просто не возвращается.
stream = client.chat.completions.create(
model="claude-code/sonnet:high",
messages=[{"role": "user", "content": "Привет"}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta
if getattr(delta, "reasoning", None):
print("[думает]", delta.reasoning, end="")
if delta.content:
print(delta.content, end="")
Формат обычный: кадры chat.completion.chunk и
data: [DONE] в конце. У claude-code текст идёт по мере
появления, у codex и у цикла инструментов — одним кадром: там ответ
становится известен только целиком.
Расход едет в закрывающем кадре; отдельным кадром он приедет по
stream_options.include_usage.
Формат OpenAI целиком: объявляешь функции, получаешь
tool_calls, возвращаешь результаты сообщениями с ролью
tool. Только бэкенд claude-code.
{
"model": "claude-code/sonnet",
"messages": [{"role": "user", "content": "Какая погода в Томске?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Возвращает погоду в городе",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}]
}
Ответ придёт с finish_reason: "tool_calls". Дальше
присылаешь тот же диалог плюс сообщение с результатом —
{"role": "tool", "tool_call_id": "…", "content": "…"} —
и получаешь финальный ответ.
Важная особенность: пока ты считаешь результат, сессия агента висит в памяти и ждёт. Не вернёшь ничего — она отпустится по таймауту из настроек (по умолчанию 150 секунд).
{
"model": "claude-code/sonnet",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "Что на картинке?"},
{"type": "image_url",
"image_url": {"url": "data:image/png;base64,iVBOR..."}}
]
}]
}
До 8 картинок, по 8 МБ — пределы меняются в настройках. Ссылки не скачиваются, только вложенный base64: иначе шлюз стал бы открытым прокси, через который можно тянуть что угодно откуда угодно.
curl http://10.200.0.2:4110/v1/images/generations \
-H "Authorization: Bearer ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{
"model": "codex/gpt-5.6-luna",
"prompt": "кот в скафандре"
}'
Ответ — data[].b64_json. Только бэкенд codex: у него
рисование встроено инструментом. Занимает заметно дольше чата —
считай десятки секунд.
Ошибки приходят в формате OpenAI:
{"error": {"code": "…", "message": "…", "type": "…"}}.
Тело каждого вызова, включая упавшие, лежит в журнале на вкладке
«Обзор» — вместе с текстом ошибки.
Команда повторного входа — выполнить в терминале сервера.