use super::*;
use crate::scheduler::io::Effect;
pub(super) fn emit_content_token(
a: &mut ActiveSeq,
tok: u32,
model: &dyn Model,
sched: &crate::scheduler::sched_ctx::SchedCtx,
) {
a.output_tokens.push(tok);
crate::scheduler::emit_step::update_tool_param_state(a, tok);
if !a.inside_thinking {
rollback::snapshot_boundary_if_ssm(a, model, sched);
let _ = sched
.io
.dev
.apply(Effect::MarconiCheckpoint { seq: &mut a.seq });
}
let suppress_stream = a.inside_thinking && !a.enable_thinking;
if a.sink.is_streaming() && !suppress_stream {
let event = if let Some(lp) = a.logprobs_data.last().cloned() {
StreamEvent::TokenWithLogprobs(tok, lp)
} else {
StreamEvent::Token(tok)
};
if !sched.io.req.emit(&a.sink, event, "decode_logits token") {
tracing::debug!(target: "met::scheduler::decode_logits_step", "Streaming receiver dropped (decode_logits), finishing seq");
a.finished = true;
}
}
if a.remaining == 0 {
crate::scheduler::emit_step::emit_grammar_close(
a,
&sched.io,
sched.levers.grammar_budget_close,
);
tracing::info!(target: "met::scheduler::decode_logits_step", "process_decode_logits: remaining=0, output_tokens={}, thinking_tokens={}",
a.output_tokens.len(),
a.thinking_tokens
);
a.finished = true;
}
if !a.finished && seqlen_force_stop(a.seq.seq_len, sched.limits.max_seq_len) {
tracing::info!(target: "met::scheduler::decode_logits_step", seq_len = a.seq.seq_len,
max_seq_len = sched.limits.max_seq_len,
output_tokens = a.output_tokens.len(),
"process_decode_logits: max_seq_len ceiling reached; force-stop (finish=length)"
);
a.finished = true;
}
if a.grammar_state
.as_ref()
.is_some_and(|gs| gs.is_terminated())
{
a.finished = true;
}
let last_tc_start = a
.tool_call_start_token
.and_then(|t| a.output_tokens.iter().rposition(|&tok| tok == t));
let last_tc_end = a
.tool_call_end_token
.and_then(|t| a.output_tokens.iter().rposition(|&tok| tok == t));
let inside_tool_call = match (last_tc_start, last_tc_end) {
(Some(start), Some(end)) => start > end,
(Some(_), None) => true,
_ => false,
};
if sched.levers.loop_watchdog()
&& !a.finished
&& !a.inside_thinking
&& !inside_tool_call
&& let Some((pattern_len, mis_a, mis_b)) =
detect_fuzzy_repetition(&a.output_tokens, sched.watchdog.fuzzy_repeat_tolerance_div)
{
let min_keep = pattern_len * 3;
match rollback_to_boundary(a, min_keep, model, sched) {
RollbackOutcome::RolledBack { dropped } => {
tracing::warn!(target: "met::scheduler::decode_logits_step", pattern_len,
mismatches = mis_a + mis_b,
dropped,
rollback = a.rollback_count,
"Fuzzy repetition detected; rolled back to boundary, re-steering"
);
}
RollbackOutcome::Fallback(reason) => {
tracing::warn!(target: "met::scheduler::decode_logits_step", "Fuzzy repetition: {pattern_len}-tok pattern x3 ({mis_a}+{mis_b} \
mismatches), stopping at {} tokens (rollback declined: {reason:?})",
a.output_tokens.len()
);
a.guard_stop = Some("fuzzy_repetition");
a.finished = true;
}
}
}
}