Files
crank/docs/streaming-runtime-design.md
T
2026-04-06 02:35:00 +03:00

14 KiB

Streaming Runtime Design

1. Назначение документа

Этот документ фиксирует, как потоковая модель должна быть разложена по crates, traits, services и функциям.

Цель:

  • избежать god-services;
  • зафиксировать boundaries между core, registry, runtime, protocol adapters и mcp-server;
  • расписать ожидаемые функции и их ответственность до уровня инженерной спецификации.

2. Общая схема слоев

2.1. crank-core

Отвечает за:

  • типы ExecutionMode, StreamingConfig, AggregationMode;
  • типы StreamSession, AsyncJobHandle, StreamStatus, JobStatus;
  • validation helpers без инфраструктуры.

2.2. crank-registry

Отвечает за:

  • persistence session/job state;
  • optimistic transitions;
  • cleanup expired state.

2.3. crank-runtime

Отвечает за:

  • orchestration execution modes;
  • dispatch в protocol adapter;
  • bounded collection;
  • aggregation;
  • state transitions;
  • invocation logging.

2.4. Protocol adapters

Отвечают за:

  • protocol-specific upstream behavior;
  • connect/request/collect/close;
  • преобразование upstream payload в нормализованный JSON.

2.5. apps/mcp-server

Отвечает за:

  • downstream MCP transport;
  • tool-family generation;
  • JSON-RPC lifecycle;
  • transport/session routing.

2.6. apps/admin-api

Отвечает за:

  • config CRUD;
  • test-runs;
  • validation;
  • UI-oriented metadata and previews.

3. Core domain types

3.1. ExecutionMode

pub enum ExecutionMode {
    Unary,
    Window,
    Session,
    AsyncJob,
}

Методы:

  • fn is_stateful(&self) -> bool
  • fn requires_tool_family(&self) -> bool

3.2. AggregationMode

pub enum AggregationMode {
    RawItems,
    SummaryOnly,
    SummaryPlusSamples,
    Stats,
    LatestState,
}

Методы:

  • fn needs_items(&self) -> bool
  • fn needs_summary(&self) -> bool

3.3. StreamingConfig

pub struct StreamingConfig {
    pub mode: ExecutionMode,
    pub transport_behavior: TransportBehavior,
    pub window_duration_ms: Option<u64>,
    pub poll_interval_ms: Option<u64>,
    pub upstream_timeout_ms: Option<u64>,
    pub idle_timeout_ms: Option<u64>,
    pub max_session_lifetime_ms: Option<u64>,
    pub max_items: Option<u32>,
    pub max_bytes: Option<u32>,
    pub aggregation_mode: AggregationMode,
    pub summary_path: Option<String>,
    pub items_path: Option<String>,
    pub cursor_path: Option<String>,
    pub status_path: Option<String>,
    pub done_path: Option<String>,
    pub redacted_paths: Vec<String>,
    pub truncate_item_fields: bool,
    pub max_field_length: Option<u32>,
    pub drop_duplicates: bool,
    pub sampling_rate: Option<f64>,
    pub tool_family: ToolFamilyConfig,
}

Методы:

  • fn validate_common(&self) -> Result<(), StreamingConfigError>
  • fn validate_for_protocol(&self, protocol: Protocol) -> Result<(), StreamingConfigError>
  • fn effective_max_items(&self, limits: &StreamingLimits) -> u32
  • fn effective_max_bytes(&self, limits: &StreamingLimits) -> u32
  • fn effective_timeout(&self, defaults: &StreamingDefaults) -> Duration

3.4. StreamSession

pub struct StreamSession {
    pub id: StreamSessionId,
    pub workspace_id: WorkspaceId,
    pub agent_id: Option<AgentId>,
    pub operation_id: OperationId,
    pub protocol: Protocol,
    pub mode: ExecutionMode,
    pub status: StreamStatus,
    pub cursor: Option<Value>,
    pub state: Value,
    pub expires_at: DateTime<Utc>,
    pub last_poll_at: Option<DateTime<Utc>>,
    pub created_at: DateTime<Utc>,
    pub closed_at: Option<DateTime<Utc>>,
}

Методы:

  • fn is_expired(&self, now: DateTime<Utc>) -> bool
  • fn can_poll(&self, now: DateTime<Utc>) -> bool
  • fn mark_polled(&mut self, now: DateTime<Utc>)
  • fn mark_closed(&mut self, now: DateTime<Utc>)

3.5. AsyncJobHandle

pub struct AsyncJobHandle {
    pub id: AsyncJobId,
    pub workspace_id: WorkspaceId,
    pub agent_id: Option<AgentId>,
    pub operation_id: OperationId,
    pub status: JobStatus,
    pub progress: Value,
    pub result: Option<Value>,
    pub error: Option<Value>,
    pub expires_at: Option<DateTime<Utc>>,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub finished_at: Option<DateTime<Utc>>,
}

Методы:

  • fn is_finished(&self) -> bool
  • fn can_cancel(&self) -> bool
  • fn mark_finished(&mut self, now: DateTime<Utc>, result: Value)
  • fn mark_failed(&mut self, now: DateTime<Utc>, error: Value)

4. Registry contracts

4.1. StreamSessionRepository

#[async_trait]
pub trait StreamSessionRepository {
    async fn create_stream_session(&self, session: NewStreamSession) -> Result<StreamSession, RegistryError>;
    async fn get_stream_session(&self, id: &StreamSessionId) -> Result<Option<StreamSession>, RegistryError>;
    async fn update_stream_session_state(&self, update: StreamSessionStateUpdate) -> Result<StreamSession, RegistryError>;
    async fn close_stream_session(&self, id: &StreamSessionId, now: DateTime<Utc>) -> Result<(), RegistryError>;
    async fn list_stream_sessions(&self, filter: StreamSessionFilter) -> Result<Page<StreamSession>, RegistryError>;
    async fn delete_expired_stream_sessions(&self, now: DateTime<Utc>) -> Result<u64, RegistryError>;
}

4.2. AsyncJobRepository

#[async_trait]
pub trait AsyncJobRepository {
    async fn create_async_job(&self, job: NewAsyncJob) -> Result<AsyncJobHandle, RegistryError>;
    async fn get_async_job(&self, id: &AsyncJobId) -> Result<Option<AsyncJobHandle>, RegistryError>;
    async fn update_async_job_status(&self, update: AsyncJobStatusUpdate) -> Result<AsyncJobHandle, RegistryError>;
    async fn cancel_async_job(&self, id: &AsyncJobId, now: DateTime<Utc>) -> Result<(), RegistryError>;
    async fn list_async_jobs(&self, filter: AsyncJobFilter) -> Result<Page<AsyncJobHandle>, RegistryError>;
    async fn delete_expired_async_jobs(&self, now: DateTime<Utc>) -> Result<u64, RegistryError>;
}

4.3. Storage rules

  • update methods должны использовать optimistic concurrency, если state transitions конфликтуют;
  • poll не должен silently reopen closed session;
  • cleanup job должен быть отдельным service;
  • session/job state не должен хранить необрезанные raw payloads без лимитов.

5. Runtime contracts

5.1. ProtocolAdapter

Базовый trait не должен пытаться описать все streaming cases в одном методе.

#[async_trait]
pub trait ProtocolAdapter {
    async fn execute_unary(&self, ctx: UnaryExecutionContext) -> Result<NormalizedResponse, AdapterError>;
    async fn execute_window(&self, ctx: WindowExecutionContext) -> Result<WindowExecutionResult, AdapterError>;
    async fn start_session(&self, ctx: SessionStartContext) -> Result<SessionStartResult, AdapterError>;
    async fn poll_session(&self, ctx: SessionPollContext) -> Result<SessionPollResult, AdapterError>;
    async fn stop_session(&self, ctx: SessionStopContext) -> Result<(), AdapterError>;
    async fn start_async_job(&self, ctx: AsyncJobStartContext) -> Result<AsyncJobStartResult, AdapterError>;
    async fn get_async_job_status(&self, ctx: AsyncJobStatusContext) -> Result<AsyncJobStatusResult, AdapterError>;
    async fn get_async_job_result(&self, ctx: AsyncJobResultContext) -> Result<NormalizedResponse, AdapterError>;
    async fn cancel_async_job(&self, ctx: AsyncJobCancelContext) -> Result<(), AdapterError>;
}

Не каждый adapter обязан поддерживать все методы. Capability mismatch должен проверяться выше.

5.2. OperationExecutor

Главная orchestration точка в crank-runtime.

Ожидаемые функции:

  • execute_unary_operation
  • execute_window_operation
  • start_stream_session
  • poll_stream_session
  • stop_stream_session
  • start_async_job
  • get_async_job_status
  • get_async_job_result
  • cancel_async_job

execute_unary_operation

Отвечает за:

  • schema validation;
  • auth resolution;
  • adapter dispatch;
  • output mapping;
  • observability write.

execute_window_operation

Отвечает за:

  • schema validation;
  • auth resolution;
  • adapter dispatch в execute_window;
  • aggregation;
  • truncation flags;
  • observability write.

start_stream_session

Отвечает за:

  • schema validation;
  • auth resolution;
  • adapter start_session;
  • registry create_stream_session;
  • initial preview result;
  • observability write.

poll_stream_session

Отвечает за:

  • load session from registry;
  • expiration check;
  • adapter poll_session;
  • registry update_stream_session_state;
  • output mapping;
  • observability write.

stop_stream_session

Отвечает за:

  • load session;
  • adapter stop_session;
  • registry close;
  • observability write.

start_async_job

Отвечает за:

  • schema validation;
  • adapter start_async_job;
  • registry create_async_job;
  • observability write.

get_async_job_status

Отвечает за:

  • registry load;
  • adapter get_async_job_status, если upstream status is live;
  • registry update;
  • normalized status result.

get_async_job_result

Отвечает за:

  • job readiness check;
  • adapter get_async_job_result, если result lazy-loaded;
  • output mapping;
  • observability write.

cancel_async_job

Отвечает за:

  • adapter cancel;
  • registry cancel transition;
  • observability write.

5.3. Aggregation services

Нужно отделить aggregation от adapters.

Отдельные services:

  • WindowAggregator
  • SummaryBuilder
  • CursorTracker
  • PayloadLimiter
  • RedactionService

WindowAggregator

Функции:

  • collect_items
  • apply_item_limit
  • apply_byte_limit
  • mark_truncated

SummaryBuilder

Функции:

  • build_summary
  • build_stats_summary
  • build_latest_state_summary
  • build_summary_plus_samples

PayloadLimiter

Функции:

  • truncate_item_fields
  • truncate_bytes
  • enforce_max_items

RedactionService

Функции:

  • redact_paths
  • redact_object
  • redact_item

6. Adapter responsibilities

6.1. REST adapter

Функции:

  • execute_rest_unary
  • execute_rest_window
  • start_rest_session
  • poll_rest_session
  • stop_rest_session
  • start_rest_async_job
  • get_rest_async_job_status
  • get_rest_async_job_result
  • cancel_rest_async_job

Дополнительные helpers:

  • open_sse_stream
  • collect_sse_window
  • parse_sse_event
  • close_sse_stream

6.2. GraphQL adapter

Функции:

  • execute_graphql_unary

Отдельно отложено:

  • execute_graphql_subscription_window
  • start_graphql_subscription_session

6.3. gRPC adapter

Функции:

  • execute_grpc_unary
  • execute_grpc_window
  • start_grpc_session
  • poll_grpc_session
  • stop_grpc_session
  • start_grpc_async_job
  • get_grpc_async_job_status
  • get_grpc_async_job_result
  • cancel_grpc_async_job

Helpers:

  • invoke_unary_method
  • open_server_stream
  • collect_server_stream_window
  • decode_stream_item

6.4. WebSocket adapter

Функции:

  • execute_websocket_window
  • start_websocket_session
  • poll_websocket_session
  • stop_websocket_session
  • start_websocket_async_job
  • get_websocket_async_job_status
  • cancel_websocket_async_job

Helpers:

  • connect_websocket
  • send_subscribe_message
  • send_unsubscribe_message
  • read_next_frame
  • decode_text_frame
  • heartbeat_tick
  • reconnect_if_needed

6.5. SOAP adapter

Функции:

  • execute_soap_unary
  • start_soap_async_job
  • get_soap_async_job_status
  • get_soap_async_job_result
  • cancel_soap_async_job

Helpers:

  • render_soap_envelope
  • render_soap_headers
  • parse_soap_envelope
  • parse_soap_fault
  • normalize_xml_value

7. MCP server design

7.1. Route handlers

Ожидаемые handlers:

  • handle_initialize
  • handle_notifications_initialized
  • handle_ping
  • handle_tools_list
  • handle_tools_call
  • handle_get_sse_stream
  • handle_delete_session

7.2. Tool-family generation

Функции:

  • build_unary_tool_definition
  • build_window_tool_definition
  • build_session_tool_family
  • build_async_job_tool_family

Для session:

  • build_session_start_tool
  • build_session_poll_tool
  • build_session_stop_tool

Для async_job:

  • build_async_job_start_tool
  • build_async_job_status_tool
  • build_async_job_result_tool
  • build_async_job_cancel_tool

7.3. Transport session management

Функции:

  • ensure_protocol_version
  • resolve_transport_mode
  • ensure_mcp_session
  • attach_session_headers
  • stream_sse_event
  • close_transport_session

8. Admin API service design

Ожидаемые service функции:

  • validate_streaming_config
  • list_protocol_capabilities
  • list_streaming_presets
  • 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

9. Cleanup jobs

Отдельные jobs:

  • expire_stream_sessions
  • expire_async_jobs
  • reap_orphaned_transport_sessions
  • compact_stream_payloads, если будет нужен storage optimization layer

10. Design rules

  • adapters не агрегируют product-level summaries;
  • runtime не знает деталей transport framing;
  • mcp-server не знает про upstream protocols;
  • admin-api не управляет live protocol connections напрямую;
  • session/job lifecycle должен быть явным в names и contracts;
  • cancellation не должна зависеть от TCP disconnect;
  • bounded limits должны применяться раньше, чем payload попадет в final result.