use crate::{Result, invalid};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u32)]
pub enum State {
Idle = 0,
Counting = 1,
Forcing = 2,
WaitingUtf8 = 3,
Done = 4,
}
impl TryFrom<u32> for State {
type Error = crate::Error;
fn try_from(value: u32) -> Result<Self> {
match value {
0 => Ok(Self::Idle),
1 => Ok(Self::Counting),
2 => Ok(Self::Forcing),
3 => Ok(Self::WaitingUtf8),
4 => Ok(Self::Done),
_ => Err(invalid("unknown reasoning budget state")),
}
}
}
#[derive(Clone, Debug)]
struct Matcher {
sequences: Vec<Vec<i32>>,
suffix: Vec<i32>,
maximum: usize,
}
impl Matcher {
fn new(sequences: Vec<Vec<i32>>) -> Result<Self> {
if sequences.len() > 64 || sequences.iter().any(|sequence| sequence.len() > 1024) {
return Err(invalid("reasoning delimiter extent exceeds bound"));
}
let mut unique = Vec::new();
for sequence in sequences {
if !sequence.is_empty() && !unique.contains(&sequence) {
unique.push(sequence);
}
}
let maximum = unique.iter().map(Vec::len).max().unwrap_or(0);
let mut suffix = Vec::new();
suffix.try_reserve_exact(maximum)?;
Ok(Self {
sequences: unique,
suffix,
maximum,
})
}
fn advance(&mut self, token: i32) -> Option<usize> {
if self.maximum == 0 {
return None;
}
if self.suffix.len() == self.maximum {
self.suffix.remove(0);
}
self.suffix.push(token);
let matched = self
.sequences
.iter()
.enumerate()
.filter(|(_, sequence)| self.suffix.ends_with(sequence))
.max_by_key(|(_, sequence)| sequence.len())
.map(|(index, _)| index);
if matched.is_some() {
self.suffix.clear();
}
matched
}
}
#[derive(Clone, Debug)]
pub struct ReasoningBudget {
start: Matcher,
end: Matcher,
forced: Vec<i32>,
budget: i32,
remaining: i32,
state: State,
force_position: usize,
end_match: Option<usize>,
}
impl ReasoningBudget {
pub fn new(
start: Vec<Vec<i32>>,
end: Vec<Vec<i32>>,
forced: Vec<i32>,
budget: i32,
initial: State,
) -> Result<Self> {
if forced.len() > 1024 {
return Err(invalid("reasoning forced-token extent exceeds bound"));
}
Ok(Self {
start: Matcher::new(start)?,
end: Matcher::new(end)?,
forced,
budget,
remaining: budget,
state: if initial == State::Counting && budget <= 0 {
State::Forcing
} else {
initial
},
force_position: 0,
end_match: None,
})
}
#[must_use]
pub const fn state(&self) -> State {
self.state
}
#[must_use]
pub fn end_match(&self) -> Option<&[i32]> {
self.end_match
.map(|index| self.end.sequences[index].as_slice())
}
#[must_use]
pub fn forced_token(&self) -> Option<i32> {
if self.state == State::Forcing {
self.forced.get(self.force_position).copied()
} else {
None
}
}
pub fn apply(&self, tokens: &[i32], logits: &mut [f32]) -> Result<()> {
if tokens.len() != logits.len() {
return Err(invalid("reasoning candidate token/logit extent mismatch"));
}
if let Some(forced) = self.forced_token() {
for (&token, logit) in tokens.iter().zip(logits) {
if token != forced {
*logit = f32::NEG_INFINITY;
}
}
}
Ok(())
}
const fn begin_forcing(&mut self) {
self.state = State::Forcing;
self.force_position = 0;
}
pub fn accept(&mut self, token: i32) {
self.accept_with_utf8(token, true);
}
pub fn accept_with_utf8(&mut self, token: i32, complete: bool) {
match self.state {
State::Idle | State::Done => {
if self.start.advance(token).is_some() {
if self.state == State::Done {
self.end.suffix.clear();
self.end_match = None;
}
self.state = State::Counting;
self.remaining = self.budget;
if self.remaining <= 0 {
self.begin_forcing();
}
}
}
State::Counting | State::WaitingUtf8 => {
if let Some(index) = self.end.advance(token) {
self.state = State::Done;
self.end_match = Some(index);
return;
}
if self.state == State::WaitingUtf8 {
if complete {
self.begin_forcing();
self.end.suffix.clear();
}
} else {
self.remaining -= 1;
if self.remaining <= 0 {
if complete {
self.begin_forcing();
} else {
self.state = State::WaitingUtf8;
}
self.end.suffix.clear();
}
}
}
State::Forcing => {
let matched = self.end.advance(token);
self.force_position += 1;
if self.force_position >= self.forced.len() {
self.state = State::Done;
self.end_match = matched;
}
}
}
}
pub fn force(&mut self) -> bool {
if self.state != State::Counting {
return false;
}
self.begin_forcing();
self.end.suffix.clear();
true
}
pub fn reset(&mut self) {
self.state = State::Idle;
self.remaining = self.budget;
self.start.suffix.clear();
self.end.suffix.clear();
self.force_position = 0;
self.end_match = None;
}
}
#[must_use]
pub fn utf8_is_complete(bytes: &[u8]) -> bool {
if bytes.is_empty() {
return true;
}
for (index, &byte) in bytes.iter().rev().take(4).enumerate() {
if byte & 0xc0 != 0x80 {
let expected = match byte {
0xf0..=0xff => 4,
0xe0..=0xef => 3,
0xc0..=0xdf => 2,
_ => 1,
};
return index + 1 >= expected;
}
}
false
}