- OpenAI-compatible gateway with intelligent routing via cortiq-router (allaigate) - Embedded build-free admin SPA at /admin: 7 languages (en/ru/de/fr/es/zh/tr), light/dark themes, hot config reload (no restart) - Admin API, per-request statistics + Prometheus /metrics, secret store, playground - English documentation (primary) with Russian translations (*.ru.md)
322 lines
11 KiB
Markdown
322 lines
11 KiB
Markdown
*Read this in [English](PROTOCOLS.md).*
|
||
|
||
# Протоколы Cortiq Gateway
|
||
|
||
Контракты входящих протоколов и кросс-стандартных механизмов. Каждый протокол
|
||
включается флагом в `[protocols]`. Внутри всё приводится к канонической модели
|
||
(см. [ARCHITECTURE.md](ARCHITECTURE.ru.md#3-каноническая-модель)).
|
||
|
||
Общее правило маршрутизации во всех протоколах:
|
||
|
||
- `model = "cortiq-auto"` → включить интеллектуальный роутинг через cortiq-router.
|
||
- `model = "cortiq-auto:quality-first"` → роутинг с указанным policy-профилем
|
||
(`cost-saver` | `balanced` | `quality-first`).
|
||
- `model = "<реальный id из конфига>"` → passthrough к этой модели без роутинга.
|
||
|
||
---
|
||
|
||
## Оглавление
|
||
|
||
1. [OpenAI Chat Completions](#1-openai-chat-completions)
|
||
2. [OpenAI Completions (legacy)](#2-openai-completions-legacy)
|
||
3. [OpenAI Embeddings](#3-openai-embeddings)
|
||
4. [OpenAI Models](#4-openai-models)
|
||
5. [Anthropic Messages](#5-anthropic-messages)
|
||
6. [MCP (Model Context Protocol)](#6-mcp-model-context-protocol)
|
||
7. [Native passthrough](#7-native-passthrough)
|
||
8. [Кросс-стандарты](#8-кросс-стандарты)
|
||
|
||
---
|
||
|
||
## 1. OpenAI Chat Completions
|
||
|
||
`POST /v1/chat/completions` — основной endpoint, де-факто стандарт для агентов.
|
||
|
||
### Запрос
|
||
|
||
```jsonc
|
||
{
|
||
"model": "cortiq-auto", // роутинг; или реальный id для passthrough
|
||
"messages": [
|
||
{ "role": "system", "content": "You are a helpful assistant." },
|
||
{ "role": "user", "content": "Solve x^2 - 5x + 6 = 0" }
|
||
],
|
||
"temperature": 0.7,
|
||
"max_tokens": 1024,
|
||
"stream": false,
|
||
"tools": [ /* function-calling, проксируется к провайдеру как есть */ ],
|
||
"tool_choice": "auto",
|
||
"stop": ["\n\n"],
|
||
"top_p": 1.0,
|
||
"response_format": { "type": "json_object" }
|
||
}
|
||
```
|
||
|
||
Поддерживаются стандартные поля OpenAI; неизвестные поля проксируются к провайдеру
|
||
без изменений (forward-compatible).
|
||
|
||
### Ответ (без стриминга)
|
||
|
||
```jsonc
|
||
{
|
||
"id": "chatcmpl-9f...",
|
||
"object": "chat.completion",
|
||
"created": 1718800000,
|
||
"model": "local-qwen", // РЕАЛЬНО выбранная модель
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": { "role": "assistant", "content": "x = 2 или x = 3" },
|
||
"finish_reason": "stop"
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 24,
|
||
"completion_tokens": 11,
|
||
"total_tokens": 35
|
||
},
|
||
"cortiq": { // расширение (включается cortiq.echo=true)
|
||
"task_label": "math",
|
||
"complexity": { "score": 0.22, "tier": "low" },
|
||
"selected_model": "local-qwen",
|
||
"route_source": "router", // router | cache | fallback | pinned
|
||
"router_request_id": "req_...",
|
||
"cost_usd": 0.0
|
||
}
|
||
}
|
||
```
|
||
|
||
> Поле `cortiq` — необязательное расширение. OpenAI-SDK его игнорируют. Если клиент
|
||
> не хочет «лишних» полей — выключите `cortiq.echo`, метаданные останутся в заголовках.
|
||
|
||
### Метаданные в заголовках (всегда)
|
||
|
||
```
|
||
X-Cortiq-Request-Id: req_...
|
||
X-Cortiq-Task-Label: math
|
||
X-Cortiq-Complexity-Score: 0.22
|
||
X-Cortiq-Complexity-Tier: low
|
||
X-Cortiq-Selected-Model: local-qwen
|
||
X-Cortiq-Route-Source: router
|
||
X-Cortiq-Cost-Usd: 0.000000
|
||
```
|
||
|
||
### Ответ (стриминг, `stream: true`)
|
||
|
||
SSE, формат OpenAI: серия чанков `chat.completion.chunk`, завершается `data: [DONE]`.
|
||
Шлюз проксирует чанки провайдера один-в-один (без буферизации), добавив метаданные
|
||
роутинга в HTTP-заголовки до начала потока.
|
||
|
||
```
|
||
data: {"id":"chatcmpl-9f","object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant"},"index":0}]}
|
||
|
||
data: {"id":"chatcmpl-9f","object":"chat.completion.chunk","choices":[{"delta":{"content":"x = 2"},"index":0}]}
|
||
|
||
data: {"id":"chatcmpl-9f","object":"chat.completion.chunk","choices":[{"delta":{},"finish_reason":"stop","index":0}]}
|
||
|
||
data: [DONE]
|
||
```
|
||
|
||
---
|
||
|
||
## 2. OpenAI Completions (legacy)
|
||
|
||
`POST /v1/completions` — для старых интеграций. Внутри `prompt` оборачивается в
|
||
одно user-сообщение и идёт по тому же пути. Ответ — `object: "text_completion"`.
|
||
|
||
```jsonc
|
||
// запрос
|
||
{ "model": "cortiq-auto", "prompt": "Translate to French: hello", "max_tokens": 64 }
|
||
// ответ
|
||
{ "object": "text_completion", "model": "local-qwen",
|
||
"choices": [ { "text": "bonjour", "index": 0, "finish_reason": "stop" } ],
|
||
"usage": { "prompt_tokens": 6, "completion_tokens": 2, "total_tokens": 8 } }
|
||
```
|
||
|
||
---
|
||
|
||
## 3. OpenAI Embeddings
|
||
|
||
`POST /v1/embeddings`. Два режима (настраивается):
|
||
|
||
- `provider` — проксировать к embedding-модели из пула;
|
||
- `router` — вернуть эмбеддинг **энкодером cortiq-router** (тот же вектор, что роутер
|
||
использует для классификации — удобно для согласованного RAG).
|
||
|
||
```jsonc
|
||
// запрос
|
||
{ "model": "cortiq-embed", "input": ["first text", "second text"] }
|
||
// ответ
|
||
{
|
||
"object": "list",
|
||
"data": [
|
||
{ "object": "embedding", "index": 0, "embedding": [0.01, -0.02, /* ... */] },
|
||
{ "object": "embedding", "index": 1, "embedding": [0.03, 0.00, /* ... */] }
|
||
],
|
||
"model": "cortiq-embed",
|
||
"usage": { "prompt_tokens": 4, "total_tokens": 4 }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. OpenAI Models
|
||
|
||
`GET /v1/models` — автодискавери для клиентов. Возвращает виртуальные модели шлюза
|
||
(`cortiq-auto`, `cortiq-embed`) и все реальные модели пула.
|
||
|
||
```jsonc
|
||
{
|
||
"object": "list",
|
||
"data": [
|
||
{ "id": "cortiq-auto", "object": "model", "owned_by": "cortiq-gateway" },
|
||
{ "id": "local-qwen", "object": "model", "owned_by": "cortiq-gateway" },
|
||
{ "id": "gpt-4o-mini", "object": "model", "owned_by": "cortiq-gateway" },
|
||
{ "id": "claude-opus", "object": "model", "owned_by": "cortiq-gateway" }
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Anthropic Messages
|
||
|
||
`POST /v1/messages` — для Claude-нативных агентов и Anthropic SDK.
|
||
|
||
```jsonc
|
||
// запрос
|
||
{
|
||
"model": "cortiq-auto",
|
||
"max_tokens": 1024,
|
||
"system": "You are a helpful assistant.",
|
||
"messages": [ { "role": "user", "content": "Solve x^2 - 5x + 6 = 0" } ],
|
||
"stream": false
|
||
}
|
||
// ответ
|
||
{
|
||
"id": "msg_...",
|
||
"type": "message",
|
||
"role": "assistant",
|
||
"model": "local-qwen",
|
||
"content": [ { "type": "text", "text": "x = 2 или x = 3" } ],
|
||
"stop_reason": "end_turn",
|
||
"usage": { "input_tokens": 24, "output_tokens": 11 }
|
||
}
|
||
```
|
||
|
||
Стриминг — нативные Anthropic-события (`message_start`, `content_block_delta`,
|
||
`message_delta`, `message_stop`). Если выбранная модель — не-Anthropic-провайдер,
|
||
шлюз транслирует через каноническую модель в обе стороны.
|
||
|
||
---
|
||
|
||
## 6. MCP (Model Context Protocol)
|
||
|
||
Шлюз поднимает **MCP-сервер** (JSON-RPC 2.0 поверх HTTP+SSE или stdio) и публикует
|
||
маршрутизацию как инструменты для MCP-нативных оркестраторов.
|
||
|
||
Публикуемые инструменты:
|
||
|
||
| Инструмент | Назначение |
|
||
|---|---|
|
||
| `route_and_complete` | классифицировать + выполнить на выбранной модели, вернуть ответ |
|
||
| `classify_task` | только классификация (тип задачи + сложность), без выполнения |
|
||
|
||
`tools/list` отдаёт JSON-схемы. Пример вызова `tools/call`:
|
||
|
||
```jsonc
|
||
// запрос (JSON-RPC)
|
||
{
|
||
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
|
||
"params": {
|
||
"name": "route_and_complete",
|
||
"arguments": { "prompt": "Solve x^2 - 5x + 6 = 0", "profile": "balanced" }
|
||
}
|
||
}
|
||
// ответ
|
||
{
|
||
"jsonrpc": "2.0", "id": 1,
|
||
"result": {
|
||
"content": [ { "type": "text", "text": "x = 2 или x = 3" } ],
|
||
"isError": false,
|
||
"_meta": { "task_label": "math", "tier": "low", "selected_model": "local-qwen" }
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Native passthrough
|
||
|
||
`POST /route` — тонкая обёртка над `cortiq-router /v1/route` для клиентов, которым
|
||
нужно только **решение** (без выполнения). Тело и ответ совпадают с контрактом роутера
|
||
(см. `cortiq-router/docs/ФОРМАТ_ЗАПРОСА_И_ОТВЕТА.md`). Полезно для отладки и для тех,
|
||
кто хочет сам управлять вызовом LLM.
|
||
|
||
---
|
||
|
||
## 8. Кросс-стандарты
|
||
|
||
Едины для всех HTTP-протоколов.
|
||
|
||
### 8.1 Аутентификация
|
||
|
||
```
|
||
Authorization: Bearer sk-gw-...
|
||
```
|
||
Виртуальный ключ шлюза. Маппится на аккаунт (`[[api_keys]]`). Если ключи не настроены —
|
||
открытый режим (для локальной разработки). Ключи провайдеров наружу не отдаются.
|
||
|
||
### 8.2 Идемпотентность
|
||
|
||
```
|
||
Idempotency-Key: <уникальный-id-операции>
|
||
```
|
||
Повтор с тем же ключом в окне `idempotency.ttl` отдаёт **сохранённый** ответ
|
||
(безопасные ретраи агентов, без двойного вызова LLM и двойного биллинга).
|
||
|
||
### 8.3 Rate-limit
|
||
|
||
На `429` возвращаются:
|
||
|
||
```
|
||
Retry-After: 2
|
||
X-RateLimit-Limit: 600
|
||
X-RateLimit-Remaining: 0
|
||
X-RateLimit-Reset: 1718800060
|
||
```
|
||
|
||
### 8.4 Трассировка
|
||
|
||
Приём и проброс W3C `traceparent`; OpenTelemetry-спаны `inbound→route→provider`.
|
||
В ответе — `X-Request-Id`. Это позволяет сшить сквозной трейс агент → шлюз → провайдер.
|
||
|
||
### 8.5 Формат ошибки (OpenAI-совместимый)
|
||
|
||
```jsonc
|
||
{
|
||
"error": {
|
||
"message": "no healthy model available for tier 'high'",
|
||
"type": "upstream_unavailable", // invalid_request_error | authentication_error | rate_limit_error | upstream_unavailable | internal_error
|
||
"code": "no_healthy_model",
|
||
"param": null
|
||
}
|
||
}
|
||
```
|
||
|
||
OpenAI/Anthropic SDK парсят такой конверт нативно. Сопоставление статусов:
|
||
|
||
| Тип | HTTP | retriable |
|
||
|---|---|---|
|
||
| `invalid_request_error` | 400 | нет |
|
||
| `authentication_error` | 401 | нет |
|
||
| `rate_limit_error` | 429 | да |
|
||
| `upstream_unavailable` | 502/503 | да |
|
||
| `internal_error` | 500 | да |
|
||
|
||
### 8.6 Учёт стоимости
|
||
|
||
`usage` отдаётся в формате протокола (OpenAI/Anthropic). Дополнительно — стоимость
|
||
в заголовке `X-Cortiq-Cost-Usd` и в `cortiq.cost_usd` (если включено `cortiq.echo`).
|
||
Цена считается из `price_in`/`price_out` модели в конфиге × токены.
|