Hello world worker

We see how to run tasks that are not Tierkreis built-in tasks. Although we can build workers that manually read and write to the appropriate files, in this section we use the helper functions in the Tierkreis Python library.

Worker code

Our worker will consist of a single function returning a string. We first instantiate a Worker class, which is constructed using the name of the worker. The name of the worker tells the executor which worker a task comes from. Therefore all of the different workers used in a single graph should have distinct names.

from tierkreis import Worker

worker = Worker("hello_world_worker")

The Worker.task method is used as a decorator to add functions to the worker.

@worker.task()
def greet(greeting: str, subject: str) -> str: ...

Note

There are restrictions on the types of functions that Worker.task will accept. If the function arguments and return types correspond to JSON types then the function will be accepted. In addition there are a few ways of using more complex data structures, but for now we keep things simple. For more information please see Complex types in Tierkreis Python workers.

We use the Worker.app method to turn our Python programme into a legitimate Tierkreis worker.

from sys import argv

if __name__ == "__main__":
    worker.app(argv)

The complete worker file is as follows:

# /// script
# requires-python = ">=3.12"
# dependencies = ["pydantic", "tierkreis"]
#
# [tool.uv.sources]
# tierkreis = { path = "../../../tierkreis", editable = true }
# ///
import logging
from sys import argv
from tierkreis import Worker

logger = logging.getLogger(__name__)
worker = Worker("hello_world_worker")


@worker.task()
def greet(greeting: str, subject: str) -> str:
    logger.info("%s %s", greeting, subject)
    return greeting + subject


if __name__ == "__main__":
    worker.app(argv)

Generating stubs

Since this worker uses the Tierkreis Python library, we can automatically generate stub files using the following command.

!cd ../../../examples/example_workers/hello_world_worker && uv run main.py --stubs-path ../../../docs/source/worker/hello_stubs.py > /dev/null 2>&1

Graph creation

Now can we import the greet function from the stubs file and use it in GraphBuilder.task.

from tierkreis.builder import GraphBuilder
from tierkreis.models import TKR
from hello_stubs import greet

g = GraphBuilder(TKR[str], TKR[str])
output = g.task(greet(greeting=g.const("hello "), subject=g.inputs))
g.outputs(output)

Execution

We use the FileStorage as usual but this time use the UvExecutor instead of the ShellExecutor. To configure the UvExecutor we provide a path to a ‘registry’ folder of workers constructed using uv. To add a worker to the registry we create a sub-folder of the registry folder containing a main.py that is executable by uv. (The directory could be a whole uv project with a pyproject.toml or it might be that the main.py is a uv script.) The folder name should correspond to the name of the worker.

from uuid import UUID
from pathlib import Path

from tierkreis import run_graph
from tierkreis.executor import UvExecutor
from tierkreis.storage import FileStorage, read_outputs

storage = FileStorage(UUID(int=99), "hello_world_tutorial", do_cleanup=True)
executor = UvExecutor(
    registry_path=Path("../../../examples/example_workers"), logs_path=storage.logs_path
)
run_graph(storage, executor, g.data, "world!")
read_outputs(storage)
'hello world!'