Promote pipeline-parallel-inference to a first-class app and consolidate observability on the datastream wire, decoupling the dashboard crate from `distribution`. - apps/pipeline-parallel-inference: move the example out of `examples/` into `apps/` as its own workspace, rename binaries to `pp-worker`/`pp-orchestrator`, and strip release binaries - cluster: add `ClusterNode`, a synchronous facade over the actorized distribution protocol (IrohDriver + per-node Runtime hosting Swim/Registry/Metadata/Directory actors with a `MembershipFanout`), replacing ad-hoc `driver.node()`/`tick()` call sites - fleet: add per-node fleet telemetry that ships identity/resource records as `DatastreamFrame`s over the cluster transport to the orchestrator's `DatastreamSink`, folded into a `FleetView` on a 3s tick - provision: add best-effort, opt-in SSH boot-phase telemetry (`PP_DEPLOY_KEY`) that streams rented-node boot logs onto the orchestrator's datastream as `proc.boot.<stage>.*` - dashboard: rewire the crate dependency from `distribution` to `datastream`, drop the standalone `swactor-datastream-dashboard` binary, and rewrite `datastream_source.rs` to demux per-node frames into Overview/Distribution/Fleet views with live-node TTL filtering - distribution: refresh dist/netmap plugin copy and README from "Kademlia routing" to gossip-directory terminology Signed-off-by: Zachery Aaron Shores-Chmielewski <zacheryasc@gmail.com>
119 lines
3.6 KiB
Rust
119 lines
3.6 KiB
Rust
//! Pipeline topology helpers.
|
|
//!
|
|
//! The chain is a linear sequence of `NUM_STAGES` stages. Each stage knows
|
|
//! only its own `STAGE` index and `NUM_STAGES` from env — neighbour names are
|
|
//! derived locally, never read from a central topology config (SPEC §3).
|
|
//!
|
|
//! Names registered on the SWIM registry:
|
|
//!
|
|
//! * `pp-entry` — chain entry point (stage 0).
|
|
//! * `pp-exit` — chain exit point (last stage).
|
|
//! * `pp-stage-{i}` — every stage's per-index handle.
|
|
//!
|
|
//! Stage 0 and the last stage register two names that resolve to the same
|
|
//! `ActorAddress`: the per-index handle plus the entry/exit alias.
|
|
|
|
use crate::cluster::ClusterNode;
|
|
use swactor::actor::ActorAddress;
|
|
|
|
/// SWIM name for the chain entry. The orchestrator resolves this to submit a
|
|
/// request; stage 0 registers it.
|
|
pub const ENTRY_NAME: &str = "pp-entry";
|
|
|
|
/// SWIM name for the chain exit. The last stage registers it; stage 1 (in the
|
|
/// two-stage MVP) uses its own address here so the orchestrator can resolve
|
|
/// the response sink if it ever needs to.
|
|
pub const EXIT_NAME: &str = "pp-exit";
|
|
|
|
/// The per-index name for a stage, e.g. `pp-stage-0`.
|
|
pub fn stage_name(stage: u32) -> String {
|
|
format!("pp-stage-{stage}")
|
|
}
|
|
|
|
/// First stage in the chain owns the prompt-side entry point.
|
|
pub fn is_first_stage(stage: u32) -> bool {
|
|
stage == 0
|
|
}
|
|
|
|
/// Last stage in the chain owns the sampler and detokenizer.
|
|
pub fn is_last_stage(stage: u32, num_stages: u32) -> bool {
|
|
// `num_stages == 0` is degenerate; treat any stage as terminal so callers
|
|
// do not try to compute a next neighbour.
|
|
num_stages == 0 || stage + 1 >= num_stages
|
|
}
|
|
|
|
/// Name of the downstream neighbour (where `StageActivation`s go), or `None`
|
|
/// if this is the last stage.
|
|
pub fn next_stage_name(stage: u32, num_stages: u32) -> Option<String> {
|
|
if is_last_stage(stage, num_stages) {
|
|
None
|
|
} else {
|
|
Some(stage_name(stage + 1))
|
|
}
|
|
}
|
|
|
|
/// Name of the upstream neighbour (where `NextToken`s flow back), or `None`
|
|
/// if this is the first stage.
|
|
pub fn prev_stage_name(stage: u32) -> Option<String> {
|
|
if is_first_stage(stage) {
|
|
None
|
|
} else {
|
|
Some(stage_name(stage - 1))
|
|
}
|
|
}
|
|
|
|
/// Register every name this stage is responsible for, all pointing at the
|
|
/// same `addr`. Returns the list of names registered, mostly for log lines
|
|
/// and tests.
|
|
///
|
|
/// * Stage 0 → `pp-entry`, `pp-stage-0`.
|
|
/// * Last stage → `pp-exit`, `pp-stage-{stage}`.
|
|
/// * Middle → `pp-stage-{stage}` only.
|
|
///
|
|
/// In the two-stage MVP, stage 1 is the last stage, so it registers
|
|
/// `pp-exit` and `pp-stage-1`.
|
|
pub fn register_stage_names(
|
|
node: &ClusterNode,
|
|
stage: u32,
|
|
num_stages: u32,
|
|
addr: ActorAddress,
|
|
) -> Vec<String> {
|
|
let mut names = Vec::new();
|
|
|
|
let idx_name = stage_name(stage);
|
|
node.register_name(&idx_name, addr);
|
|
names.push(idx_name);
|
|
|
|
if is_first_stage(stage) {
|
|
node.register_name(ENTRY_NAME, addr);
|
|
names.push(ENTRY_NAME.into());
|
|
}
|
|
if is_last_stage(stage, num_stages) {
|
|
node.register_name(EXIT_NAME, addr);
|
|
names.push(EXIT_NAME.into());
|
|
}
|
|
|
|
names
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn stage_name_uses_pp_prefix() {
|
|
assert_eq!(stage_name(0), "pp-stage-0");
|
|
assert_eq!(stage_name(7), "pp-stage-7");
|
|
}
|
|
|
|
#[test]
|
|
fn first_stage_has_no_prev() {
|
|
assert_eq!(prev_stage_name(0), None);
|
|
}
|
|
|
|
#[test]
|
|
fn middle_stage_has_both_neighbours() {
|
|
assert_eq!(next_stage_name(1, 4).as_deref(), Some("pp-stage-2"));
|
|
assert_eq!(prev_stage_name(1).as_deref(), Some("pp-stage-0"));
|
|
}
|
|
}
|