pub trait FfiNode:
Send
+ Sync
+ 'static {
// Required methods
fn node_type(&self) -> RString;
fn process(&self, input: RVec<u8>) -> FfiFuture<RResult<RVec<u8>, RString>>;
// Provided methods
fn process_multi(
&self,
input: RVec<u8>,
) -> FfiFuture<RResult<RVec<RVec<u8>>, RString>> { ... }
fn initialize(
&self,
_session_id: RString,
_node_id: RString,
) -> FfiFuture<RResult<(), RString>> { ... }
}Expand description
FFI-safe node.
process returns an FfiFuture — an ABI-stable future the host
can .await directly. Plugin-side async runtimes (or none) work as
long as the future polls to completion without referencing
runtime-specific globals; in practice, plugins that call back into
host services do so by polling synchronous state from the async
block.
§Forward compatibility (multi-output extension)
process is marked #[sabi(last_prefix_field)] — that’s the cut
between the original FFI surface (1.x) and any methods added in
minor versions. Methods added below it must carry a default impl
so older plugins (whose vtables only expose process) continue to
load. Hosts compiled against the newer ABI then transparently fall
back to the default whenever a plugin omits the new method.
process_multi is the multi-output sibling of process: a node’s
process_streaming callback can fire N times per input (think
SileroVAD emitting Json(event) plus the audio passthrough), and
the single-output process would silently drop everything but the
first emission. process_multi returns the full RVec so the
host can dispatch each blob into the streaming callback chain.
The default impl wraps process as a 1-element RVec so plugins
that only implement single-output stay correct (just lossy when
the underlying node was actually multi-output — same behaviour as
before this method existed).
Required Methods§
fn node_type(&self) -> RString
fn process(&self, input: RVec<u8>) -> FfiFuture<RResult<RVec<u8>, RString>>
Provided Methods§
Sourcefn process_multi(
&self,
input: RVec<u8>,
) -> FfiFuture<RResult<RVec<RVec<u8>>, RString>>
fn process_multi( &self, input: RVec<u8>, ) -> FfiFuture<RResult<RVec<RVec<u8>>, RString>>
Multi-output process. Returns ALL emissions from one input as
a flat RVec<RVec<u8>> (each inner vec is one rmp-serde
RuntimeData blob, in emission order).
Default impl: delegate to process and wrap its single output
as a 1-element vec. Plugins that haven’t been rebuilt against
the multi-output ABI keep working — just without multi-output
semantics. Plugins that override this method get full N-output
fidelity through the LoadableNodeAdapter host wiring.
Sourcefn initialize(
&self,
_session_id: RString,
_node_id: RString,
) -> FfiFuture<RResult<(), RString>>
fn initialize( &self, _session_id: RString, _node_id: RString, ) -> FfiFuture<RResult<(), RString>>
One-time, per-session initialization hook for lazy-load plugins.
Forwarded from the host’s AsyncStreamingNode::initialize()
once per session, before the first process call. Plugins that
do all their work eagerly inside FfiNodeFactory::create()
(e.g. audio2face’s Audio2FaceLipSyncNode::load, live2d-render’s
WgpuBackend::new) can leave this defaulted. Plugins with a
non-trivial init (e.g. llama-cpp spawning a worker thread that
loads a multi-GB GGUF) must override it — without forwarding,
the worker is never spawned and process returns “worker not
running”.
session_id and node_id are forwarded as RStrings so the
plugin can log / tag work with them. emit_progress is NOT
forwarded today — progress events emitted from inside a
loadable plugin’s initialize() are silently dropped. Plugin
authors who need progress visibility should wrap heavy init in
the host (e.g. via WarmSessionPool::prewarm which fires its
own progress before delegating).
Default impl: no-op. Older plugins not rebuilt against this method keep compiling, just without lazy-init semantics.