Моки LLM API
Когда вы строите приложение поверх LLM‑провайдера (OpenAI, Azure OpenAI,
Anthropic, Ollama, OpenRouter, DeepSeek, …), его нужно тестировать без обращения
к реальной модели: без затрат, без лимитов запросов, без плавающей задержки и со
строго детерминированными ответами в CI. Мок LLM API заставляет Mockarty
отвечать как эндпоинт чат‑комплишена — в том числе с реалистичным потоковым
выводом токен‑за‑токеном — так что LLM‑клиент вашего приложения общается с
Mockarty вместо провайдера.
Это обычный HTTP‑мок с особым режимом ответа: вы сопоставляете эндпоинт
провайдера (например, POST /v1/chat/completions), а Mockarty возвращает ответ в
форме провайдера. Поскольку это обычный мок, всё привычное работает поверх —
условия запроса (разный ответ на разный промпт или модель), динамический
контент (эхо промпта, данные Faker), приоритеты и пространства имён.
Что отдаёт мок
Mockarty смотрит на флаг stream в теле входящего запроса:
streamотсутствует илиfalse→ один JSON‑ответ (chat.completionдля
OpenAI, объектmessageдля Anthropic) с блоком токеновusage.stream: true→ потокtext/event-stream, который отдаёт ответ
токен‑за‑токеном, ровно как реальный провайдер (дельты OpenAI
chat.completion.chunk, завершаемыеdata: [DONE]; последовательность событий
Anthropicmessage_start→content_block_delta→message_stop).
Поддерживаются два формата провайдеров: openai (де‑факто стандарт — также
покрывает Azure OpenAI, Ollama, OpenRouter, DeepSeek и большинство
OpenAI‑совместимых клиентов) и anthropic (Messages API).
Создание в Конструкторе
- Откройте Конструктор, оставьте протокол HTTP, задайте маршрут (например,
/v1/chat/completions) и метод. - В блоке Тип тела ответа нажмите LLM.
- Выберите провайдера (OpenAI‑совместимый или Anthropic), при желании укажите
модель и напишите текст ответа. Опционально задайте причину завершения и
размер/задержку чанка стрима. - Сохраните. Теперь мок отвечает как chat‑completion эндпоинт.
Те же поля доступны через API в блоке llmResponse ниже — используйте удобный путь
(UI для людей, API для скриптов и AI‑агентов).
Настройка LLM‑ответа
LLM‑мок — это мок, у которого response содержит блок llmResponse:
| Поле | Назначение | По умолчанию |
|---|---|---|
provider |
openai или anthropic |
openai |
model |
Имя модели, возвращаемое в ответе | model из запроса |
content |
Текст ответа ассистента. Поддерживает динамические значения (см. ниже) | — (обязательно) |
finishReason |
OpenAI finish_reason / Anthropic stop_reason |
stop / end_turn |
promptTokens |
Сообщаемые токены промпта | авто‑оценка |
completionTokens |
Сообщаемые токены ответа | авто‑оценка |
chunkChars |
Символов в одной дельте при стриминге | 4 (≈ один токен) |
chunkDelayMs |
Задержка перед каждой дельтой при стриминге, мс | 0 |
Динамический контент
content проходит через тот же шаблонизатор, что и любой payload мока, поэтому
ответ может зависеть от запроса:
- Эхо промпта / поля запроса — задайте
contentкак JsonPath в тело
запроса, например$.req.modelили$.req.messages[-1].content. - Сгенерировать фейковые данные — задайте
contentкак выражение Faker,
например$.fake.Sentenceили$.fake.FirstName.
(Полный синтаксис выражений — в Руководстве по JsonPath.)
Пример
Создайте потоковый мок в стиле OpenAI (замените localhost:5770 на свой сервер):
curl -X POST http://localhost:5770/api/v1/mocks \
-H "Authorization: Bearer $MOCKARTY_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "chat-mock",
"namespace": "sandbox",
"http": { "route": "/v1/chat/completions", "httpMethod": "POST" },
"response": {
"statusCode": 200,
"llmResponse": {
"provider": "openai",
"content": "Привет! Это мок-ответ ассистента.",
"chunkChars": 4,
"chunkDelayMs": 20
}
}
}'
Укажите в LLM‑клиенте базовый URL http://localhost:5770/stubs/sandbox и
вызывайте как обычно:
# Без стриминга → один JSON-ответ
curl -X POST http://localhost:5770/stubs/sandbox/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o","messages":[{"role":"user","content":"привет"}]}'
# Стриминг → text/event-stream токен-за-токеном
curl -N -X POST http://localhost:5770/stubs/sandbox/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"gpt-4o","stream":true,"messages":[{"role":"user","content":"привет"}]}'
Потоковый вызов возвращает ответ по одной дельте за раз:
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant"}}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Прив"}}]}
…
data: [DONE]
Разные ответы на разные промпты
Поскольку LLM‑мок — обычный мок, добавьте условия запроса, чтобы возвращать
разный ответ в зависимости от тела запроса (например, другой content, когда
model равно gpt-4o, или когда промпт содержит ключевое слово). Создайте по
одному моку на случай на одном и том же маршруте; Mockarty выберет подходящий по
условиям и приоритету — так же, как для любого HTTP‑мока.
Тестирование устойчивости
Чтобы проверить, как приложение переживает сбой провайдера, задайте моку
не‑200 statusCode (например, 429 для лимита запросов) или большой
chunkDelayMs для имитации медленного потока. Сочетайте LLM‑моки с функциями
хаоса Mockarty, чтобы инъецировать задержки и сбои на том же эндпоинте.
Вызовы инструментов / функций
Чтобы тестировать function-calling (tool-use), возвращайте вызовы инструментов
вместо текста (или вместе с ним). В Конструкторе откройте Вызовы инструментов
и введите JSON-массив; через API задайте llmResponse.toolCalls:
"llmResponse": {
"provider": "openai",
"content": "",
"toolCalls": [{ "name": "get_weather", "arguments": { "city": "NYC" } }]
}
Мок вернёт форму вызова провайдера — OpenAI message.tool_calls с
finish_reason: "tool_calls" (аргументы как JSON-строка); Anthropic блок
tool_use со stop_reason: "tool_use". Текст ответа может быть пустым для
ответа-только-с-инструментом.
Многоходовые диалоги
Чат-клиент пересылает всю переписку в messages при каждом вызове, поэтому
мок может отвечать на последнюю реплику и хранить состояние между ходами:
- Ответ на последнюю реплику — задайте
content=$.req.lastUserMessage
(последняя реплика пользователя),$.req.lastMessage,
$.req.lastAssistantMessageили$.req.systemPrompt.$.req.turnCount—
число сообщений. (Используйте их вместо$.req.messages[N].content, который
работает только с фиксированным позитивным индексом.) - Ветвление по ходу / содержимому — добавьте условия запроса на
$.req.messages(например, совпадение, когда переписка содержит ключевое слово)
и создайте по моку на случай; Mockarty выберет подходящий. - Память между ходами — добавьте правило Extract, записывающее значение
из запроса в Chain- или Global-стор (например,
cStore.lastQuestion = $.req.lastUserMessage). Следующий запрос прочитает его
через$.cS.lastQuestion— поэтому ответ хода N может ссылаться на то, что было
на ходу N‑1. (См. Системы хранилищ.)
Пример — ассистент, всегда отвечающий на последнюю реплику пользователя:
"llmResponse": { "provider": "openai", "content": "Вы сказали: $.req.lastUserMessage" }
См. также
- Скриптовые ответы — полный программный контроль ответа
- Руководство по JsonPath — выражения динамического контента
- Системы хранилищ — многошаговые сценарии с состоянием