diff --git a/crates/tinyinference-llm/src/failure/context_limit.rs b/crates/tinyinference-llm/src/failure/context_limit.rs index 809b09a2..637aa417 100644 --- a/crates/tinyinference-llm/src/failure/context_limit.rs +++ b/crates/tinyinference-llm/src/failure/context_limit.rs @@ -132,9 +132,14 @@ fn first_number(text: &str, window: usize) -> Option { /// window. fn trailing_number(text: &str) -> Option { let trimmed = text.trim_end(); + // Step past the whole character that precedes the digits: it can be + // multi-byte (a no-break space, a CJK character), and `index + 1` would + // land inside it. let begin = trimmed - .rfind(|character: char| !(character.is_ascii_digit() || character == ',')) - .map_or(0, |index| index + 1); + .char_indices() + .rev() + .find(|&(_, character)| !(character.is_ascii_digit() || character == ',')) + .map_or(0, |(index, character)| index + character.len_utf8()); let digits = trimmed[begin..].trim_start_matches(','); if digits.is_empty() { return None; diff --git a/crates/tinyinference-llm/src/failure/context_limit_tests.rs b/crates/tinyinference-llm/src/failure/context_limit_tests.rs index abe065b0..2af6d7df 100644 --- a/crates/tinyinference-llm/src/failure/context_limit_tests.rs +++ b/crates/tinyinference-llm/src/failure/context_limit_tests.rs @@ -43,6 +43,16 @@ fn parses_mistral_trailing_limit() { assert_eq!(parse_context_limit_from_error(message), Some(32_768)); } +#[test] +fn parses_trailing_limit_after_a_multibyte_character() { + // A no-break space or a CJK character right before the number used to + // panic: the start of the digits was taken as one byte past it. + let message = "too large for model with\u{a0}32768 maximum context length"; + assert_eq!(parse_context_limit_from_error(message), Some(32_768)); + let message = "输入过长,模型32768 maximum context length"; + assert_eq!(parse_context_limit_from_error(message), Some(32_768)); +} + #[test] fn parses_gemini_allowed_maximum() { let message = "The input token count (1200000) exceeds the maximum number of tokens \