use crate::api::sanitizer::sanitize_content_chunk;
use crate::api::stream_guards::check_loop_watchdog;
use crate::ir::StreamDelta;
use super::super::ctx::StreamCtx;
use super::super::state::StreamState;
use super::{DeltaVec, simhash_loop_enabled, watchdogs_disabled};
pub(super) fn process_detector_content(
state: &mut StreamState,
ctx: &StreamCtx,
sanitized_or_raw: &str,
) -> Option<DeltaVec> {
let sanitized = sanitized_or_raw;
let semantic_trip = if simhash_loop_enabled() && !state.loop_watchdog_triggered {
state.simhash_pending.push_str(sanitized);
let mut dup = false;
if crate::loop_simhash::ends_at_sentence_boundary(&state.simhash_pending).is_some()
|| state.simhash_pending.len() >= 1024
{
dup = state.simhash_guard.check(&state.simhash_pending);
state.simhash_pending.clear();
}
if state.simhash_pending.len() > 4096 {
let drop_to = state.simhash_pending.len() / 2;
state.simhash_pending.drain(..drop_to);
}
dup
} else {
false
};
let token_trip = !watchdogs_disabled()
&& check_loop_watchdog(
sanitized,
&mut state.loop_scan_buf,
state.loop_watchdog_triggered,
);
if semantic_trip || token_trip {
if semantic_trip {
tracing::warn!(target: "met::api::chat_stream::handle_token", ring_len = state.simhash_guard.len(),
"SimHash semantic-loop watchdog fired (paraphrased sentence repeat)"
);
}
state.loop_watchdog_triggered = true;
state.stop_string_triggered = true;
state.guard_stop = Some(if semantic_trip {
"simhash_semantic_loop"
} else {
"token_loop_watchdog"
});
state
.cancel_flag
.store(true, std::sync::atomic::Ordering::Release);
return Some(DeltaVec::new());
}
if !sanitized.is_empty() {
if state.refusal_scan_buf.len() < 16_384 {
state.refusal_scan_buf.push_str(sanitized);
}
let out: DeltaVec = vec![StreamDelta::Content {
text: sanitized.to_string(),
token_ids: state.take_ids_if(ctx.req_return_token_ids),
}];
return Some(out);
}
None
}
pub(super) fn detector_content_arm(
state: &mut StreamState,
ctx: &StreamCtx,
text: &str,
) -> Option<DeltaVec> {
let sanitized = sanitize_content_chunk(
text,
&mut state.tag_scan_buf,
&mut state.suppressing_param_leak,
&mut state.inside_envelope,
&ctx.leak_markers,
);
process_detector_content(state, ctx, &sanitized)
}