@@ -624,15 +624,24 @@ impl OpenAIPreprocessor {
624624 ) -> std:: result:: Result < bool , Error > {
625625 match ( tool_call_parser, tool_choice, has_tools) {
626626 // No parser but tools requested - error cases
627- ( None , Some ( ChatCompletionToolChoiceOption :: Required ) , true ) => Err ( anyhow:: anyhow!(
628- "Tool choice 'required' specified but no tool parser configured"
629- ) ) ,
630- ( None , Some ( ChatCompletionToolChoiceOption :: Auto ) , true ) => Err ( anyhow:: anyhow!(
631- "Tool choice 'auto' specified but no tool parser configured"
632- ) ) ,
633- ( None , Some ( ChatCompletionToolChoiceOption :: Named ( _) ) , _) => Err ( anyhow:: anyhow!(
634- "Named tool choice specified but no tool parser configured"
635- ) ) ,
627+ ( None , Some ( ChatCompletionToolChoiceOption :: Required ) , true ) => {
628+ tracing:: warn!(
629+ "Tool choice 'required' specified but no tool parser configured; proceeding without jailing"
630+ ) ;
631+ Ok ( false )
632+ }
633+ ( None , Some ( ChatCompletionToolChoiceOption :: Auto ) , true ) => {
634+ tracing:: warn!(
635+ "Tool choice 'auto' specified but no tool parser configured; proceeding without jailing"
636+ ) ;
637+ Ok ( false )
638+ }
639+ ( None , Some ( ChatCompletionToolChoiceOption :: Named ( _) ) , _) => {
640+ tracing:: warn!(
641+ "Named tool choice specified but no tool parser configured; proceeding without jailing"
642+ ) ;
643+ Ok ( false )
644+ }
636645
637646 // Parser exists and tools might be called
638647 ( Some ( _) , Some ( ChatCompletionToolChoiceOption :: None ) , _) => {
@@ -864,182 +873,4 @@ impl
864873 }
865874}
866875
867- #[ allow( deprecated, dead_code) ]
868- #[ cfg( test) ]
869- mod tests {
870- use super :: * ;
871- use dynamo_async_openai:: types:: {
872- ChatChoiceStream , ChatCompletionStreamResponseDelta , FinishReason as OAIFinishReason , Role ,
873- } ;
874-
875- use dynamo_runtime:: protocols:: annotated:: Annotated ;
876-
877- use std:: sync:: Arc ;
878-
879- // Helper function to create a mock chat response chunk
880- fn create_mock_response_chunk (
881- content : String ,
882- index : u32 ,
883- ) -> Annotated < NvCreateChatCompletionStreamResponse > {
884- let choice = ChatChoiceStream {
885- index,
886- delta : ChatCompletionStreamResponseDelta {
887- role : Some ( Role :: Assistant ) ,
888- content : Some ( content) ,
889- tool_calls : None ,
890- function_call : None ,
891- refusal : None ,
892- reasoning_content : None ,
893- } ,
894- finish_reason : None ,
895- logprobs : None ,
896- } ;
897-
898- let response = NvCreateChatCompletionStreamResponse {
899- id : "test-id" . to_string ( ) ,
900- choices : vec ! [ choice] ,
901- created : 1234567890 ,
902- model : "test-model" . to_string ( ) ,
903- system_fingerprint : Some ( "test-fingerprint" . to_string ( ) ) ,
904- object : "chat.completion.chunk" . to_string ( ) ,
905- usage : None ,
906- service_tier : None ,
907- } ;
908-
909- Annotated {
910- data : Some ( response) ,
911- id : None ,
912- event : None ,
913- comment : None ,
914- }
915- }
916-
917- // Helper function to create a final response chunk with finish reason
918- fn create_final_response_chunk ( index : u32 ) -> Annotated < NvCreateChatCompletionStreamResponse > {
919- let choice = ChatChoiceStream {
920- index,
921- delta : ChatCompletionStreamResponseDelta {
922- role : None ,
923- content : None ,
924- tool_calls : None ,
925- function_call : None ,
926- refusal : None ,
927- reasoning_content : None ,
928- } ,
929- finish_reason : Some ( OAIFinishReason :: Stop ) ,
930- logprobs : None ,
931- } ;
932-
933- let response = NvCreateChatCompletionStreamResponse {
934- id : "test-id" . to_string ( ) ,
935- choices : vec ! [ choice] ,
936- created : 1234567890 ,
937- model : "test-model" . to_string ( ) ,
938- system_fingerprint : Some ( "test-fingerprint" . to_string ( ) ) ,
939- object : "chat.completion.chunk" . to_string ( ) ,
940- usage : None ,
941- service_tier : None ,
942- } ;
943-
944- Annotated {
945- data : Some ( response) ,
946- id : None ,
947- event : None ,
948- comment : None ,
949- }
950- }
951-
952- // Mock async engine context for testing
953- #[ derive( Debug ) ]
954- struct MockAsyncEngineContext {
955- id : String ,
956- stopped : std:: sync:: atomic:: AtomicBool ,
957- }
958-
959- impl MockAsyncEngineContext {
960- fn new ( id : String ) -> Self {
961- Self {
962- id,
963- stopped : std:: sync:: atomic:: AtomicBool :: new ( false ) ,
964- }
965- }
966- }
967-
968- #[ async_trait]
969- impl dynamo_runtime:: pipeline:: AsyncEngineContext for MockAsyncEngineContext {
970- fn id ( & self ) -> & str {
971- & self . id
972- }
973-
974- fn stop ( & self ) {
975- self . stopped
976- . store ( true , std:: sync:: atomic:: Ordering :: Relaxed ) ;
977- }
978-
979- fn stop_generating ( & self ) {
980- self . stopped
981- . store ( true , std:: sync:: atomic:: Ordering :: Relaxed ) ;
982- }
983-
984- fn kill ( & self ) {
985- self . stopped
986- . store ( true , std:: sync:: atomic:: Ordering :: Relaxed ) ;
987- }
988-
989- fn is_stopped ( & self ) -> bool {
990- self . stopped . load ( std:: sync:: atomic:: Ordering :: Relaxed )
991- }
992-
993- fn is_killed ( & self ) -> bool {
994- self . stopped . load ( std:: sync:: atomic:: Ordering :: Relaxed )
995- }
996-
997- async fn stopped ( & self ) {
998- // No-op for testing
999- }
1000-
1001- async fn killed ( & self ) {
1002- // No-op for testing
1003- }
1004-
1005- fn link_child ( & self , _: Arc < dyn dynamo_runtime:: pipeline:: AsyncEngineContext > ) {
1006- // No-op for testing
1007- }
1008- }
1009-
1010- // Test for tool call detection with different parsers - still valuable to keep
1011- #[ tokio:: test]
1012- async fn test_detect_tool_call_start_different_parsers ( ) {
1013- use dynamo_parsers:: tool_calling:: detect_tool_call_start;
1014-
1015- // Test nemotron_deci parser
1016- assert ! ( detect_tool_call_start( "<TOOLCALL>" , Some ( "nemotron_deci" ) ) . unwrap( ) ) ;
1017- assert ! ( !detect_tool_call_start( "Hello world" , Some ( "nemotron_deci" ) ) . unwrap( ) ) ;
1018- assert ! ( !detect_tool_call_start( "<tool_call>" , Some ( "nemotron_deci" ) ) . unwrap( ) ) ; // Wrong format
1019-
1020- // Test hermes parser - now also detects JSON patterns
1021- assert ! ( detect_tool_call_start( "<tool_call>" , Some ( "hermes" ) ) . unwrap( ) ) ;
1022- assert ! ( detect_tool_call_start( "{\" name\" : \" test\" }" , Some ( "hermes" ) ) . unwrap( ) ) ; // JSON detection
1023- assert ! ( !detect_tool_call_start( "Hello world" , Some ( "hermes" ) ) . unwrap( ) ) ;
1024- assert ! ( !detect_tool_call_start( "<TOOLCALL>" , Some ( "hermes" ) ) . unwrap( ) ) ; // Wrong format
1025-
1026- // Test phi4 parser
1027- assert ! ( detect_tool_call_start( "functools[" , Some ( "phi4" ) ) . unwrap( ) ) ;
1028- assert ! ( detect_tool_call_start( "{\" name\" : \" test\" }" , Some ( "phi4" ) ) . unwrap( ) ) ; // JSON detection
1029- assert ! ( !detect_tool_call_start( "Hello world" , Some ( "phi4" ) ) . unwrap( ) ) ;
1030-
1031- // Test mistral parser
1032- assert ! ( detect_tool_call_start( "[{" , Some ( "mistral" ) ) . unwrap( ) ) ;
1033- assert ! ( detect_tool_call_start( "[TOOL_CALLS]" , Some ( "mistral" ) ) . unwrap( ) ) ;
1034- assert ! ( !detect_tool_call_start( "Hello world" , Some ( "mistral" ) ) . unwrap( ) ) ;
1035-
1036- // Test llama3_json parser
1037- assert ! ( detect_tool_call_start( "<|python_tag|>" , Some ( "llama3_json" ) ) . unwrap( ) ) ;
1038- assert ! ( detect_tool_call_start( "{\" name\" : \" test\" }" , Some ( "llama3_json" ) ) . unwrap( ) ) ; // JSON detection
1039-
1040- // Test default parser (should behave like nemotron_deci)
1041- assert ! ( detect_tool_call_start( "<TOOLCALL>" , None ) . unwrap( ) ) ;
1042- assert ! ( detect_tool_call_start( "{\" name\" : \" test\" }" , None ) . unwrap( ) ) ; // JSON detection
1043- assert ! ( !detect_tool_call_start( "Hello world" , None ) . unwrap( ) ) ;
1044- }
1045- }
876+ // Note: tests for jailing and parser detection live in `lib/llm/tests/test_jail.rs`
0 commit comments