Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions llama-cpp-bindings-tests/src/classify_sample_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ mod tests {

assert_eq!(outcome.observed_tool_call, 1);
assert_eq!(outcome.generated_raw, "{");
assert!(outcome.content_stream.is_empty());
assert_eq!(outcome.content_stream, "");
}

#[test]
Expand All @@ -149,8 +149,8 @@ mod tests {
});

assert_eq!(outcome.observed_undeterminable, 1);
assert!(outcome.content_stream.is_empty());
assert!(outcome.reasoning_stream.is_empty());
assert_eq!(outcome.content_stream, "");
assert_eq!(outcome.reasoning_stream, "");
}

#[test]
Expand Down
5 changes: 3 additions & 2 deletions llama-cpp-bindings-tests/tests/chat_protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use llama_cpp_bindings::ChatTemplateError;
use llama_cpp_bindings::ParseChatMessageError;
use llama_cpp_bindings::model::LlamaChatMessage;
use llama_cpp_bindings_tests::build_user_prompt_with_media_marker::build_user_prompt_with_media_marker;
use llama_cpp_bindings_types::ParsedToolCall;
use llama_cpp_test_harness::LlamaFixture;
use llama_cpp_test_harness::llama_test;

Expand Down Expand Up @@ -254,7 +255,7 @@ fn parses_pure_content_response(fixture: &LlamaFixture<'_>) -> Result<()> {
let ChatMessageParseOutcome::Recognized(parsed) = outcome else {
bail!("expected Recognized for plain content; got Unrecognized");
};
assert!(parsed.tool_calls.is_empty());
assert_eq!(parsed.tool_calls, Vec::<ParsedToolCall>::new());
assert!(!parsed.is_empty());
assert!(parsed.content.contains("hello world"));

Expand Down Expand Up @@ -348,7 +349,7 @@ fn parses_empty_input_yields_empty_message(fixture: &LlamaFixture<'_>) -> Result
let ChatMessageParseOutcome::Recognized(parsed) = outcome else {
bail!("expected Recognized for empty input; got Unrecognized");
};
assert!(parsed.tool_calls.is_empty());
assert_eq!(parsed.tool_calls, Vec::<ParsedToolCall>::new());

Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions llama-cpp-bindings-tests/tests/context_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ fn decode_and_get_logits(fixture: &LlamaFixture<'_>) -> Result<()> {
assert!(decode_result.is_ok());

let logits = context.get_logits()?;
assert!(!logits.is_empty());
assert_ne!(logits, Vec::<f32>::new());

Ok(())
}
Expand Down Expand Up @@ -282,7 +282,7 @@ fn token_data_array_has_entries_after_decode(fixture: &LlamaFixture<'_>) -> Resu

let token_data_array = context.token_data_array()?;

assert!(!token_data_array.data.is_empty());
assert_ne!(token_data_array.data, Vec::<LlamaTokenData>::new());

Ok(())
}
Expand Down
4 changes: 2 additions & 2 deletions llama-cpp-bindings-tests/tests/embedding_models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,8 +597,8 @@ fn embedding_model_exposes_tool_call_markers(fixture: &LlamaFixture<'_>) -> Resu
.tool_call_markers()?
.expect("Qwen3 embedding model must expose tool-call markers");

assert!(!markers.open.is_empty());
assert!(!markers.close.is_empty());
assert_ne!(markers.open, "");
assert_ne!(markers.close, "");

Ok(())
}
Expand Down
18 changes: 9 additions & 9 deletions llama-cpp-bindings-tests/tests/model_introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ fn meta_count_returns_positive(fixture: &LlamaFixture<'_>) -> Result<()> {
)]
fn meta_key_by_index_returns_valid_key(fixture: &LlamaFixture<'_>) -> Result<()> {
let key = fixture.model.meta_key_by_index(0)?;
assert!(!key.is_empty());
assert_ne!(key, "");
Ok(())
}

Expand Down Expand Up @@ -502,7 +502,7 @@ fn meta_key_by_index_returns_valid_key(fixture: &LlamaFixture<'_>) -> Result<()>
)]
fn meta_val_str_by_index_returns_valid_value(fixture: &LlamaFixture<'_>) -> Result<()> {
let value = fixture.model.meta_val_str_by_index(0)?;
assert!(!value.is_empty());
assert_ne!(value, "");
Ok(())
}

Expand Down Expand Up @@ -618,7 +618,7 @@ fn meta_val_str_returns_value_for_known_key(fixture: &LlamaFixture<'_>) -> Resul
let model = fixture.model;
let first_key = model.meta_key_by_index(0)?;
let value = model.meta_val_str(&first_key)?;
assert!(!value.is_empty());
assert_ne!(value, "");
Ok(())
}

Expand Down Expand Up @@ -1184,12 +1184,12 @@ fn token_attr_returns_attrs_for_bos(fixture: &LlamaFixture<'_>) -> Result<()> {
fn str_to_token_roundtrip(fixture: &LlamaFixture<'_>) -> Result<()> {
let model = fixture.model;
let tokens = model.str_to_token("hello world", AddBos::Never, ParseSpecialTokens::Always)?;
assert!(!tokens.is_empty());
assert_ne!(tokens, Vec::<LlamaToken>::new());
let mut decoder = encoding_rs::UTF_8.new_decoder();
let piece =
model.token_to_piece(&SampledToken::Content(tokens[0]), &mut decoder, false, None)?;

assert!(!piece.is_empty());
assert_ne!(piece, "");

Ok(())
}
Expand Down Expand Up @@ -1375,7 +1375,7 @@ fn token_to_piece_bytes_returns_bytes_for_known_token(fixture: &LlamaFixture<'_>
let tokens = model.str_to_token("hello", AddBos::Never, ParseSpecialTokens::Always)?;
let bytes = model.token_to_piece_bytes(tokens[0], 32, false, None)?;

assert!(!bytes.is_empty());
assert_ne!(bytes, Vec::<u8>::new());
Ok(())
}

Expand Down Expand Up @@ -1563,7 +1563,7 @@ fn token_to_piece_decodes_reasoning_variant(fixture: &LlamaFixture<'_>) -> Resul
None,
)?;

assert!(!piece.is_empty());
assert_ne!(piece, "");
Ok(())
}

Expand Down Expand Up @@ -1607,7 +1607,7 @@ fn token_to_piece_decodes_tool_call_variant(fixture: &LlamaFixture<'_>) -> Resul
let piece =
model.token_to_piece(&SampledToken::ToolCall(tokens[0]), &mut decoder, true, None)?;

assert!(!piece.is_empty());
assert_ne!(piece, "");
Ok(())
}

Expand Down Expand Up @@ -1655,7 +1655,7 @@ fn token_to_piece_decodes_undeterminable_variant(fixture: &LlamaFixture<'_>) ->
None,
)?;

assert!(!piece.is_empty());
assert_ne!(piece, "");
Ok(())
}

Expand Down
3 changes: 2 additions & 1 deletion llama-cpp-bindings-tests/tests/multimodal_vision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use llama_cpp_bindings::mtmd::MtmdInputText;
use llama_cpp_bindings::mtmd::NonCausalChunkMicroBatchMismatch;
use llama_cpp_bindings::mtmd::mtmd_default_marker;
use llama_cpp_bindings::sampling::LlamaSampler;
use llama_cpp_bindings::token::LlamaToken;
use llama_cpp_bindings_tests::build_user_prompt_with_media_marker::build_user_prompt_with_media_marker;
use llama_cpp_bindings_tests::chunk_token_breakdown::ChunkTokenBreakdown;
use llama_cpp_bindings_tests::classify_sample_loop::ClassifySampleLoop;
Expand Down Expand Up @@ -290,7 +291,7 @@ fn text_chunk_returns_text_tokens(fixture: &LlamaFixture<'_>) -> Result<()> {
.text_tokens_of(first_chunk.chunk_type()?)
.ok_or_else(|| anyhow::anyhow!("the first chunk of a text prompt must carry tokens"))?;

assert!(!tokens.is_empty());
assert_ne!(tokens, Vec::<LlamaToken>::new());

Ok(())
}
Expand Down
55 changes: 42 additions & 13 deletions llama-cpp-bindings-tests/tests/structured_chat_output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ What is 2 + 2?

let usage = classifier.usage();

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert_eq!(outcome.observed_reasoning, 0);
assert_eq!(outcome.observed_undeterminable, 0);
assert_eq!(usage.reasoning_tokens, 0);
Expand Down Expand Up @@ -1080,7 +1080,7 @@ What is 2 + 2?
let usage = classifier.usage();
let parsed = parse_partial_reasoning_response(model, &outcome.generated_raw)?;

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert!(outcome.observed_reasoning > 0);
assert!(usage.reasoning_tokens > 0);
assert_eq!(outcome.observed_undeterminable, 0);
Expand Down Expand Up @@ -1253,7 +1253,7 @@ fn mistral3_classifier_does_not_emit_reasoning_for_thinking_disabled_prompt(

let usage = classifier.usage();

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert_eq!(outcome.observed_reasoning, 0);
assert_eq!(outcome.observed_undeterminable, 0);
assert_eq!(usage.reasoning_tokens, 0);
Expand Down Expand Up @@ -1334,7 +1334,7 @@ to the user.[/THINK]Here, provide a self-contained response.[/SYSTEM_PROMPT]\
let usage = classifier.usage();
let parsed = parse_partial_reasoning_response(model, &outcome.generated_raw)?;

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert!(outcome.observed_reasoning > 0);
assert!(usage.reasoning_tokens > 0);
assert_eq!(outcome.observed_undeterminable, 0);
Expand All @@ -1343,7 +1343,7 @@ to the user.[/THINK]Here, provide a self-contained response.[/SYSTEM_PROMPT]\
usage.completion_tokens(),
outcome.observed_content + outcome.observed_reasoning,
);
assert!(!parsed.reasoning_content.is_empty());
assert_ne!(parsed.reasoning_content, "");
assert_eq!(outcome.reasoning_stream, parsed.reasoning_content);
assert_eq!(outcome.content_stream, parsed.content);

Expand Down Expand Up @@ -1459,7 +1459,7 @@ fn qwen35_chat_inference_emits_reasoning_when_template_auto_opens(
}
.run()?;

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert!(outcome.observed_reasoning > 0);
assert_eq!(outcome.observed_undeterminable, 0);
assert_eq!(outcome.observed_tool_call, 0);
Expand Down Expand Up @@ -1528,6 +1528,35 @@ fn qwen35_shared_reasoning_close_and_tool_call_open_is_one_transition(
Ok(())
}

#[llama_test(
model_source = HuggingFace("unsloth/Ministral-3-3B-Reasoning-2512-GGUF", "Ministral-3-3B-Reasoning-2512-Q4_K_M.gguf"),
n_gpu_layers = 999,
load_mode = Mmap,
n_ctx = 2048,
n_batch = 512,
n_ubatch = 128,
)]
fn mistral3_tool_call_turn_finished_in_the_prompt_leaves_the_classifier_pending(
fixture: &LlamaFixture<'_>,
) -> Result<()> {
let model = fixture.model;
let mut finished_tool_call_turn = model.str_to_token(
"[TOOL_CALLS]read[ARGS]{\"path\":\"marker.txt\"}",
AddBos::Never,
ParseSpecialTokens::Always,
)?;

finished_tool_call_turn.push(model.token_eos().context("model declares an EOS token")?);

let mut classifier = model.sampled_token_classifier(BareJsonToolCalls::Detect)?;

classifier.ingest_prompt_tokens(&finished_tool_call_turn);

assert_eq!(classifier.current_section(), SampledTokenSection::Pending);

Ok(())
}

#[llama_test(
model_source = HuggingFace("unsloth/Qwen3.5-0.8B-GGUF", "Qwen3.5-0.8B-Q4_K_M.gguf"),
n_gpu_layers = 999,
Expand Down Expand Up @@ -1599,7 +1628,7 @@ What is 2 + 2?<|im_end|>

let usage = classifier.usage();

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert_eq!(outcome.observed_reasoning, 0);
assert_eq!(outcome.observed_undeterminable, 0);
assert_eq!(usage.reasoning_tokens, 0);
Expand Down Expand Up @@ -1683,7 +1712,7 @@ What is 2 + 2?<|im_end|>
let usage = classifier.usage();
let parsed = parse_partial_reasoning_response(model, &outcome.generated_raw)?;

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert!(outcome.observed_reasoning > 0);
assert!(usage.reasoning_tokens > 0);
assert_eq!(outcome.observed_undeterminable, 0);
Expand Down Expand Up @@ -2048,14 +2077,14 @@ fn qwen36_chat_inference_emits_reasoning_when_template_auto_opens(
}
.run()?;

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert!(outcome.observed_reasoning > 0);
assert!(outcome.observed_content > 0);
assert_eq!(outcome.observed_undeterminable, 0);
assert_eq!(outcome.observed_tool_call, 0);

let parsed = parse_partial_reasoning_response(model, &outcome.generated_raw)?;
assert!(!parsed.content.is_empty());
assert_ne!(parsed.content, "");

let usage = classifier.into_usage();
assert_eq!(usage.prompt_tokens, prompt_token_count);
Expand Down Expand Up @@ -2136,7 +2165,7 @@ What is 2 + 2?<|im_end|>

let usage = classifier.usage();

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert_eq!(outcome.observed_reasoning, 0);
assert_eq!(outcome.observed_undeterminable, 0);
assert_eq!(usage.reasoning_tokens, 0);
Expand Down Expand Up @@ -2220,7 +2249,7 @@ What is 2 + 2?<|im_end|>
let usage = classifier.usage();
let parsed = parse_partial_reasoning_response(model, &outcome.generated_raw)?;

assert!(!outcome.generated_raw.is_empty());
assert_ne!(outcome.generated_raw, "");
assert!(outcome.observed_reasoning > 0);
assert!(usage.reasoning_tokens > 0);
assert_eq!(outcome.observed_undeterminable, 0);
Expand All @@ -2230,7 +2259,7 @@ What is 2 + 2?<|im_end|>
outcome.observed_content + outcome.observed_reasoning,
);

assert!(!parsed.reasoning_content.is_empty());
assert_ne!(parsed.reasoning_content, "");
assert_eq!(outcome.reasoning_stream, parsed.reasoning_content);
assert_eq!(outcome.content_stream, parsed.content);

Expand Down
4 changes: 2 additions & 2 deletions llama-cpp-bindings-types/src/parsed_tool_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ mod tests {
fn default_is_empty_strings_and_invalid_arguments() {
let parsed = ParsedToolCall::default();

assert!(parsed.id.is_empty());
assert!(parsed.name.is_empty());
assert_eq!(parsed.id, "");
assert_eq!(parsed.name, "");
assert_eq!(
parsed.arguments,
ToolCallArguments::InvalidJson(String::new())
Expand Down
3 changes: 2 additions & 1 deletion llama-cpp-bindings/src/chat_message_parse_outcome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub enum ChatMessageParseOutcome {
#[cfg(test)]
mod tests {
use llama_cpp_bindings_types::ParsedChatMessage;
use llama_cpp_bindings_types::ParsedToolCall;

use super::ChatMessageParseOutcome;
use crate::raw_chat_message::RawChatMessage;
Expand All @@ -37,7 +38,7 @@ mod tests {
ChatMessageParseOutcome::Recognized(parsed) => {
assert_eq!(parsed.content, "content");
assert_eq!(parsed.reasoning_content, "reasoning");
assert!(parsed.tool_calls.is_empty());
assert_eq!(parsed.tool_calls, Vec::<ParsedToolCall>::new());
saw_recognized = true;
}
ChatMessageParseOutcome::Unrecognized(raw) => {
Expand Down
Loading
Loading