sixty-nine/neuron-graph

Finite State Machine for AI

1.0.0 2025-05-15 20:43 UTC

This package is not auto-updated.

Last update: 2025-05-16 20:20:49 UTC


README

Table of Contents

  1. Overview
  2. Installation
  3. Key Features
  4. Components
  5. Simple Graph Example
  6. Observability
  7. Export to Mermaid
  8. More Examples
  9. Contribution Guidelines
  10. License

Overview

Neuron Graph is a Finite State Machine (FSM) inspired by Pydantic Graphs.

It allows you to create "nodes" that perform single tasks and chain them together to achieve more complex workflows.

Key Features

  • Modular Design: Build reusable and composable nodes.
  • Finite State Machine: Define and execute directed graphs of nodes.
  • Observability: Log and monitor graph execution events.
  • Integration Ready: Easily integrate with external systems or libraries.

Installation

To install Neuron Graph, use Composer:

composer require sixty-nine/neuron-graph

Components

Graph

The Graph class defines and executes the Finite State Machine. It manages the nodes and their connections.

Key Points:

  • The run method takes an instance of the initial node and executes the graph.
  • Every graph must have at least one node that returns an EndNode.
  • There must be a path from every node to the EndNode.

Example:

$graph = new Graph([FirstNode::class, NodeLeadingToEnd::class]);
$result = $graph->run(new FirstNode());

Nodes

A Node is a class that extends the abstract Node class. It represents a single task in the graph.

Key Points:

  • The run method defines the node's behavior.
  • It can receive a shared state and static data.
  • The run method must return other nodes or the special EndNode.
  • The return type-hints of the run method define the graph's edges.

Example:

class MyNode extends Node
{
    public function run($state, $data): MyOtherNode
    {
        return new MyOtherNode();
    }
}

EndNode

The EndNode is a special type of node that marks the end of the graph execution.

Key Points:

  • It can hold arbitrary data set by the previous node.
  • Every graph must have at least one node that returns an EndNode.

Example:

class FinalNode extends Node
{
    public function run($state, $data): EndNode
    {
        return new EndNode([
            'some-data' => 'whatever',
        ]);
    }
}

GraphState

The GraphState is a mutable key/value store that holds the shared state of the graph during execution.

Key Points:

  • The initial state is passed to the Graph::run method.
  • The state is shared across all nodes in the graph.
  • Nodes can read and mutate the state as needed.

Example:

$initialState = new GraphState([
    'counter' => 0,
]);

$graph->run(new FirstNode(), $initialState);

GraphResult

The GraphResult represents the outcome of the graph execution.

Key Points:

  • It is returned by the Graph::run method.
  • It contains:
    • The final mutated state after all nodes have executed.
    • The data passed to the EndNode.

Example:

$result = $graph->run(new FirstNode(), $initialState);

$finalState = $result->state; // Access the final state
$endNodeData = $result->data; // Access data from the EndNode

Simple graph example

This example demonstrates how to create and execute a simple graph using Neuron Graph.

Diagram

flowchart LR;
   Increment --> Check42;
   Check42([Check42]) --> Increment & EndNode;

Code

<?php

use Sixtynine\NeuronGraph\Graph\Graph;
use Sixtynine\NeuronGraph\Graph\GraphState;
use Sixtynine\NeuronGraph\Nodes\Node;
use Sixtynine\NeuronGraph\Nodes\EndNode;

class Increment extends Node
{
    public function __construct(
        private readonly int $number
    ) {
    }

    public function run(GraphState $state): Check42
    {
        return new Check42($this->number + 1);
    }
}

class Check42 extends Node
{
    public function __construct(
        private readonly int $number
    ) {
    }

    public function run(GraphState $state): Increment|EndNode
    {
        if ($this->number !== 42) {
            return new Increment($this->number);
        }

        return new EndNode($this->number);
    }
}


$graph = new Graph([Increment::class, Check42::class]);

$result = $graph->run(new Check42(2));

echo $result->data; // --> 42

Observability

Neuron Graph supports observability by allowing you to attach observers to monitor and log graph execution events.

Using and observer

A SplObserver can be passed to the graph to log its events. For example, you can use the provided LogObserver to send events to a PSR-compatible logger.

Example:

// Create a PSR-compatible logger
$logger = new Logger('my_logger', [
    new TestHandler(),
]);

// Create a LogObserver
$observer = new LogObserver($logger);

// Create and execute the graph with the observer
$graph = new Graph([Node1::class, /* ... */,  NodeN::class]);
$result = $graph->observe($observer)->run(new Node1());

Logged events

The LogObserver logs the following events during graph execution:

  • graph-start - triggered at the start of the execution of the graph.
  • graph-end - triggered when the execution of the graph is finished.
  • graph-node-start - triggered before the execution of a node of the graph.
  • graph-node-end - triggered when the execution of a node finishes.

Custom Observers

You can also create your own observer by implementing the SplObserver interface. This allows you to customize how graph events are handled and logged.

Example:
<?php
use SplObserver;
use SplSubject;

class CustomObserver implements SplObserver
{
    public function update(SplSubject $subject): void
    {
        // Handle the graph event
        echo "Event triggered: " . $subject->getEventName();
    }
}

Export to Mermaid

Neuron Graph allows you to export your graph to the Mermaid format, which can be used to visualize the graph as a diagram.

Example:

$graph = new Graph([Increment::class, Check42::class]);

echo $graph->toMermaid();

Output Example:

graph TD
    Increment --> Check42
    Check42 --> Increment & EndNode

How to Use:

1) Copy the output from the toMermaid method. 2) Paste it into the Mermaid Live Editor or embed it in your documentation.

More examples

Explore additional examples to see how Neuron Graph can be used in various scenarios:

  • Stateful Graph Example:
    A vending machine simulation demonstrating how to use the state.
    View Example

  • Data Transformation Pipeline Example:
    This example demonstrate how to subclass the Graph class to create custom, predefined graphs.
    View Example

  • Integration with NeuronAI - Chain of Thought:
    An example showcasing how Neuron Graph integrates with NeuronAI to implement a chain-of-thought reasoning process.
    View Example

  • Integration with NeuronAI - Cadavre Exquis Generator:
    A creative example that uses Neuron Graph and NeuronAI to generate a "cadavre exquis" (a collaborative storytelling game).
    View Example

Contribution Guidelines

We welcome contributions to Neuron Graph! Whether it's fixing bugs, adding new features, improving documentation, or providing feedback, your help is greatly appreciated.

Sumitting a Pull Request

To ensure a smooth contribution process, please follow these steps:

  1. Follow Conventional Commits
    Commit messages in this repository adhere to the Conventional Commits Guidelines.
    Ensure your commit messages follow this format for consistency and clarity.

  2. Write Tests
    Ensure your code is well-tested by writing unit tests for any new features or bug fixes. Cover edge cases and ensure your tests validate the expected behavior.

  3. Run Tests
    Before submitting your pull request, make sure all tests pass. You can run the tests using:

    composer run tests
    
  4. Run PHPStan
    Ensure that PHPStan does not report any issues. Run PHPStan with:

    composer run phpstan
    
  5. Open a Pull Request
    Once your changes are ready, open a pull request with a clear description of the problem you're solving or the feature you're adding. Include any relevant details or context.

License

This project is licensed under the MIT License.
You are free to use, modify, and distribute this software in accordance with the terms of the license.

For more details, see the LICENSE file.