API

Справочник API

Схемы запросов и ответов очень похожи на OpenAI Chat API. AITUNNEL нормализует их для всех моделей и провайдеров — достаточно выучить одну.

Индекс документации

Полный список статей — /llms.txt. К любой странице добавьте .md, чтобы получить исходный markdown без парсинга HTML.

Этот справочник про POST https://api.aitunnel.ru/v1/chat/completions. Те же поля принимают /responses и /messages. Картинки, видео, речь и эмбеддинги — в введении. Понимание и генерация картинок — картинки. PDF в чате — PDF. Понимание и генерация видео — видео. Аудио во входе и выходе чата — аудио. Озвучка текста — озвучка. Распознавание речи — распознавание речи. Методы кабинета — в AITUNNEL API.

Запросы

Схема запроса

Тело POST на /chat/completions:

TypeScript
// Определения подтипов — ниже
type Request = {
  // Нужно одно из двух: messages или prompt
  messages?: Message[];
  prompt?: string;

  // Имя модели, пресета или слаг provider/model.
  // "auto" — AITUNNEL выберет модель сам.
  model?: string;

  // JSON по схеме. Подробнее: /docs/structured-outputs
  response_format?: ResponseFormat;

  stop?: string | string[];
  stream?: boolean;

  // Сжатие слишком длинного промпта. Включено по умолчанию.
  // Подробнее: /docs/transforms
  plugins?: Plugin[];

  max_tokens?: number; // [1, context_length)
  temperature?: number; // [0, 2]

  // Function-tools и серверные инструменты (aitunnel:web_search).
  // Подробнее: /docs/tool-calling, /docs/web-search
  tools?: Tool[];
  tool_choice?: ToolChoice;
  parallel_tool_calls?: boolean;

  seed?: number;
  top_p?: number; // (0, 1]
  top_k?: number; // [1, ∞) — у OpenAI игнорируется
  frequency_penalty?: number; // [-2, 2]
  presence_penalty?: number; // [-2, 2]
  repetition_penalty?: number; // (0, 2]
  logit_bias?: { [key: number]: number };
  top_logprobs?: number;
  min_p?: number; // [0, 1]
  top_a?: number; // [0, 1]

  // Предсказанный хвост ответа — меньше задержка, если угадали.
  prediction?: { type: "content"; content: string };

  // Голосовой ответ. Подробнее: /docs/audio
  modalities?: ("text" | "audio")[];
  audio?: { voice: string; format: string };

  // Параметры AITUNNEL
  models?: string[]; // запасные модели. /docs/fallback
  provider?: { sort?: "price" | "throughput" | "latency" }; // /docs/provider
  reasoning?: Reasoning; // /docs/reasoning
  cache_control?: { type: "ephemeral"; ttl?: "5m" | "1h" }; // /docs/caching
  session_id?: string; // липкая маршрутизация кэша
  service_tier?: "flex" | "priority"; // /docs/service-tiers
};

type TextContent = {
  type: "text";
  text: string;
};

type ImageContentPart = {
  type: "image_url";
  image_url: {
    url: string; // URL или data:…;base64. Подробнее: /docs/images
    detail?: string; // по умолчанию "auto"
  };
};

type FileContentPart = {
  type: "file";
  file: {
    filename: string;
    file_data: string; // URL или data:application/pdf;base64,…  /docs/pdf
  };
};

type VideoContentPart = {
  type: "video_url";
  video_url: {
    url: string; // URL или data:video/mp4;base64. Подробнее: /docs/videos
  };
};

type AudioContentPart = {
  type: "input_audio";
  input_audio: {
    data: string; // сырой base64, не URL. Подробнее: /docs/audio
    format: string; // например "wav" или "mp3"
  };
};

type ContentPart =
  | TextContent
  | ImageContentPart
  | FileContentPart
  | VideoContentPart
  | AudioContentPart;

type Message =
  | {
      role: "user" | "assistant" | "system";
      content: string | ContentPart[];
      name?: string;
    }
  | {
      role: "tool";
      content: string;
      tool_call_id: string;
      name?: string;
    };

type FunctionDescription = {
  description?: string;
  name: string;
  parameters: object; // JSON Schema
};

type Tool =
  | { type: "function"; function: FunctionDescription }
  | { type: "aitunnel:web_search"; parameters?: object };

type ToolChoice =
  | "none"
  | "auto"
  | "required"
  | { type: "function"; function: { name: string } };

type ResponseFormat =
  | { type: "json_object" }
  | {
      type: "json_schema";
      json_schema: {
        name: string;
        strict?: boolean;
        schema: object;
      };
    };

type Plugin =
  | {
      id: "context-compression";
      enabled?: boolean;
    }
  | {
      id: "file-parser"; // PDF. /docs/pdf
      pdf?: { engine?: "mistral-ocr" | "cloudflare-ai" | "native" };
    };

type Reasoning = {
  effort?:
    | "max"
    | "xhigh"
    | "high"
    | "medium"
    | "low"
    | "minimal"
    | "none";
  max_tokens?: number;
  exclude?: boolean;
  enabled?: boolean;
};

Сэмплинг, штрафы, stop, logprobs параметры. Аудио во входе и выходе — аудио.

Структурированный вывод

response_format заставляет модель ответить JSON:

  • { "type": "json_object" } — валидный JSON, без схемы
  • { "type": "json_schema", "json_schema": { … } } — строго по вашей JSON Schema, strict: true

Подробности и примеры — структурированный вывод. Есть не у всех моделей: смотрите страницу модели.

Плагины

Плагины расширяют запрос на стороне AITUNNEL, не модели. Сжатие середины промпта включено по умолчанию. Чтобы выключить:

JSON
{
  "plugins": [{ "id": "context-compression", "enabled": false }]
}

Подробнее — оптимизация сообщений. PDF — плагин file-parser, см. PDF. Поиск в сети — не плагин, а серверный инструмент aitunnel:web_search.

Стриминг

SSE для всех моделей

Передайте stream: true. Формат — Server-Sent Events. usage приходит один раз в финальном чанке с пустым choices, перед [DONE]. Примеры и разбор ошибок — стриминг.

Нестандартные параметры

Если модель не умеет параметр (например logit_bias не у OpenAI или top_k у OpenAI), он игнорируется. Остальное уходит провайдеру как есть.

Маршрутизация модели

В ответе поле model — фактически использованная модель, не пресет и не auto.

Предзаполнение ответа

Можно попросить модель продолжить начатую реплику: последнее сообщение с role: "assistant" — это префикс, который она допишет.

curl https://api.aitunnel.ru/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-aitunnel-xxx" \
  -d '{
    "model": "gpt-5.4",
    "messages": [
      { "role": "user", "content": "В чём смысл жизни?" },
      { "role": "assistant", "content": "Не уверен, но моё лучшее предположение:" }
    ]
  }'

Ответы

Схема нормализована под OpenAI Chat Completions. choices всегда массив. При стриминге у выбора есть delta, иначе — message. Один и тот же код работает со всеми моделями.

TypeScript
type Response = {
  id: string;
  choices: (NonStreamingChoice | StreamingChoice | NonChatChoice)[];
  created: number; // Unix timestamp
  model: string; // фактически использованная модель
  object: "chat.completion" | "chat.completion.chunk";
  system_fingerprint?: string;
  usage?: ResponseUsage;
};

type ResponseUsage = {
  prompt_tokens: number;
  completion_tokens: number;
  total_tokens: number;
  cost_rub: number; // сколько списали за этот запрос
  balance: number; // остаток кабинета после списания
  prompt_tokens_details?: {
    cached_tokens?: number;
    cache_write_tokens?: number;
  };
  completion_tokens_details?: {
    reasoning_tokens?: number;
  };
  server_tool_use?: {
    web_search_requests?: number;
  };
};

type NonChatChoice = {
  finish_reason: string | null;
  text: string;
  error?: ErrorResponse;
};

type NonStreamingChoice = {
  finish_reason: string | null;
  native_finish_reason: string | null;
  message: {
    content: string | null;
    role: string;
    tool_calls?: ToolCall[];
    reasoning?: string;
    reasoning_details?: unknown[];
    audio?: { data: string; transcript?: string };
  };
  error?: ErrorResponse;
};

type StreamingChoice = {
  finish_reason: string | null;
  native_finish_reason: string | null;
  delta: {
    content: string | null;
    role?: string;
    tool_calls?: ToolCall[];
    audio?: { data?: string; transcript?: string };
  };
  error?: ErrorResponse;
};

type ErrorResponse = {
  code: number;
  message: string;
  metadata?: Record<string, unknown>;
};

type ToolCall = {
  id: string;
  type: "function";
  function: { name: string; arguments: string };
};

Пример:

JSON
{
  "id": "chatcmpl-xxxxxxxxxxxxxx",
  "choices": [
    {
      "finish_reason": "stop",
      "native_finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": "Привет!"
      }
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 4,
    "total_tokens": 16,
    "prompt_tokens_details": { "cached_tokens": 0 },
    "completion_tokens_details": { "reasoning_tokens": 0 },
    "cost_rub": 0.51,
    "balance": 1212.38933
  },
  "model": "gpt-5.4"
}

Причина завершения

finish_reason приводится к одному из: tool_calls, stop, length, content_filter, error.

Исходная строка провайдера — в native_finish_reason.

Стоимость и usage

usage всегда есть в нестриминговом ответе. При стриминге — в последнем чанке.

  • prompt_tokens / completion_tokens / total_tokens — как вернул провайдер
  • cost_rub — сколько списали за этот запрос
  • balance — остаток кабинета после списания
  • prompt_tokens_details.cached_tokens — чтение кэша промпта
  • completion_tokens_details.reasoning_tokens токены рассуждений
  • server_tool_use.web_search_requests — фактические вызовы веб-поиска

История расхода по ключу — статистика, не отдельный generation-эндпоинт.

Дальше