{ "cells": [ { "cell_type": "markdown", "id": "4035de03", "metadata": {}, "source": [ "# Hello World: Tierkreis Edition\n", "In this example we will construct a workflow graph that uses the `greet` function from the `hello_world_worker` we defined in the previous example.\n", "We want to write a graph that produces the output `hello world`.\n", "First we provide the necessary imports, the `GraphBuilder`, a class for constructing workflows, and `TKR`; you can think of it as tierkreis types (more on this at the bottom of the page). \n", "This ensures, that only types are used that can be processed internally." ] }, { "cell_type": "code", "execution_count": null, "id": "98c7c0cd", "metadata": {}, "outputs": [], "source": [ "%pip install tierkreis" ] }, { "cell_type": "code", "execution_count": null, "id": "d57959b4", "metadata": {}, "outputs": [], "source": [ "from tierkreis.builder import GraphBuilder\n", "from tierkreis.controller.data.models import TKR" ] }, { "cell_type": "markdown", "id": "f62ea127", "metadata": {}, "source": [ "A workflow is just a representation of a computation.\n", "The example we are going to build represents a function `str -> str`.\n", "This signature is defined at instantiation of the Graph builder object" ] }, { "cell_type": "code", "execution_count": null, "id": "9224b7d7", "metadata": {}, "outputs": [], "source": [ "graph = GraphBuilder(inputs_type=TKR[str], outputs_type=TKR[str])" ] }, { "cell_type": "markdown", "id": "9b76964b", "metadata": {}, "source": [ "We could simply use a graph that just returns its input.\n", "Instead were going to construct the string `hello world` from an constant value and an input.\n", "First we define the constant part, by adding a constant node to the graph." ] }, { "cell_type": "code", "execution_count": null, "id": "8e8bb728", "metadata": {}, "outputs": [], "source": [ "hello = graph.const(\"Hello \")" ] }, { "cell_type": "markdown", "id": "ded9f606", "metadata": {}, "source": [ "We capture the output of that node in the variable `hello`, which we can use as input to other nodes to insert an edge in the graph.\n", "Inputs to the graph cah be referenced in two ways depending if we have a single input (`graph.inputs`) or multiple inputs (`graph.inputs.`).\n", "We will cover multiple inputs in a later example.\n", "For now, we want to call the `greet` function from the `hello_world_worker`.\n", "To use the type hints from that worker we can import the interface and then use it as a task." ] }, { "cell_type": "code", "execution_count": null, "id": "48c72fcc", "metadata": {}, "outputs": [], "source": [ "from hello_world_worker import greet\n", "\n", "output = graph.task(greet(greeting=hello, subject=graph.inputs))" ] }, { "cell_type": "markdown", "id": "7771239c", "metadata": {}, "source": [ "The benefit of using the function interface is the type checking.\n", "The python typechecker will warn us if we provide a type of an incorrect input.\n", "To complete our graph, we now have to define an output.\n", "As an invariant, a well-formed graph has exactly one output.\n", "Although, this output can consist of multiple values, we will cover this in a later example." ] }, { "cell_type": "code", "execution_count": null, "id": "f75d9ba5", "metadata": {}, "outputs": [], "source": [ "graph.outputs(output)" ] }, { "cell_type": "markdown", "id": "7eeab853", "metadata": {}, "source": [ "Now that we have defined a workflow we can run the graph.\n", "The most generic way to do this is the `run_workflow` function which requires the following inputs:\n", "- The graph to run.\n", "- A dictionary providing the inputs to the graph. A single input will always have the key `\"value\"`. In this case we want to set it to `{\"value\": \"world!\"}`\n", "\n", "To make this reproducible we can define a `name` and a `run_id`.\n", "To make sure tierkreis can find our worker, we have to define the `registry_path`.\n", "Since it is a python worker we use uv to run it." ] }, { "cell_type": "code", "execution_count": null, "id": "20f07fa0", "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "from tierkreis.cli.run_workflow import run_workflow\n", "\n", "\n", "def main(input_value: str) -> None:\n", " run_workflow(\n", " graph.data,\n", " {\"value\": input_value},\n", " name=\"hello_world\",\n", " run_id=100, # Assign a fixed uuid for our workflow.\n", " registry_path=Path().parent\n", " / \"example_workers\", # Look for workers in the `example_workers` directory.\n", " use_uv_executor=True,\n", " print_output=True,\n", " )" ] }, { "cell_type": "markdown", "id": "4488c5ea", "metadata": {}, "source": [ "Finally we can run the code, which will print the desired output `Hello world!`." ] }, { "cell_type": "code", "execution_count": null, "id": "2689da35", "metadata": {}, "outputs": [], "source": [ "if __name__ == \"__main__\":\n", " main(\"world!\")" ] }, { "cell_type": "markdown", "id": "84ab8e63", "metadata": {}, "source": [ "## On tierkreis types\n", "\n", "Tierkreis values correspond to the edges in the graph.\n", "These values can have a type assigned to them at construction time.\n", "The set of available types is a subset of all python types, e.g. we require serialization; see more in [complex types](../worker/complex_types.md) how to add your own serialization.\n", "As result we use the `TKR` container to promote the python types into tierkreis compatible types." ] } ], "metadata": { "kernelspec": { "display_name": "tierkreis (3.12.10)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.10" } }, "nbformat": 4, "nbformat_minor": 5 }