Feature/seq agents (#2798)

* update build functions

* sequential agents

* update langchain to 0.2, added sequential agent nodes

* add marketplace templates

* update howto wordings

* Merge branch 'main' into feature/Seq-Agents

# Conflicts:
#	pnpm-lock.yaml

* update deprecated functions and add new sequential nodes

* add marketplace templates

* update marketplace templates, add structured output to llm node

* add multi agents template

* update llm node with bindmodels

* update cypress version

* update templates sticky note wordings

* update tool node to include human in loop action

* update structured outputs error from models

* update cohere package to resolve google genai pipeThrough bug

* update mistral package version, added message reconstruction before invoke seq agent

* add HITL to agent

* update state messages restructuring

* update load and split methods for s3 directory
This commit is contained in:
Henry Heng
2024-07-22 17:46:14 +01:00
committed by GitHub
parent 34d0e4302c
commit bca4de0c63
152 changed files with 55307 additions and 35236 deletions
+156 -9
View File
@@ -1,4 +1,5 @@
import moment from 'moment'
import { uniq } from 'lodash'
export const getUniqueNodeId = (nodeData, nodes) => {
// Get amount of same nodes
@@ -52,7 +53,9 @@ export const initNode = (nodeData, newNodeId) => {
'code',
'date',
'file',
'folder'
'folder',
'tabs',
'conditionFunction' // This is a special type for condition functions
]
// Inputs
@@ -80,6 +83,7 @@ export const initNode = (nodeData, newNodeId) => {
// Outputs
const outputAnchors = []
for (let i = 0; i < outgoing; i += 1) {
if (nodeData.hideOutput) continue
if (nodeData.outputs && nodeData.outputs.length) {
const options = []
for (let j = 0; j < nodeData.outputs.length; j += 1) {
@@ -100,7 +104,9 @@ export const initNode = (nodeData, newNodeId) => {
name: nodeData.outputs[j].name,
label: nodeData.outputs[j].label,
description: nodeData.outputs[j].description ?? '',
type
type,
isAnchor: nodeData.outputs[j]?.isAnchor,
hidden: nodeData.outputs[j]?.hidden
}
options.push(newOutputOption)
}
@@ -404,14 +410,43 @@ export const getAvailableNodesForVariable = (nodes, edges, target, targetHandle)
// example edge id = "llmChain_0-llmChain_0-output-outputPrediction-string|json-llmChain_1-llmChain_1-input-promptValues-string"
// {source} -{sourceHandle} -{target} -{targetHandle}
const parentNodes = []
const inputEdges = edges.filter((edg) => edg.target === target && edg.targetHandle === targetHandle)
if (inputEdges && inputEdges.length) {
for (let j = 0; j < inputEdges.length; j += 1) {
const node = nodes.find((nd) => nd.id === inputEdges[j].source)
parentNodes.push(node)
}
const isSeqAgent = nodes.find((nd) => nd.id === target)?.data?.category === 'Sequential Agents'
function collectParentNodes(targetNodeId, nodes, edges) {
const inputEdges = edges.filter(
(edg) => edg.target === targetNodeId && edg.targetHandle.includes(`${targetNodeId}-input-sequentialNode`)
)
// Traverse each edge found
inputEdges.forEach((edge) => {
const parentNode = nodes.find((nd) => nd.id === edge.source)
if (!parentNode) return
// Recursive call to explore further up the tree
collectParentNodes(parentNode.id, nodes, edges)
// Check and add the parent node to the list if it does not include specific names
const excludeNodeNames = ['seqAgent', 'seqLLMNode', 'seqToolNode']
if (excludeNodeNames.includes(parentNode.data.name)) {
parentNodes.push(parentNode)
}
})
}
if (isSeqAgent) {
collectParentNodes(target, nodes, edges)
return uniq(parentNodes)
} else {
const inputEdges = edges.filter((edg) => edg.target === target && edg.targetHandle === targetHandle)
if (inputEdges && inputEdges.length) {
for (let j = 0; j < inputEdges.length; j += 1) {
const node = nodes.find((nd) => nd.id === inputEdges[j].source)
parentNodes.push(node)
}
}
return parentNodes
}
return parentNodes
}
export const getUpsertDetails = (nodes, edges) => {
@@ -783,3 +818,115 @@ export const kFormatter = (num) => {
const item = lookup.findLast((item) => num >= item.value)
return item ? (num / item.value).toFixed(1).replace(regexp, '').concat(item.symbol) : '0'
}
const toCamelCase = (str) => {
return str
.split(' ') // Split by space to process each word
.map((word, index) => (index === 0 ? word.toLowerCase() : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()))
.join('') // Join the words back into a single string
}
const createJsonArray = (labels) => {
return labels.map((label) => {
return {
label: label,
name: toCamelCase(label),
baseClasses: ['Agent', 'LLMNode', 'ToolNode'],
isAnchor: true
}
})
}
export const getCustomConditionOutputs = (value, nodeId, existingEdges, isDataGrid) => {
// Regex to find return statements and capture returned values
const regex = /return\s+(['"`])(.*?)\1/g
let match
const numberOfReturns = []
if (!isDataGrid) {
// Loop over the matches of the regex
while ((match = regex.exec(value)) !== null) {
// Push the captured group, which is the actual return value, into results
numberOfReturns.push(match[2])
}
} else {
try {
const parsedValue = JSON.parse(value)
if (parsedValue && parsedValue.length) {
for (const item of parsedValue) {
if (!item.variable) {
alert('Please specify a Variable. Try connecting Condition node to a previous node and select the variable')
return undefined
}
if (!item.output) {
alert('Please specify an Output Name')
return undefined
}
if (!item.operation) {
alert('Please select an operation for the condition')
return undefined
}
numberOfReturns.push(item.output)
}
numberOfReturns.push('End')
}
} catch (e) {
console.error('Error parsing JSON', e)
}
}
if (numberOfReturns.length === 0) {
if (isDataGrid) alert('Please add an item for the condition')
else
alert(
'Please add a return statement in the condition code to define the output. You can refer to How to Use for more information.'
)
return undefined
}
const outputs = createJsonArray(numberOfReturns.sort())
const outputAnchors = []
const options = []
for (let j = 0; j < outputs.length; j += 1) {
let baseClasses = ''
let type = ''
const outputBaseClasses = outputs[j].baseClasses ?? []
if (outputBaseClasses.length > 1) {
baseClasses = outputBaseClasses.join('|')
type = outputBaseClasses.join(' | ')
} else if (outputBaseClasses.length === 1) {
baseClasses = outputBaseClasses[0]
type = outputBaseClasses[0]
}
const newOutputOption = {
id: `${nodeId}-output-${outputs[j].name}-${baseClasses}`,
name: outputs[j].name,
label: outputs[j].label,
type,
isAnchor: outputs[j]?.isAnchor
}
options.push(newOutputOption)
}
const newOutput = {
name: 'output',
label: 'Output',
type: 'options',
options
}
outputAnchors.push(newOutput)
// Remove edges
let newEdgeSourceHandles = []
for (const anchor of options) {
const anchorId = anchor.id
newEdgeSourceHandles.push(anchorId)
}
const toBeRemovedEdgeIds = existingEdges.filter((edge) => !newEdgeSourceHandles.includes(edge.sourceHandle)).map((edge) => edge.id)
return { outputAnchors, toBeRemovedEdgeIds }
}