chrisjpatty / flume

Extract logic from your apps with a user-friendly node editor powered by React.
https://flume.dev
MIT License
1.46k stars 147 forks source link

Model Decision Trees #29

Open PhilGarb opened 4 years ago

PhilGarb commented 4 years ago

Hi Chris,

first of all great work! I have been on the lookout for something like this for a couple of weeks and your implementation is by far the best I have found.

I have a question though. From my understanding the main purpose of the NodeEditor is to allow anyone to customize the business logic of an application to their needs. We are trying to build something with a similar aim. We want to give domain experts a tool to build decision trees to model their decision making process. The result should be an interactive questionnaire for end users. The business logic in this case would be the decision making logic.

While trying to implement something like this with flume I have found that the building of a decision tree is possible, because it is very similar to what flume should be used for. I had however difficulties when trying to make the building process dynamic. For example in our case the bet case would be for the ports to be addable and removable by the domain expert, because the ports would represent options to answer a question. While I could probably dynamically add ports during the runtime of the NodeEditor I see no way to integrate such a functionality into the current Node UI.

It would be great to get your insight into this approach. If flume can be the core of our application I would also be happy to help you out in development.

Cheers Philipp

chrisjpatty commented 4 years ago

@PhilGarb Thanks! This sounds like an intriguing use-case. If I'm understanding correctly, you'd want to leverage the node editor as more of a workflow modeling tool is that correct? Do you maybe have any wireframes or mockups of what you're envisioning so I can make sure I understand? Right now the node editor relies on the configuration not changing throughout the lifecycle of the editor, which probably isn't clearly noted enough in the docs. Each node also creates a skeleton data structure matching the schema of the inputs and outputs for that node when it is added to the editor. Allowing that schema to change ad hoc breaks some of the assumptions that the editor and root engine rely on. That being said, I'm not necessarily opposed to exploring making that possible if it can be done in a way that doesn't overcomplicate the schema builders.

PhilGarb commented 4 years ago

@chrisjpatty thank you for the quick reply. I think workflow modeling is a pretty good term for what we are trying to do. I believe, that I am breaking with the idea of flume when I am trying to model multiple answers as ports. Since we do not want to restrict the number of possible answers, but want to leave this open to the user. Because of this ports would need to be extendable. This also means, that the data coming into an input does not matter to us in these situations. We only care which path the user is taking. I attached a wireframe for our current idea of the nodeEditor. At the most basic level their are nodes (questions) and edges (answers) between them. We would then serialize the nodes and edges and use them to show an interactive form to users where they can get an answer to their question based on their input.

wireframe_nodeEditor

Your docs are actually pretty great. I could easily understand how to implement flume and how to configure it. My unsupported use case is the problem here :smile: This is what a current (very simple) implementation looks like for me right now: flume-implementation

The Dynamic port should not be connected to two nodes and their should instead be a second port leading to a different question. (Please don't mind the german). This is a link to the corresponding config file.

chrisjpatty commented 4 years ago

Ah, that makes sense. It seems like there's a few missing pieces to make this kind of workflow possible. I would be worried that allowing users to edit the ports of a node would be a difficult UI to intuitively use, but also breaks too many assumptions of the editor. But, that being said, I think this could still work if a couple features were added. Here's a graph to show what I mean:

Screen Shot 2020-08-14 at 11 56 24 AM

In this graph, I've modeled a question workflow, where there's two node types, questions and answers. Questions can only feed into answers, and answers can only feed into questions. This is a little different than adding additional ports to represent answers, instead, answer nodes represent those answers, and feed back into question nodes. This works great for the first question, but right now the node editor hides the controls for ports that are connected, so the textbox for writing in the answer, and subsequent questions are hidden.

So feature one: Ports should be configurable to not hide controls when connected.

The next issue is that currently, outputs may be connected to any number of inputs, but inputs may only be connected to one output. This works well for the main Flume use-case using the root engine, but not for this kind of workflow.

So feature two: Inputs should be configurable to accept any number of outputs.

Those two features I've already considered in the past, and adding them as options to turn on wouldn't break any existing assumptions. If you had those two features in place, do you think this would cover your use-case well enough? Obviously the root engine would not work for this type of configuration, and you would have to resolve your workflow graphs yourself, but it sounds like you're already planning on that.

PS I've created the above example in a code sandbox you can find here

chrisjpatty commented 4 years ago

Also, with upcoming theming capabilities, the goal would be to make it possible to restyle the editor any way you like, including restyling nodes specifically based on their type. That means your answer nodes could be more visually distinct from the question nodes similar to how you have it setup in your mockup.

PhilGarb commented 4 years ago

Wow! That is a really good idea. I didn't think of just using nodes as answers. My only gripe with this would be, that this might become quite cluttered, but you are right when nodes can be styled based on their type, answers could just be made smaller. The benefit of this would be, that we are actually planning on introducing logic based nodes as well. By making everything a node the application might become a lot more approachable.

The hiding of controls when a port is connected is actually an issue I had as well. I added input controls and hid the port (again kinda hacky). Accepting multiple outputs into one input would be quite important as well. Also the ability to constrain the number of edges from one output (an answer should only have one next question for example) would be very very helpful. I do think that with the features you mentioned and constraining the number of possible output edges I could model our use case.

In regards to resolving the graph you are right. We are planning to have an entirely separate frontend that consumes the data created by the nodeEditor. I must admit however, that so far I haven"t even considered resolving something automatically so we might explore something like this in the future.

If you like any help with these features or the theming implementation feel free to mention me in any issue you might create or for any task you think would be appropriate. I would just need to get into the codebase.

chrisjpatty commented 4 years ago

Awesome! Yeah I'd love to get your feedback on the theming issue, the goal would be to make this use-case possible. I'm going to add both of these features to the v1.0.0 board and try to get them added here shortly.

andraz-at commented 4 years ago

DefaultNodes should also include defaultConnections, else initial nodes won't be connected when initializing a graph.

https://github.com/chrisjpatty/flume/blob/master/docs/docs/NodeEditor.mdx#defaultnodes--arrayobject

chrisjpatty commented 4 years ago

@andraz-at That sounds like a possible enhancement, but an API for it would need to be defined. If you open a separate issue for default connections I'll get it added to the feature boards.

danlobo commented 2 years ago

I don't know the code and the possible side effects in depth, but controlling the new input and output connections in nodesReducer could solve it?

Something like below (nodesReducer.js, lines 227~250)

const nodesReducer = (
  nodes,
  action = {},
  { nodeTypes, portTypes, cache, circularBehavior, context, connectionMode = 'root' }, // root to maintain compatibility, but for this case, connectionMode must be "flow"
  dispatchToasts
) => {
  switch (action.type) {
    case "ADD_CONNECTION": {
      const { input, output } = action;

      // root     - inputs max 1, outputs unlimited
      // decision - inputs unlimited, outputs unlimited
      // flow     - inputs max unlimited, outputs max 1

      const inputIsNotConnected = connectionMode !== 'root' || (
                                   nodes[input.nodeId].connections.inputs[input.portName] == null ||
                                   nodes[input.nodeId].connections.inputs[input.portName].length === 0
      );

      const outputIsNotConnected = connectionMode !== 'flow' || (
                                   nodes[output.nodeId].connections.outputs[output.portName] == null ||
                                   nodes[output.nodeId].connections.outputs[output.portName].length === 0
      );
      if (inputIsNotConnected && outputIsNotConnected) {
//      ( ... )