Для получения чистого 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.

PDF‑вводы

MD версия

VEGA поддерживает обработку PDF через API /api/v1/chat/completions. PDF можно отправлять как прямые URL или base64‑закодированные data URL в массиве messages, используя тип содержимого file. Эта функция работает с любой моделью в VEGA.

Поддержка URL: отправляйте публично доступные PDF‑файлы напрямую без скачивания или кодирования.
Поддержка Base64: требуется для локальных файлов или закрытых документов, которые недоступны публично.

PDF‑файлы также работают в чат‑комнате для интерактивного тестирования.

Когда модель поддерживает ввод файлов нативно, PDF передаётся напрямую модели. Когда модель не поддерживает ввод файлов нативно, VEGA будет разбирать файл и передавать результаты разбора запрошенной модели.

Вы можете отправлять как PDF, так и другие типы файлов в одном запросе.

Конфигурация плагина

Для настройки обработки PDF используйте параметр plugins в вашем запросе. VEGA предоставляет несколько движков обработки PDF с разными возможностями и ценой:

typescript
{
  plugins: [
    {
      id: 'file-parser',
      pdf: {
        engine: 'cloudflare-ai', // или 'mistral-ocr' или 'native'
      },
    },
  ],
}

Ценообразование

VEGA предоставляет несколько движков обработки PDF:

  1. <code>"{PDFParserEngine.MistralOCR}"</code>: лучший вариант для отсканированных документов или PDF с изображениями (${MISTRAL_OCR_COST.toString()} за 1 000 страниц).
  2. <code>"{PDFParserEngine.CloudflareAI}"</code>: преобразует PDF в markdown с помощью Cloudflare Workers AI (Бесплатно).
  3. <code>"{PDFParserEngine.Native}"</code>: доступен только для моделей, которые поддерживают ввод файлов нативно (оплачивается как токены ввода).

Движок "pdf-text" устарел и автоматически перенаправляется на "cloudflare-ai". Существующие запросы, использующие "pdf-text", продолжат работать.

Стоимость OCR применяется ко всем запросам, включая BYOK. VEGA использует собственный ключ Mistral для OCR (не ваш BYOK‑ключ), поэтому плата за страницу всегда списывается с вашего аккаунта VEGA.

Если вы явно не указываете движок, VEGA сначала будет использовать нативные возможности модели по обработке файлов, а если их нет — применит движок <code>"{DEFAULT_PDF_ENGINE}"</code>.

Ограничения изображений OCR

Когда движок <code>"{PDFParserEngine.MistralOCR}"</code> извлекает изображения из PDF, VEGA запрашивает не более 8 изображений на PDF у Mistral через параметр image_limit OCR‑API и передаёт не более 8 изображений за запрос downstream‑модели. Избыточные изображения отбрасываются, при этом весь извлечённый текст сохраняется полностью.

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

Если ваша downstream‑модель не принимает изображения вообще, извлечённые OCR‑изображения полностью удаляются, и передаётся только разобранный текст.

Использование URL PDF

Для публично доступных PDF‑файлов вы можете отправлять URL напрямую без необходимости скачивать и кодировать файл:

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

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

const result = await client.chat.send({
  model: '{{MODEL}}',
  messages: [
    {
      role: 'user',
      content: [
        {
          type: 'text',
          text: 'What are the main points in this document?',
        },
        {
          type: 'file',
          file: {
            filename: 'document.pdf',
            fileData: 'https://bitcoin.org/bitcoin.pdf',
          },
        },
      ],
    },
  ],
  // Optional: Configure PDF processing engine
  plugins: [
    {
      id: 'file-parser',
      pdf: {
        engine: '{{ENGINE}}',
      },
    },
  ],
  stream: false,
});

console.log(result);
python
import requests
import json

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

messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": "What are the main points in this document?"
            },
            {
                "type": "file",
                "file": {
                    "filename": "document.pdf",
                    "file_data": "https://bitcoin.org/bitcoin.pdf"
                }
            },
        ]
    }
]

# Optional: Configure PDF processing engine
plugins = [
    {
        "id": "file-parser",
        "pdf": {
            "engine": "{{ENGINE}}"
        }
    }
]

payload = {
    "model": "{{MODEL}}",
    "messages": messages,
    "plugins": plugins
}

response = requests.post(url, headers=headers, json=payload)
print(response.json())
typescript
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: [
          {
            type: 'text',
            text: 'What are the main points in this document?',
          },
          {
            type: 'file',
            file: {
              filename: 'document.pdf',
              file_data: 'https://bitcoin.org/bitcoin.pdf',
            },
          },
        ],
      },
    ],
    // Optional: Configure PDF processing engine
    plugins: [
      {
        id: 'file-parser',
        pdf: {
          engine: '{{ENGINE}}',
        },
      },
    ],
  }),
});

const data = await response.json();
console.log(data);

URL‑адреса PDF работают со всеми движками обработки. Для Mistral OCR URL передаётся напрямую в сервис. Для остальных движков VEGA скачивает PDF и обрабатывает его внутренне.

Использование PDF, закодированных в Base64

Для локальных PDF‑файлов или когда необходимо отправить содержимое PDF напрямую, вы можете закодировать файл в base64:

python
import requests
import json
import base64
from pathlib import Path

def encode_pdf_to_base64(pdf_path):
    with open(pdf_path, "rb") as pdf_file:
        return base64.b64encode(pdf_file.read()).decode('utf-8')

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

# Read and encode the PDF
pdf_path = "path/to/your/document.pdf"
base64_pdf = encode_pdf_to_base64(pdf_path)
data_url = f"data:application/pdf;base64,{base64_pdf}"

messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": "What are the main points in this document?"
            },
            {
                "type": "file",
                "file": {
                    "filename": "document.pdf",
                    "file_data": data_url
                }
            },
        ]
    }
]

# Optional: Configure PDF processing engine
# PDF parsing will still work even if the plugin is not explicitly set
plugins = [
    {
        "id": "file-parser",
        "pdf": {
            "engine": "{{ENGINE}}"  # defaults to "{{DEFAULT_PDF_ENGINE}}". See Pricing above
        }
    }
]

payload = {
    "model": "{{MODEL}}",
    "messages": messages,
    "plugins": plugins
}

response = requests.post(url, headers=headers, json=payload)
print(response.json())
typescript
async function encodePDFToBase64(pdfPath: string): Promise<string> {
  const pdfBuffer = await fs.promises.readFile(pdfPath);
  const base64PDF = pdfBuffer.toString('base64');
  return `data:application/pdf;base64,${base64PDF}`;
}

// Read and encode the PDF
const pdfPath = 'path/to/your/document.pdf';
const base64PDF = await encodePDFToBase64(pdfPath);

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: [
          {
            type: 'text',
            text: 'What are the main points in this document?',
          },
          {
            type: 'file',
            file: {
              filename: 'document.pdf',
              file_data: base64PDF,
            },
          },
        ],
      },
    ],
    // Optional: Configure PDF processing engine
    // PDF parsing will still work even if the plugin is not explicitly set
    plugins: [
      {
        id: 'file-parser',
        pdf: {
          engine: '{{ENGINE}}', // defaults to "{{DEFAULT_PDF_ENGINE}}". See Pricing above
        },
      },
    ],
  }),
});

const data = await response.json();
console.log(data);

Пропуск расходов на повторный разбор

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

Как переиспользовать аннотации файла:

python
import requests
import json
import base64
from pathlib import Path

# First, encode and send the PDF
def encode_pdf_to_base64(pdf_path):
    with open(pdf_path, "rb") as pdf_file:
        return base64.b64encode(pdf_file.read()).decode('utf-8')

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

# Read and encode the PDF
pdf_path = "path/to/your/document.pdf"
base64_pdf = encode_pdf_to_base64(pdf_path)
data_url = f"data:application/pdf;base64,{base64_pdf}"

# Initial request with the PDF
messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": "What are the main points in this document?"
            },
            {
                "type": "file",
                "file": {
                    "filename": "document.pdf",
                    "file_data": data_url
                }
            },
        ]
    }
]

payload = {
    "model": "{{MODEL}}",
    "messages": messages
}

response = requests.post(url, headers=headers, json=payload)
response_data = response.json()

# Store the annotations from the response
file_annotations = None
if response_data.get("choices") and len(response_data["choices"]) > 0:
    if "annotations" in response_data["choices"][0]["message"]:
        file_annotations = response_data["choices"][0]["message"]["annotations"]

# Follow-up request using the annotations (without sending the PDF again)
if file_annotations:
    follow_up_messages = [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "What are the main points in this document?"
                },
                {
                    "type": "file",
                    "file": {
                        "filename": "document.pdf",
                        "file_data": data_url
                    }
                }
            ]
        },
        {
            "role": "assistant",
            "content": "The document contains information about...",
            "annotations": file_annotations
        },
        {
            "role": "user",
            "content": "Can you elaborate on the second point?"
        }
    ]

    follow_up_payload = {
        "model": "{{MODEL}}",
        "messages": follow_up_messages
    }

    follow_up_response = requests.post(url, headers=headers, json=follow_up_payload)
    print(follow_up_response.json())
typescript
import fs from 'fs/promises';

async function encodePDFToBase64(pdfPath: string): Promise<string> {
  const pdfBuffer = await fs.readFile(pdfPath);
  const base64PDF = pdfBuffer.toString('base64');
  return `data:application/pdf;base64,${base64PDF}`;
}

// Initial request with the PDF
async function processDocument() {
  // Read and encode the PDF
  const pdfPath = 'path/to/your/document.pdf';
  const base64PDF = await encodePDFToBase64(pdfPath);

  const initialResponse = 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: [
              {
                type: 'text',
                text: 'What are the main points in this document?',
              },
              {
                type: 'file',
                file: {
                  filename: 'document.pdf',
                  file_data: base64PDF,
                },
              },
            ],
          },
        ],
      }),
    },
  );

  const initialData = await initialResponse.json();

  // Store the annotations from the response
  let fileAnnotations = null;
  if (initialData.choices && initialData.choices.length > 0) {
    if (initialData.choices[0].message.annotations) {
      fileAnnotations = initialData.choices[0].message.annotations;
    }
  }

  // Follow-up request using the annotations (without sending the PDF again)
  if (fileAnnotations) {
    const followUpResponse = 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: [
                {
                  type: 'text',
                  text: 'What are the main points in this document?',
                },
                {
                  type: 'file',
                  file: {
                    filename: 'document.pdf',
                    file_data: base64PDF,
                  },
                },
              ],
            },
            {
              role: 'assistant',
              content: 'The document contains information about...',
              annotations: fileAnnotations,
            },
            {
              role: 'user',
              content: 'Can you elaborate on the second point?',
            },
          ],
        }),
      },
    );

    const followUpData = await followUpResponse.json();
    console.log(followUpData);
  }
}

processDocument();

Когда вы включаете аннотации файла из предыдущего ответа в последующие запросы, VEGA будет использовать эту предварительно разобранную информацию вместо повторного разбора PDF, что экономит время обработки и затраты. Это особенно полезно для больших документов или при использовании движка mistral-ocr, который влечёт дополнительные расходы.

Схема аннотаций файла

Когда VEGA разбирает PDF, ответ включает аннотации файла в сообщении ассистента. Ниже приведён тип TypeScript для схемы аннотации:

typescript
type FileAnnotation = {
  type: 'file';
  file: {
    hash: string;           // Уникальный хеш, идентифицирующий разобранный файл
    name?: string;          // Исходное имя файла (необязательно)
    content: ContentPart[]; // Разобранное содержимое файла
  };
};

type ContentPart =
  | { type: 'text'; text: string }
  | { type: 'image_url'; image_url: { url: string } };

Массив content содержит разобранное содержимое PDF, которое может включать текстовые блоки и изображения (в виде base64 data URL). Поле hash уникально идентифицирует содержимое разобранного файла и используется для пропуска повторного разбора, когда вы включаете аннотацию в последующие запросы.

Формат ответа

API вернёт ответ в следующем формате:

json
{
  "id": "gen-1234567890",
  "provider": "DeepInfra",
  "model": "google/gemma-3-27b-it",
  "object": "chat.completion",
  "created": 1234567890,
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "The document discusses...",
        "annotations": [
          {
            "type": "file",
            "file": {
              "hash": "abc123...",
              "name": "document.pdf",
              "content": [
                { "type": "text", "text": "Parsed text content..." },
                { "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }
              ]
            }
          }
        ]
      }
    }
  ],
  "usage": {
    "prompt_tokens": 1000,
    "completion_tokens": 100,
    "total_tokens": 1100
  }
}

Ошибочные ответы с разобранными аннотациями

Если VEGA успешно разбирает ваш PDF, но каждый провайдер вывода затем не может сгенерировать завершение, ответ с ошибкой всё равно будет включать разобранные аннотации в error.metadata.file_annotations. Структура совпадает с FileAnnotation, описанным выше, поэтому вы можете передать тот же массив обратно в VEGA при повторной попытке, чтобы пропустить повторный разбор.

Это относится к движкам <code>"{PDFParserEngine.MistralOCR}"</code> и <code>"{PDFParserEngine.CloudflareAI}"</code>, которые разбирают PDF перед отправкой модели. Движок <code>"{PDFParserEngine.Native}"</code> не создаёт аннотаций, потому что файл передаётся напрямую модели.

json
{
  "error": {
    "code": 502,
    "message": "Provider returned an error",
    "metadata": {
      "file_annotations": [
        {
          "type": "file",
          "file": {
            "hash": "abc123...",
            "name": "document.pdf",
            "content": [
              { "type": "text", "text": "Parsed text content..." }
            ]
          }
        }
      ]
    }
  }
}

Когда вы читаете аннотации как из успешного, так и из ошибочного пути, удаляйте дубликаты по file.hash — хеш стабилен для обеих форм для одного и того же разобранного файла:

typescript
function isFileAnnotation(value: unknown): value is FileAnnotation {
  if (typeof value !== 'object' || value === null) return false;
  const candidate = value as { type?: unknown; file?: { hash?: unknown } };
  return (
    candidate.type === 'file' &&
    typeof candidate.file?.hash === 'string'
  );
}

function extractFileAnnotations(response: unknown): FileAnnotation[] {
  if (typeof response !== 'object' || response === null) return [];

  const root = response as {
    choices?: Array<{ message?: { annotations?: unknown[] } }>;
    error?: { metadata?: { file_annotations?: unknown[] } };
  };

  const fromMessage = root.choices?.[0]?.message?.annotations ?? [];
  const fromError = root.error?.metadata?.file_annotations ?? [];

  const seen = new Set<string>();
  const out: FileAnnotation[] = [];
  for (const a of [...fromMessage, ...fromError]) {
    if (isFileAnnotation(a) && !seen.has(a.file.hash)) {
      seen.add(a.file.hash);
      out.push(a);
    }
  }
  return out;
}