diff --git a/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts b/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts index 26190de0..3641f0bd 100644 --- a/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts +++ b/packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts @@ -1,5 +1,5 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface' -import { initializeAgentExecutor, AgentExecutor } from 'langchain/agents' +import { initializeAgentExecutorWithOptions, AgentExecutor } from 'langchain/agents' import { Tool } from 'langchain/tools' import { BaseChatModel } from 'langchain/chat_models/base' import { BaseChatMemory } from 'langchain/memory' @@ -48,7 +48,10 @@ class ConversationalAgent_Agents implements INode { const tools = nodeData.inputs?.tools as Tool[] const memory = nodeData.inputs?.memory as BaseChatMemory - const executor = await initializeAgentExecutor(tools, model, 'chat-conversational-react-description', true) + const executor = await initializeAgentExecutorWithOptions(tools, model, { + agentType: 'chat-conversational-react-description', + verbose: true + }) executor.memory = memory return executor } diff --git a/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts b/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts index d74f0b32..1a619e6a 100644 --- a/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts +++ b/packages/components/nodes/agents/MRKLAgentChat/MRKLAgentChat.ts @@ -1,7 +1,8 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface' -import { initializeAgentExecutor, AgentExecutor, Tool } from 'langchain/agents' +import { initializeAgentExecutorWithOptions, AgentExecutor } from 'langchain/agents' import { BaseChatModel } from 'langchain/chat_models/base' import { getBaseClasses } from '../../../src/utils' +import { Tool } from 'langchain/tools' class MRKLAgentChat_Agents implements INode { label: string @@ -39,8 +40,10 @@ class MRKLAgentChat_Agents implements INode { async init(nodeData: INodeData): Promise { const model = nodeData.inputs?.model as BaseChatModel const tools = nodeData.inputs?.tools as Tool[] - - const executor = await initializeAgentExecutor(tools, model, 'chat-zero-shot-react-description', true) + const executor = await initializeAgentExecutorWithOptions(tools, model, { + agentType: 'chat-zero-shot-react-description', + verbose: true + }) return executor } diff --git a/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts b/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts index 4df6ea41..499b9383 100644 --- a/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts +++ b/packages/components/nodes/agents/MRKLAgentLLM/MRKLAgentLLM.ts @@ -1,5 +1,5 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface' -import { initializeAgentExecutor, AgentExecutor } from 'langchain/agents' +import { initializeAgentExecutorWithOptions, AgentExecutor } from 'langchain/agents' import { Tool } from 'langchain/tools' import { BaseLLM } from 'langchain/llms/base' import { getBaseClasses } from '../../../src/utils' @@ -41,7 +41,10 @@ class MRKLAgentLLM_Agents implements INode { const model = nodeData.inputs?.model as BaseLLM const tools = nodeData.inputs?.tools as Tool[] - const executor = await initializeAgentExecutor(tools, model, 'zero-shot-react-description', true) + const executor = await initializeAgentExecutorWithOptions(tools, model, { + agentType: 'zero-shot-react-description', + verbose: true + }) return executor } diff --git a/packages/components/nodes/chains/VectorDBQAChain/VectorDBQAChain.ts b/packages/components/nodes/chains/VectorDBQAChain/VectorDBQAChain.ts new file mode 100644 index 00000000..91b3fed1 --- /dev/null +++ b/packages/components/nodes/chains/VectorDBQAChain/VectorDBQAChain.ts @@ -0,0 +1,57 @@ +import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { getBaseClasses } from '../../../src/utils' +import { VectorDBQAChain } from 'langchain/chains' +import { BaseLanguageModel } from 'langchain/base_language' +import { VectorStore } from 'langchain/vectorstores' + +class VectorDBQAChain_Chains implements INode { + label: string + name: string + type: string + icon: string + category: string + baseClasses: string[] + description: string + inputs: INodeParams[] + + constructor() { + this.label = 'VectorDB QA Chain' + this.name = 'vectorDBQAChain' + this.type = 'VectorDBQAChain' + this.icon = 'chain.svg' + this.category = 'Chains' + this.description = 'QA chain for vector databases' + this.baseClasses = [this.type, ...getBaseClasses(VectorDBQAChain)] + this.inputs = [ + { + label: 'Language Model', + name: 'model', + type: 'BaseLanguageModel' + }, + { + label: 'Vector Store', + name: 'vectorStore', + type: 'VectorStore' + } + ] + } + + async init(nodeData: INodeData): Promise { + const model = nodeData.inputs?.model as BaseLanguageModel + const vectorStore = nodeData.inputs?.vectorStore as VectorStore + + const chain = VectorDBQAChain.fromLLM(model, vectorStore) + return chain + } + + async run(nodeData: INodeData, input: string): Promise { + const chain = nodeData.instance as VectorDBQAChain + const obj = { + query: input + } + const res = await chain.call(obj) + return res?.text + } +} + +module.exports = { nodeClass: VectorDBQAChain_Chains } diff --git a/packages/components/nodes/chains/VectorDBQAChain/chain.svg b/packages/components/nodes/chains/VectorDBQAChain/chain.svg new file mode 100644 index 00000000..a5b32f90 --- /dev/null +++ b/packages/components/nodes/chains/VectorDBQAChain/chain.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/components/nodes/tools/ChainTool/ChainTool.ts b/packages/components/nodes/tools/ChainTool/ChainTool.ts new file mode 100644 index 00000000..32e414af --- /dev/null +++ b/packages/components/nodes/tools/ChainTool/ChainTool.ts @@ -0,0 +1,73 @@ +import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { getBaseClasses } from '../../../src/utils' +import { ChainTool } from 'langchain/tools' +import { BaseChain } from 'langchain/chains' + +class ChainTool_Tools implements INode { + label: string + name: string + description: string + type: string + icon: string + category: string + baseClasses: string[] + inputs: INodeParams[] + + constructor() { + this.label = 'Chain Tool' + this.name = 'chainTool' + this.type = 'ChainTool' + this.icon = 'chaintool.svg' + this.category = 'Tools' + this.description = 'Use a chain as allowed tool for agent' + this.baseClasses = [this.type, ...getBaseClasses(ChainTool)] + this.inputs = [ + { + label: 'Chain Name', + name: 'name', + type: 'string', + placeholder: 'state-of-union-qa' + }, + { + label: 'Chain Description', + name: 'description', + type: 'string', + rows: 3, + placeholder: + 'State of the Union QA - useful for when you need to ask questions about the most recent state of the union address.' + }, + { + label: 'Return Direct', + name: 'returnDirect', + type: 'boolean', + optional: true + }, + { + label: 'Base Chain', + name: 'baseChain', + type: 'BaseChain' + } + ] + } + + async init(nodeData: INodeData): Promise { + const name = nodeData.inputs?.name as string + const description = nodeData.inputs?.description as string + const baseChain = nodeData.inputs?.baseChain as BaseChain + const returnDirect = nodeData.inputs?.returnDirect as boolean + + const obj = { + name, + description, + chain: baseChain + } as any + + if (returnDirect) obj.returnDirect = returnDirect + + const tool = new ChainTool(obj) + + return tool + } +} + +module.exports = { nodeClass: ChainTool_Tools } diff --git a/packages/components/nodes/tools/ChainTool/chaintool.svg b/packages/components/nodes/tools/ChainTool/chaintool.svg new file mode 100644 index 00000000..c5bd0fbc --- /dev/null +++ b/packages/components/nodes/tools/ChainTool/chaintool.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/packages/components/nodes/vectorstores/Chroma_Existing/Chroma_Existing.ts b/packages/components/nodes/vectorstores/Chroma_Existing/Chroma_Existing.ts index 1e16140f..904c44ed 100644 --- a/packages/components/nodes/vectorstores/Chroma_Existing/Chroma_Existing.ts +++ b/packages/components/nodes/vectorstores/Chroma_Existing/Chroma_Existing.ts @@ -1,6 +1,7 @@ -import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface' import { Chroma } from 'langchain/vectorstores/chroma' import { Embeddings } from 'langchain/embeddings/base' +import { getBaseClasses } from '../../../src/utils' class Chroma_Existing_VectorStores implements INode { label: string @@ -11,6 +12,7 @@ class Chroma_Existing_VectorStores implements INode { category: string baseClasses: string[] inputs: INodeParams[] + outputs: INodeOutputsValue[] constructor() { this.label = 'Chroma Load Existing Index' @@ -32,17 +34,36 @@ class Chroma_Existing_VectorStores implements INode { type: 'string' } ] + this.outputs = [ + { + label: 'Chroma Retriever', + name: 'retriever', + baseClasses: [this.type, 'BaseRetriever'] + }, + { + label: 'Chroma Vector Store', + name: 'vectorStore', + baseClasses: [this.type, ...getBaseClasses(Chroma)] + } + ] } async init(nodeData: INodeData): Promise { const collectionName = nodeData.inputs?.collectionName as string const embeddings = nodeData.inputs?.embeddings as Embeddings + const output = nodeData.outputs?.output as string const vectorStore = await Chroma.fromExistingCollection(embeddings, { collectionName }) - const retriever = vectorStore.asRetriever() - return retriever + + if (output === 'retriever') { + const retriever = vectorStore.asRetriever() + return retriever + } else if (output === 'vectorStore') { + return vectorStore + } + return vectorStore } } diff --git a/packages/components/nodes/vectorstores/Chroma_Upsert/Chroma_Upsert.ts b/packages/components/nodes/vectorstores/Chroma_Upsert/Chroma_Upsert.ts index 71d3edf5..d71f96ec 100644 --- a/packages/components/nodes/vectorstores/Chroma_Upsert/Chroma_Upsert.ts +++ b/packages/components/nodes/vectorstores/Chroma_Upsert/Chroma_Upsert.ts @@ -1,7 +1,8 @@ -import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface' import { Chroma } from 'langchain/vectorstores/chroma' import { Embeddings } from 'langchain/embeddings/base' import { Document } from 'langchain/document' +import { getBaseClasses } from '../../../src/utils' class ChromaUpsert_VectorStores implements INode { label: string @@ -12,6 +13,7 @@ class ChromaUpsert_VectorStores implements INode { category: string baseClasses: string[] inputs: INodeParams[] + outputs: INodeOutputsValue[] constructor() { this.label = 'Chroma Upsert Document' @@ -38,12 +40,25 @@ class ChromaUpsert_VectorStores implements INode { type: 'string' } ] + this.outputs = [ + { + label: 'Chroma Retriever', + name: 'retriever', + baseClasses: [this.type, 'BaseRetriever'] + }, + { + label: 'Chroma Vector Store', + name: 'vectorStore', + baseClasses: [this.type, ...getBaseClasses(Chroma)] + } + ] } async init(nodeData: INodeData): Promise { const collectionName = nodeData.inputs?.collectionName as string const docs = nodeData.inputs?.document as Document[] const embeddings = nodeData.inputs?.embeddings as Embeddings + const output = nodeData.outputs?.output as string const finalDocs = [] for (let i = 0; i < docs.length; i += 1) { @@ -53,8 +68,14 @@ class ChromaUpsert_VectorStores implements INode { const vectorStore = await Chroma.fromDocuments(finalDocs, embeddings, { collectionName }) - const retriever = vectorStore.asRetriever() - return retriever + + if (output === 'retriever') { + const retriever = vectorStore.asRetriever() + return retriever + } else if (output === 'vectorStore') { + return vectorStore + } + return vectorStore } } diff --git a/packages/components/nodes/vectorstores/Pinecone_Existing/Pinecone_Existing.ts b/packages/components/nodes/vectorstores/Pinecone_Existing/Pinecone_Existing.ts index bdc5ba87..fd55de48 100644 --- a/packages/components/nodes/vectorstores/Pinecone_Existing/Pinecone_Existing.ts +++ b/packages/components/nodes/vectorstores/Pinecone_Existing/Pinecone_Existing.ts @@ -1,7 +1,8 @@ -import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface' import { PineconeClient } from '@pinecone-database/pinecone' import { PineconeStore } from 'langchain/vectorstores/pinecone' import { Embeddings } from 'langchain/embeddings/base' +import { getBaseClasses } from '../../../src/utils' class Pinecone_Existing_VectorStores implements INode { label: string @@ -12,6 +13,7 @@ class Pinecone_Existing_VectorStores implements INode { category: string baseClasses: string[] inputs: INodeParams[] + outputs: INodeOutputsValue[] constructor() { this.label = 'Pinecone Load Existing Index' @@ -43,6 +45,18 @@ class Pinecone_Existing_VectorStores implements INode { type: 'string' } ] + this.outputs = [ + { + label: 'Pinecone Retriever', + name: 'retriever', + baseClasses: [this.type, 'BaseRetriever'] + }, + { + label: 'Pinecone Vector Store', + name: 'vectorStore', + baseClasses: [this.type, ...getBaseClasses(PineconeStore)] + } + ] } async init(nodeData: INodeData): Promise { @@ -50,6 +64,7 @@ class Pinecone_Existing_VectorStores implements INode { const pineconeEnv = nodeData.inputs?.pineconeEnv as string const index = nodeData.inputs?.pineconeIndex as string const embeddings = nodeData.inputs?.embeddings as Embeddings + const output = nodeData.outputs?.output as string const client = new PineconeClient() await client.init({ @@ -62,8 +77,14 @@ class Pinecone_Existing_VectorStores implements INode { const vectorStore = await PineconeStore.fromExistingIndex(embeddings, { pineconeIndex }) - const retriever = vectorStore.asRetriever() - return retriever + + if (output === 'retriever') { + const retriever = vectorStore.asRetriever() + return retriever + } else if (output === 'vectorStore') { + return vectorStore + } + return vectorStore } } diff --git a/packages/components/nodes/vectorstores/Pinecone_Upsert/Pinecone_Upsert.ts b/packages/components/nodes/vectorstores/Pinecone_Upsert/Pinecone_Upsert.ts index d498e9ca..cbce7b4b 100644 --- a/packages/components/nodes/vectorstores/Pinecone_Upsert/Pinecone_Upsert.ts +++ b/packages/components/nodes/vectorstores/Pinecone_Upsert/Pinecone_Upsert.ts @@ -1,8 +1,9 @@ -import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface' import { PineconeClient } from '@pinecone-database/pinecone' import { PineconeStore } from 'langchain/vectorstores/pinecone' import { Embeddings } from 'langchain/embeddings/base' import { Document } from 'langchain/document' +import { getBaseClasses } from '../../../src/utils' class PineconeUpsert_VectorStores implements INode { label: string @@ -13,6 +14,7 @@ class PineconeUpsert_VectorStores implements INode { category: string baseClasses: string[] inputs: INodeParams[] + outputs: INodeOutputsValue[] constructor() { this.label = 'Pinecone Upsert Document' @@ -49,6 +51,18 @@ class PineconeUpsert_VectorStores implements INode { type: 'string' } ] + this.outputs = [ + { + label: 'Pinecone Retriever', + name: 'retriever', + baseClasses: [this.type, 'BaseRetriever'] + }, + { + label: 'Pinecone Vector Store', + name: 'vectorStore', + baseClasses: [this.type, ...getBaseClasses(PineconeStore)] + } + ] } async init(nodeData: INodeData): Promise { @@ -57,6 +71,7 @@ class PineconeUpsert_VectorStores implements INode { const index = nodeData.inputs?.pineconeIndex as string const docs = nodeData.inputs?.document as Document[] const embeddings = nodeData.inputs?.embeddings as Embeddings + const output = nodeData.outputs?.output as string const client = new PineconeClient() await client.init({ @@ -74,8 +89,14 @@ class PineconeUpsert_VectorStores implements INode { const vectorStore = await PineconeStore.fromDocuments(finalDocs, embeddings, { pineconeIndex }) - const retriever = vectorStore.asRetriever() - return retriever + + if (output === 'retriever') { + const retriever = vectorStore.asRetriever() + return retriever + } else if (output === 'vectorStore') { + return vectorStore + } + return vectorStore } } diff --git a/packages/components/package.json b/packages/components/package.json index 0e845475..412b505c 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -27,7 +27,7 @@ "dotenv": "^16.0.0", "express": "^4.17.3", "form-data": "^4.0.0", - "langchain": "^0.0.53", + "langchain": "^0.0.59", "moment": "^2.29.3", "node-fetch": "2", "pdf-parse": "^1.1.1", diff --git a/packages/ui/src/ui-component/switch/Switch.js b/packages/ui/src/ui-component/switch/Switch.js new file mode 100644 index 00000000..04ea1704 --- /dev/null +++ b/packages/ui/src/ui-component/switch/Switch.js @@ -0,0 +1,28 @@ +import { useState } from 'react' +import PropTypes from 'prop-types' +import { FormControl, Switch } from '@mui/material' + +export const SwitchInput = ({ value, onChange, disabled = false }) => { + const [myValue, setMyValue] = useState(!!value ?? false) + + return ( + <> + + { + setMyValue(event.target.checked) + onChange(event.target.checked) + }} + /> + + + ) +} + +SwitchInput.propTypes = { + value: PropTypes.string, + onChange: PropTypes.func, + disabled: PropTypes.bool +} diff --git a/packages/ui/src/views/canvas/NodeInputHandler.js b/packages/ui/src/views/canvas/NodeInputHandler.js index 4698f572..686448ad 100644 --- a/packages/ui/src/views/canvas/NodeInputHandler.js +++ b/packages/ui/src/views/canvas/NodeInputHandler.js @@ -12,6 +12,7 @@ import { IconArrowsMaximize } from '@tabler/icons' import { Dropdown } from 'ui-component/dropdown/Dropdown' import { Input } from 'ui-component/input/Input' import { File } from 'ui-component/file/File' +import { SwitchInput } from 'ui-component/switch/Switch' import { flowContext } from 'store/context/ReactFlowContext' import { isValidConnection, getAvailableNodesForVariable } from 'utils/genericHelper' @@ -146,6 +147,13 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false }) = value={data.inputs[inputParam.name] ?? inputParam.default ?? 'Choose a file to upload'} /> )} + {inputParam.type === 'boolean' && ( + (data.inputs[inputParam.name] = newValue)} + value={data.inputs[inputParam.name] ?? inputParam.default ?? false} + /> + )} {(inputParam.type === 'string' || inputParam.type === 'password' || inputParam.type === 'number') && (