docs: add detailed streaming specifications
This commit is contained in:
@@ -0,0 +1,607 @@
|
||||
# Streaming Admin API
|
||||
|
||||
## 1. Назначение документа
|
||||
|
||||
Этот документ фиксирует точные HTTP-контракты для потоковой модели Crank.
|
||||
|
||||
Он дополняет:
|
||||
|
||||
- [admin-api.md](/home/a.tolmachev/code/rust/mcpaas/docs/admin-api.md)
|
||||
- [streaming-mcp-plan.md](/home/a.tolmachev/code/rust/mcpaas/docs/streaming-mcp-plan.md)
|
||||
- [streaming-ui-contract.md](/home/a.tolmachev/code/rust/mcpaas/docs/streaming-ui-contract.md)
|
||||
- [streaming-runtime-design.md](/home/a.tolmachev/code/rust/mcpaas/docs/streaming-runtime-design.md)
|
||||
|
||||
Цель документа:
|
||||
|
||||
- зафиксировать DTO;
|
||||
- зафиксировать route groups;
|
||||
- зафиксировать валидацию;
|
||||
- зафиксировать expected error model;
|
||||
- зафиксировать page-to-endpoint contract для streaming configuration и test-runs.
|
||||
|
||||
## 2. Общие принципы
|
||||
|
||||
- все ресурсы являются `workspace-scoped`;
|
||||
- streaming configuration является частью `operation version`;
|
||||
- `session` и `async_job` state не редактируются напрямую из UI;
|
||||
- test-runs могут создавать временные sessions и jobs, но не публикуют их как runtime resources;
|
||||
- transport errors и validation errors разделяются;
|
||||
- лимиты и safety-параметры валидируются на сервере, а не только в UI.
|
||||
|
||||
Базовый префикс:
|
||||
|
||||
```text
|
||||
/api/admin/workspaces/{workspace_id}
|
||||
```
|
||||
|
||||
## 3. Основные ресурсы
|
||||
|
||||
- `streaming-presets`
|
||||
- `streaming-validation`
|
||||
- `stream-test-runs`
|
||||
- `stream-sessions`
|
||||
- `async-jobs`
|
||||
- `protocol-capabilities`
|
||||
|
||||
## 4. Общие DTO
|
||||
|
||||
### 4.1. `ExecutionMode`
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "unary"
|
||||
}
|
||||
```
|
||||
|
||||
Допустимые значения:
|
||||
|
||||
- `unary`
|
||||
- `window`
|
||||
- `session`
|
||||
- `async_job`
|
||||
|
||||
### 4.2. `StreamingConfig`
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "window",
|
||||
"transport_behavior": "server_stream",
|
||||
"window_duration_ms": 5000,
|
||||
"poll_interval_ms": 2000,
|
||||
"upstream_timeout_ms": 10000,
|
||||
"idle_timeout_ms": 30000,
|
||||
"max_session_lifetime_ms": 300000,
|
||||
"max_items": 200,
|
||||
"max_bytes": 131072,
|
||||
"aggregation_mode": "summary_plus_samples",
|
||||
"summary_path": "$.summary",
|
||||
"items_path": "$.items",
|
||||
"cursor_path": "$.cursor",
|
||||
"status_path": "$.status",
|
||||
"done_path": "$.done",
|
||||
"redacted_paths": [
|
||||
"$.items[*].token",
|
||||
"$.summary.secret"
|
||||
],
|
||||
"truncate_item_fields": true,
|
||||
"max_field_length": 512,
|
||||
"drop_duplicates": true,
|
||||
"sampling_rate": 1.0,
|
||||
"tool_family": {
|
||||
"start_tool_name": "cluster_events_start",
|
||||
"poll_tool_name": "cluster_events_poll",
|
||||
"stop_tool_name": "cluster_events_stop",
|
||||
"status_tool_name": "deploy_status",
|
||||
"result_tool_name": "deploy_result",
|
||||
"cancel_tool_name": "deploy_cancel"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3. `StreamingValidationError`
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "streaming_validation_error",
|
||||
"message": "Streaming configuration is invalid",
|
||||
"details": [
|
||||
{
|
||||
"field": "window_duration_ms",
|
||||
"reason": "must_be_positive"
|
||||
},
|
||||
{
|
||||
"field": "max_items",
|
||||
"reason": "must_not_exceed_workspace_limit"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4. `ProtocolCapability`
|
||||
|
||||
```json
|
||||
{
|
||||
"protocol": "grpc",
|
||||
"supports_execution_modes": [
|
||||
"unary",
|
||||
"window",
|
||||
"session",
|
||||
"async_job"
|
||||
],
|
||||
"supports_transport_behaviors": [
|
||||
"request_response",
|
||||
"server_stream"
|
||||
],
|
||||
"supports_auth_kinds": [
|
||||
"none",
|
||||
"bearer",
|
||||
"basic",
|
||||
"api_key_header",
|
||||
"api_key_query"
|
||||
],
|
||||
"supports_upload_artifacts": [
|
||||
"proto",
|
||||
"descriptor_set"
|
||||
],
|
||||
"supports_cursor_path": true,
|
||||
"supports_done_path": true,
|
||||
"supports_aggregation_mode": [
|
||||
"raw_items",
|
||||
"summary_only",
|
||||
"summary_plus_samples",
|
||||
"stats",
|
||||
"latest_state"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Capabilities endpoints
|
||||
|
||||
### `GET /api/admin/workspaces/{workspace_id}/protocol-capabilities`
|
||||
|
||||
Назначение:
|
||||
|
||||
- отдать UI полную capability matrix;
|
||||
- убрать protocol-specific hardcode из frontend.
|
||||
|
||||
Ответ:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"protocol": "rest",
|
||||
"supports_execution_modes": ["unary", "window", "session", "async_job"],
|
||||
"supports_transport_behaviors": ["request_response", "server_stream"],
|
||||
"supports_auth_kinds": ["none", "bearer", "basic", "api_key_header", "api_key_query"],
|
||||
"supports_upload_artifacts": [],
|
||||
"supports_cursor_path": true,
|
||||
"supports_done_path": true,
|
||||
"supports_aggregation_mode": ["raw_items", "summary_only", "summary_plus_samples", "stats", "latest_state"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 6. Streaming validation endpoints
|
||||
|
||||
### `POST /api/admin/workspaces/{workspace_id}/streaming/validate`
|
||||
|
||||
Назначение:
|
||||
|
||||
- проверить streaming config до сохранения operation;
|
||||
- вернуть protocol-aware ошибки.
|
||||
|
||||
Тело:
|
||||
|
||||
```json
|
||||
{
|
||||
"protocol": "websocket",
|
||||
"target": {},
|
||||
"execution_config": {
|
||||
"streaming": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Успех:
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": true,
|
||||
"warnings": [
|
||||
{
|
||||
"field": "max_bytes",
|
||||
"code": "may_truncate_large_event_payloads",
|
||||
"message": "Large event payloads may be truncated"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Ошибка:
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": false,
|
||||
"errors": [
|
||||
{
|
||||
"field": "transport_behavior",
|
||||
"code": "unsupported_transport_behavior"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 7. Streaming presets endpoints
|
||||
|
||||
### `GET /api/admin/workspaces/{workspace_id}/streaming-presets`
|
||||
|
||||
Назначение:
|
||||
|
||||
- отдать рекомендованные UI presets.
|
||||
|
||||
Ответ:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"preset_id": "logs_window_5s",
|
||||
"display_name": "Logs Window 5s",
|
||||
"protocols": ["rest", "grpc", "websocket"],
|
||||
"streaming": {
|
||||
"mode": "window",
|
||||
"window_duration_ms": 5000,
|
||||
"max_items": 100,
|
||||
"max_bytes": 65536,
|
||||
"aggregation_mode": "summary_plus_samples"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 8. Streaming operation test-runs
|
||||
|
||||
### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/stream-test-runs`
|
||||
|
||||
Назначение:
|
||||
|
||||
- выполнить `window`, `session` или `async_job` test-run для draft version;
|
||||
- показать оператору runtime behavior до publish.
|
||||
|
||||
Тело:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 4,
|
||||
"input": {
|
||||
"service": "billing",
|
||||
"level": "error"
|
||||
},
|
||||
"test_mode": "window",
|
||||
"overrides": {
|
||||
"streaming": {
|
||||
"window_duration_ms": 3000,
|
||||
"max_items": 50
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Успех для `window`:
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "strun_01j0test",
|
||||
"mode": "window",
|
||||
"status": "completed",
|
||||
"window_complete": true,
|
||||
"truncated": false,
|
||||
"has_more": false,
|
||||
"summary": {},
|
||||
"items": [],
|
||||
"cursor": null,
|
||||
"duration_ms": 2871
|
||||
}
|
||||
```
|
||||
|
||||
Успех для `session`:
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "strun_01j0test",
|
||||
"mode": "session",
|
||||
"status": "running",
|
||||
"session": {
|
||||
"session_id": "sess_01j0stream",
|
||||
"expires_at": "2026-04-06T12:05:00Z",
|
||||
"poll_after_ms": 2000
|
||||
},
|
||||
"preview": {
|
||||
"summary": {},
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Успех для `async_job`:
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "strun_01j0test",
|
||||
"mode": "async_job",
|
||||
"status": "running",
|
||||
"job": {
|
||||
"job_id": "job_01j0deploy",
|
||||
"status": "running",
|
||||
"progress": {
|
||||
"pct": 12
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/stream-test-runs/{run_id}/poll`
|
||||
|
||||
Назначение:
|
||||
|
||||
- продолжить session-oriented test-run.
|
||||
|
||||
Тело:
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "sess_01j0stream"
|
||||
}
|
||||
```
|
||||
|
||||
Ответ:
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "strun_01j0test",
|
||||
"mode": "session",
|
||||
"status": "running",
|
||||
"window_complete": false,
|
||||
"truncated": false,
|
||||
"has_more": true,
|
||||
"summary": {},
|
||||
"items": [],
|
||||
"cursor": "next_cursor"
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/admin/workspaces/{workspace_id}/operations/{operation_id}/stream-test-runs/{run_id}/stop`
|
||||
|
||||
Назначение:
|
||||
|
||||
- остановить test session.
|
||||
|
||||
### `GET /api/admin/workspaces/{workspace_id}/operations/{operation_id}/stream-test-runs/{run_id}/result`
|
||||
|
||||
Назначение:
|
||||
|
||||
- получить финальный результат async-job test-run.
|
||||
|
||||
## 9. Runtime session resources
|
||||
|
||||
### `GET /api/admin/workspaces/{workspace_id}/stream-sessions`
|
||||
|
||||
Назначение:
|
||||
|
||||
- список активных и недавних session resources для observability/debug UI.
|
||||
|
||||
Query params:
|
||||
|
||||
- `operation_id`
|
||||
- `agent_id`
|
||||
- `status`
|
||||
- `page`
|
||||
- `page_size`
|
||||
|
||||
Ответ:
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"id": "sess_01j0stream",
|
||||
"operation_id": "op_01j0",
|
||||
"agent_id": "agent_01j0",
|
||||
"mode": "session",
|
||||
"status": "running",
|
||||
"expires_at": "2026-04-06T12:05:00Z",
|
||||
"last_poll_at": "2026-04-06T12:00:10Z",
|
||||
"created_at": "2026-04-06T12:00:00Z"
|
||||
}
|
||||
],
|
||||
"page": 1,
|
||||
"page_size": 20,
|
||||
"total": 1
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/admin/workspaces/{workspace_id}/stream-sessions/{session_id}`
|
||||
|
||||
Назначение:
|
||||
|
||||
- detail session metadata;
|
||||
- без возврата полного raw state.
|
||||
|
||||
### `POST /api/admin/workspaces/{workspace_id}/stream-sessions/{session_id}/stop`
|
||||
|
||||
Назначение:
|
||||
|
||||
- административно остановить runtime session.
|
||||
|
||||
### `DELETE /api/admin/workspaces/{workspace_id}/stream-sessions/{session_id}`
|
||||
|
||||
Назначение:
|
||||
|
||||
- hard cleanup завершенной session;
|
||||
- доступен только для `stopped`, `failed`, `expired`.
|
||||
|
||||
## 10. Async job resources
|
||||
|
||||
### `GET /api/admin/workspaces/{workspace_id}/async-jobs`
|
||||
|
||||
Назначение:
|
||||
|
||||
- список активных и недавних job resources.
|
||||
|
||||
### `GET /api/admin/workspaces/{workspace_id}/async-jobs/{job_id}`
|
||||
|
||||
Назначение:
|
||||
|
||||
- status/progress/result metadata.
|
||||
|
||||
### `POST /api/admin/workspaces/{workspace_id}/async-jobs/{job_id}/cancel`
|
||||
|
||||
Назначение:
|
||||
|
||||
- административная отмена long-running job.
|
||||
|
||||
### `GET /api/admin/workspaces/{workspace_id}/async-jobs/{job_id}/result`
|
||||
|
||||
Назначение:
|
||||
|
||||
- получить финальный нормализованный результат.
|
||||
|
||||
## 11. Integration into operation contracts
|
||||
|
||||
`streaming` не является отдельным top-level resource для опубликованной операции. Он живет внутри:
|
||||
|
||||
- `OperationVersionDocument.execution_config.streaming`
|
||||
|
||||
и входит в:
|
||||
|
||||
- `POST /operations`
|
||||
- `PATCH /operations/{operation_id}`
|
||||
- `POST /operations/{operation_id}/versions`
|
||||
- `GET /operations/{operation_id}/versions/{version}`
|
||||
|
||||
### 11.1. `OperationVersionDocument.execution_config.streaming`
|
||||
|
||||
```json
|
||||
{
|
||||
"execution_config": {
|
||||
"timeout_ms": 10000,
|
||||
"retries": 0,
|
||||
"auth_profile_ref": "auth_profile_01j0",
|
||||
"streaming": {
|
||||
"mode": "window",
|
||||
"transport_behavior": "server_stream",
|
||||
"window_duration_ms": 5000,
|
||||
"upstream_timeout_ms": 10000,
|
||||
"max_items": 100,
|
||||
"max_bytes": 65536,
|
||||
"aggregation_mode": "summary_plus_samples",
|
||||
"summary_path": "$.summary",
|
||||
"items_path": "$.items",
|
||||
"cursor_path": "$.cursor",
|
||||
"status_path": "$.status",
|
||||
"done_path": "$.done",
|
||||
"redacted_paths": [],
|
||||
"truncate_item_fields": true,
|
||||
"max_field_length": 512,
|
||||
"drop_duplicates": true,
|
||||
"sampling_rate": 1.0,
|
||||
"tool_family": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 12. Validation rules
|
||||
|
||||
### 12.1. Общие
|
||||
|
||||
- `mode=unary` запрещает `tool_family`;
|
||||
- `max_items` > 0;
|
||||
- `max_bytes` > 0;
|
||||
- `window_duration_ms` > 0 для `window`;
|
||||
- `idle_timeout_ms` обязателен для `session`;
|
||||
- `max_session_lifetime_ms` обязателен для `session`;
|
||||
- `tool_family.start_tool_name/poll_tool_name/stop_tool_name` обязательны для `session`;
|
||||
- `tool_family.start_tool_name/status_tool_name/result_tool_name/cancel_tool_name` обязательны для `async_job`.
|
||||
|
||||
### 12.2. Protocol-aware
|
||||
|
||||
REST:
|
||||
|
||||
- `transport_behavior=server_stream` допустим только при streaming-capable target.
|
||||
|
||||
GraphQL:
|
||||
|
||||
- `window`, `session`, `async_job` пока запрещены;
|
||||
- `subscription` невалиден как target type.
|
||||
|
||||
gRPC:
|
||||
|
||||
- `transport_behavior=server_stream` допустим только для server-streaming method.
|
||||
|
||||
WebSocket:
|
||||
|
||||
- `mode=unary` запрещен;
|
||||
- требуется `subscribe_message_template` для `session` и `window`.
|
||||
|
||||
SOAP:
|
||||
|
||||
- `mode=session` запрещен в первой волне;
|
||||
- `mode=window` допустим только при наличии polling-style status operation family;
|
||||
- `mode=async_job` требует status/result contract.
|
||||
|
||||
## 13. Error model
|
||||
|
||||
Коды:
|
||||
|
||||
- `streaming_validation_error`
|
||||
- `unsupported_execution_mode`
|
||||
- `unsupported_transport_behavior`
|
||||
- `stream_window_timeout`
|
||||
- `stream_window_truncated`
|
||||
- `stream_session_expired`
|
||||
- `stream_session_not_found`
|
||||
- `async_job_not_found`
|
||||
- `async_job_not_ready`
|
||||
- `async_job_cancelled`
|
||||
- `protocol_capability_mismatch`
|
||||
|
||||
### 13.1. Ошибка несовместимого protocol mode
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "protocol_capability_mismatch",
|
||||
"message": "websocket target does not support unary execution mode",
|
||||
"details": [
|
||||
{
|
||||
"field": "execution_config.streaming.mode",
|
||||
"reason": "unsupported_for_protocol"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 14. Route-to-service mapping
|
||||
|
||||
Ожидаемые service handlers в `apps/admin-api`:
|
||||
|
||||
- `list_protocol_capabilities`
|
||||
- `list_streaming_presets`
|
||||
- `validate_streaming_config`
|
||||
- `start_stream_test_run`
|
||||
- `poll_stream_test_run`
|
||||
- `stop_stream_test_run`
|
||||
- `get_stream_test_result`
|
||||
- `list_stream_sessions`
|
||||
- `get_stream_session`
|
||||
- `stop_stream_session`
|
||||
- `delete_stream_session`
|
||||
- `list_async_jobs`
|
||||
- `get_async_job`
|
||||
- `cancel_async_job`
|
||||
- `get_async_job_result`
|
||||
|
||||
Это не route names, а service-level функции orchestration.
|
||||
Reference in New Issue
Block a user