OpenAI Chat Completions
Подключение OpenAI SDK и любых OpenAI-совместимых клиентов к Veda AI Gateway. Потоковая передача, инструменты, структурированный вывод, рассуждения и изображения.
Veda AI Gateway реализует формат OpenAI Chat Completions. Существующий код на OpenAI SDK, AI SDK или любом OpenAI-совместимом клиенте начинает работать с моделями Veda после смены адреса и ключа.
Адрес и аутентификация
| Параметр | Значение |
|---|---|
| Базовый адрес | $VEDA_BASE_URL/v1 |
| Маршрут | POST /v1/chat/completions |
| Аутентификация | Authorization: Bearer $VEDA_API_KEY |
Первый запрос
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.VEDA_API_KEY,
baseURL: `${process.env.VEDA_BASE_URL}/v1`,
});
const completion = await client.chat.completions.create({
model: "anthropic/claude-sonnet-5.5",
messages: [
{ role: "system", content: "Отвечай кратко." },
{ role: "user", content: "Почему небо голубое?" },
],
});
console.log(completion.choices[0].message.content);Ответ приходит в стандартном формате: текст в choices[0].message.content, причина остановки в choices[0].finish_reason, расход токенов в usage.
Потоковая передача
С "stream": true ответ приходит частями по Server-Sent Events. Каждое событие содержит объект chat.completion.chunk с фрагментом текста в choices[0].delta.content, поток заканчивается строкой data: [DONE].
const stream = await client.chat.completions.create({
model: "openai/gpt-6.1-sol",
messages: [{ role: "user", content: "Напиши короткое стихотворение о море." }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}Если клиент закрывает соединение, Veda прекращает чтение ответа модели.
Вызов инструментов
Опишите функции в tools. Если модель решит вызвать функцию, ответ придёт с finish_reason: "tool_calls" и списком message.tool_calls. Выполните функцию у себя и отправьте результат сообщением с ролью tool и тем же tool_call_id.
const tools = [
{
type: "function" as const,
function: {
name: "getWeather",
description: "Текущая погода в городе",
parameters: {
type: "object",
properties: { location: { type: "string", description: "Город и страна" } },
required: ["location"],
},
},
},
];
const messages: OpenAI.ChatCompletionMessageParam[] = [
{ role: "user", content: "Какая погода в Казани?" },
];
const first = await client.chat.completions.create({
model: "anthropic/claude-sonnet-5.5",
messages,
tools,
});
const assistant = first.choices[0].message;
messages.push(assistant);
for (const call of assistant.tool_calls ?? []) {
if (call.type !== "function") continue;
const { location } = JSON.parse(call.function.arguments);
const result = { location, temperatureC: 18 }; // здесь ваш код
messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify(result) });
}
const second = await client.chat.completions.create({
model: "anthropic/claude-sonnet-5.5",
messages,
tools,
});
console.log(second.choices[0].message.content);tool_choice управляет выбором: "auto" (по умолчанию), "none", "required" или конкретная функция. Вызов инструментов работает, если модель его поддерживает; это указано на её странице в каталоге.
Структурированный вывод
Чтобы получить JSON по схеме, передайте response_format с типом json_schema. Вариант { "type": "json_object" } просит любой корректный JSON без схемы.
const completion = await client.chat.completions.create({
model: "openai/gpt-6.1-sol",
messages: [{ role: "user", content: "Извлеки данные: Анна, 34 года, Новосибирск." }],
response_format: {
type: "json_schema",
json_schema: {
name: "person",
strict: true,
schema: {
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer" },
city: { type: "string" },
},
required: ["name", "age", "city"],
additionalProperties: false,
},
},
},
});
const person = JSON.parse(completion.choices[0].message.content ?? "{}");Структурированный вывод работает, если модель поддерживает response_format. Проверяйте результат на своей модели перед тем, как полагаться на строгое соответствие схеме.
Рассуждения
Для моделей с рассуждениями глубина задаётся объектом reasoning. Используйте одно из полей:
| Поле | Пример | Назначение |
|---|---|---|
effort | { "effort": "high" } | Уровень усилий, если модель поддерживает уровни |
max_tokens | { "max_tokens": 2048 } | Бюджет токенов на рассуждение |
enabled | { "enabled": true } | Включить рассуждение с настройками модели |
curl "$VEDA_BASE_URL/v1/chat/completions" \
-H "Authorization: Bearer $VEDA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-6.1-sol",
"messages": [{ "role": "user", "content": "Объясни парадокс Монти Холла по шагам." }],
"reasoning": { "effort": "high" }
}'Какие уровни поддерживает модель, видно на её странице в каталоге: там приведён готовый пример запроса.
Изображения на входе
Моделям, которые принимают изображения, передайте content массивом частей: текст и image_url. Адрес может быть публичным URL или data URL с base64.
const completion = await client.chat.completions.create({
model: "google/gemini-3.8-flash",
messages: [
{
role: "user",
content: [
{ type: "text", text: "Опиши это изображение." },
{ type: "image_url", image_url: { url: "https://example.com/photo.jpg" } },
],
},
],
});В журнале запросов встроенные данные (base64 и длинные data URL) заменяются пометкой с размером, само изображение не сохраняется.
Параметры
Veda передаёт тело запроса модели без изменений. Ниже основные параметры формата; поддержку необязательных параметров определяет модель.
| Параметр | Тип | Описание |
|---|---|---|
model | string | Обязательный. Идентификатор модели, например openai/gpt-6.1-sol |
messages | array | Обязательный. Сообщения с ролями system, user, assistant, tool |
stream | boolean | Потоковая передача. По умолчанию false |
max_tokens, max_completion_tokens | integer | Ограничение длины ответа |
temperature | number | Случайность ответа |
top_p | number | Ядерная выборка |
stop | string или array | Последовательности остановки |
seed | integer | Зерно для воспроизводимости, если модель его учитывает |
tools, tool_choice, parallel_tool_calls | array, string или object, boolean | Вызов инструментов |
response_format | object | Структурированный вывод |
reasoning | object | Настройки рассуждений |
Ошибки
Ошибки проверки ключа и лимитов приходят в формате { "error": { "code": ..., "message": ... } }:
{ "error": { "code": 429, "message": "Rate limit exceeded" } }| Код | Когда |
|---|---|
401 | Ключ неверен, отключён, удалён или истёк |
402 | Достигнут лимит расходов ключа |
429 | Больше 60 запросов в минуту на один ключ |
Полный список кодов приведён в обзоре API. OpenAI SDK выбрасывает для них исключения AuthenticationError, RateLimitError и другие подклассы APIError.
Атрибуция приложения
Чтобы разделять статистику нескольких приложений, передавайте заголовки X-Veda-App-*. В OpenAI SDK это делается через defaultHeaders:
const client = new OpenAI({
apiKey: process.env.VEDA_API_KEY,
baseURL: `${process.env.VEDA_BASE_URL}/v1`,
defaultHeaders: { "X-Veda-App-Id": "my-assistant", "X-Veda-App-Name": "My Assistant" },
});Подробнее: Статистика приложений.