@@ -165,143 +165,3 @@ fn parse_one(inner: &str, index: usize) -> Option<ToolCall> {
165165 invalid : None ,
166166 } )
167167}
168-
169- #[ cfg( test) ]
170- mod test {
171- use super :: * ;
172-
173- fn schema ( name : & str ) -> ToolSchema {
174- ToolSchema {
175- name : name. to_string ( ) ,
176- description : format ! ( "{name} description" ) ,
177- parameters : serde_json:: json!( { "type" : "object" } ) ,
178- format : Default :: default ( ) ,
179- }
180- }
181-
182- #[ test]
183- fn instructions_list_each_tool ( ) {
184- let text = prompt_tool_instructions ( & [ schema ( "read_file" ) , schema ( "write_file" ) ] ) ;
185- assert ! ( text. contains( "## Tool Use Protocol" ) ) ;
186- assert ! ( text. contains( "<tool_call>" ) ) ;
187- assert ! ( text. contains( "**read_file**" ) ) ;
188- assert ! ( text. contains( "**write_file**" ) ) ;
189- }
190-
191- #[ test]
192- fn with_tool_instructions_appends_to_system ( ) {
193- let msgs = vec ! [ Message :: system( "You are helpful." ) , Message :: user( "hi" ) ] ;
194- let out = with_prompt_tool_instructions ( & msgs, & [ schema ( "read_file" ) ] ) ;
195- assert_eq ! ( out. len( ) , 2 ) ;
196- let Message :: System ( sys) = & out[ 0 ] else {
197- panic ! ( "first message should stay system" )
198- } ;
199- // Original prompt preserved + protocol appended.
200- let joined: String = sys
201- . content
202- . iter ( )
203- . filter_map ( |b| match b {
204- ContentBlock :: Text ( t) => Some ( t. as_str ( ) ) ,
205- _ => None ,
206- } )
207- . collect ( ) ;
208- assert ! ( joined. contains( "You are helpful." ) ) ;
209- assert ! ( joined. contains( "Tool Use Protocol" ) ) ;
210- }
211-
212- #[ test]
213- fn with_tool_instructions_inserts_system_when_absent ( ) {
214- let msgs = vec ! [ Message :: user( "hi" ) ] ;
215- let out = with_prompt_tool_instructions ( & msgs, & [ schema ( "read_file" ) ] ) ;
216- assert_eq ! ( out. len( ) , 2 ) ;
217- assert ! ( matches!( out[ 0 ] , Message :: System ( _) ) ) ;
218- }
219-
220- #[ test]
221- fn empty_tools_leaves_messages_unchanged ( ) {
222- let msgs = vec ! [ Message :: user( "hi" ) ] ;
223- assert_eq ! ( with_prompt_tool_instructions( & msgs, & [ ] ) , msgs) ;
224- }
225-
226- #[ test]
227- fn coalesces_consecutive_tool_results_into_one_user_turn ( ) {
228- let messages = vec ! [
229- Message :: user( "question" ) ,
230- Message :: assistant( "calling tools" ) ,
231- Message :: tool( "call-1" , "first" ) ,
232- Message :: tool( "call-2" , "second" ) ,
233- Message :: assistant( "done" ) ,
234- ] ;
235-
236- let out = coalesce_prompt_tool_results ( & messages) ;
237-
238- assert_eq ! ( out. len( ) , 4 ) ;
239- assert ! ( matches!( out[ 0 ] , Message :: User ( _) ) ) ;
240- assert ! ( matches!( out[ 1 ] , Message :: Assistant ( _) ) ) ;
241- assert ! ( matches!( out[ 2 ] , Message :: User ( _) ) ) ;
242- assert_eq ! ( out[ 2 ] . text( ) , "[Tool results]\n first\n second" ) ;
243- assert ! ( matches!( out[ 3 ] , Message :: Assistant ( _) ) ) ;
244- }
245-
246- #[ test]
247- fn coalescing_without_tool_results_is_identity ( ) {
248- let messages = vec ! [ Message :: system( "system" ) , Message :: user( "question" ) ] ;
249- assert_eq ! ( coalesce_prompt_tool_results( & messages) , messages) ;
250- }
251-
252- #[ test]
253- fn parses_single_tool_call ( ) {
254- let text = r#"Let me read it.
255- <tool_call>
256- {"name": "read_file", "arguments": {"path": "a.txt"}}
257- </tool_call>"# ;
258- let ( cleaned, calls) = parse_prompt_tool_calls_from_text ( text) ;
259- assert_eq ! ( cleaned, "Let me read it." ) ;
260- assert_eq ! ( calls. len( ) , 1 ) ;
261- assert_eq ! ( calls[ 0 ] . name, "read_file" ) ;
262- assert_eq ! ( calls[ 0 ] . id, "call_1" ) ;
263- assert_eq ! ( calls[ 0 ] . arguments, serde_json:: json!( { "path" : "a.txt" } ) ) ;
264- }
265-
266- #[ test]
267- fn parses_multiple_calls_and_keeps_prose ( ) {
268- let text = r#"a<tool_call>{"name":"one","arguments":{}}</tool_call>b<tool_call>{"name":"two","arguments":{"x":1}}</tool_call>c"# ;
269- let ( cleaned, calls) = parse_prompt_tool_calls_from_text ( text) ;
270- assert_eq ! ( cleaned, "abc" ) ;
271- assert_eq ! ( calls. len( ) , 2 ) ;
272- assert_eq ! ( calls[ 0 ] . name, "one" ) ;
273- assert_eq ! ( calls[ 1 ] . name, "two" ) ;
274- assert_eq ! ( calls[ 1 ] . id, "call_2" ) ;
275- }
276-
277- #[ test]
278- fn missing_arguments_defaults_to_empty_object ( ) {
279- let ( _, calls) =
280- parse_prompt_tool_calls_from_text ( r#"<tool_call>{"name":"noargs"}</tool_call>"# ) ;
281- assert_eq ! ( calls. len( ) , 1 ) ;
282- assert_eq ! ( calls[ 0 ] . arguments, serde_json:: json!( { } ) ) ;
283- }
284-
285- #[ test]
286- fn malformed_block_is_dropped ( ) {
287- let ( cleaned, calls) =
288- parse_prompt_tool_calls_from_text ( "<tool_call>not json</tool_call>done" ) ;
289- assert ! ( calls. is_empty( ) ) ;
290- assert_eq ! ( cleaned, "done" ) ;
291- }
292-
293- #[ test]
294- fn unterminated_block_kept_as_text ( ) {
295- let text = "text <tool_call>{\" name\" :\" x\" }" ;
296- let ( cleaned, calls) = parse_prompt_tool_calls_from_text ( text) ;
297- assert ! ( calls. is_empty( ) ) ;
298- assert_eq ! ( cleaned, "text <tool_call>{\" name\" :\" x\" }" ) ;
299- }
300-
301- #[ test]
302- fn no_blocks_returns_text_verbatim ( ) {
303- let ( cleaned, calls) = parse_prompt_tool_calls_from_text ( "just a normal answer" ) ;
304- assert ! ( calls. is_empty( ) ) ;
305- assert_eq ! ( cleaned, "just a normal answer" ) ;
306- }
307- }
0 commit comments