> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mainly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Thinking with Nodes

> MainlyAI is a node-based platform for building AI workflows. What does this mean?

export const Node = () => {
  const [hovering, setHovering] = useState("");
  return <>
            <p>It is defined using regular-ish Python code by placing decorators on functions. "Inputs" to the nodes are defined using <code onMouseEnter={() => setHovering("[7]")} onMouseLeave={() => setHovering("")}>@wob.receiver</code> and "outputs" using <code onMouseEnter={() => setHovering("[11]")} onMouseLeave={() => setHovering("")}>@wob.transmitter</code>.</p>
            <div className="w-full flex items-center flex-col gap-8 pt-8 bg-gray-50 dark:bg-gray-900 p-4 rounded-xl">
                <div className="wob-node" style={{
    width: 230
  }} onMouseLeave={() => setHovering("")}>
                    <div className="node-header expanded">
                        <div className="has-detail cursor-grab whitespace-nowrap h-6 w-6 flex items-center justify-center">
                            <svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg" style={{
    fontSize: "1.2rem",
    cursor: "grab"
  }}><path stroke-linecap="round" stroke-linejoin="round" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"></path></svg>
                        </div>
                        <input className="unstyled mb-px w-full overflow-hidden text-ellipsis bg-transparent focus:outline-none" value="Node" />
                        <div className="flex-1"></div>
                        <button type="button" className="has-detail detail-left">
                            <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" className="lucide lucide-chevron-down"><path d="m6 9 6 6 6-6"></path></svg>
                        </button>
                    </div>
                    <div className="node-content">
                        <div className="node-row-wrapper" onMouseEnter={() => setHovering("[7,8,9]")}>
                            <div className="node-row dark:text-gray-100">
                                <div className="react-flow__handle react-flow__handle-left nodrag nopan target connectable connectablestart connectableend connectionindicator" style={{
    background: "rgb(238, 66, 102)"
  }}></div>
                                <span className="flex-1" style={{
    fontWeight: hovering.includes("7") ? "bold" : "normal"
  }}>input</span>
                            </div>
                        </div>
                        <div className="node-row-wrapper" onMouseEnter={() => setHovering("[11,12,13]")}>
                            <div className="node-row dark:text-gray-100">
                                <span className="flex-1" style={{
    fontWeight: hovering.includes("11") ? "bold" : "normal"
  }}>output</span>
                                <div className="react-flow__handle react-flow__handle-right nodrag nopan source connectable connectablestart connectableend connectionindicator" style={{
    background: "rgb(238, 66, 102)"
  }}></div>
                            </div>
                        </div>
                    </div>
                </div>

                <CodeBlock className="flex-1 w-full mx-0 my-0" highlight={hovering} language="python">{`
from mirmod import miranda

@wob.init()
def init(self):
    self.value = None

@wob.receiver("value","input")
def receive_value(self, value):
    self.value = value

@wob.transmitter("value", "output")
def transmit_value(self):
    return self.value

@wob.execute()
async def execute(self):
    print(self.value)`}
                </CodeBlock>
            </div>
        </>;
};

## This is a node

*Also known as a Workflow Object, or WOB for short.*

<Node />

## Nodes are connected by Edges

A transmitter can be connected to multiple receivers, but a receiver can only be connected to one transmitter.

<video src="https://mintcdn.com/mainlyai/5I0niqUGYQkLSnXv/assets/thinking_with_nodes/connecting.mp4?fit=max&auto=format&n=5I0niqUGYQkLSnXv&q=85&s=b4b4b33e5a88a81046dc8a5755693b84" muted loop autoPlay playsInline className="rounded-lg" data-path="assets/thinking_with_nodes/connecting.mp4" />

## Edges move data between nodes

Edges move data from transmitters to receivers. Nodes are ran sequentially depth-first.

<video src="https://mintcdn.com/mainlyai/5I0niqUGYQkLSnXv/assets/thinking_with_nodes/pancakes.mp4?fit=max&auto=format&n=5I0niqUGYQkLSnXv&q=85&s=2c2624f47991870e5107701d2f5964f0" muted loop autoPlay playsInline className="rounded-lg" data-path="assets/thinking_with_nodes/pancakes.mp4" />

## Edges care about types

Transmitters and receivers can have different types. Edges can only connect transmitters and receivers of the same type. Each type has a different color.

* **<span style={{ color: "#ee4266" }}>value</span>** - individual values, such as strings, numbers, and vectors.
* **<span style={{ color: "#4361ee" }}>data</span>** - collections of values, such as tables, datasets, and tensors.
* **<span style={{ color: "#0ead69" }}>state</span>** - JSON objects and classes, such as LLM messages and HTTP requests.
* **<span style={{ color: "#ad41d5" }}>model</span>** - functions/functors that can be called by nodes later

<video src="https://mintcdn.com/mainlyai/5I0niqUGYQkLSnXv/assets/thinking_with_nodes/types.mp4?fit=max&auto=format&n=5I0niqUGYQkLSnXv&q=85&s=864f7dde4e9070f0fcfc9c0ed4309262" muted loop autoPlay playsInline className="rounded-lg" data-path="assets/thinking_with_nodes/types.mp4" />

## Receivers can have controls

Controls are configurable input and display elements for nodes. They can be added onto receivers using the `control` argument. If an edge is connected to a receiver with a control, the control will disappear and the value will be taken from the edge instead.

[Read more about controls](/core_concepts/controls).

```python theme={null}
from mirmod.controls import Textbox

@wob.receiver("value", "input", control=Textbox())
def receive_value(self, value):
    self.value = value
```

<video src="https://mintcdn.com/mainlyai/5I0niqUGYQkLSnXv/assets/thinking_with_nodes/controls.mp4?fit=max&auto=format&n=5I0niqUGYQkLSnXv&q=85&s=6c7048b6ae7ba115646344bbdcd9f2a1" muted loop autoPlay playsInline className="rounded-lg" data-path="assets/thinking_with_nodes/controls.mp4" />

## Fields let nodes run in loops

They are defined by a pair of receivers and transmitters that are both marked as `is_field=True`. Visually, you can tell that a transmitter or receiver is a field transmitter/receiver by their pointier shape. Nodes within a field are continously run until the field transmitter runs out of values to transmit.

[Read more about fields](/core_concepts/fields).

<video src="https://mintcdn.com/mainlyai/5I0niqUGYQkLSnXv/assets/thinking_with_nodes/fields.mp4?fit=max&auto=format&n=5I0niqUGYQkLSnXv&q=85&s=cb6a8148b2421411bcf4704d5a7ee105" muted loop autoPlay playsInline className="rounded-lg" data-path="assets/thinking_with_nodes/fields.mp4" />
