Гібрид - Rete.js

Гібрид

Щоб короткого огляду концепції поєднання Dataflow і Control flow, рекомендуємо прочитати статтю Гібрид

ГібридЧат-ботПлагінDataflowControl flow

Встановити залежності

bash
npm i rete rete-engine

Підготовка вузлів

Для належної роботи всі класи вузлів повинні реалізовувати метод execute для керування потоком та метод data для потоку даних.

Клас Start призначений для передачі керування та має стандартний метод data, який повертає порожній об'єкт.

ts
const socket = new ClassicPreset.Socket("socket");

class Start extends ClassicPreset.Node {
  constructor() {
    super("Start");
    this.addOutput("exec", new ClassicPreset.Output(socket, "Exec"));
  }

  execute(_: never, forward: (output: "exec") => void) {
    forward("exec");
  }

  data() {
    return {};
  }
}

Разом із отриманням керування, клас Log також може запитувати дані від вхідних вузлів через порт message за допомогою методу fetchInputs екземпляра DataflowEngine.

ts
class Log extends ClassicPreset.Node {
  constructor() {
    super("Log");

    this.addInput("exec", new ClassicPreset.Input(socket, "Exec", true));
    this.addInput("message", new ClassicPreset.Input(socket, "Text"));
    this.addOutput("exec", new ClassicPreset.Output(socket, "Exec"));
  }

  async execute(input: "exec", forward: (output: "exec") => void) {
    const inputs = (await dataflow.fetchInputs(this.id)) as {
      message: string[];
    };

    console.log((inputs.message && inputs.message[0]) || "");

    forward("exec");
  }

  data() {
    return {};
  }
}

Клас TextNode відповідає лише за надання даних і не може приймати чи передавати керування.

ts
class TextNode extends ClassicPreset.Node {
  constructor(private text: string) {
    super("Text");
    this.addOutput("value", new ClassicPreset.Output(socket, "Number"));
  }

  execute() {}

  data(): { value: string } {
    return {
      value: this.text
    };
  }
}

class Connection<A extends NodeProps, B extends NodeProps> extends ClassicPreset.Connection<A, B> {}

type NodeProps = Start | Log | TextNode;
type ConnProps = Connection<Start, Log> | Connection<TextNode, Log>;
type Schemes = GetSchemes<NodeProps, ConnProps>;

Підключення

ts
import { NodeEditor } from "rete";
import { DataflowEngine, ControlFlowEngine } from "rete-engine";

const editor = new NodeEditor<Schemes>();
const dataflow = new DataflowEngine<Schemes>(({ inputs, outputs }) => {
  return {
    inputs: () => Object.keys(inputs).filter((name) => name !== "exec"),
    outputs: () => Object.keys(outputs).filter((name) => name !== "exec")
  };
});
const controlflow = new ControlFlowEngine<Schemes>(() => {
  return {
    inputs: () => ["exec"],
    outputs: () => ["exec"]
  };
});

editor.use(dataflow);
editor.use(controlflow);

Додавання вузлів та з’єднань

ts
const start = new Start();
const text1 = new TextNode("log");
const log1 = new Log();

const con1 = new Connection(start, "exec", log1, "exec");
const con2 = new Connection(text1, "value", log1, "message");

await editor.addNode(start);
await editor.addNode(text1);
await editor.addNode(log1);

await editor.addConnection(con1);
await editor.addConnection(con2);

Виконання

Вузол start служить початковою точкою для виконання графа.

ts
engine.execute(start.id);

У результаті вузол start передає керування вузлу log1, який отримує дані з вузла text1 за допомогою методу fetchInputs.

Перегляньте повний результат на сторінці прикладу Гібрид. Крім того, ви можете дослідити інший більш складний приклад Чат-бота, який використовує цей підхід.