use foundation::{LogitValue, TokenId};
pub trait LogitProducer {
fn reset(&mut self);
fn try_reuse_prefix(&mut self, _prompt_ids: &[TokenId]) -> usize {
0
}
fn session_slot_evicted(&self) -> bool {
false
}
fn produce(
&mut self,
token: i32,
position: usize,
logits: &mut [LogitValue],
) -> Result<(), String>;
fn produce_prefill(
&mut self,
token: i32,
position: usize,
scratch: &mut [LogitValue],
) -> Result<(), String> {
self.produce(token, position, scratch)
}
}
pub trait ChunkedPrefillRunner: LogitProducer {
fn prefill_chunk(
&mut self,
tokens: &[i32],
start_position: usize,
logits: &mut [LogitValue],
) -> Result<(), String>;
fn prefill_chunk_with_status(
&mut self,
tokens: &[i32],
start_position: usize,
logits: &mut [LogitValue],
is_final_prompt_chunk: bool,
) -> Result<(), String> {
let _ = is_final_prompt_chunk;
self.prefill_chunk(tokens, start_position, logits)
}
}
pub trait SpeculativeProducer: LogitProducer {
type Checkpoint;
fn prime_drafter(&mut self, next: TokenId, position: usize) -> Result<(), String>;
fn supports_chunked_drafter_prefill(&self) -> bool {
false
}
fn prefill_chunk_priming_drafter(
&mut self,
tokens: &[TokenId],
start_position: usize,
logits: &mut [LogitValue],
next_token: Option<TokenId>,
) -> Result<(), String> {
let _ = (tokens, start_position, logits, next_token);
Err("this producer has no chunked drafter prefill".to_string())
}
fn draft_step(
&mut self,
token: TokenId,
position: usize,
logits: &mut [LogitValue],
) -> Result<(), String>;
fn drafts_block_passes(&self) -> bool {
false
}
fn draft_block(
&mut self,
anchor: TokenId,
base: usize,
block: usize,
proposals: &mut Vec<TokenId>,
) -> Result<(), String> {
let _ = (anchor, base, block, proposals);
Err("this drafter is step-wise; drive it with draft_step".to_string())
}
fn rewind_drafter(&mut self, position: usize) -> Result<(), String>;
fn checkpoint(&mut self) -> Self::Checkpoint;
fn rollback(&mut self, point: &Self::Checkpoint);
fn supports_headless_prefill(&self) -> bool {
false
}
fn record_committed(&mut self, _tokens: &[TokenId], _pos0: usize) {}
fn supports_retaining_rollback(&self) -> bool {
false
}
fn rollback_retaining(
&mut self,
_point: &Self::Checkpoint,
_keep_rows: usize,
) -> Result<(), String> {
Err("this producer does not record a verify tape".to_string())
}
fn verify(
&mut self,
feed: &[TokenId],
base: usize,
logits: &mut [LogitValue],
) -> Result<(), String>;
}
pub struct ScriptedLogitProducer {
steps: Vec<Vec<LogitValue>>,
cursor: usize,
}
impl ScriptedLogitProducer {
pub fn new(steps: Vec<Vec<LogitValue>>) -> Self {
Self { steps, cursor: 0 }
}
}
impl LogitProducer for ScriptedLogitProducer {
fn reset(&mut self) {
self.cursor = 0;
}
fn produce(
&mut self,
_token: i32,
_position: usize,
logits: &mut [LogitValue],
) -> Result<(), String> {
let step = self
.steps
.get(self.cursor)
.ok_or_else(|| "ScriptedLogitProducer exhausted its scripted steps".to_string())?;
if step.len() != logits.len() {
return Err(format!(
"scripted step {} has {} logits, expected {}",
self.cursor,
step.len(),
logits.len()
));
}
logits.copy_from_slice(step);
self.cursor += 1;
Ok(())
}
}
impl ChunkedPrefillRunner for ScriptedLogitProducer {
fn prefill_chunk(
&mut self,
tokens: &[i32],
_start_position: usize,
logits: &mut [LogitValue],
) -> Result<(), String> {
if tokens.is_empty() {
return Err("prefill_chunk called with an empty chunk".to_string());
}
self.produce(*tokens.last().unwrap(), 0, logits)
}
}