use std::time::Instant;
use super::*;
use metrale_model_engine::traits::FedAheadPolicy;
use metrale_scheduler::{FeedSource, RowMask, Ticket};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PipelineFaults {
pub keep_overrun: bool,
}
impl PipelineFaults {
pub const NONE: Self = Self {
keep_overrun: false,
};
}
pub(super) struct Inflight {
pub(super) ticket: Ticket,
pub(super) t0: Instant,
pub(super) rows: Vec<(usize, usize)>,
pub(super) masks: Vec<RowMask>,
pub(super) inputs: Option<Vec<FeedSource>>,
pub(super) discard: Vec<bool>,
pub(super) discard_all: bool,
pub(super) host_logits: bool,
}
pub(super) struct Pipeline {
pub(super) inflight: Option<Inflight>,
pub(super) next: Option<Inflight>,
pub(super) faults: PipelineFaults,
}
impl Pipeline {
pub(super) fn new(faults: PipelineFaults) -> Self {
Self {
inflight: None,
next: None,
faults,
}
}
pub(super) fn has_inflight(&self) -> bool {
self.inflight.is_some()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Settled {
Done,
Redo,
Failed,
Idle,
}
pub(crate) fn row_mask(a: &ActiveSeq, think_end_token: Option<u32>) -> RowMask {
if a.think_ended {
[
think_end_token.unwrap_or(u32::MAX),
a.think_start_token.unwrap_or(u32::MAX),
]
} else {
[u32::MAX, u32::MAX]
}
}
fn masked_repick_exact(a: &ActiveSeq, adaptive_sampling: bool) -> bool {
!a.think_ended
|| (a.logit_bias.is_empty()
&& !a.suppress_tool_call
&& !a.think_just_ended
&& !adaptive_sampling)
}
pub(super) fn ahead_allowed(
active: &[ActiveSeq],
sched: &crate::scheduler::sched_ctx::SchedCtx,
adaptive_sampling: bool,
) -> bool {
let model = sched.io.dev.model();
let n = active.len();
sched.io.dev.max_depth() >= 2
&& model.supports_device_token_feed()
&& n > 0
&& match model.fed_ahead_policy() {
FedAheadPolicy::Redo => !model.has_ssm_layers() && !model.is_ep(),
FedAheadPolicy::RepickOnly { max_rows } => n <= max_rows,
}
&& super::super::decode_logits_step::argmax_readback_eligible(active.iter(), sched)
&& active
.iter()
.all(|a| masked_repick_exact(a, adaptive_sampling))
&& active.iter().all(|a| {
!a.finished
&& a.remaining > 1
&& !seqlen_force_stop(a.seq.seq_len + 1, sched.limits.max_seq_len)
})
}
impl SchedulerCore {
pub(super) fn pipelining_configured(&self) -> bool {
self.ctx.io.dev.max_depth() >= 2
&& !self.use_mtp
&& !self.use_ngram_speculative
&& !self.use_self_speculative
}
}
#[derive(Clone, Copy)]
pub(super) struct CommitTokens {
pub think_end_token: Option<u32>,
pub think_start_token: Option<u32>,
pub code_fence_token: Option<u32>,
pub tool_call_start_token: Option<u32>,
pub tool_call_end_token: Option<u32>,
pub adaptive_sampling: bool,
}
pub(super) use super::pipeline_lane::{drain, step_decode_pipelined};