From c975ec5f486af8af0fe9746f6b97769e3ca2d22f Mon Sep 17 00:00:00 2001 From: Afloat <132809511+Afloat16@users.noreply.github.com> Date: Tue, 20 Oct 2026 21:22:38 +0800 Subject: [PATCH] Count all decoded tokens in the backend chunk containing a stop --- deepseek-recipe/src/stream/processor.rs | 56 ++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/deepseek-recipe/src/stream/processor.rs b/deepseek-recipe/src/stream/processor.rs index 870a419..3c9fb7b 100644 --- a/deepseek-recipe/src/stream/processor.rs +++ b/deepseek-recipe/src/stream/processor.rs @@ -175,8 +175,8 @@ where } started = true; } + completion_usage.completion_tokens += pieces.iter().map(|piece| piece.token_count).sum::(); for piece in pieces { - completion_usage.completion_tokens += piece.token_count; let actions = parser.feed(&piece.content, piece.kind); pending.push(piece.content, piece.logprobs); for protocol_chunk in pending.apply_actions(actions, &mut generator).await { @@ -1143,3 +1143,57 @@ mod tests { assert_eq!(finish_chunk(&output).2.completion_tokens, 2); } } + +#[cfg(test)] +mod contribution_regression { + use super::*; + + #[tokio::test] + async fn stop_usage_includes_remaining_pieces_of_the_same_chunk() { + #[derive(Debug)] + struct Pieces; + impl StreamTokenizer for Pieces { + fn decode_ids(&self, ids: &[u32], _: bool) -> Result { + Ok(ids + .iter() + .map(|id| match id { + 0 => "STOP", + 1 => "", + _ => "tail", + }) + .collect()) + } + fn is_special_token(&self, id: u32) -> bool { + id == 1 + } + } + struct Pass; + impl ChunkGenerator for Pass { + type Chunk = OutputChunk; + async fn generate(&mut self, chunk: OutputChunk) -> Vec { + vec![chunk] + } + } + let processor = StreamProcessor::new( + Pass, + ParsingOptions { + stop_sequences: vec!["STOP".into()], + ..Default::default() + }, + ) + .with_tokenizer(Pieces); + let output: Vec<_> = processor + .into_protocol_stream(tokio_stream::iter(vec![InferenceChunk::Tokens { + token_ids: vec![0, 1, 2], + logprobs: None, + }])) + .collect() + .await; + match output.last().unwrap().as_ref().unwrap() { + OutputChunk::Finish { + completion_usage, .. + } => assert_eq!(completion_usage.completion_tokens, 3), + other => panic!("{other:?}"), + } + } +}