Repository: microsoft/semantic-kernel
File: python/semantic_kernel/kernel.py (Python)

Code before:
                f"only `{len(parsed_args) if parsed_args is not None else 0}` received. The required arguments are: "
                f"{[param.name for param in function_to_call.parameters if param.is_required]}. "
                "Please provide the required arguments and try again."
            )
            logger.info(msg)
            frc = FunctionResultContent.from_function_call_content_and_result(
                function_call_content=function_call,
                result=msg,
            )
            chat_history.add_message(message=frc.to_chat_message_content())
            return None

        # Check for missing or unexpected parameters
        required_param_names = {param.name for param in function_to_call.parameters if param.is_required}
        received_param_names = set(parsed_args or {})

        missing_params = required_param_names - received_param_names
        unexpected_params = received_param_names - {param.name for param in function_to_call.parameters}

        if missing_params or unexpected_params:
            msg_parts = []
            if missing_params:
                msg_parts.append(f"Missing required argument(s): {sorted(missing_params)}.")
            if unexpected_params:
                msg_parts.append(f"Received unexpected argument(s): {sorted(unexpected_params)}.")
            msg = " ".join(msg_parts) + " Please revise the arguments to match the function signature."

            logger.info(msg)
            frc = FunctionResultContent.from_function_call_content_and_result(
                function_call_content=function_call,
                result=msg,
            )
            chat_history.add_message(message=frc.to_chat_message_content())
            return None

        logger.info(f"Calling {function_call.name} function with args: {function_call.arguments}")

        _rebuild_auto_function_invocation_context()
        invocation_context = AutoFunctionInvocationContext(
            function=function_to_call,
            kernel=self,
            arguments=args_cloned,
            is_streaming=is_streaming,
            chat_history=chat_history,
            execution_settings=execution_settings,
            function_result=FunctionResult(function=function_to_call.metadata, value=None),
            function_count=function_call_count or 0,
            request_sequence_index=request_index or 0,
        )
        if function_call.index is not None:
            invocation_context.function_sequence_index = function_call.index

Review comment (bug):
this is not a good idea, the KernelArguments used might be propagated onwards towards this, and then this will raise, I think the behavior should be:

- missing_params: this
- unexpected_params: return this msg only if the function call fails, in addition to the actual error

Code after:
                    msg_parts.append(f"Received unexpected argument(s): {sorted(unexpected_params)}.")
                msg = " ".join(msg_parts) + " Please revise the arguments to match the function signature."

                logger.info(msg)
                frc = FunctionResultContent.from_function_call_content_and_result(
                    function_call_content=function_call,
                    result=msg,
                )
                chat_history.add_message(message=frc.to_chat_message_content())
                return None

            if parsed_args:
                args_cloned.update(parsed_args)
        except (FunctionCallInvalidArgumentsException, TypeError) as exc:
            logger.info(f"Received invalid arguments for function {function_call.name}: {exc}. Trying tool call again.")
            frc = FunctionResultContent.from_function_call_content_and_result(
                function_call_content=function_call,
                result="The tool call arguments are malformed. Arguments must be in JSON format. Please try again.",
            )
            chat_history.add_message(message=frc.to_chat_message_content())
            return None

        num_required_func_params = len([param for param in function_to_call.parameters if param.is_required])
        if parsed_args is None or len(parsed_args) < num_required_func_params:
            msg = (
                f"There are `{num_required_func_params}` tool call arguments required and "
                f"only `{len(parsed_args) if parsed_args is not None else 0}` received. The required arguments are: "
                f"{[param.name for param in function_to_call.parameters if param.is_required]}. "
                "Please provide the required arguments and try again."
            )
            logger.info(msg)
            frc = FunctionResultContent.from_function_call_content_and_result(
                function_call_content=function_call,
                result=msg,
            )
            chat_history.add_message(message=frc.to_chat_message_content())
            return None

        logger.info(f"Calling {function_call.name} function with args: {function_call.arguments}")

        _rebuild_auto_function_invocation_context()
        invocation_context = AutoFunctionInvocationContext(
            function=function_to_call,
            kernel=self,
            arguments=args_cloned,
            is_streaming=is_streaming,
            chat_history=chat_history,
            execution_settings=execution_settings,
            function_result=FunctionResult(function=function_to_call.metadata, value=None),
            function_count=function_call_count or 0,
            request_sequence_index=request_index or 0,