# Copyright (c) Microsoft. All rights reserved. import pytest from semantic_kernel.contents.function_call_content import FunctionCallContent from semantic_kernel.exceptions.content_exceptions import ( ContentAdditionException, FunctionCallInvalidArgumentsException, FunctionCallInvalidNameException, ) from semantic_kernel.functions.kernel_arguments import KernelArguments def test_init_from_names(): # Test initializing function call from names fc = FunctionCallContent(function_name="Function", plugin_name="Test", arguments="""{"input": "world"}""") assert fc.name == "Test-Function" assert fc.function_name == "Function" assert fc.plugin_name == "Test" assert fc.arguments == """{"input": "world"}""" assert str(fc) == 'Test-Function({"input": "world"})' def test_init_dict_args(): # Test initializing function call with the args already as a dictionary fc = FunctionCallContent(function_name="Function", plugin_name="Test", arguments={"input": "world"}) assert fc.name == "Test-Function" assert fc.function_name == "Function" assert fc.plugin_name == "Test" assert fc.arguments == {"input": "world"} assert str(fc) == 'Test-Function({"input": "world"})' def test_init_with_metadata(): # Test initializing function call from names fc = FunctionCallContent(function_name="Function", plugin_name="Test", metadata={"test": "test"}) assert fc.name == "Test-Function" assert fc.function_name == "Function" assert fc.plugin_name == "Test" assert fc.metadata == {"test": "test"} def test_function_call(function_call: FunctionCallContent): assert function_call.name == "Test-Function" assert function_call.arguments == """{"input": "world"}""" assert function_call.function_name == "Function" assert function_call.plugin_name == "Test" def test_add(function_call: FunctionCallContent): # Test adding two function calls fc2 = FunctionCallContent(id="test", name="Test-Function", arguments="""{"input2": "world2"}""") fc3 = function_call + fc2 assert fc3.name == "Test-Function" assert fc3.arguments == """{"input": "world"}{"input2": "world2"}""" def test_add_empty(): # Test adding two function calls fc1 = FunctionCallContent(id="test1", name="Test-Function", arguments=None) fc2 = FunctionCallContent(id="test1", name="Test-Function", arguments="") fc3 = fc1 + fc2 assert fc3.name == "Test-Function" assert fc3.arguments == "{}" fc1 = FunctionCallContent(id="test1", name="Test-Function", arguments="""{"input2": "world2"}""") fc2 = FunctionCallContent(id="test1", name="Test-Function", arguments="") fc3 = fc1 + fc2 assert fc3.name == "Test-Function" assert fc3.arguments == """{"input2": "world2"}""" fc1 = FunctionCallContent(id="test1", name="Test-Function", arguments="{}") fc2 = FunctionCallContent(id="test1", name="Test-Function", arguments="""{"input2": "world2"}""") fc3 = fc1 + fc2 assert fc3.name == "Test-Function" assert fc3.arguments == """{"input2": "world2"}""" def test_add_none(function_call: FunctionCallContent): # Test adding two function calls with one being None fc2 = None fc3 = function_call + fc2 assert fc3.name == "Test-Function" assert fc3.arguments == """{"input": "world"}""" def test_add_dict_args(): # Test adding two function calls fc1 = FunctionCallContent(id="test1", name="Test-Function", arguments={"input1": "world"}) fc2 = FunctionCallContent(id="test1", name="Test-Function", arguments={"input2": "world2"}) fc3 = fc1 + fc2 assert fc3.name == "Test-Function" assert fc3.arguments == {"input1": "world", "input2": "world2"} def test_add_one_dict_args_fail(): # Test adding two function calls fc1 = FunctionCallContent(id="test1", name="Test-Function", arguments="""{"input1": "world"}""") fc2 = FunctionCallContent(id="test1", name="Test-Function", arguments={"input2": "world2"}) with pytest.raises(ContentAdditionException): fc1 + fc2 def test_add_fail_id(): # Test adding two function calls fc1 = FunctionCallContent(id="test1", name="Test-Function", arguments="""{"input2": "world2"}""") fc2 = FunctionCallContent(id="test2", name="Test-Function", arguments="""{"input2": "world2"}""") with pytest.raises(ContentAdditionException): fc1 + fc2 def test_add_fail_index(): # Test adding two function calls fc1 = FunctionCallContent(id="test", index=0, name="Test-Function", arguments="""{"input2": "world2"}""") fc2 = FunctionCallContent(id="test", index=1, name="Test-Function", arguments="""{"input2": "world2"}""") with pytest.raises(ContentAdditionException): fc1 + fc2 def test_parse_arguments(function_call: FunctionCallContent): # Test parsing arguments to dictionary assert function_call.parse_arguments() == {"input": "world"} def test_parse_arguments_dict(): # Test parsing arguments to dictionary fc = FunctionCallContent(id="test", name="Test-Function", arguments={"input": "world"}) assert fc.parse_arguments() == {"input": "world"} def test_parse_arguments_none(): # Test parsing arguments to dictionary fc = FunctionCallContent(id="test", name="Test-Function") assert fc.parse_arguments() is None def test_parse_arguments_single_quotes(): # Test parsing arguments that are single quoted fc = FunctionCallContent(id="test", name="Test-Function", arguments="{'input': 'world'}") assert fc.parse_arguments() == {"input": "world"} fc = FunctionCallContent(id="test", name="Test-Function", arguments="{'input': 'Let\\'s go'}") assert fc.parse_arguments() == {"input": "Let's go"} # Note this the below test is not a valid json string fc = FunctionCallContent(id="test", name="Test-Function", arguments="{'input': 'Let's go'}") with pytest.raises(FunctionCallInvalidArgumentsException): fc.parse_arguments() def test_parse_arguments_fail(): # Test parsing arguments to dictionary fc = FunctionCallContent(id=None, name="Test-Function", arguments="""{"input": "world}""") with pytest.raises(FunctionCallInvalidArgumentsException): fc.parse_arguments() def test_to_kernel_arguments(function_call: FunctionCallContent): # Test parsing arguments to variables arguments = KernelArguments() assert isinstance(function_call.to_kernel_arguments(), KernelArguments) arguments.update(function_call.to_kernel_arguments()) assert arguments["input"] == "world" def test_to_kernel_arguments_none(): # Test parsing arguments to variables fc = FunctionCallContent(id="test", name="Test-Function") assert fc.to_kernel_arguments() == KernelArguments() def test_split_name(function_call: FunctionCallContent): # Test splitting the name into plugin and function name assert function_call.split_name() == ["Test", "Function"] def test_split_name_name_only(): # Test splitting the name into plugin and function name fc = FunctionCallContent(id="test", name="Function") assert fc.split_name() == ["", "Function"] def test_split_name_dict(function_call: FunctionCallContent): # Test splitting the name into plugin and function name assert function_call.split_name_dict() == {"plugin_name": "Test", "function_name": "Function"} def test_split_name_none(): fc = FunctionCallContent(id="1234") with pytest.raises(FunctionCallInvalidNameException): fc.split_name() def test_fc_dump(function_call: FunctionCallContent): # Test dumping the function call to dictionary dumped = function_call.model_dump(exclude_none=True) assert dumped == { "content_type": "function_call", "id": "test", "name": "Test-Function", "function_name": "Function", "plugin_name": "Test", "arguments": '{"input": "world"}', "metadata": {}, } def test_fc_dump_json(function_call: FunctionCallContent): # Test dumping the function call to dictionary dumped = function_call.model_dump_json(exclude_none=True) assert ( dumped == """{"metadata":{},"content_type":"function_call","id":"test","name":"Test-Function","function_name":"Function","plugin_name":"Test","arguments":"{\\"input\\": \\"world\\"}"}""" # noqa: E501 )