Для получения чистого Markdown любой страницы добавьте .md к URL страницы.
Для полного индекса документации см. https://api.vega.chat/docs/llms.txt.
Для полного содержания документации см. https://api.vega.chat/docs/llms-full.txt.
Для интеграции AI‑клиентов (Claude Code, Cursor и др.) подключитесь к MCP‑серверу по адресу https://api.vega.chat/docs/_mcp/server.

Потоковая передача

MD версия

VEGA API позволяет передавать ответы в режиме потока от любой модели. Это удобно для создания чат‑интерфейсов или других приложений, где UI должен обновляться по мере генерации ответа моделью.

Чтобы включить потоковую передачу, установите параметр stream в true в вашем запросе. Модель будет передавать ответ клиенту кусками, а не возвращать весь ответ сразу.

Ниже пример того, как потокировать ответ и обрабатывать его:

typescript
import { OpenRouter } from '@openrouter/sdk';

const client = new OpenRouter({
  apiKey: '{{API_KEY_REF}}',
});

const question = 'How would you build the tallest building ever?';

const stream = await client.chat.send({
  model: '{{MODEL}}',
  messages: [{ role: 'user', content: question }],
  stream: true,
});

for await (const chunk of stream) {
  const content = chunk.choices?.[0]?.delta?.content;
  if (content) {
    console.log(content);
  }

  // Финальный кусок содержит статистику использования
  if (chunk.usage) {
    console.log('Usage:', chunk.usage);
  }
}
python
import requests
import json

question = "How would you build the tallest building ever?"

url = "https://api.vega.chat/api/v1/chat/completions"
headers = {
  "Authorization": f"Bearer {{API_KEY_REF}}",
  "Content-Type": "application/json"
}

payload = {
  "model": "{{MODEL}}",
  "messages": [{"role": "user", "content": question}],
  "stream": True
}

buffer = ""
with requests.post(url, headers=headers, json=payload, stream=True) as r:
  for chunk in r.iter_content(chunk_size=1024, decode_unicode=True):
    buffer += chunk
    while True:
      try:
        # Находим следующую полную строку SSE
        line_end = buffer.find('\n')
        if line_end == -1:
          break

        line = buffer[:line_end].strip()
        buffer = buffer[line_end + 1:]

        if line.startswith('data: '):
          data = line[6:]
          if data == '[DONE]':
            break

          try:
            data_obj = json.loads(data)
            content = data_obj["choices"][0]["delta"].get("content")
            if content:
              print(content, end="", flush=True)
          except json.JSONDecodeError:
            pass
      except Exception:
        break
typescript
const question = 'How would you build the tallest building ever?';
const response = await fetch('https://api.vega.chat/api/v1/chat/completions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${API_KEY_REF}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: '{{MODEL}}',
    messages: [{ role: 'user', content: question }],
    stream: true,
  }),
});

const reader = response.body?.getReader();
if (!reader) {
  throw new Error('Response body is not readable');
}

const decoder = new TextDecoder();
let buffer = '';

try {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    // Добавляем новый кусок к буферу
    buffer += decoder.decode(value, { stream: true });

    // Обрабатываем полные строки из буфера
    while (true) {
      const lineEnd = buffer.indexOf('\n');
      if (lineEnd === -1) break;

      const line = buffer.slice(0, lineEnd).trim();
      buffer = buffer.slice(lineEnd + 1);

      if (line.startsWith('data: ')) {
        const data = line.slice(6);
        if (data === '[DONE]') break;

        try {
          const parsed = JSON.parse(data);
          const content = parsed.choices[0].delta.content;
          if (content) {
            console.log(content);
          }
        } catch (e) {
          // Игнорируем некорректный JSON
        }
      }
    }
  }
} finally {
  reader.cancel();
}

Дополнительная информация

Для SSE (Server-Sent Events) потоков VEGA иногда отправляет комментарии, чтобы предотвратить тайм‑ауты соединения. Такие комментарии выглядят так:

text
: OPENROUTER PROCESSING

Полезную нагрузку комментариев можно безопасно игнорировать в соответствии со спецификацией SSE. Тем не менее, при желании вы можете использовать их для улучшения UX, например, показывая динамический индикатор загрузки.

Идентификатор генерации возвращается в заголовке ответа X-Generation-Id для всех конечных точек (chat completions, completions, responses и messages) и может быть полезен для отладки и корреляции запросов.

Некоторые реализации SSE‑клиентов могут не разбирать полезную нагрузку согласно спецификации, что приводит к необработанной ошибке при JSON.stringify не‑JSON данных. Мы рекомендуем использовать следующие клиенты:

Отмена потока

Запросы с потоковой передачой можно отменить, прервав соединение. Для поддерживаемых провайдеров это немедленно останавливает обработку модели и биллинг.

Поддерживается

  • OpenAI, Azure, Anthropic
  • Fireworks, Mancer, Recursal
  • AnyScale, Lepton, OctoAI
  • Novita, DeepInfra, Together
  • Cohere, Hyperbolic, Infermatic
  • Avian, XAI, Cloudflare
  • SFCompute, Nineteen, Liquid
  • Friendli, Chutes, DeepSeek

В данный момент не поддерживается

  • AWS Bedrock, Groq, Modal
  • Google, Google AI Studio, Minimax
  • HuggingFace, Replicate, Perplexity
  • Mistral, AI21, Featherless
  • Lynn, Lambda, Reflection
  • SambaNova, Inflection, ZeroOneAI
  • AionLabs, Alibaba, Nebius
  • Kluster, Targon, InferenceNet

Для реализации отмены потока:

typescript
import { OpenRouter } from '@openrouter/sdk';

const client = new OpenRouter({
  apiKey: '{{API_KEY_REF}}',
});

const controller = new AbortController();

try {
  const stream = await client.chat.send({
    model: '{{MODEL}}',
    messages: [{ role: 'user', content: 'Write a story' }],
    stream: true,
  }, {
    signal: controller.signal,
  });

  for await (const chunk of stream) {
    const content = chunk.choices?.[0]?.delta?.content;
    if (content) {
      console.log(content);
    }
  }
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Stream cancelled');
  } else {
    throw error;
  }
}

// Чтобы отменить поток:
controller.abort();
python
import requests
from threading import Event, Thread

def stream_with_cancellation(prompt: str, cancel_event: Event):
    with requests.Session() as session:
        response = session.post(
            "https://api.vega.chat/api/v1/chat/completions",
            headers={"Authorization": f"Bearer {{API_KEY_REF}}"},
            json={"model": "{{MODEL}}", "messages": [{"role": "user", "content": prompt}], "stream": True},
            stream=True
        )

        try:
            for line in response.iter_lines():
                if cancel_event.is_set():
                    response.close()
                    return
                if line:
                    print(line.decode(), end="", flush=True)
        finally:
            response.close()

# Пример использования:
cancel_event = Event()
stream_thread = Thread(target=lambda: stream_with_cancellation("Write a story", cancel_event))
stream_thread.start()

# Чтобы отменить поток:
cancel_event.set()
typescript
const controller = new AbortController();

try {
  const response = await fetch(
    'https://api.vega.chat/api/v1/chat/completions',
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${{{API_KEY_REF}}}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        model: '{{MODEL}}',
        messages: [{ role: 'user', content: 'Write a story' }],
        stream: true,
      }),
      signal: controller.signal,
    },
  );

  // Обрабатываем поток...
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Stream cancelled');
  } else {
    throw error;
  }
}

// Чтобы отменить поток:
controller.abort();

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

Обработка ошибок во время потоковой передачи

VEGA обрабатывает ошибки по‑разному в зависимости от того, когда они происходят в процессе потоковой передачи:

Ошибки до отправки любых токенов

Если ошибка происходит до того, как какие‑либо токены были переданы клиенту, VEGA возвращает стандартный JSON‑ответ об ошибке с соответствующим HTTP‑статусом. Формат ошибки выглядит так:

json
{
  "error": {
    "code": 400,
    "message": "Invalid model specified"
  }
}

Распространённые коды статуса:

  • 400: Bad Request (некорректные параметры)
  • 401: Unauthorized (неверный API‑ключ)
  • 402: Payment Required (недостаточно кредитов)
  • 429: Too Many Requests (превышен лимит запросов)
  • 502: Bad Gateway (ошибка провайдера)
  • 503: Service Unavailable (нет доступных провайдеров)

Ошибки после отправки токенов (Mid-Stream)

Если ошибка происходит после того, как часть токенов уже передана клиенту, VEGA не может изменить HTTP‑статус (уже отправлен 200 OK). Вместо этого ошибка передаётся как Server‑Sent Event (SSE) с унифицированной структурой:

text
data: {"id":"cmpl-abc123","object":"chat.completion.chunk","created":1234567890,"model":"openai/gpt-4o","provider":"openai","error":{"code":"server_error","message":"Provider disconnected unexpectedly"},"choices":[{"index":0,"delta":{"content":""},"finish_reason":"error"}]}

Ключевые особенности ошибок mid‑stream:

  • Ошибка появляется на верхнем уровне вместе со стандартными полями ответа (id, object, created и т.д.)
  • В массиве choices присутствует finish_reason: "error" для корректного завершения потока
  • HTTP‑статус остаётся 200 OK, так как заголовки уже отправлены
  • После этого события поток завершается

Примеры кода

Ниже показано, как правильно обрабатывать оба типа ошибок в реализации потоковой передачи:

typescript
import { OpenRouter } from '@openrouter/sdk';

const client = new OpenRouter({
  apiKey: '{{API_KEY_REF}}',
});

async function streamWithErrorHandling(prompt: string) {
  try {
    const stream = await client.chat.send({
      model: '{{MODEL}}',
      messages: [{ role: 'user', content: prompt }],
      stream: true,
    });

    for await (const chunk of stream) {
      // Проверяем наличие ошибки в куске
      if ('error' in chunk) {
        console.error(`Stream error: ${chunk.error.message}`);
        if (chunk.choices?.[0]?.finish_reason === 'error') {
          console.log('Stream terminated due to error');
        }
        return;
      }

      // Обрабатываем обычный контент
      const content = chunk.choices?.[0]?.delta?.content;
      if (content) {
        console.log(content);
      }
    }
  } catch (error) {
    // Обрабатываем ошибки до начала потока
    console.error(`Error: ${error.message}`);
  }
}
python
import requests
import json

async def stream_with_error_handling(prompt):
    response = requests.post(
        'https://api.vega.chat/api/v1/chat/completions',
        headers={'Authorization': f'Bearer {{API_KEY_REF}}'},
        json={
            'model': '{{MODEL}}',
            'messages': [{'role': 'user', 'content': prompt}],
            'stream': True
        },
        stream=True
    )

    # Проверяем начальный HTTP‑статус для ошибок до начала потока
    if response.status_code != 200:
        error_data = response.json()
        print(f"Error: {error_data['error']['message']}")
        return

    # Обрабатываем поток и учитываем ошибки mid‑stream
    for line in response.iter_lines():
        if line:
            line_text = line.decode('utf-8')
            if line_text.startswith('data: '):
                data = line_text[6:]
                if data == '[DONE]':
                    break

                try:
                    parsed = json.loads(data)

                    # Проверяем наличие ошибки mid‑stream
                    if 'error' in parsed:
                        print(f"Stream error: {parsed['error']['message']}")
                        if parsed.get('choices', [{}])[0].get('finish_reason') == 'error':
                            print("Stream terminated due to error")
                        break

                    # Обрабатываем обычный контент
                    content = parsed['choices'][0]['delta'].get('content')
                    if content:
                        print(content, end='', flush=True)

                except json.JSONDecodeError:
                    pass
typescript
async function streamWithErrorHandling(prompt: string) {
  const response = await fetch(
    'https://api.vega.chat/api/v1/chat/completions',
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${{{API_KEY_REF}}}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        model: '{{MODEL}}',
        messages: [{ role: 'user', content: prompt }],
        stream: true,
      }),
    }
  );

  // Проверяем начальный HTTP‑статус для ошибок до начала потока
  if (!response.ok) {
    const error = await response.json();
    console.error(`Error: ${error.error.message}`);
    return;
  }

  const reader = response.body?.getReader();
  if (!reader) throw new Error('No response body');

  const decoder = new TextDecoder();
  let buffer = '';

  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });

      while (true) {
        const lineEnd = buffer.indexOf('\n');
        if (lineEnd === -1) break;

        const line = buffer.slice(0, lineEnd).trim();
        buffer = buffer.slice(lineEnd + 1);

        if (line.startsWith('data: ')) {
          const data = line.slice(6);
          if (data === '[DONE]') return;

          try {
            const parsed = JSON.parse(data);

            // Проверяем наличие ошибки mid‑stream
            if (parsed.error) {
              console.error(`Stream error: ${parsed.error.message}`);
              if (parsed.choices?.[0]?.finish_reason === 'error') {
                console.log('Stream terminated due to error');
              }
              return;
            }

            // Обрабатываем обычный контент
            const content = parsed.choices[0].delta.content;
            if (content) {
              console.log(content);
            }
          } catch (e) {
            // Игнорируем ошибки парсинга
          }
        }
      }
    }
  } finally {
    reader.cancel();
  }
}

Специфическое поведение API

Разные конечные точки API могут слегка различаться в обработке ошибок потоковой передачи:

  • OpenAI Chat Completions API: возвращает ErrorResponse напрямую, если ни один кусок не был обработан, либо включает информацию об ошибке в ответ, если часть кусков уже отправлена.
  • OpenAI Responses API: может преобразовать некоторые коды ошибок (например, context_length_exceeded) в успешный ответ с finish_reason: "length" вместо того, чтобы рассматривать их как ошибки.