Перейти до основного вмісту

Сервер GenAI

Використовуйте GenAIServer, коли браузер, інтерфейс користувача, сервіс або віддалений клієнт повинні викликати одну або кілька моделей GenAI через HTTP. Сервер відповідає за реєстрацію моделей, маршрутизацію запитів, передачу даних у режимі потокового відтворення та скасування. Для логіки застосунку, яка виконується в тому ж процесі, що й модель, використовуйте прямі API для генеративного штучного інтелекту.

Створіть і запустіть сервер.

За замовчуванням сервер прив’язується до 0.0.0.0:9998. Зареєструйте кожну розгорнуту директорію моделі LLiMa за допомогою стабільної назви, перш ніж запускати сервер:

#include <neat/genai.h>

int main() {
simaai::neat::genai::GenAIServer server;
server.add_model(
"/media/nvme/llima/models/Qwen3-4B-Instruct-2507-GPTQ-a16w4",
"llm");
server.add_model(
"/media/nvme/llima/models/Qwen3-VL-4B-Instruct-GPTQ-a16w4",
"vlm");
server.serve();
}

У C++ використовуйте serve() для створення серверу, який працює в основному потоці та блокує інші операції. Використовуйте start() і stop(), коли ваша програма на C++ або Python керує часом життя сервера. Налаштуйте GenAIServerOptions, якщо стандартний хост або порт не підходять. Виклик stop() також видаляє всі зареєстровані моделі; додайте моделі знову перед повторним запуском того ж об’єкта сервера.

GenAIServer не забезпечує автентифікацію або завершення TLS і дозволяє запити CORS з будь-якого джерела. Прив’язуйте його лише до надійного інтерфейсу або розміщуйте за мережевим рівнем, який забезпечує необхідний контроль доступу та шифрування.

Перегляньте доступні моделі

Перелічіть назви зареєстрованих моделей перед надсиланням запиту на генерацію:

curl http://<modalix-ip>:9998/v1/models

У відповіді використовується формат списку моделей OpenAI:

{
"object": "list",
"data": [
{"id": "llm", "object": "model", "owned_by": "simaai"},
{"id": "vlm", "object": "model", "owned_by": "simaai"}
]
}

Кожен запит щодо генерації та аудіо повинен містити одне з наведених імен у відповідному полі model.

Кінцеві точки

МетодМаршрутМета
GET/v1/modelsПерелічіть назви зареєстрованих моделей, які використовуються.
POST/v1/chat/completionsЧат, сумісний з OpenAI, що підтримує обмін текстовими повідомленнями, зображеннями, використання інструментів і потокову передачу даних.
POST/v1/completionsАвтоматичне завершення підказок, сумісних з OpenAI.
POST/v1/audio/transcriptionsТранскрибуйте багатокомпонентний аудіовхід.
POST/v1/audio/translationsПерекладіть багатокомпонентну мову на англійську.
POST/api/chatЧат, сумісний з Ollama; за замовчуванням передає дані у форматі NDJSON.
POST/api/generateГенерація запитів, сумісних з Ollama; за замовчуванням передає дані у форматі NDJSON.
POST/stopСкасуйте активні потоки для однієї моделі або для всіх моделей.
POST/set_loraАктивуйте або замініть динамічний адаптер LoRA.
POST/unset_loraПоверніть динамічно адаптовану модель до її початкових значень параметрів.

Також приймаються альтернативні назви для забезпечення сумісності: /audio/transcriptions та /audio/translations. У нових клієнтах рекомендується використовувати маршрути /v1/audio/....

Сумісність стосується маршрутів і форматів відповідей, реалізованих GenAIServer; це не означає підтримку всіх полів у вищих рівнях OpenAI або Ollama API. Підтримувані поля запитів описано нижче.

Запити, сумісні з OpenAI.

Надсилайте model та messages до кінцевої точки чату. Встановіть stream на значення true для технології Server-Sent Events; за замовчуванням встановлено значення false:

curl http://<modalix-ip>:9998/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llm",
"messages": [
{"role": "user", "content": "Explain an API gateway in one sentence."}
],
"max_tokens": 64,
"stream": false
}'

/v1/chat/completions приймає max_tokens або max_completion_tokens, tools та tool_choice на додаток до model, messages та stream. Для визначення інструментів використовується формат функціональних інструментів OpenAI. tool_choice підтримує "auto" та "none"; якщо його не вказано або встановлено значення null, залишається стандартна поведінка інструменту.

/v1/completions приймає рядок prompt або масив рядків, а також model, max_tokens або max_completion_tokens та stream, значення за замовчуванням для якого становить false. Коли prompt є масивом, сервер об’єднує його рядкові елементи символами нового рядка та виконує один запит на завершення.

Для запитів VLM частина вмісту OpenAI chat image_url повинна містити URI даних у форматі base64, наприклад data:image/jpeg;base64,.... Для декодування зображення потрібна збірка Neat з підтримкою OpenCV.

Запити, сумісні з Ollama.

Використовуйте /api/chat для перегляду історії повідомлень або /api/generate для створення запиту:

curl http://<modalix-ip>:9998/api/generate \
-H "Content-Type: application/json" \
-d '{
"model": "llm",
"prompt": "Give me three API design tips.",
"options": {"num_predict": 96},
"stream": false
}'

Обидва сумісні з Ollama кінцеві пункти за замовчуванням передають JSON-дані, розділені символом нового рядка. Встановіть stream на false, щоб отримати одну повну відповідь у форматі JSON. /api/chat також приймає tools та tool_choice. Для запитів VLM розміщуйте необроблені рядки зображень у форматі base64 у масиві images кожного повідомлення /api/chat або у верхньому масиві images для /api/generate.

Моделі логічного мислення

Моделі, що здійснюють логічне обґрунтування, такі як Qwen3 або Gemma 4 E2B/E4B, можуть надавати обґрунтування окремо від остаточної відповіді. Для запиту, сумісного з OpenAI, встановіть enable_thinking на найвищому рівні:

curl http://<modalix-ip>:9998/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "llm",
"messages": [
{"role": "user", "content": "Which is larger: 9.11 or 9.9? Explain."}
],
"enable_thinking": true,
"max_tokens": 256,
"stream": false
}'

У відповіді, яка не передбачає потокову передачу, обґрунтування розміщується в choices[0].message.reasoning_content, а остаточна відповідь – у choices[0].message.content. У випадку з stream: true, для частин відповіді використовуються choices[0].delta.reasoning_content та choices[0].delta.content.

Для запиту, сумісного з Ollama, використовуйте think:

curl http://<modalix-ip>:9998/api/chat \
-H "Content-Type: application/json" \
-d '{
"model": "llm",
"messages": [
{"role": "user", "content": "Which is larger: 9.11 or 9.9? Explain."}
],
"think": true,
"options": {"num_predict": 256},
"stream": false
}'

/api/chat повертає обґрунтування у message.thinking та остаточну відповідь у message.content. /api/generate приймає те саме поле верхнього рівня think і повертає обґрунтування у поле верхнього рівня thinking та відповідь у response.

Поля з обґрунтуванням не відображаються, якщо вони порожні. Вони призначені лише для виведення: вхідні поля reasoning_content або thinking не відтворюються як історія розмови.

Аудіозапити

Аудіоінтерфейси приймають багатокомпонентні дані у форматі форми разом із назвою моделі автоматичного розпізнавання мовлення (ASR) та аудіофайлом. За замовчуванням language встановлено на auto, а stream – на false:

curl http://<modalix-ip>:9998/v1/audio/transcriptions \
-F model=asr \
-F file=@speech.wav \
-F language=auto \
-F stream=false

Використовуйте /v1/audio/translations з тими ж полями форми, щоб перекласти мовлення на англійську мову. Обидва способи підтримують stream=true. Результати містять згенерований текст, визначену мову та показники впевненості системи автоматичного розпізнавання мовлення (ASR), якщо модель їх надає.

Трансляція та скасування

Сумісні з OpenAI чати, завершення та аудіопотоки використовують text/event-stream, генерують події data: і завершуються подією data: [DONE]. Сумісні з Ollama потоки використовують application/x-ndjson. Потокові відповіді містять ttft і tps, якщо вони доступні. Для підрахунку згенерованих токенів у потоках, сумісних з OpenAI, використовується generated_tokens, а в потоках, сумісних з Ollama, – eval_count.

Скасуйте активні потоки для однієї з використаних моделей:

curl http://<modalix-ip>:9998/stop \
-H "Content-Type: application/json" \
-d '{"model": "llm"}'

Пропустіть model, щоб скасувати всі активні потоки. Цей HTTP-маршрут скасовує активне генерування потоків; він не скасовує синхронні запити та не зупиняє процес сервера. Викличте server.stop() з програми, яка його використовує, щоб зупинити сервер, запущений за допомогою server.start().

Перемикання адаптера LoRA

Для моделі, скомпільованої з використанням режиму LORA_BRANCH від LLiMa, активуйте адаптер із директорії npy_files/<adapter-name> моделі:

curl http://<modalix-ip>:9998/set_lora \
-H "Content-Type: application/json" \
-d '{"model":"llm","name":"customer-adapter"}'

Поверніться до початкових значень ваг за допомогою:

curl http://<modalix-ip>:9998/unset_lora \
-H "Content-Type: application/json" \
-d '{"model":"llm"}'

Поле model можна опустити лише тоді, коли зареєстровано рівно одну текстову модель або модель, що поєднує обробку зображень і мови. Перемикання чекає завершення активного запиту до цієї моделі та не впливає на інші моделі, які використовуються. Назви адаптерів повинні являти собою назву однієї директорії; шляхи та переходи заборонені. Динамічне перемикання не підтримується для ASR, пакетів спекулятивного декодування або постійно об’єднаних LORA_MERGED ваг.

Помилки

У разі явних помилок під час перевірки кінцевої точки повертається HTTP 400, зокрема, якщо відсутня model, якщо конфігурація інструменту недійсна, якщо можливості моделі несумісні або якщо відсутній багатокомпонентний аудіофайл file. Якщо використовується невідома модель, повертається HTTP 404.

Перш ніж буде встановлено потік відповіді, винятки, що виникають під час обробки запиту або виконання моделі, призводять до повернення HTTP 500.

Перед початком потокової передачі помилки використовують JSON-обгортку {"error":{"message":"...","type":"invalid_request_error"}}. Після початку потокової відповіді помилки передаються в потоці SSE або NDJSON кінцевої точки, а не змінюють HTTP-статус.

Наступні кроки