add fixes to bugs where nodes are triggered multiple times

This commit is contained in:
Henry 2023-04-14 22:55:52 +01:00
parent 140b3cd2a2
commit 7e3c8f268f
3 changed files with 88 additions and 18 deletions

View File

@ -95,6 +95,10 @@ export interface INodeQueue {
depth: number
}
export interface IDepthQueue {
[key: string]: number
}
export interface IMessage {
message: string
type: MessageType

View File

@ -5,7 +5,7 @@ import http from 'http'
import * as fs from 'fs'
import { IChatFlow, IncomingInput, IReactFlowNode, IReactFlowObject } from './Interface'
import { getNodeModulesPackagePath, getStartingNode, buildLangchain, getEndingNode, constructGraphs } from './utils'
import { getNodeModulesPackagePath, getStartingNodes, buildLangchain, getEndingNode, constructGraphs } from './utils'
import { cloneDeep } from 'lodash'
import { getDataSource } from './DataSource'
import { NodesPool } from './NodesPool'
@ -209,13 +209,26 @@ export class App {
const flowData = chatflow.flowData
const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
/*** Get Ending Node with Directed Graph ***/
const { graph, nodeDependencies } = constructGraphs(parsedFlowData.nodes, parsedFlowData.edges)
const directedGraph = graph
const endingNodeId = getEndingNode(nodeDependencies, directedGraph)
if (!endingNodeId) return res.status(500).send(`Ending node must be either a Chain or Agent`)
const startingNodeIds = getStartingNode(nodeDependencies)
const endingNodeId = getEndingNode(nodeDependencies, graph)
if (!endingNodeId) return res.status(500).send(`Ending node must be either Chain or Agent`)
/*** Get Starting Nodes with Non-Directed Graph ***/
const constructedObj = constructGraphs(parsedFlowData.nodes, parsedFlowData.edges, true)
const nonDirectedGraph = constructedObj.graph
const { startingNodeIds, depthQueue } = getStartingNodes(nonDirectedGraph, endingNodeId)
const reactFlowNodes = await buildLangchain(startingNodeIds, parsedFlowData.nodes, graph, this.nodesPool.componentNodes)
/*** BFS to traverse from Starting Nodes to Ending Node ***/
const reactFlowNodes = await buildLangchain(
startingNodeIds,
parsedFlowData.nodes,
graph,
depthQueue,
this.nodesPool.componentNodes
)
const nodeToExecute = reactFlowNodes.find((node: IReactFlowNode) => node.id === endingNodeId)
if (!nodeToExecute) return res.status(404).send(`Node ${endingNodeId} not found`)

View File

@ -2,6 +2,7 @@ import path from 'path'
import fs from 'fs'
import {
IComponentNodes,
IDepthQueue,
IExploredNode,
INodeDependencies,
INodeDirectedGraph,
@ -54,11 +55,12 @@ export const getNodeModulesPackagePath = (packageName: string): string => {
}
/**
* Construct directed graph and node dependencies score
* Construct graph and node dependencies score
* @param {IReactFlowNode[]} reactFlowNodes
* @param {IReactFlowEdge[]} reactFlowEdges
* @param {boolean} isNondirected
*/
export const constructGraphs = (reactFlowNodes: IReactFlowNode[], reactFlowEdges: IReactFlowEdge[]) => {
export const constructGraphs = (reactFlowNodes: IReactFlowNode[], reactFlowEdges: IReactFlowEdge[], isNondirected = false) => {
const nodeDependencies = {} as INodeDependencies
const graph = {} as INodeDirectedGraph
@ -77,6 +79,14 @@ export const constructGraphs = (reactFlowNodes: IReactFlowNode[], reactFlowEdges
} else {
graph[source] = [target]
}
if (isNondirected) {
if (Object.prototype.hasOwnProperty.call(graph, target)) {
graph[target].push(source)
} else {
graph[target] = [source]
}
}
nodeDependencies[target] += 1
}
@ -84,18 +94,52 @@ export const constructGraphs = (reactFlowNodes: IReactFlowNode[], reactFlowEdges
}
/**
* Get starting node and check if flow is valid
* @param {INodeDependencies} nodeDependencies
* Get starting nodes and check if flow is valid
* @param {INodeDependencies} graph
* @param {string} endNodeId
*/
export const getStartingNode = (nodeDependencies: INodeDependencies) => {
// Find starting node
const startingNodeIds = [] as string[]
Object.keys(nodeDependencies).forEach((nodeId) => {
if (nodeDependencies[nodeId] === 0) {
startingNodeIds.push(nodeId)
export const getStartingNodes = (graph: INodeDirectedGraph, endNodeId: string) => {
const visited = new Set<string>()
const queue: Array<[string, number]> = [[endNodeId, 0]]
const depthQueue: IDepthQueue = {
[endNodeId]: 0
}
let maxDepth = 0
let startingNodeIds: string[] = []
while (queue.length > 0) {
const [currentNode, depth] = queue.shift()!
if (visited.has(currentNode)) {
continue
}
})
return startingNodeIds
visited.add(currentNode)
if (depth > maxDepth) {
maxDepth = depth
startingNodeIds = [currentNode]
} else if (depth === maxDepth) {
startingNodeIds.push(currentNode)
}
for (const neighbor of graph[currentNode]) {
if (!visited.has(neighbor)) {
queue.push([neighbor, depth + 1])
depthQueue[neighbor] = depth + 1
}
}
}
const depthQueueReversed: IDepthQueue = {}
for (const nodeId in depthQueue) {
if (Object.prototype.hasOwnProperty.call(depthQueue, nodeId)) {
depthQueueReversed[nodeId] = Math.abs(depthQueue[nodeId] - maxDepth)
}
}
return { startingNodeIds, depthQueue: depthQueueReversed }
}
/**
@ -104,7 +148,6 @@ export const getStartingNode = (nodeDependencies: INodeDependencies) => {
* @param {INodeDirectedGraph} graph
*/
export const getEndingNode = (nodeDependencies: INodeDependencies, graph: INodeDirectedGraph) => {
// Find ending node
let endingNodeId = ''
Object.keys(graph).forEach((nodeId) => {
if (Object.keys(nodeDependencies).length === 1) {
@ -121,12 +164,14 @@ export const getEndingNode = (nodeDependencies: INodeDependencies, graph: INodeD
* @param {string} startingNodeId
* @param {IReactFlowNode[]} reactFlowNodes
* @param {INodeDirectedGraph} graph
* @param {IDepthQueue} depthQueue
* @param {IComponentNodes} componentNodes
*/
export const buildLangchain = async (
startingNodeIds: string[],
reactFlowNodes: IReactFlowNode[],
graph: INodeDirectedGraph,
depthQueue: IDepthQueue,
componentNodes: IComponentNodes
) => {
const flowNodes = cloneDeep(reactFlowNodes)
@ -166,6 +211,14 @@ export const buildLangchain = async (
const neighbourNodeIds = graph[nodeId]
const nextDepth = depth + 1
// Find other nodes that are on the same depth level
const sameDepthNodeIds = Object.keys(depthQueue).filter((key) => depthQueue[key] === nextDepth)
for (const id of sameDepthNodeIds) {
if (neighbourNodeIds.includes(id)) continue
neighbourNodeIds.push(id)
}
for (let i = 0; i < neighbourNodeIds.length; i += 1) {
const neighNodeId = neighbourNodeIds[i]