@@ -5135,17 +5135,24 @@ async def test_get_completion_inputs_tool_choice_none_without_tool_config():
51355135
51365136@pytest .mark .asyncio
51375137async def test_get_completion_inputs_tool_choice_required_for_any_mode ():
5138- """tool_choice must be 'required' when mode=ANY."""
5138+ """tool_choice must be 'required' when mode=ANY and tools are present ."""
51395139 llm_request = LlmRequest (
51405140 contents = [
51415141 types .Content (role = "user" , parts = [types .Part .from_text (text = "Hello" )])
51425142 ],
51435143 config = types .GenerateContentConfig (
5144+ tools = [
5145+ types .Tool (
5146+ function_declarations = [
5147+ types .FunctionDeclaration (name = "my_func" , description = "A func" )
5148+ ]
5149+ )
5150+ ],
51445151 tool_config = types .ToolConfig (
51455152 function_calling_config = types .FunctionCallingConfig (
51465153 mode = types .FunctionCallingConfigMode .ANY
51475154 )
5148- )
5155+ ),
51495156 ),
51505157 )
51515158
@@ -5158,17 +5165,24 @@ async def test_get_completion_inputs_tool_choice_required_for_any_mode():
51585165
51595166@pytest .mark .asyncio
51605167async def test_get_completion_inputs_tool_choice_none_for_none_mode ():
5161- """tool_choice must be 'none' when mode=NONE."""
5168+ """tool_choice must be 'none' when mode=NONE and tools are present ."""
51625169 llm_request = LlmRequest (
51635170 contents = [
51645171 types .Content (role = "user" , parts = [types .Part .from_text (text = "Hello" )])
51655172 ],
51665173 config = types .GenerateContentConfig (
5174+ tools = [
5175+ types .Tool (
5176+ function_declarations = [
5177+ types .FunctionDeclaration (name = "my_func" , description = "A func" )
5178+ ]
5179+ )
5180+ ],
51675181 tool_config = types .ToolConfig (
51685182 function_calling_config = types .FunctionCallingConfig (
51695183 mode = types .FunctionCallingConfigMode .NONE
51705184 )
5171- )
5185+ ),
51725186 ),
51735187 )
51745188
@@ -5206,7 +5220,7 @@ async def test_get_completion_inputs_tool_choice_none_for_auto_mode():
52065220async def test_generate_content_async_propagates_tool_choice_required (
52075221 mock_acompletion , mock_completion
52085222):
5209- """generate_content_async must pass tool_choice='required' to acompletion."""
5223+ """generate_content_async must pass tool_choice='required' to acompletion when tools are present ."""
52105224 llm_client = MockLLMClient (mock_acompletion , mock_completion )
52115225 lite_llm_instance = LiteLlm (model = "openai/gpt-4o" , llm_client = llm_client )
52125226
@@ -5217,11 +5231,18 @@ async def test_generate_content_async_propagates_tool_choice_required(
52175231 )
52185232 ],
52195233 config = types .GenerateContentConfig (
5234+ tools = [
5235+ types .Tool (
5236+ function_declarations = [
5237+ types .FunctionDeclaration (name = "my_func" , description = "A func" )
5238+ ]
5239+ )
5240+ ],
52205241 tool_config = types .ToolConfig (
52215242 function_calling_config = types .FunctionCallingConfig (
52225243 mode = types .FunctionCallingConfigMode .ANY
52235244 )
5224- )
5245+ ),
52255246 ),
52265247 )
52275248
@@ -5237,7 +5258,7 @@ async def test_generate_content_async_propagates_tool_choice_required(
52375258async def test_generate_content_async_propagates_tool_choice_none_mode (
52385259 mock_acompletion , mock_completion
52395260):
5240- """generate_content_async must pass tool_choice='none' to acompletion for NONE mode."""
5261+ """generate_content_async must pass tool_choice='none' to acompletion for NONE mode when tools are present ."""
52415262 llm_client = MockLLMClient (mock_acompletion , mock_completion )
52425263 lite_llm_instance = LiteLlm (model = "openai/gpt-4o" , llm_client = llm_client )
52435264
@@ -5248,11 +5269,18 @@ async def test_generate_content_async_propagates_tool_choice_none_mode(
52485269 )
52495270 ],
52505271 config = types .GenerateContentConfig (
5272+ tools = [
5273+ types .Tool (
5274+ function_declarations = [
5275+ types .FunctionDeclaration (name = "my_func" , description = "A func" )
5276+ ]
5277+ )
5278+ ],
52515279 tool_config = types .ToolConfig (
52525280 function_calling_config = types .FunctionCallingConfig (
52535281 mode = types .FunctionCallingConfigMode .NONE
52545282 )
5255- )
5283+ ),
52565284 ),
52575285 )
52585286
@@ -5313,3 +5341,63 @@ async def test_generate_content_async_omits_tool_choice_without_tool_config(
53135341 mock_acompletion .assert_called_once ()
53145342 _ , kwargs = mock_acompletion .call_args
53155343 assert "tool_choice" not in kwargs
5344+
5345+
5346+ @pytest .mark .asyncio
5347+ async def test_get_completion_inputs_tool_choice_coerced_to_none_when_no_tools ():
5348+ """tool_choice must be coerced to None when mode=ANY but no function_declarations exist."""
5349+ llm_request = LlmRequest (
5350+ contents = [
5351+ types .Content (role = "user" , parts = [types .Part .from_text (text = "Hello" )])
5352+ ],
5353+ config = types .GenerateContentConfig (
5354+ tool_config = types .ToolConfig (
5355+ function_calling_config = types .FunctionCallingConfig (
5356+ mode = types .FunctionCallingConfigMode .ANY
5357+ )
5358+ )
5359+ ),
5360+ )
5361+
5362+ _ , tools , _ , _ , tool_choice = await _get_completion_inputs (
5363+ llm_request , model = "openai/gpt-4o"
5364+ )
5365+
5366+ assert not tools
5367+ assert tool_choice is None
5368+
5369+
5370+ @pytest .mark .asyncio
5371+ async def test_generate_content_async_omits_tool_choice_when_functions_override (
5372+ mock_acompletion , mock_completion
5373+ ):
5374+ """When `functions` is passed as an additional kwarg, tools is nulled and tool_choice must also be dropped."""
5375+ llm_client = MockLLMClient (mock_acompletion , mock_completion )
5376+ lite_llm_instance = LiteLlm (
5377+ model = "openai/gpt-4o" ,
5378+ llm_client = llm_client ,
5379+ functions = [{"name" : "noop" , "parameters" : {"type" : "object" }}],
5380+ )
5381+
5382+ llm_request = LlmRequest (
5383+ contents = [
5384+ types .Content (
5385+ role = "user" , parts = [types .Part .from_text (text = "Call something" )]
5386+ )
5387+ ],
5388+ config = types .GenerateContentConfig (
5389+ tool_config = types .ToolConfig (
5390+ function_calling_config = types .FunctionCallingConfig (
5391+ mode = types .FunctionCallingConfigMode .ANY
5392+ )
5393+ )
5394+ ),
5395+ )
5396+
5397+ async for _ in lite_llm_instance .generate_content_async (llm_request ):
5398+ pass
5399+
5400+ mock_acompletion .assert_called_once ()
5401+ _ , kwargs = mock_acompletion .call_args
5402+ assert kwargs .get ("tools" ) is None
5403+ assert "tool_choice" not in kwargs
0 commit comments