mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 15:00:57 +03:00
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:
@@ -0,0 +1,199 @@
|
||||
import { START } from '@langchain/langgraph'
|
||||
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeParams, ISeqAgentNode } from '../../../src/Interface'
|
||||
import { availableDependencies, defaultAllowBuiltInDep, getVars, prepareSandboxVars } from '../../../src/utils'
|
||||
import { NodeVM } from 'vm2'
|
||||
import { DataSource } from 'typeorm'
|
||||
|
||||
const defaultFunc = `{
|
||||
aggregate: {
|
||||
value: (x, y) => x.concat(y), // here we append the new message to the existing messages
|
||||
default: () => []
|
||||
}
|
||||
}`
|
||||
|
||||
const howToUse = `
|
||||
Specify the Key, Operation Type, and Default Value for the state object. The Operation Type can be either "Replace" or "Append".
|
||||
|
||||
**Replace**
|
||||
- Replace the existing value with the new value.
|
||||
- If the new value is null, the existing value will be retained.
|
||||
|
||||
**Append**
|
||||
- Append the new value to the existing value.
|
||||
- Default value can be empty or an array. Ex: ["a", "b"]
|
||||
- Final value is an array.
|
||||
`
|
||||
const TAB_IDENTIFIER = 'selectedStateTab'
|
||||
|
||||
class State_SeqAgents implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
description: string
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
baseClasses: string[]
|
||||
credential: INodeParams
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'State'
|
||||
this.name = 'seqState'
|
||||
this.version = 1.0
|
||||
this.type = 'State'
|
||||
this.icon = 'state.svg'
|
||||
this.category = 'Sequential Agents'
|
||||
this.description = 'A centralized state object, updated by nodes in the graph, passing from one node to another'
|
||||
this.baseClasses = [this.type]
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'State',
|
||||
name: 'stateMemory',
|
||||
type: 'tabs',
|
||||
tabIdentifier: TAB_IDENTIFIER,
|
||||
additionalParams: true,
|
||||
default: 'stateMemoryUI',
|
||||
tabs: [
|
||||
{
|
||||
label: 'State (Table)',
|
||||
name: 'stateMemoryUI',
|
||||
type: 'datagrid',
|
||||
description:
|
||||
'Structure for state. By default, state contains "messages" that got updated with each message sent and received.',
|
||||
hint: {
|
||||
label: 'How to use',
|
||||
value: howToUse
|
||||
},
|
||||
datagrid: [
|
||||
{ field: 'key', headerName: 'Key', editable: true },
|
||||
{
|
||||
field: 'type',
|
||||
headerName: 'Operation',
|
||||
type: 'singleSelect',
|
||||
valueOptions: ['Replace', 'Append'],
|
||||
editable: true
|
||||
},
|
||||
{ field: 'defaultValue', headerName: 'Default Value', flex: 1, editable: true }
|
||||
],
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'State (Code)',
|
||||
name: 'stateMemoryCode',
|
||||
type: 'code',
|
||||
description: `JSON object representing the state`,
|
||||
hideCodeExecute: true,
|
||||
codeExample: defaultFunc,
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData, input: string, options: ICommonObject): Promise<any> {
|
||||
const tabIdentifier = nodeData.inputs?.[`${TAB_IDENTIFIER}_${nodeData.id}`] as string
|
||||
const stateMemoryUI = nodeData.inputs?.stateMemoryUI as string
|
||||
const stateMemoryCode = nodeData.inputs?.stateMemoryCode as string
|
||||
const appDataSource = options.appDataSource as DataSource
|
||||
const databaseEntities = options.databaseEntities as IDatabaseEntity
|
||||
const selectedTab = tabIdentifier ? tabIdentifier.split(`_${nodeData.id}`)[0] : 'stateMemoryUI'
|
||||
|
||||
if (!stateMemoryUI && !stateMemoryCode) {
|
||||
const returnOutput: ISeqAgentNode = {
|
||||
id: nodeData.id,
|
||||
node: {},
|
||||
name: 'state',
|
||||
label: 'state',
|
||||
type: 'state',
|
||||
output: START
|
||||
}
|
||||
return returnOutput
|
||||
}
|
||||
|
||||
if (selectedTab === 'stateMemoryUI' && stateMemoryUI) {
|
||||
try {
|
||||
const parsedSchema = typeof stateMemoryUI === 'string' ? JSON.parse(stateMemoryUI) : stateMemoryUI
|
||||
const obj: ICommonObject = {}
|
||||
for (const sch of parsedSchema) {
|
||||
const key = sch.key
|
||||
if (!key) throw new Error(`Key is required`)
|
||||
const type = sch.type
|
||||
const defaultValue = sch.defaultValue
|
||||
|
||||
if (type === 'Append') {
|
||||
obj[key] = {
|
||||
value: (x: any, y: any) => (Array.isArray(y) ? x.concat(y) : x.concat([y])),
|
||||
default: () => (defaultValue ? JSON.parse(defaultValue) : [])
|
||||
}
|
||||
} else {
|
||||
obj[key] = {
|
||||
value: (x: any, y: any) => y ?? x,
|
||||
default: () => defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
const returnOutput: ISeqAgentNode = {
|
||||
id: nodeData.id,
|
||||
node: obj,
|
||||
name: 'state',
|
||||
label: 'state',
|
||||
type: 'state',
|
||||
output: START
|
||||
}
|
||||
return returnOutput
|
||||
} catch (e) {
|
||||
throw new Error(e)
|
||||
}
|
||||
} else if (selectedTab === 'stateMemoryCode' && stateMemoryCode) {
|
||||
const variables = await getVars(appDataSource, databaseEntities, nodeData)
|
||||
const flow = {
|
||||
chatflowId: options.chatflowid,
|
||||
sessionId: options.sessionId,
|
||||
chatId: options.chatId,
|
||||
input
|
||||
}
|
||||
|
||||
let sandbox: any = {}
|
||||
sandbox['$vars'] = prepareSandboxVars(variables)
|
||||
sandbox['$flow'] = flow
|
||||
|
||||
const builtinDeps = process.env.TOOL_FUNCTION_BUILTIN_DEP
|
||||
? defaultAllowBuiltInDep.concat(process.env.TOOL_FUNCTION_BUILTIN_DEP.split(','))
|
||||
: defaultAllowBuiltInDep
|
||||
const externalDeps = process.env.TOOL_FUNCTION_EXTERNAL_DEP ? process.env.TOOL_FUNCTION_EXTERNAL_DEP.split(',') : []
|
||||
const deps = availableDependencies.concat(externalDeps)
|
||||
|
||||
const nodeVMOptions = {
|
||||
console: 'inherit',
|
||||
sandbox,
|
||||
require: {
|
||||
external: { modules: deps },
|
||||
builtin: builtinDeps
|
||||
}
|
||||
} as any
|
||||
|
||||
const vm = new NodeVM(nodeVMOptions)
|
||||
try {
|
||||
const response = await vm.run(`module.exports = async function() {return ${stateMemoryCode}}()`, __dirname)
|
||||
if (typeof response !== 'object') throw new Error('State must be an object')
|
||||
const returnOutput: ISeqAgentNode = {
|
||||
id: nodeData.id,
|
||||
node: response,
|
||||
name: 'state',
|
||||
label: 'state',
|
||||
type: 'state',
|
||||
output: START
|
||||
}
|
||||
return returnOutput
|
||||
} catch (e) {
|
||||
throw new Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: State_SeqAgents }
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-device-sd-card"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M7 21h10a2 2 0 0 0 2 -2v-14a2 2 0 0 0 -2 -2h-6.172a2 2 0 0 0 -1.414 .586l-3.828 3.828a2 2 0 0 0 -.586 1.414v10.172a2 2 0 0 0 2 2z" /><path d="M13 6v2" /><path d="M16 6v2" /><path d="M10 7v1" /></svg>
|
||||
|
After Width: | Height: | Size: 523 B |
Reference in New Issue
Block a user