The Blocks are foundational to any robust design of an intelligent system. How the blocks are thought of and used within an intelligent solution has a wide ranging impact on the accuracy, robustness and flexibility of the solution. 


As part of the ongoing effort by RazorThink, we strive to constantly improve the functionality and usability of blocks within the aiOS platform. As part of our ongoing effort to improve the functionality of the blocks, we have introduced a new way of creating blocks in the latest version of the platform. 


OLD WAY A BLOCK WAS DEFINED

Previously a block had three main attributes, which are listed below

  1. An annotation to define the inputs

  2. An annotation to define the outputs

  3. Run function which encapsulated any operation performed within the block.


An example of a block in the old structure is shown below

# Decorator for defining the input to the block
@inputs.atomic.generic("project_space_path")

# Decorator for defining the output of the block
@outputs.atomic.generic("dataframe")

# Block class which subclasses the main Block class
class ReadCSVProjectSpace(Block):
    
    
# Run function which takes the input and output variables as arguments
    
def run(project_space_path, dataframe):
        file_path = razor.api.project_space_path(project_space_path)
        df = pd.read_csv(file_path)
        
        dataframe.put(df)


However, this presented with three important challenges to the block developer:

  1. Strong type checking was not feasible through the input and output annotations

  2. Since the inputs and outputs were defined using annotations, the full functionality of the object-oriented programming concepts of Python could not be used. 

  3. Also, hindered the block developers when it came to leveraging the functionalities of the python Integrated Development Environments (IDE) like autocomplete.


To overcome these challenges we have designed a completely new way of creating a block.


THE NEW WAY OF CREATING A BLOCK

The new way of creating a block uses class objects for defining the input and output parameters of a block. An example of a block defined using the new structure is as shown below

# Decorator to define the class a block
# The block annotation also provides the users to control the method of execution by defining the executor
@rf.block(executor = rf.ContainerExecutor(cores=1, memory=1024))
class 
ReadCSVProjectSpace:
    
# The inputs to the block can be defined as variables strongly typed using Python Typing
    project_space_path: str
    
    
# The outputs defined and strongly typed using the Python Typing
    
# Outputs can also be assigned Transport class to define the transport mechanism
    dataframe: rf.Output[pd.DataFrame] = rf.Output(transport=rf.FileTransport)
    
    
# The run function that performs the required operation
    def 
run(self):
        file_path = razor.api.project_space_path(self.project_space_path)
        df = pd.read_csv(file_path)
        
        self.dataframe.put(df)


In the above mentioned code, we have provided a new structure for defining the inputs and outputs of a block. Some of the advantages of migrating to the new structure are as follows

  1. The input and output attributes of the block support strong typing. The python typing library can be used to strongly type the input and output attributes of the block

  2. The input and output attributes are now defined as classes and can handle additional default parameters like Transports. This allows the block developers to define a default transport mechanism for an output, reducing the burden on the users to define such constructs at the time of using the block

  3. Cleaner code following the Object-Oriented Programming (OOP) concepts of python

  4. Better support for Python IDEs for autocorrect and autofill.