HTTP API v3
Ниже — HTTP API версии v3: отдельный набор маршрутов с префиксом /api/v3/…. Он рассчитан на чаты с версией v3 (история в виде цепочки «run items»), генерацию ответов и работу с вложениями.
Замените BASE_URL на адрес вашего окружения (например, https://api.er-gpt.ru). Токен — тот же способ входа, что и для остального API портала (заголовок Authorization: Bearer …).
Зачем версия чата v3
- У каждого чата есть версия (
v2илиv3). Дляv3история и генерация идут через эндпоинты/api/v3/…. - Если вызвать v3 для чата другой версии, API вернёт ошибку и подскажет, какой префикс использовать.
POST /api/v3/chatвсегда создаёт чат версииv3.
Обзор маршрутов
| Префикс | Назначение |
|---|---|
/api/v3/message | История чата и генерация ответа (в т.ч. one-shot без сохранённого чата) |
/api/v3/chat | Список и настройка чатов, избранное, вложения в контексте чата |
/api/v3/attachment | Загрузка файла и список ваших вложений |
/api/v3/knowledge | То же, что /api/knowledge: базы знаний, документы, поиск (удобный дубль префикса) |
Сообщения
Получить историю — GET /api/v3/message
История чата v3 — постраничная выдача элементов run (result) и курсор в pagination.
Параметры запроса
Endpoint: GET https://api.er-gpt.ru/api/v3/message
50.pagination.cursor предыдущего ответа. При первом запросе не передаётся.Ответ
Тело ответа — JSON с полями result (массив строк истории) и pagination (в т.ч. следующий cursor).
GET или null, если страниц больше нет.Поля одного элемента result[]
type из item, если оно есть (удобно для списков без разбора JSON).status из item, если оно есть.output Responses API (см. ниже).Содержимое item
- Вход пользователя / системы:
type: "message",role—user|system|developer, массивcontentв формате частей OpenAI Responses (input_text,input_imageи т.д.). Встроенные id сообщений OpenAI в этих строках могут отсутствовать. - Вывод модели: те же объекты, что в массиве
outputзавершённого ответа Responses API (ответ ассистента,function_call,reasoning,file_search_callи др. — см. Response output items в документации OpenAI).
В TypeScript удобно сочетать типы пакета openai (ResponseOutputItem и смежные) с узким интерфейсом для пользовательского type: "message".
Примеры реализации
Ниже — разнесение по файлам: модели, метод с httpx / axios, точка входа (как на странице Одиночные запросы).
import logging
import os
import httpx
from methods import fetch_message_history
logging.basicConfig(level=logging.INFO)
BASE_URL = os.getenv("API_BASE_URL", "https://api.er-gpt.ru")
API_TOKEN = os.getenv("API_TOKEN")
CHAT_ID = os.getenv("CHAT_ID", "00000000-0000-4000-8000-000000000000")
def main() -> None:
if not API_TOKEN:
return
headers = {"Authorization": f"Bearer {API_TOKEN}"}
with httpx.Client(base_url=BASE_URL, headers=headers, timeout=60.0) as client:
page = fetch_message_history(client, chat_id=CHAT_ID, limit=50)
if page:
for row in page["result"]:
item = row.get("item") or {}
print(row["item_type"], row.get("item_status"), item.get("type"))
if __name__ == "__main__":
main()
import axios from "axios";
import { fetchMessageHistory } from "./methods";
const BASE_URL = process.env.API_BASE_URL || "https://api.er-gpt.ru";
const API_TOKEN = process.env.API_TOKEN || "YOUR_ACCESS_TOKEN";
const CHAT_ID =
process.env.CHAT_ID || "00000000-0000-4000-8000-000000000000";
async function main() {
const client = axios.create({
baseURL: BASE_URL,
headers: {
Authorization: `Bearer ${API_TOKEN}`,
},
timeout: 60_000,
});
const page = await fetchMessageHistory(client, {
chat_id: CHAT_ID,
limit: 50,
});
if (page) {
for (const row of page.result) {
const t = row.item && typeof row.item === "object" ? (row.item as { type?: string }).type : undefined;
console.log(row.item_type, row.item_status, t);
}
}
}
main();
Сгенерировать ответ в чате — POST /api/v3/message/generate
Один URL: в теле запроса поле stream выбирает формат ответа.
Обязательный заголовок: Idempotency-Key — любая уникальная строка на один пользовательский «ход» (UUID подойдёт). Повтор с тем же ключом не создаст дубликат того же шага.
Тело (основное)
| Поле | Смысл |
|---|---|
stream | false — один JSON с готовым ответом; true — поток SSE |
chat_id, assistant_id, model_id | Чат, ассистент и модель из каталога |
input | Строка или массив элементов в формате OpenAI Responses (входные сообщения и медиа) |
parent_id | Необязательно — связь с предыдущим run |
think | Включить размышления модели (отдельные фрагменты в потоке, если модель их поддерживает); флаг think |
rag | См. раздел RAG ниже |
include_tools / exclude_tools | Ограничить список инструментов (нельзя указать оба сразу) |
max_tokens, temperature, top_p, presence_penalty | Параметры генерации |
При stream: false в ответе приходит JSON финального объекта (как поле response у события response.completed в потоке ниже). При stream: true тело ответа — SSE (Content-Type: text/event-stream): блоки вида event: <имя_события> + одна или несколько строк data: с JSON, затем пустая строка. Имена событий и структура JSON в data совпадают с потоковыми событиями OpenAI Responses API (тот же контракт, что у типа ResponseStreamEvent в официальных SDK: response.created, response.output_text.delta, response.reasoning_text.delta, response.output_item.added, response.completed и т.д.). На стороне сервера эти события собираются из типов openai.types.responses.*, поэтому удобнее всего читать поток официальным клиентом OpenAI с base_url на хост ERGPT и низкоуровневым post(..., stream=True) — ниже примеры для Python и Node.
import uuid
import httpx
BASE_URL = "https://api.er-gpt.ru"
TOKEN = "ВАШ_ТОКЕН"
CHAT_ID = "00000000-0000-4000-8000-000000000001"
ASSISTANT_ID = "00000000-0000-4000-8000-000000000002"
MODEL_ID = "имя-модели-из-каталога"
payload = {
"stream": False,
"chat_id": CHAT_ID,
"assistant_id": ASSISTANT_ID,
"model_id": MODEL_ID,
"input": "Кратко объясни, что такое RAG.",
"rag": False,
}
r = httpx.post(
f"{BASE_URL}/api/v3/message/generate",
json=payload,
headers={
"Authorization": f"Bearer {TOKEN}",
"Idempotency-Key": str(uuid.uuid4()),
"Content-Type": "application/json",
},
timeout=120.0,
)
r.raise_for_status()
print(r.json())
const BASE_URL = 'https://api.er-gpt.ru';
const TOKEN = 'ВАШ_ТОКЕН';
const CHAT_ID = '00000000-0000-4000-8000-000000000001';
const ASSISTANT_ID = '00000000-0000-4000-8000-000000000002';
const MODEL_ID = 'имя-модели-из-каталога';
const payload = {
stream: false,
chat_id: CHAT_ID,
assistant_id: ASSISTANT_ID,
model_id: MODEL_ID,
input: 'Кратко объясни, что такое RAG.',
rag: false,
};
const res = await fetch(`${BASE_URL}/api/v3/message/generate`, {
method: 'POST',
headers: {
Authorization: `Bearer ${TOKEN}`,
'Idempotency-Key': crypto.randomUUID(),
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(await res.text());
console.log(await res.json());
Поток SSE (stream: true)
Зависимости: Python — pip install "openai>=1.76" (лучше актуальную ветку 2.x, как в нашем бэкенде); Node — npm install openai. Укажите base_url / baseURL без суффикса /v1 (например https://api.er-gpt.ru), иначе клиент по умолчанию ходит на api.openai.com/v1.
Используется низкоуровневый post: тот же разбор SSE и типизация событий, что и у стрима responses.create у OpenAI, но путь и тело запроса — наши (/api/v3/message/generate). Для one-shot с stream: true всё то же самое, путь /api/v3/message/one-shot, заголовок Idempotency-Key не нужен.
import uuid
from openai import OpenAI, Stream
from openai.types.responses.response_stream_event import ResponseStreamEvent
BASE_URL = "https://api.er-gpt.ru"
TOKEN = "ВАШ_ТОКЕН"
payload = {
"stream": True,
"chat_id": "00000000-0000-4000-8000-000000000001",
"assistant_id": "00000000-0000-4000-8000-000000000002",
"model_id": "имя-модели-из-каталога",
"input": "Привет!",
}
client = OpenAI(api_key=TOKEN, base_url=BASE_URL, timeout=120.0)
stream = client.post(
"/api/v3/message/generate",
cast_to=ResponseStreamEvent,
body=payload,
stream=True,
stream_cls=Stream[ResponseStreamEvent],
options={"headers": {"Idempotency-Key": str(uuid.uuid4())}},
)
with stream:
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
elif event.type == "response.reasoning_text.delta":
print(event.delta, end="", flush=True)
elif event.type == "response.completed":
print("\n--- done ---")
break
Пример для Node.js 18+ (пакет openai по умолчанию не разрешает браузер без dangerouslyAllowBrowser).
import OpenAI from 'openai';
import type { ResponseStreamEvent } from 'openai/resources/responses/responses';
const client = new OpenAI({
apiKey: process.env.ERGPT_TOKEN!,
baseURL: 'https://api.er-gpt.ru',
});
const stream = await client.post('/api/v3/message/generate', {
body: {
stream: true,
chat_id: '00000000-0000-4000-8000-000000000001',
assistant_id: '00000000-0000-4000-8000-000000000002',
model_id: 'имя-модели-из-каталога',
input: 'Привет!',
},
stream: true,
headers: { 'Idempotency-Key': crypto.randomUUID() },
});
for await (const raw of stream) {
const event = raw as ResponseStreamEvent;
if (event.type === 'response.output_text.delta') {
process.stdout.write(event.delta ?? '');
} else if (event.type === 'response.reasoning_text.delta') {
process.stdout.write(event.delta ?? '');
} else if (event.type === 'response.completed') {
process.stdout.write('\n--- done ---\n');
break;
}
}
One-shot — POST /api/v3/message/one-shot
Один запрос без сохранённого чата: подходит для разовых ответов. Поле stream работает так же, как у генерации в чате. Заголовок Idempotency-Key не нужен.
Кратко по полям:
inputили устаревшееcontent— текст пользователя.runs— необязательно: можно передать «накопленную» историю в виде массива объектов с полями вродеidиoutput.assistant_id:null(кастомный или универсальный сценарий по полюassistant), строка"searcher"(режим «поиск по базе» с опциональнымkb_id), либо UUID ассистента из каталога.ragпо умолчанию"agent"(см. RAG).
import httpx
BASE_URL = "https://api.er-gpt.ru"
TOKEN = "ВАШ_ТОКЕН"
payload = {
"stream": False,
"model_id": "имя-модели-из-каталога",
"input": "Суммируй в двух предложениях: что такое embeddings.",
"assistant_id": None,
"rag": "agent",
}
r = httpx.post(
f"{BASE_URL}/api/v3/message/one-shot",
json=payload,
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=120.0,
)
r.raise_for_status()
print(r.json())
const BASE_URL = 'https://api.er-gpt.ru';
const TOKEN = 'ВАШ_ТОКЕН';
const payload = {
stream: false,
model_id: 'имя-модели-из-каталога',
input: 'Суммируй в двух предложениях: что такое embeddings.',
assistant_id: null,
rag: 'agent',
};
const res = await fetch(`${BASE_URL}/api/v3/message/one-shot`, {
method: 'POST',
headers: {
Authorization: `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(await res.text());
console.log(await res.json());
Чаты — /api/v3/chat
| Метод | Путь | Назначение |
|---|---|---|
| GET | /api/v3/chat | Список чатов (search, limit, cursor, exclude_favorites) |
| POST | /api/v3/chat | Создать чат (тело — имя и др.; версия будет v3) |
| GET | /api/v3/chat/favorite | Избранные чаты |
| GET | /api/v3/chat/{chat_id} | Получить чат |
| PATCH | /api/v3/chat/{chat_id} | Обновить |
| DELETE | /api/v3/chat/{chat_id} | Удалить |
| POST | /api/v3/chat/{chat_id}/generate-name | Сгенерировать название |
| GET | /api/v3/chat/{chat_id}/attachments | Вложения чата (поиск по имени файла — query search) |
| POST / DELETE | /api/v3/chat/{chat_id}/favorite | В избранное / из избранного |
import httpx
BASE_URL = "https://api.er-gpt.ru"
TOKEN = "ВАШ_ТОКЕН"
r = httpx.post(
f"{BASE_URL}/api/v3/chat",
json={"name": "Мой чат v3"},
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=30.0,
)
r.raise_for_status()
print(r.json())
const BASE_URL = 'https://api.er-gpt.ru';
const TOKEN = 'ВАШ_ТОКЕН';
const res = await fetch(`${BASE_URL}/api/v3/chat`, {
method: 'POST',
headers: {
Authorization: `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'Мой чат v3' }),
});
if (!res.ok) throw new Error(await res.text());
console.log(await res.json());
Вложения — /api/v3/attachment
Список — GET /api/v3/attachment
Параметры: chat_id (необязательно — тогда все ваши вложения или только по чату), search, limit, cursor.
Загрузка — POST /api/v3/attachment
Один файл, multipart/form-data, поле с файлом (как принимает ваш клиент — часто имя поля file).
import httpx
BASE_URL = "https://api.er-gpt.ru"
TOKEN = "ВАШ_ТОКЕН"
with open("notes.txt", "rb") as f:
r = httpx.post(
f"{BASE_URL}/api/v3/attachment",
headers={"Authorization": f"Bearer {TOKEN}"},
files={"file": ("notes.txt", f, "text/plain")},
timeout=120.0,
)
r.raise_for_status()
print(r.json())
const BASE_URL = 'https://api.er-gpt.ru';
const TOKEN = 'ВАШ_ТОКЕН';
const form = new FormData();
form.append('file', new Blob(['hello'], { type: 'text/plain' }), 'notes.txt');
const res = await fetch(`${BASE_URL}/api/v3/attachment`, {
method: 'POST',
headers: { Authorization: `Bearer ${TOKEN}` },
body: form,
});
if (!res.ok) throw new Error(await res.text());
console.log(await res.json());
Базы знаний — /api/v3/knowledge
Поведение совпадает с /api/knowledge: список баз, загрузка документов, поиск по базе и т.д. Удобно использовать тот префикс, который совпадает с остальными v3-вызовами в вашем коде.
RAG: поиск по базе
Поле rag в теле генерации (чат или one-shot) задаёт, как подмешиваются знания ассистента:
| Значение | Поведение |
|---|---|
false | Автоматический поиск по базе до ответа модели не выполняется. |
true | Если у ассистента привязана база знаний, сервер сам выполняет поиск и подставляет найденное в контекст (как заранее выполненный шаг инструмента поиска по базе). |
"agent" | Автоматического пред-поиска нет: модель опирается на свои инструменты и настройки ассистента. |
объект с полями threshold, search_type (hybrid, dense, sparse), limit, rerank, summary_mode | То же, что true, но с переопределением параметров поиска. |
У сценария one-shot по умолчанию стоит "agent"; у генерации в чате по умолчанию false.
Что смотреть дальше
- Полные схемы запросов и ответов — в
Swaggerвашего стенда. - Формат входа
inputкак уOpenAI Responsesи имена событий в SSE — в документации OpenAI по Responses API; отдельная страница про OpenAI-совместимость у нас — OpenAI-совместимый API.