Customize your own Graph

One can easily customize their own Graph by creating a new package and defining their own Tasks, Sockets, Properties, Executors and Serializers. Additionally, one can also provide the method to run the graph using their own executors and database.

Package structure

Your package should follow the following structure similar to this:

<your_package_name>/
├── __init__.py
├── tasks/
│   ├── __init__.py
│   ├── test.py
│   ├── ...
├── properties/
│   ├── __init__.py
│   ├── test.py
│   ├── ...
├── serializers/
│   ├── __init__.py
│   ├── test.py
│   ├── ...
├── sockets/
│   ├── __init__.py
│   ├── test.py
│   ├── ...

Define your new Task, Socket and Property in the folders tasks, sockets and properties respectively. Define your executor, serialization methods in the folders executors and serialization. For example, in the tasks.test.py file, define your test Task class.

Serialization adapters

Serialization is mounted per-graph via Graph(serialization=...). The adapter is responsible for validating and serializing values for persistence, while the core graph remains framework-agnostic.

For per-socket control, override serialization on the socket itself:

def serialize_special(self, store: bool = False):
    return {"special": self._value}

task.inputs["x"].set_serializer(serialize_special)

When Task.to_dict(should_serialize=True) is called, inputs are collected through sockets, so socket-level serialization runs before the adapter fallback.

Entry point

In the pyproject.toml file of your packages, you should define the entry points for your tasks, sockets and properties. For example, if your package name is abc, you should define the entry points like this:

[project.entry-points."abc.task"]
"abc.add" = "abc.tasks.test:Add"
"abc.multiply" = "abc.tasks.test:Multiply"

[project.entry-points."abc.property"]
"abc.any" = "abc.properties.test:SocketAny"
"abc.int" = "abc.properties.test:SocketInt"

[project.entry-points."abc.socket"]
"abc.any" = "abc.sockets.test:SocketAny"
"abc.int" = "abc.sockets.test:SocketInt"

Type mapping for annotations

When you use Python type annotations on task inputs/outputs, node-graph maps the annotated types to socket identifiers. If a type is not in the mapping, it falls back to the generic node_graph.annotated socket and records the Python type in socket metadata (extras["py_type"]). This keeps links type-safe without requiring custom socket classes for every domain type.

You can extend the mapping via an entry point:

[project.entry-points."node_graph.type_mapping"]
"abc.type_mapping" = "abc.mapping:type_mapping"
# abc/mapping.py
from some_pkg import CustomType

type_mapping = {
    CustomType: "abc.custom_type",
}

Installation

Install your package locally:

pip instal -e .

or publish your package to pypi, and install it using pip.