use std::time::Instant;
use skippy_runtime::SamplingConfig;
use crate::CacheAffinity;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SequenceStatus {
Waiting,
Running,
Preempted,
Finished,
Failed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PrefixRestoreKind {
ResidentKv,
RecurrentWholeState,
KvAndRecurrentWholeState,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PrefixRestore {
pub page_id: String,
pub token_count: usize,
pub kind: PrefixRestoreKind,
}
#[derive(Debug, Clone)]
pub struct Sequence {
pub id: String,
pub prompt_tokens: Vec<i32>,
pub generated_tokens: Vec<i32>,
pub max_tokens: u32,
pub sampling: Option<SamplingConfig>,
pub priority: u64,
pub admission_tokens: usize,
pub status: SequenceStatus,
pub prefix_restore: Option<PrefixRestore>,
pub cache_affinity: CacheAffinity,
pub admitted_at: Option<Instant>,
pub(crate) prefill_cursor: usize,
pub(crate) enqueued_turn: u64,
pub(crate) enqueue_order: u64,
}
impl Sequence {
pub fn new(
id: String,
prompt_tokens: Vec<i32>,
max_tokens: u32,
sampling: Option<SamplingConfig>,
priority: u64,
) -> Self {
let admission_tokens = prompt_tokens
.len()
.saturating_add(usize::try_from(max_tokens).unwrap_or(usize::MAX));
Self {
id,
prompt_tokens,
generated_tokens: Vec::new(),
max_tokens,
sampling,
priority,
admission_tokens,
status: SequenceStatus::Waiting,
prefix_restore: None,
cache_affinity: CacheAffinity::default(),
admitted_at: None,
prefill_cursor: 0,
enqueued_turn: 0,
enqueue_order: 0,
}
}
pub fn with_admission_tokens(mut self, admission_tokens: usize) -> Self {
self.admission_tokens = admission_tokens.max(self.prompt_tokens.len());
self
}
pub fn with_prefix_restore(mut self, restore: PrefixRestore) -> Self {
let replay_len = self.recompute_tokens().len();
self.prefill_cursor = restore.token_count.min(replay_len.saturating_sub(1));
self.prefix_restore = Some(restore);
self
}
pub fn with_cache_affinity(mut self, affinity: CacheAffinity) -> Self {
self.cache_affinity = affinity;
self
}
pub fn with_prefilled_generation(mut self, generated_tokens: Vec<i32>) -> Self {
self.generated_tokens = generated_tokens;
self.prefill_cursor = self.recompute_token_count();
self
}
pub fn is_finished(&self) -> bool {
matches!(
self.status,
SequenceStatus::Finished | SequenceStatus::Failed
)
}
pub fn recompute_tokens(&self) -> Vec<i32> {
let replay_generated = self.generated_tokens.len().saturating_sub(1);
let mut tokens = Vec::with_capacity(self.prompt_tokens.len() + replay_generated);
tokens.extend_from_slice(&self.prompt_tokens);
tokens.extend_from_slice(&self.generated_tokens[..replay_generated]);
tokens
}
pub(crate) fn recompute_token_count(&self) -> usize {
self.prompt_tokens
.len()
.saturating_add(self.generated_tokens.len().saturating_sub(1))
}
pub(crate) fn pending_decode_token(&self) -> Option<i32> {
self.generated_tokens.last().copied()
}
pub(crate) fn reset_for_recompute(&mut self) {
self.status = SequenceStatus::Preempted;
self.admitted_at = None;
self.prefill_cursor = self
.prefix_restore
.as_ref()
.map_or(0, |restore| restore.token_count)
.min(self.recompute_tokens().len());
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IterationPhase {
Prefill,
Recompute,
Decode,
}
#[derive(Debug, Clone, PartialEq)]
pub struct IterationWork {
pub sequence_id: String,
pub tokens: Vec<i32>,
pub positions: Vec<i32>,
pub sample_last: bool,
pub phase: IterationPhase,
pub sampling: Option<SamplingConfig>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IterationPrediction {
pub work_index: usize,
pub token: i32,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct IterationPlan {
pub work: Vec<IterationWork>,
pub token_count: usize,
pub admitted: usize,
pub preempted: usize,
}