Асинхронные задачи
- Нормативная
- Черновик 1.0
- Обновлено
- Открыть как Markdown
7.1 Создание задачи
Клиент создаёт задачу, отправляя запрос ответа, который хочет выполнить, обёрнутый в конверт задачи. Рекомендуется заголовок Idempotency-Key (раздел 9.2).
POST /lmsp/v1/tasks
Authorization: Bearer <token>
Idempotency-Key: 01J8M7C4Y3N5P8R2T6V9X1Z4BH
{
"type": "response",
"input": {
"model": "qwen3:14b-q4_k_m",
"input": [
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "Write a long report."
}
]
}
]
},
"on_restart": "fail"
}
| Поле | Обязательное | Значение |
|---|---|---|
type |
Да | response — единственный тип задачи в версии 1.0. |
input |
Да | Запрос ответа, точно такой же, как для POST /lmsp/v1/responses. Его поле stream игнорируется. |
on_restart |
Нет | fail (по умолчанию), retry или resume. См. раздел 7.5. |
Сервис ДОЛЖЕН сохранить задачу до того, как ответит. Затем он возвращает:
HTTP/1.1 202 Accepted
{
"id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC",
"object": "task",
"type": "response",
"status": "queued",
"created_at": 1794200100,
"updated_at": 1794200100,
"response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW",
"events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events",
"on_restart": "fail",
"response": null,
"error": null
}
| Поле задачи | Значение |
|---|---|
id |
Идентификатор задачи, например task_.... |
object |
Всегда "task". |
type |
response. |
status |
Текущее состояние (раздел 7.2). |
created_at, updated_at |
Время в секундах с начала эпохи Unix. |
response_id |
Идентификатор ответа, который создаёт задача. Присваивается при создании задачи. |
events_url |
Путь к потоку событий. Присутствует, когда возможность event_resume равна true. |
on_restart |
Политика перезапуска задачи. |
response |
Объект ответа, когда status равен succeeded, иначе null. |
error |
Объект проблемы (раздел 9.3), когда status равен failed, иначе null. |
GET /lmsp/v1/tasks/{task_id} возвращает объект задачи. Клиенты, которые не используют потоки событий, опрашивают этот эндпоинт. Неизвестный идентификатор даёт 404 с кодом task_not_found. Завершённая задача выглядит так:
{
"id": "task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC",
"object": "task",
"type": "response",
"status": "succeeded",
"created_at": 1794200100,
"updated_at": 1794200162,
"response_id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW",
"events_url": "/lmsp/v1/tasks/task_01J8M8R5X3V7B2N4Q9T1W6Y0ZC/events",
"on_restart": "fail",
"response": {
"id": "resp_01J8M8R5X4B9D2F6H1K3N7Q0ZW",
"object": "response",
"created_at": 1794200100,
"status": "completed",
"model": "qwen3:14b-q4_k_m",
"output": [
{
"id": "msg_01J8M8R6A2C5E8G1J4M7P0S3VY",
"type": "message",
"role": "assistant",
"content": [
{
"type": "output_text",
"text": "Local model services make on-device inference a shared, discoverable utility..."
}
]
}
],
"usage": {
"input_tokens": 12,
"output_tokens": 1480,
"total_tokens": 1492
},
"error": null,
"incomplete_details": null
},
"error": null
}
7.2 Конечный автомат
queued --> loading --> running --> succeeded
| | |
| | +--> failed
| | +--> cancelling --> cancelled
| +--------------> failed
+--------------------------> cancelled
| Состояние | Значение | Следующие состояния |
|---|---|---|
queued |
Принята и сохранена, ожидает запуска. | loading, cancelled, failed |
loading |
Модель загружается. | running, cancelling, failed |
running |
Модель генерирует. | succeeded, failed, cancelling |
cancelling |
Отмена запрошена, но бэкенд ещё не остановился. | cancelled |
succeeded |
Конечное. Ответ готов. | нет |
failed |
Конечное. | нет |
cancelled |
Конечное. | нет |
Конечные состояния — succeeded, failed и cancelled. Сервис НЕ ДОЛЖЕН переводить задачу из конечного состояния в другое. Задача, ответ которой заканчивается статусом incomplete, например по лимиту токенов, считается succeeded.
Каждый переход атомарен, поэтому задача никогда не бывает одновременно отменённой и завершённой. Побеждает тот переход, который зафиксирован первым.
7.3 Отмена
POST /lmsp/v1/tasks/{task_id}/cancel
Сервис возвращает 200 OK и текущий объект задачи. Задача queued сразу становится cancelled. Задача loading или running становится cancelling, пока бэкенд не остановится, а затем cancelled.
Сервис ДОЛЖЕН обеспечивать идемпотентность отмены. Отмена задачи, которая уже cancelling или cancelled, возвращает текущую задачу и не порождает новой ошибки.
Отмена задачи, которая уже succeeded или failed, получает ответ 409 с кодом task_not_cancelable. Неизвестный идентификатор даёт 404 с кодом task_not_found.
7.4 Потоки событий
GET /lmsp/v1/tasks/{task_id}/events
Accept: text/event-stream
Этот эндпоинт необязателен. Он доступен, когда возможность event_resume равна true.
- События — это события ответа из раздела 6.3 для ответа, который создаёт задача. Нумерация и возобновление следуют разделу 6.4.
- Поток начинается с
response.created, которое записывается при приёме задачи. Он заканчивается одним последним событием, соответствующим исходу:response.completed,response.incomplete,response.failedилиresponse.cancelled. - Клиент, подключившийся поздно, сначала получает пропущенные события, затем живые. После последнего события сервис закрывает поток.
- События хранятся в течение срока хранения (раздел 11.2). Более старые события дают
410 Goneсevent_history_expired.
7.5 Поведение при перезапуске
on_restart определяет, что происходит с незавершённой задачей при перезапуске сервиса:
| Значение | Поведение |
|---|---|
fail |
По умолчанию. Сервис помечает незавершённую задачу как failed с кодом ошибки server_restarted. |
retry |
Сервис запускает задачу заново с начала. |
resume |
Сервис продолжает со снимка состояния. Это допускается, только если бэкенд явно поддерживает возобновляемые снимки. В противном случае запрос на создание отклоняется с 400 и кодом unsupported_capability. |
Задача, которая находится в состоянии cancelling при перезапуске сервиса, завершает отмену и становится cancelled независимо от своей политики.
Значение retry не рекомендуется использовать по умолчанию, потому что задача могла уже запустить инструменты или вызвать внешние побочные эффекты. Клиент, выбравший retry, ДОЛЖЕН сочетать его с Idempotency-Key и сделать вызовы своих инструментов идемпотентными.