From d6dca4b890c6d1a6765ccf4c3d3427b442edbe40 Mon Sep 17 00:00:00 2001 From: Chummy Date: Thu, 19 Feb 2026 17:36:57 +0800 Subject: [PATCH] fix(provider): align native tool system-flattening and add regressions --- src/agent/prompt.rs | 21 ++++++++++++++ src/providers/compatible.rs | 55 ++++++++++++++++++++++++++++++++++++- src/security/policy.rs | 1 + 3 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/agent/prompt.rs b/src/agent/prompt.rs index d1d93aa..940d077 100644 --- a/src/agent/prompt.rs +++ b/src/agent/prompt.rs @@ -348,4 +348,25 @@ mod tests { assert!(prompt.contains("test_tool")); assert!(prompt.contains("instr")); } + + #[test] + fn datetime_section_includes_timestamp_and_timezone() { + let tools: Vec> = vec![]; + let ctx = PromptContext { + workspace_dir: Path::new("/tmp"), + model_name: "test-model", + tools: &tools, + skills: &[], + identity_config: None, + dispatcher_instructions: "instr", + }; + + let rendered = DateTimeSection.build(&ctx).unwrap(); + assert!(rendered.starts_with("## Current Date & Time\n\n")); + + let payload = rendered.trim_start_matches("## Current Date & Time\n\n"); + assert!(payload.chars().any(|c| c.is_ascii_digit())); + assert!(payload.contains(" (")); + assert!(payload.ends_with(')')); + } } diff --git a/src/providers/compatible.rs b/src/providers/compatible.rs index ed92b00..d8077af 100644 --- a/src/providers/compatible.rs +++ b/src/providers/compatible.rs @@ -1117,7 +1117,12 @@ impl Provider for OpenAiCompatibleProvider { ) })?; - let api_messages: Vec = messages + let effective_messages = if self.merge_system_into_user { + Self::flatten_system_messages(messages) + } else { + messages.to_vec() + }; + let api_messages: Vec = effective_messages .iter() .map(|m| Message { role: m.role.clone(), @@ -1863,6 +1868,54 @@ mod tests { assert_eq!(converted[0].content.as_deref(), Some("done")); } + #[test] + fn flatten_system_messages_merges_into_first_user() { + let input = vec![ + ChatMessage::system("core policy"), + ChatMessage::assistant("ack"), + ChatMessage::system("delivery rules"), + ChatMessage::user("hello"), + ChatMessage::assistant("post-user"), + ]; + + let output = OpenAiCompatibleProvider::flatten_system_messages(&input); + assert_eq!(output.len(), 3); + assert_eq!(output[0].role, "assistant"); + assert_eq!(output[0].content, "ack"); + assert_eq!(output[1].role, "user"); + assert_eq!(output[1].content, "core policy\n\ndelivery rules\n\nhello"); + assert_eq!(output[2].role, "assistant"); + assert_eq!(output[2].content, "post-user"); + assert!(output.iter().all(|m| m.role != "system")); + } + + #[test] + fn flatten_system_messages_inserts_user_when_missing() { + let input = vec![ + ChatMessage::system("core policy"), + ChatMessage::assistant("ack"), + ]; + + let output = OpenAiCompatibleProvider::flatten_system_messages(&input); + assert_eq!(output.len(), 2); + assert_eq!(output[0].role, "user"); + assert_eq!(output[0].content, "core policy"); + assert_eq!(output[1].role, "assistant"); + assert_eq!(output[1].content, "ack"); + } + + #[test] + fn strip_think_tags_removes_multiple_blocks() { + let input = "axbyc"; + assert_eq!(strip_think_tags(input), "abc"); + } + + #[test] + fn strip_think_tags_drops_unclosed_block_suffix() { + let input = "visiblehidden"; + assert_eq!(strip_think_tags(input), "visible"); + } + #[test] fn native_tool_schema_unsupported_detection_is_precise() { assert!(OpenAiCompatibleProvider::is_native_tool_schema_unsupported( diff --git a/src/security/policy.rs b/src/security/policy.rs index fda45c5..d2f0d29 100644 --- a/src/security/policy.rs +++ b/src/security/policy.rs @@ -691,6 +691,7 @@ mod tests { assert!(p.is_command_allowed("cargo build --release")); assert!(p.is_command_allowed("cat file.txt")); assert!(p.is_command_allowed("grep -r pattern .")); + assert!(p.is_command_allowed("date")); } #[test]