use super::{KvCacheHandle, PlanRuntimePrefillAuthority, PrefixCaptureLease};
use ferrum_types::{FerrumError, RequestId, Result, TokenId};
use std::{fmt, sync::Arc};
#[derive(Debug, serde::Serialize)]
pub struct PrefixRestoreObservation<'a> {
pub request_id: &'a RequestId,
pub source: PrefixRestoreSource,
pub decision: PrefixRestoreDecision<'a>,
}
#[derive(Debug, Clone, Copy, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum PrefixRestoreSource {
Index,
Rendezvous,
}
#[derive(Debug, serde::Serialize)]
#[serde(tag = "outcome", rename_all = "snake_case")]
pub enum PrefixRestoreDecision<'a> {
NoReusableEntry,
SequenceCapacityNotReady {
candidate_prefix_tokens: usize,
capacity: &'a super::ExecutorExecutionCapacityDeferral,
},
RequestStateNotReady {
candidate_prefix_tokens: usize,
request_state: &'a super::ExecutorRequestStateDeferral,
},
NativeRestoreSkipped {
candidate_prefix_tokens: usize,
},
Restored {
candidate_prefix_tokens: usize,
},
}
#[derive(Debug, Clone, Copy)]
pub struct PlanRuntimePrefixRestoreInput<'a> {
pub request_id: &'a RequestId,
pub input_tokens: &'a [TokenId],
pub maximum_sequence_tokens: usize,
pub checkpoint: Option<&'a dyn PrefixCaptureLease>,
pub retry: Option<&'a PlanRuntimePrefixRestoreDeferral>,
}
#[derive(Debug)]
pub struct PlanRuntimePrefixRestoreDeferral {
capacity: super::ExecutorExecutionCapacityDeferral,
checkpoint: Arc<dyn PrefixCaptureLease>,
source: PrefixRestoreSource,
}
impl PlanRuntimePrefixRestoreDeferral {
pub fn new(
capacity: super::ExecutorExecutionCapacityDeferral,
checkpoint: Arc<dyn PrefixCaptureLease>,
source: PrefixRestoreSource,
) -> Self {
Self {
capacity,
checkpoint,
source,
}
}
pub fn capacity(&self) -> &super::ExecutorExecutionCapacityDeferral {
&self.capacity
}
pub fn checkpoint(&self) -> &Arc<dyn PrefixCaptureLease> {
&self.checkpoint
}
pub fn source(&self) -> PrefixRestoreSource {
self.source
}
}
#[derive(Debug)]
#[must_use = "restore publication or a retained capacity deferral must be handled"]
pub enum PlanRuntimePrefixRestoreOutcome {
Unavailable,
Restored(PlanRuntimePrefixRestoreOutput),
Deferred(PlanRuntimePrefixRestoreDeferral),
}
#[must_use = "publish matching progress and acknowledge, or drop to cancel the restored target"]
pub struct PlanRuntimePrefixRestoreOutput {
authority: PlanRuntimePrefillAuthority,
prompt_tokens: usize,
acknowledge: Box<dyn FnOnce() -> Result<()> + Send>,
}
impl PlanRuntimePrefixRestoreOutput {
pub fn new(
request_id: RequestId,
restored_tokens: usize,
prompt_tokens: usize,
kv_cache: Arc<dyn KvCacheHandle>,
acknowledge: impl FnOnce() -> Result<()> + Send + 'static,
) -> Result<Self> {
let output = Self {
authority: PlanRuntimePrefillAuthority {
request_id,
committed_tokens: restored_tokens,
kv_cache,
},
prompt_tokens,
acknowledge: Box::new(acknowledge),
};
output.validate_for(output.request_id(), prompt_tokens)?;
Ok(output)
}
pub fn request_id(&self) -> &RequestId {
self.authority.request_id()
}
pub fn restored_tokens(&self) -> usize {
self.authority.committed_tokens()
}
pub fn kv_cache(&self) -> &Arc<dyn KvCacheHandle> {
self.authority.kv_cache()
}
pub fn validate_for(&self, request_id: &RequestId, prompt_tokens: usize) -> Result<()> {
if self.request_id() != request_id || self.prompt_tokens != prompt_tokens {
return Err(FerrumError::backend(
"prefix restore publication does not match the admitted request",
));
}
let restored = self.restored_tokens();
if restored == 0 || restored >= prompt_tokens {
return Err(FerrumError::backend(
"prefix restore must leave a nonempty prompt suffix for execution",
));
}
if self.kv_cache().num_tokens() != restored || !self.kv_cache().is_valid() {
return Err(FerrumError::backend(
"prefix restore cache authority does not match the restored extent",
));
}
Ok(())
}
pub fn acknowledge(self) -> Result<PlanRuntimePrefillAuthority> {
let Self {
authority,
acknowledge,
..
} = self;
acknowledge()?;
Ok(authority)
}
}
impl fmt::Debug for PlanRuntimePrefixRestoreOutput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PlanRuntimePrefixRestoreOutput")
.field("authority", &self.authority)
.field("prompt_tokens", &self.prompt_tokens)
.finish_non_exhaustive()
}
}