Functions are defined by -

  1. Wrapping a Python function with a decorator @indexify_function
  2. Defining the runnable compute logic as a class if you have to initialize expensive values.

Defining a Function with Decorators

from indexify import indexify_function

@indexify_function(name="my-funct", description="This is my function", image="my-image", accumulate=InitialValue, payload_encoder="cloudpickle")
def my_funct(a: int) -> int:
    return a + 1

Attributes:

  • name(optional): Name of the function. If not provided, the function name is used.
  • description(optional): Description of the function. If not provided, the doc string of a function is used as a description.
  • image(optional): Name of the image in which the function should run. If not provided, itโ€™s assumed the default image called tensorlake/indexify-executor-default is used.
  • accumulate(optional): Initial value for the accumulator. If not provided, the function is not an reducer. You can define any Pydantic object with default values.
  • payload_encoder(optional): Serialization method to store the outputs of the function. If not provided, cloudpickle is used. Possible values: cloudpickle, json.

Defining a Function as Class

Sometimes you want to initialize some expensive objects before running the function. In such cases, you can define the function as a class.

from indexify import IndexifyFunction

class MyFunction(IndexifyFunction):
    name: str = "my-funct"
    description: str = "This is my function"
    image: str = "my-image"
    accumulate: InitialValue = InitialValue()

    def __init__(self):
        self.model = load_model()
    
    def run(self, a: int) -> int:
        return self.model.predict(a)

The same attributes that are available for the decorator can be set as attributes of the class.

Dynamic Routers

Dynamic Routers enable routing data from an upstream function to one or more downstream functions by applying custom logic.

They are equivalent to if-else conditions in a programming language.

from indexify import indexify_function, indexify_router

@indexify_function()
def print_even(a: int) -> str:
    return f"{a} is even"

@indexify_function()
def print_odd(a: int) -> str:
    return f"{a} is odd"

@indexify_router()
def even_odd_router(a: int) -> Union[print_even, print_odd]:
    if a % 2 == 0:
        return print_even
    else:
        return print_odd

In this example even_odd_router function routes the input to print_even if the input is even, otherwise to print_odd.

They are added to the graphs as any other function and can be used to define complex workflows.

g = Graph(name="even-odd-printer", start_node=even_odd_router, description="Prints even or odd numbers")
g.route(event_odd_router, [print_even, print_odd])

The print_even and print_odd functions are added as downstream functions to the even_odd_router function