@celestia-island/plana-types 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +38 -0
- package/bindings/FileAnchor.ts +3 -0
- package/bindings/engine.ts +277 -0
- package/bindings/enums.ts +31 -0
- package/bindings/httpTypes.ts +197 -0
- package/bindings/index.ts +47 -0
- package/bindings/mcp/aporia.ts +61 -0
- package/bindings/mcp/eleos.ts +18 -0
- package/bindings/mcp/epieikeia.ts +48 -0
- package/bindings/mcp/haplotes.ts +47 -0
- package/bindings/mcp/hubris.ts +41 -0
- package/bindings/mcp/index.ts +13 -0
- package/bindings/mcp/kalos.ts +47 -0
- package/bindings/mcp/neikos.ts +76 -0
- package/bindings/mcp/orexis.ts +64 -0
- package/bindings/mcp/philia.ts +57 -0
- package/bindings/mcp/polemos.ts +55 -0
- package/bindings/mcp/skemma.ts +58 -0
- package/bindings/mcp/skopeo.ts +56 -0
- package/bindings/mcp/webAutomation.ts +31 -0
- package/bindings/model.ts +240 -0
- package/bindings/package.json +16 -0
- package/bindings/region.ts +3 -0
- package/bindings/serde_json/JsonValue.ts +3 -0
- package/bindings/ws/agentLifecycle.ts +41 -0
- package/bindings/ws/auth.ts +15 -0
- package/bindings/ws/baseMessages.ts +7 -0
- package/bindings/ws/bridgeNetwork.ts +71 -0
- package/bindings/ws/core.ts +51 -0
- package/bindings/ws/fileBrowsing.ts +57 -0
- package/bindings/ws/handshake.ts +23 -0
- package/bindings/ws/industrial.ts +72 -0
- package/bindings/ws/knowledgeBase.ts +10 -0
- package/bindings/ws/layer2.ts +25 -0
- package/bindings/ws/llmProvider.ts +74 -0
- package/bindings/ws/logs.ts +11 -0
- package/bindings/ws/malkuth.ts +62 -0
- package/bindings/ws/noa.ts +17 -0
- package/bindings/ws/stateSync.ts +19 -0
- package/bindings/ws/systemUi.ts +5 -0
- package/bindings/ws/tasks.ts +6 -0
- package/bindings/ws/views.ts +163 -0
- package/bindings/ws/workspace.ts +11 -0
- package/bindings/ws/yolo.ts +32 -0
- package/examples/schema_dump.rs +51 -0
- package/package.json +6 -0
- package/pnpm-workspace.yaml +2 -0
- package/src/engine.rs +602 -0
- package/src/enums.rs +334 -0
- package/src/external_mcp.rs +132 -0
- package/src/http.rs +1077 -0
- package/src/identity.rs +160 -0
- package/src/lib.rs +1215 -0
- package/src/malkuth.rs +145 -0
- package/src/mcp/aporia.rs +272 -0
- package/src/mcp/eleos.rs +210 -0
- package/src/mcp/epieikeia.rs +194 -0
- package/src/mcp/haplotes.rs +310 -0
- package/src/mcp/hubris.rs +377 -0
- package/src/mcp/kalos.rs +251 -0
- package/src/mcp/mod.rs +23 -0
- package/src/mcp/neikos.rs +533 -0
- package/src/mcp/orexis.rs +493 -0
- package/src/mcp/philia.rs +267 -0
- package/src/mcp/polemos.rs +241 -0
- package/src/mcp/skemma.rs +442 -0
- package/src/mcp/skopeo.rs +282 -0
- package/src/mcp/web_automation.rs +122 -0
- package/src/model.rs +421 -0
- package/src/protocol/base_messages.rs +125 -0
- package/src/protocol/handshake.rs +364 -0
- package/src/protocol/jsonrpc.rs +888 -0
- package/src/protocol/mod.rs +10 -0
- package/src/rbac.rs +786 -0
- package/src/region.rs +362 -0
- package/src/tracing_helpers.rs +9 -0
- package/src/ws/agent/agent_lifecycle.rs +221 -0
- package/src/ws/agent/layer2.rs +126 -0
- package/src/ws/agent/mod.rs +9 -0
- package/src/ws/agent/state_sync.rs +111 -0
- package/src/ws/agent/tasks.rs +43 -0
- package/src/ws/agent/yolo.rs +161 -0
- package/src/ws/mod.rs +10 -0
- package/src/ws/services/auth.rs +99 -0
- package/src/ws/services/industrial.rs +647 -0
- package/src/ws/services/knowledge_base.rs +59 -0
- package/src/ws/services/llm_provider.rs +371 -0
- package/src/ws/services/mod.rs +7 -0
- package/src/ws/ui/bridge_network.rs +96 -0
- package/src/ws/ui/file_browsing.rs +88 -0
- package/src/ws/ui/logs.rs +55 -0
- package/src/ws/ui/mod.rs +11 -0
- package/src/ws/ui/noa.rs +105 -0
- package/src/ws/ui/system_ui.rs +27 -0
- package/src/ws/ui/views.rs +159 -0
- package/src/ws/ui/workspace.rs +73 -0
package/src/engine.rs
ADDED
|
@@ -0,0 +1,602 @@
|
|
|
1
|
+
//! Celestia Engine Protocol (CEP) — unified model-runtime access protocol.
|
|
2
|
+
//!
|
|
3
|
+
//! Any model-serving engine — llama.cpp, vLLM, a speech/sensor model, or a
|
|
4
|
+
//! custom engine written in any language — speaks this protocol over a
|
|
5
|
+
//! WebSocket transport using JSON-RPC 2.0 envelopes (see
|
|
6
|
+
//! [`crate::protocol::jsonrpc`]) to join the arona gateway's Router cluster.
|
|
7
|
+
//! The gateway acts as the intermediary: clients never talk to engines
|
|
8
|
+
//! directly, only to the gateway, and the gateway multiplexes requests
|
|
9
|
+
//! across engines via least-count routing, session affinity and
|
|
10
|
+
//! capacity-aware placement.
|
|
11
|
+
//!
|
|
12
|
+
//! ## The protocol is capability-driven, not input/output-locked
|
|
13
|
+
//!
|
|
14
|
+
//! CEP does NOT assume a text LLM. Engines declare what they actually
|
|
15
|
+
//! consume and produce at handshake time (`modalities`, `content_types`),
|
|
16
|
+
//! and request payloads are free-form JSON ([`EngineInvokeParams`]) or
|
|
17
|
+
//! content-part messages ([`EngineMessage`]) that can carry text, base64
|
|
18
|
+
//! binary (audio/image/video), structured sensor readings or arbitrary
|
|
19
|
+
//! data. The convenience methods `Engine.Chat` / `Engine.Embeddings` are
|
|
20
|
+
//! merely the two most common shapes; engines with specialised I/O
|
|
21
|
+
//! (audio generation, sensor-signal processing, tensor streaming…) use
|
|
22
|
+
//! `Engine.Invoke` with engine-defined method names and payloads. The
|
|
23
|
+
//! gateway routes by declared capability and passes unknown payloads
|
|
24
|
+
//! through untouched.
|
|
25
|
+
//!
|
|
26
|
+
//! ## Wire methods (JSON-RPC method names)
|
|
27
|
+
//!
|
|
28
|
+
//! | Method | Direction | Purpose |
|
|
29
|
+
//! |--------|-----------|---------|
|
|
30
|
+
//! | `Engine.Handshake` | engine → gateway (first) | Identity + capability declaration |
|
|
31
|
+
//! | `Engine.Chat` | gateway → engine | Convenience: non-streaming text chat |
|
|
32
|
+
//! | `Engine.ChatStart` | gateway → engine | Convenience: streaming text chat |
|
|
33
|
+
//! | `Engine.ChatChunk` | engine → gateway (notification) | Streamed text token delta |
|
|
34
|
+
//! | `Engine.Embeddings` | gateway → engine | Convenience: batch text embeddings |
|
|
35
|
+
//! | `Engine.Invoke` | gateway → engine | Generic method: any engine-defined operation |
|
|
36
|
+
//! | `Engine.InvokeStart` | gateway → engine | Generic streaming invocation |
|
|
37
|
+
//! | `Engine.StreamChunk` | engine → gateway (notification) | Generic streamed data block (any mime) |
|
|
38
|
+
//! | `Engine.Models` | gateway → engine | List serving models |
|
|
39
|
+
//! | `Engine.Stats` | gateway → engine | Telemetry (GPU utilisation etc.) |
|
|
40
|
+
//! | `Engine.Shutdown` | gateway → engine | Graceful stop (deploy stop path) |
|
|
41
|
+
//!
|
|
42
|
+
//! ## Streaming
|
|
43
|
+
//!
|
|
44
|
+
//! Text streams use the convenience `Engine.ChatStart` / `Engine.ChatChunk`
|
|
45
|
+
//! pair (gateway-generated `stream_id` correlation). Generic streams —
|
|
46
|
+
//! audio frames, sensor samples, tensors — use `Engine.InvokeStart`
|
|
47
|
+
//! (same `stream_id` accept/notify shape) with [`EngineStreamChunk`]
|
|
48
|
+
//! carrying a mime + encoding description so the consumer can decode
|
|
49
|
+
//! without prior agreement. Large binary blocks may additionally travel
|
|
50
|
+
//! as WebSocket binary frames: a JSON notification announces the chunk
|
|
51
|
+
//! (`encoding: "binary-frame"`), and the immediately following WS binary
|
|
52
|
+
//! frame carries the bytes.
|
|
53
|
+
//!
|
|
54
|
+
//! ## Handshake
|
|
55
|
+
//!
|
|
56
|
+
//! The engine MUST send `Engine.Handshake` as its first message. The
|
|
57
|
+
//! gateway replies with [`EngineHandshakeResult`]; a rejected handshake
|
|
58
|
+
//! closes the connection with the given error.
|
|
59
|
+
//!
|
|
60
|
+
//! ## Binary transfer (JSON-RPC cannot carry binary — this is the escape
|
|
61
|
+
//! hatch, and every byte is labelled with a MIME type)
|
|
62
|
+
//!
|
|
63
|
+
//! JSON-RPC 2.0 defines messages as JSON objects; there is no binary frame
|
|
64
|
+
//! in the spec. WebSocket, however, has first-class binary frames that may
|
|
65
|
+
//! interleave with text frames on one connection. CEP uses that:
|
|
66
|
+
//!
|
|
67
|
+
//! 1. **Announce** — the sender emits a JSON-RPC *notification*
|
|
68
|
+
//! `Engine.BinaryStart` declaring `transfer_id`, the MIME type of the
|
|
69
|
+
//! payload, `total_bytes`, the expected binary-frame count and an
|
|
70
|
+
//! optional SHA-256 hex checksum. The receiver switches to
|
|
71
|
+
//! "binary-receive" state for this transfer id.
|
|
72
|
+
//! 2. **Payload** — the bytes travel as a sequence of raw WebSocket
|
|
73
|
+
//! **binary frames** (no JSON envelope). Senders MUST split large
|
|
74
|
+
//! payloads into frames of at most [`ENGINE_BINARY_MAX_FRAME_BYTES`]
|
|
75
|
+
//! so interop holds across implementations (browsers, embedded
|
|
76
|
+
//! engines). The receiver concatenates frames in arrival order.
|
|
77
|
+
//! 3. **Finish** — after the last frame the sender emits the JSON-RPC
|
|
78
|
+
//! *notification* `Engine.BinaryEnd` with the received byte count and
|
|
79
|
+
//! checksum status. The receiver validates and returns to normal RPC
|
|
80
|
+
//! operation. A single connection carries at most one active binary
|
|
81
|
+
//! transfer at a time (frames are ordered per-connection; concurrent
|
|
82
|
+
//! transfers would be ambiguous — use separate connections or the
|
|
83
|
+
//! stream channel for concurrency).
|
|
84
|
+
//! 4. **Abort** — either side may emit `Engine.BinaryAbort` to cancel;
|
|
85
|
+
//! the receiver discards buffered bytes and returns to normal RPC.
|
|
86
|
+
//!
|
|
87
|
+
//! ### Resynchronisation & failure handling (how the receiver always finds
|
|
88
|
+
//! the next JSON message)
|
|
89
|
+
//!
|
|
90
|
+
//! WebSocket frames carry a type (Text vs Binary) and TCP underneath makes
|
|
91
|
+
//! frames ordered and lossless within a connection — the receiver's state
|
|
92
|
+
//! machine therefore never scans bytes for JSON boundaries:
|
|
93
|
+
//!
|
|
94
|
+
//! - **Normal state** accepts only Text frames; a Binary frame without an
|
|
95
|
+
//! active transfer is a protocol violation → the connection is closed.
|
|
96
|
+
//! - **Binary-receive state** accepts only Binary frames; a Text frame that
|
|
97
|
+
//! is not `Engine.BinaryEnd`/`Engine.BinaryAbort` means the sender
|
|
98
|
+
//! abandoned the transfer → buffered bytes are discarded and the Text
|
|
99
|
+
//! frame is processed normally (the state machine simply resets).
|
|
100
|
+
//! - **Timeouts** ([`ENGINE_BINARY_RECEIVE_TIMEOUT_SECS`]) guard against a
|
|
101
|
+
//! sender that dies mid-transfer: if the first frame after
|
|
102
|
+
//! `Engine.BinaryStart`, or any inter-frame gap, exceeds the timeout the
|
|
103
|
+
//! receiver aborts the transfer (locally and/or via `Engine.BinaryAbort`)
|
|
104
|
+
//! and returns to normal RPC.
|
|
105
|
+
//! - **Corruption** (byte-count or checksum mismatch at
|
|
106
|
+
//! `Engine.BinaryEnd`) invalidates the whole transfer — binary payloads
|
|
107
|
+
//! have no internal boundaries, so partial data can never be trusted.
|
|
108
|
+
//! - **Hard failure** (connection close mid-transfer) resets everything:
|
|
109
|
+
//! reconnect + re-handshake, then re-run the transfer.
|
|
110
|
+
//!
|
|
111
|
+
//! No sliding window is needed: frames are ordered and lossless, so there
|
|
112
|
+
//! is nothing to reorder or retransmit inside a connection.
|
|
113
|
+
//!
|
|
114
|
+
//! Every transfer is labelled with a MIME type ([`EngineBinaryStartParams`])
|
|
115
|
+
//! — the same MIME vocabulary used by content parts and stream chunks — so
|
|
116
|
+
//! consumers never guess what the bytes are.
|
|
117
|
+
//!
|
|
118
|
+
//! ### Why not base64-only?
|
|
119
|
+
//!
|
|
120
|
+
//! Base64 works everywhere but costs +33% bandwidth and CPU. The
|
|
121
|
+
//! announce/payload/finish triple keeps the JSON-RPC control plane intact
|
|
122
|
+
//! (handshake, correlation, errors all stay JSON) while the bulk bytes
|
|
123
|
+
//! avoid the encoding tax. Engines that prefer simplicity may still send
|
|
124
|
+
//! base64 content parts — both paths are first-class.
|
|
125
|
+
|
|
126
|
+
use serde::{Deserialize, Serialize};
|
|
127
|
+
use ts_rs::TS;
|
|
128
|
+
|
|
129
|
+
/// CEP wire-protocol version. Bumped on incompatible payload changes.
|
|
130
|
+
pub const ENGINE_PROTOCOL_VERSION: u32 = 3;
|
|
131
|
+
|
|
132
|
+
/// Upper bound for a single binary-transfer frame, in bytes. Senders MUST
|
|
133
|
+
/// split larger payloads; receivers SHOULD refuse oversized frames.
|
|
134
|
+
pub const ENGINE_BINARY_MAX_FRAME_BYTES: usize = 256 * 1024;
|
|
135
|
+
|
|
136
|
+
/// Timeout guard for an in-flight binary transfer, in seconds. The
|
|
137
|
+
/// receiver aborts the transfer if the first frame after `Engine.BinaryStart`
|
|
138
|
+
/// or any inter-frame gap exceeds this bound (sender died mid-transfer).
|
|
139
|
+
/// A transfer this size or smaller should normally complete in well under
|
|
140
|
+
/// this budget.
|
|
141
|
+
pub const ENGINE_BINARY_RECEIVE_TIMEOUT_SECS: u64 = 60;
|
|
142
|
+
|
|
143
|
+
// ═══════════════════════════════════════════════════════════
|
|
144
|
+
// Handshake / identity
|
|
145
|
+
// ═══════════════════════════════════════════════════════════
|
|
146
|
+
|
|
147
|
+
/// `Engine.Handshake` params — the engine's first message on connect.
|
|
148
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
149
|
+
#[ts(export, export_to = "engine.ts")]
|
|
150
|
+
pub struct EngineHandshakeParams {
|
|
151
|
+
/// Optional shared token; the gateway rejects mismatches.
|
|
152
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
153
|
+
#[ts(optional)]
|
|
154
|
+
pub token: Option<String>,
|
|
155
|
+
pub engine: EngineIdentity,
|
|
156
|
+
pub capabilities: EngineCapabilities,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/// Engine implementation identity (any language is fine — this is the
|
|
160
|
+
/// interchange contract).
|
|
161
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
162
|
+
#[ts(export, export_to = "engine.ts")]
|
|
163
|
+
pub struct EngineIdentity {
|
|
164
|
+
pub name: String,
|
|
165
|
+
pub version: String,
|
|
166
|
+
/// Implementation language, e.g. "rust", "cpp".
|
|
167
|
+
#[serde(default)]
|
|
168
|
+
#[ts(optional)]
|
|
169
|
+
pub language: Option<String>,
|
|
170
|
+
/// Optional vendor URL.
|
|
171
|
+
#[serde(default)]
|
|
172
|
+
#[ts(optional)]
|
|
173
|
+
pub vendor: Option<String>,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Input/output modalities an engine can handle. The gateway does NOT
|
|
177
|
+
/// assume text — it routes and passes payloads through based on this
|
|
178
|
+
/// declaration.
|
|
179
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
|
|
180
|
+
#[ts(export, export_to = "engine.ts")]
|
|
181
|
+
pub enum EngineModality {
|
|
182
|
+
Text,
|
|
183
|
+
Audio,
|
|
184
|
+
Image,
|
|
185
|
+
Video,
|
|
186
|
+
Sensor,
|
|
187
|
+
Tensor,
|
|
188
|
+
Generic,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/// Static capability declaration supplied at handshake time.
|
|
192
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
193
|
+
#[ts(export, export_to = "engine.ts")]
|
|
194
|
+
pub struct EngineCapabilities {
|
|
195
|
+
#[serde(default = "default_true")]
|
|
196
|
+
pub streaming: bool,
|
|
197
|
+
#[serde(default)]
|
|
198
|
+
pub embeddings: bool,
|
|
199
|
+
#[serde(default = "default_context")]
|
|
200
|
+
pub max_context_length: usize,
|
|
201
|
+
#[serde(default)]
|
|
202
|
+
pub hardware: Vec<EngineGpuInfo>,
|
|
203
|
+
/// Modalities the engine can consume as input (empty = text only).
|
|
204
|
+
#[serde(default)]
|
|
205
|
+
pub input_modalities: Vec<EngineModality>,
|
|
206
|
+
/// Modalities the engine can produce as output.
|
|
207
|
+
#[serde(default)]
|
|
208
|
+
pub output_modalities: Vec<EngineModality>,
|
|
209
|
+
/// MIME content types accepted as input (e.g. "audio/wav",
|
|
210
|
+
/// "application/octet-stream", "application/json").
|
|
211
|
+
#[serde(default)]
|
|
212
|
+
pub content_types: Vec<String>,
|
|
213
|
+
/// Engine-defined `Engine.Invoke` method names beyond the standard
|
|
214
|
+
/// convenience methods (e.g. "audio.generate", "signal.filter").
|
|
215
|
+
/// Any engine-specific operation is reachable via `Engine.Invoke`
|
|
216
|
+
/// even when absent from this list.
|
|
217
|
+
#[serde(default)]
|
|
218
|
+
pub methods: Vec<String>,
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
fn default_true() -> bool {
|
|
222
|
+
true
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
fn default_context() -> usize {
|
|
226
|
+
128_000
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/// One GPU the engine can drive — used by capacity-aware placement.
|
|
230
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
231
|
+
#[ts(export, export_to = "engine.ts")]
|
|
232
|
+
pub struct EngineGpuInfo {
|
|
233
|
+
pub name: String,
|
|
234
|
+
#[serde(default)]
|
|
235
|
+
pub vram_gb: u64,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/// `Engine.Handshake` result. `ok: false` closes the connection.
|
|
239
|
+
///
|
|
240
|
+
/// Handshake direction: when the gateway connects to an engine (engine is
|
|
241
|
+
/// the server), the gateway sends `Engine.Handshake` and the engine answers
|
|
242
|
+
/// with this result carrying its **own** declared capabilities, so the
|
|
243
|
+
/// gateway learns modalities/content types before any request. When the
|
|
244
|
+
/// engine connects to the gateway (engine is the client), the engine sends
|
|
245
|
+
/// `Engine.Handshake` with its capabilities in the params and the gateway
|
|
246
|
+
/// answers with `ok` only — the `capabilities` field is then ignored.
|
|
247
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
248
|
+
#[ts(export, export_to = "engine.ts")]
|
|
249
|
+
pub struct EngineHandshakeResult {
|
|
250
|
+
pub ok: bool,
|
|
251
|
+
#[serde(default)]
|
|
252
|
+
#[ts(optional)]
|
|
253
|
+
pub error: Option<String>,
|
|
254
|
+
pub protocol_version: u32,
|
|
255
|
+
/// The engine's own capability declaration (server-mode handshake).
|
|
256
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
257
|
+
#[ts(optional)]
|
|
258
|
+
pub capabilities: Option<EngineCapabilities>,
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ═══════════════════════════════════════════════════════════
|
|
262
|
+
// Chat
|
|
263
|
+
// ═══════════════════════════════════════════════════════════
|
|
264
|
+
|
|
265
|
+
/// One content unit inside a message. Text is a plain string; everything
|
|
266
|
+
/// else is a data block described by mime + encoding so consumers can
|
|
267
|
+
/// decode without prior agreement:
|
|
268
|
+
/// - `data`: base64 bytes (standard `encoding: "base64"`)
|
|
269
|
+
/// - `encoding: "binary-frame"`: bytes arrive in the immediately following
|
|
270
|
+
/// WebSocket binary frame (JSON notification is the announcer/trailer)
|
|
271
|
+
/// - `encoding: "json"`: `data` is inline JSON (structured sensor readings,
|
|
272
|
+
/// tensors, feature vectors…)
|
|
273
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
274
|
+
#[ts(export, export_to = "engine.ts")]
|
|
275
|
+
pub struct EngineContentPart {
|
|
276
|
+
/// MIME type — "text/plain" for plain text parts.
|
|
277
|
+
pub mime: String,
|
|
278
|
+
/// Encoding of `data` ("base64" | "binary-frame" | "json" | "utf-8").
|
|
279
|
+
pub encoding: String,
|
|
280
|
+
/// Payload: base64 text, inline JSON, or raw text depending on
|
|
281
|
+
/// `encoding`. Empty for binary-frame parts (bytes follow as a WS
|
|
282
|
+
/// binary frame).
|
|
283
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
284
|
+
#[ts(optional)]
|
|
285
|
+
pub data: Option<serde_json::Value>,
|
|
286
|
+
/// Optional shape hint for tensor/sensor parts, e.g. [1, 16000]
|
|
287
|
+
/// (channels × samples) or the sensor schema id.
|
|
288
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
289
|
+
#[ts(optional)]
|
|
290
|
+
pub shape: Option<Vec<usize>>,
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
impl EngineContentPart {
|
|
294
|
+
pub fn text(content: &str) -> Self {
|
|
295
|
+
Self {
|
|
296
|
+
mime: "text/plain".into(),
|
|
297
|
+
encoding: "utf-8".into(),
|
|
298
|
+
data: Some(serde_json::Value::String(content.to_string())),
|
|
299
|
+
shape: None,
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
pub fn base64(mime: &str, bytes: &str) -> Self {
|
|
304
|
+
Self {
|
|
305
|
+
mime: mime.into(),
|
|
306
|
+
encoding: "base64".into(),
|
|
307
|
+
data: Some(serde_json::Value::String(bytes.to_string())),
|
|
308
|
+
shape: None,
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
pub fn json(mime: &str, value: serde_json::Value) -> Self {
|
|
313
|
+
Self {
|
|
314
|
+
mime: mime.into(),
|
|
315
|
+
encoding: "json".into(),
|
|
316
|
+
data: Some(value),
|
|
317
|
+
shape: None,
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
pub fn binary_frame(mime: &str) -> Self {
|
|
322
|
+
Self {
|
|
323
|
+
mime: mime.into(),
|
|
324
|
+
encoding: "binary-frame".into(),
|
|
325
|
+
data: None,
|
|
326
|
+
shape: None,
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/// A message in an `Engine.Chat` / `Engine.ChatStart` / `Engine.Invoke`
|
|
332
|
+
/// payload. Content is a list of parts so mixed-modality inputs (text +
|
|
333
|
+
/// audio + sensor…) are representable. `role` is advisory; specialised
|
|
334
|
+
/// engines may ignore it.
|
|
335
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
336
|
+
#[ts(export, export_to = "engine.ts")]
|
|
337
|
+
pub struct EngineMessage {
|
|
338
|
+
pub role: String,
|
|
339
|
+
pub content: Vec<EngineContentPart>,
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
impl EngineMessage {
|
|
343
|
+
pub fn text(role: &str, content: &str) -> Self {
|
|
344
|
+
Self {
|
|
345
|
+
role: role.into(),
|
|
346
|
+
content: vec![EngineContentPart::text(content)],
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/// `Engine.Chat` (non-streaming) / `Engine.ChatStart` (streaming) params.
|
|
352
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
353
|
+
#[ts(export, export_to = "engine.ts")]
|
|
354
|
+
pub struct EngineChatParams {
|
|
355
|
+
pub model: String,
|
|
356
|
+
pub messages: Vec<EngineMessage>,
|
|
357
|
+
#[serde(default)]
|
|
358
|
+
#[ts(optional)]
|
|
359
|
+
pub temperature: Option<f32>,
|
|
360
|
+
#[serde(default)]
|
|
361
|
+
#[ts(optional)]
|
|
362
|
+
pub max_tokens: Option<u32>,
|
|
363
|
+
/// Present for streaming requests — chunks are tagged with this id.
|
|
364
|
+
#[serde(default)]
|
|
365
|
+
#[ts(optional)]
|
|
366
|
+
pub stream_id: Option<String>,
|
|
367
|
+
/// Free-form passthrough merged into the upstream payload (same
|
|
368
|
+
/// semantics as the gateway's `extra` field).
|
|
369
|
+
#[serde(default)]
|
|
370
|
+
#[ts(optional)]
|
|
371
|
+
pub extra: Option<serde_json::Value>,
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/// `Engine.Chat` result for a non-streaming completion.
|
|
375
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
376
|
+
#[ts(export, export_to = "engine.ts")]
|
|
377
|
+
pub struct EngineChatResult {
|
|
378
|
+
pub model: String,
|
|
379
|
+
pub content: String,
|
|
380
|
+
#[serde(default)]
|
|
381
|
+
#[ts(optional)]
|
|
382
|
+
pub usage: Option<EngineUsage>,
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/// `Engine.ChatStart` acceptance result. `ok: false` rejects the stream
|
|
386
|
+
/// before any chunk is sent.
|
|
387
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
388
|
+
#[ts(export, export_to = "engine.ts")]
|
|
389
|
+
pub struct EngineChatStartResult {
|
|
390
|
+
pub ok: bool,
|
|
391
|
+
#[serde(default)]
|
|
392
|
+
#[ts(optional)]
|
|
393
|
+
pub error: Option<String>,
|
|
394
|
+
pub stream_id: String,
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/// `Engine.ChatChunk` notification — streamed token delta.
|
|
398
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
399
|
+
#[ts(export, export_to = "engine.ts")]
|
|
400
|
+
pub struct EngineChatChunk {
|
|
401
|
+
pub stream_id: String,
|
|
402
|
+
#[serde(default)]
|
|
403
|
+
pub token: String,
|
|
404
|
+
/// Set on the final chunk.
|
|
405
|
+
#[serde(default)]
|
|
406
|
+
pub is_complete: bool,
|
|
407
|
+
#[serde(default)]
|
|
408
|
+
#[ts(optional)]
|
|
409
|
+
pub usage: Option<EngineUsage>,
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
413
|
+
#[ts(export, export_to = "engine.ts")]
|
|
414
|
+
pub struct EngineUsage {
|
|
415
|
+
#[serde(default)]
|
|
416
|
+
pub prompt_tokens: usize,
|
|
417
|
+
#[serde(default)]
|
|
418
|
+
pub completion_tokens: usize,
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// ═══════════════════════════════════════════════════════════
|
|
422
|
+
// Generic invocation (capability-driven extension channel)
|
|
423
|
+
// ═══════════════════════════════════════════════════════════
|
|
424
|
+
|
|
425
|
+
/// `Engine.Invoke` / `Engine.InvokeStart` params — the generic extension
|
|
426
|
+
/// channel. `method` is engine-defined (e.g. "audio.generate",
|
|
427
|
+
/// "signal.filter", "train.step"); `params` is any JSON the engine
|
|
428
|
+
/// understands. `messages` is optional and reuses the multimodal content
|
|
429
|
+
/// model for engines that mix free-form payloads with content parts.
|
|
430
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
431
|
+
#[ts(export, export_to = "engine.ts")]
|
|
432
|
+
pub struct EngineInvokeParams {
|
|
433
|
+
pub method: String,
|
|
434
|
+
pub params: serde_json::Value,
|
|
435
|
+
#[serde(default)]
|
|
436
|
+
#[ts(optional)]
|
|
437
|
+
pub messages: Option<Vec<EngineMessage>>,
|
|
438
|
+
/// Present for streaming invocations — chunks are tagged with this id.
|
|
439
|
+
#[serde(default)]
|
|
440
|
+
#[ts(optional)]
|
|
441
|
+
pub stream_id: Option<String>,
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/// `Engine.Invoke` result — any JSON the engine returns.
|
|
445
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
446
|
+
#[ts(export, export_to = "engine.ts")]
|
|
447
|
+
pub struct EngineInvokeResult {
|
|
448
|
+
pub method: String,
|
|
449
|
+
pub result: serde_json::Value,
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/// `Engine.InvokeStart` acceptance result (same shape as ChatStart).
|
|
453
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
454
|
+
#[ts(export, export_to = "engine.ts")]
|
|
455
|
+
pub struct EngineInvokeStartResult {
|
|
456
|
+
pub ok: bool,
|
|
457
|
+
#[serde(default)]
|
|
458
|
+
#[ts(optional)]
|
|
459
|
+
pub error: Option<String>,
|
|
460
|
+
pub stream_id: String,
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/// `Engine.StreamChunk` notification — a generic streamed data block for
|
|
464
|
+
/// any output modality (audio frame, sensor sample batch, tensor slice…).
|
|
465
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
466
|
+
#[ts(export, export_to = "engine.ts")]
|
|
467
|
+
pub struct EngineStreamChunk {
|
|
468
|
+
pub stream_id: String,
|
|
469
|
+
/// MIME type of this block (e.g. "audio/wav", "application/json").
|
|
470
|
+
pub mime: String,
|
|
471
|
+
/// "base64" | "binary-frame" | "json" | "utf-8".
|
|
472
|
+
pub encoding: String,
|
|
473
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
474
|
+
#[ts(optional)]
|
|
475
|
+
pub data: Option<serde_json::Value>,
|
|
476
|
+
/// Optional shape hint for tensor/sensor blocks.
|
|
477
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
478
|
+
#[ts(optional)]
|
|
479
|
+
pub shape: Option<Vec<usize>>,
|
|
480
|
+
/// Set on the final block.
|
|
481
|
+
#[serde(default)]
|
|
482
|
+
pub is_complete: bool,
|
|
483
|
+
#[serde(default)]
|
|
484
|
+
#[ts(optional)]
|
|
485
|
+
pub usage: Option<EngineUsage>,
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// ═══════════════════════════════════════════════════════════
|
|
489
|
+
// Embeddings
|
|
490
|
+
// ═══════════════════════════════════════════════════════════
|
|
491
|
+
|
|
492
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
493
|
+
#[ts(export, export_to = "engine.ts")]
|
|
494
|
+
pub struct EngineEmbeddingsParams {
|
|
495
|
+
pub model: String,
|
|
496
|
+
pub input: Vec<String>,
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
500
|
+
#[ts(export, export_to = "engine.ts")]
|
|
501
|
+
pub struct EngineEmbeddingsResult {
|
|
502
|
+
pub model: String,
|
|
503
|
+
pub embeddings: Vec<Vec<f32>>,
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ═══════════════════════════════════════════════════════════
|
|
507
|
+
// Models / stats / shutdown
|
|
508
|
+
// ═══════════════════════════════════════════════════════════
|
|
509
|
+
|
|
510
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
511
|
+
#[ts(export, export_to = "engine.ts")]
|
|
512
|
+
pub struct EngineModelInfo {
|
|
513
|
+
pub id: String,
|
|
514
|
+
#[serde(default)]
|
|
515
|
+
#[ts(optional)]
|
|
516
|
+
pub context_length: Option<usize>,
|
|
517
|
+
#[serde(default)]
|
|
518
|
+
pub embedding: bool,
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/// `Engine.Models` result.
|
|
522
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
523
|
+
#[ts(export, export_to = "engine.ts")]
|
|
524
|
+
pub struct EngineModelsResult {
|
|
525
|
+
pub models: Vec<EngineModelInfo>,
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/// `Engine.Stats` result — live telemetry for capacity-aware placement.
|
|
529
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
530
|
+
#[ts(export, export_to = "engine.ts")]
|
|
531
|
+
pub struct EngineStatsResult {
|
|
532
|
+
/// Per-GPU utilisation percentages (0-100), same shape as the agent
|
|
533
|
+
/// control-plane heartbeats.
|
|
534
|
+
#[serde(default)]
|
|
535
|
+
pub gpu_utilization: Vec<u32>,
|
|
536
|
+
#[serde(default)]
|
|
537
|
+
pub uptime_secs: u64,
|
|
538
|
+
/// Model id currently loaded, when the engine pins a single model.
|
|
539
|
+
#[serde(default)]
|
|
540
|
+
#[ts(optional)]
|
|
541
|
+
pub model_loaded: Option<String>,
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/// `Engine.Shutdown` params — graceful stop requested by the gateway.
|
|
545
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
546
|
+
#[ts(export, export_to = "engine.ts")]
|
|
547
|
+
pub struct EngineShutdownParams {
|
|
548
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
549
|
+
#[ts(optional)]
|
|
550
|
+
pub reason: Option<String>,
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// ═══════════════════════════════════════════════════════════
|
|
554
|
+
// Binary transfer (JSON-RPC control plane + WS binary frames)
|
|
555
|
+
// ═══════════════════════════════════════════════════════════
|
|
556
|
+
|
|
557
|
+
/// `Engine.BinaryStart` notification params — the announce packet sent
|
|
558
|
+
/// BEFORE any binary frame. Every payload is labelled with a MIME type.
|
|
559
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
560
|
+
#[ts(export, export_to = "engine.ts")]
|
|
561
|
+
pub struct EngineBinaryStartParams {
|
|
562
|
+
/// Correlation id shared by announce / frames / finish.
|
|
563
|
+
pub transfer_id: String,
|
|
564
|
+
/// MIME type of the whole payload (e.g. "audio/wav",
|
|
565
|
+
/// "application/octet-stream"). Empty means unspecified binary.
|
|
566
|
+
pub mime: String,
|
|
567
|
+
pub total_bytes: u64,
|
|
568
|
+
/// Expected number of binary frames (for early truncation checks).
|
|
569
|
+
pub chunk_count: u32,
|
|
570
|
+
/// Optional SHA-256 hex digest of the payload.
|
|
571
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
572
|
+
#[ts(optional)]
|
|
573
|
+
pub checksum: Option<String>,
|
|
574
|
+
/// Optional association with a stream (generic streaming).
|
|
575
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
576
|
+
#[ts(optional)]
|
|
577
|
+
pub stream_id: Option<String>,
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/// `Engine.BinaryEnd` notification params — sent after the final binary
|
|
581
|
+
/// frame; the receiver validates and returns to normal RPC operation.
|
|
582
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
583
|
+
#[ts(export, export_to = "engine.ts")]
|
|
584
|
+
pub struct EngineBinaryEndParams {
|
|
585
|
+
pub transfer_id: String,
|
|
586
|
+
/// Bytes actually received across all frames.
|
|
587
|
+
pub bytes_received: u64,
|
|
588
|
+
/// Whether the checksum matched (None when no checksum was announced).
|
|
589
|
+
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
590
|
+
#[ts(optional)]
|
|
591
|
+
pub checksum_ok: Option<bool>,
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/// `Engine.BinaryAbort` notification params — cancels an in-flight
|
|
595
|
+
/// transfer; the receiver discards buffered bytes and returns to normal
|
|
596
|
+
/// RPC operation.
|
|
597
|
+
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
|
598
|
+
#[ts(export, export_to = "engine.ts")]
|
|
599
|
+
pub struct EngineBinaryAbortParams {
|
|
600
|
+
pub transfer_id: String,
|
|
601
|
+
pub reason: String,
|
|
602
|
+
}
|