mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 09:00:52 +03:00
Merge branch 'main' into feature/BabyAGI
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { initializeAgentExecutor, AgentExecutor } from 'langchain/agents'
|
||||
import { Tool } from 'langchain/tools'
|
||||
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
|
||||
@@ -40,8 +40,10 @@ class MRKLAgentChat_Agents implements INode {
|
||||
async init(nodeData: INodeData): Promise<any> {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
|
||||
import { getBaseClasses } from '../../../src/utils'
|
||||
import { LLMChain } from 'langchain/chains'
|
||||
import { BaseLanguageModel } from 'langchain/base_language'
|
||||
import { BasePromptTemplate } from 'langchain/prompts'
|
||||
|
||||
class LLMChain_Chains implements INode {
|
||||
label: string
|
||||
@@ -13,6 +12,7 @@ class LLMChain_Chains implements INode {
|
||||
baseClasses: string[]
|
||||
description: string
|
||||
inputs: INodeParams[]
|
||||
outputs: INodeOutputsValue[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'LLM Chain'
|
||||
@@ -34,65 +34,99 @@ class LLMChain_Chains implements INode {
|
||||
type: 'BasePromptTemplate'
|
||||
},
|
||||
{
|
||||
label: 'Format Prompt Values',
|
||||
name: 'promptValues',
|
||||
label: 'Chain Name',
|
||||
name: 'chainName',
|
||||
type: 'string',
|
||||
rows: 5,
|
||||
placeholder: `{
|
||||
"input_language": "English",
|
||||
"output_language": "French"
|
||||
}`,
|
||||
placeholder: 'Name Your Chain',
|
||||
optional: true
|
||||
}
|
||||
]
|
||||
this.outputs = [
|
||||
{
|
||||
label: 'LLM Chain',
|
||||
name: 'llmChain',
|
||||
baseClasses: [this.type, ...getBaseClasses(LLMChain)]
|
||||
},
|
||||
{
|
||||
label: 'Output Prediction',
|
||||
name: 'outputPrediction',
|
||||
baseClasses: ['string']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData): Promise<any> {
|
||||
async init(nodeData: INodeData, input: string): Promise<any> {
|
||||
const model = nodeData.inputs?.model as BaseLanguageModel
|
||||
const prompt = nodeData.inputs?.prompt as BasePromptTemplate
|
||||
const prompt = nodeData.inputs?.prompt
|
||||
const output = nodeData.outputs?.output as string
|
||||
const promptValues = prompt.promptValues as ICommonObject
|
||||
|
||||
const chain = new LLMChain({ llm: model, prompt })
|
||||
return chain
|
||||
if (output === this.name) {
|
||||
const chain = new LLMChain({ llm: model, prompt })
|
||||
return chain
|
||||
} else if (output === 'outputPrediction') {
|
||||
const chain = new LLMChain({ llm: model, prompt })
|
||||
const inputVariables = chain.prompt.inputVariables as string[] // ["product"]
|
||||
const res = await runPrediction(inputVariables, chain, input, promptValues)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('\x1b[92m\x1b[1m\n*****OUTPUT PREDICTION*****\n\x1b[0m\x1b[0m')
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(res)
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
async run(nodeData: INodeData, input: string): Promise<string> {
|
||||
const inputVariables = nodeData.instance.prompt.inputVariables as string[] // ["product"]
|
||||
const chain = nodeData.instance as LLMChain
|
||||
const promptValues = nodeData.inputs?.prompt.promptValues as ICommonObject
|
||||
|
||||
if (inputVariables.length === 1) {
|
||||
const res = await chain.run(input)
|
||||
return res
|
||||
} else if (inputVariables.length > 1) {
|
||||
const promptValuesStr = nodeData.inputs?.promptValues as string
|
||||
if (!promptValuesStr) throw new Error('Please provide Prompt Values')
|
||||
const res = await runPrediction(inputVariables, chain, input, promptValues)
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('\x1b[93m\x1b[1m\n*****FINAL RESULT*****\n\x1b[0m\x1b[0m')
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(res)
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
const promptValues = JSON.parse(promptValuesStr.replace(/\s/g, ''))
|
||||
const runPrediction = async (inputVariables: string[], chain: LLMChain, input: string, promptValues: ICommonObject) => {
|
||||
if (inputVariables.length === 1) {
|
||||
const res = await chain.run(input)
|
||||
return res
|
||||
} else if (inputVariables.length > 1) {
|
||||
let seen: string[] = []
|
||||
|
||||
let seen: string[] = []
|
||||
|
||||
for (const variable of inputVariables) {
|
||||
seen.push(variable)
|
||||
if (promptValues[variable]) {
|
||||
seen.pop()
|
||||
}
|
||||
for (const variable of inputVariables) {
|
||||
seen.push(variable)
|
||||
if (promptValues[variable]) {
|
||||
seen.pop()
|
||||
}
|
||||
|
||||
if (seen.length === 1) {
|
||||
const lastValue = seen.pop()
|
||||
if (!lastValue) throw new Error('Please provide Prompt Values')
|
||||
const options = {
|
||||
...promptValues,
|
||||
[lastValue]: input
|
||||
}
|
||||
const res = await chain.call(options)
|
||||
return res?.text
|
||||
} else {
|
||||
throw new Error('Please provide Prompt Values')
|
||||
}
|
||||
} else {
|
||||
const res = await chain.run(input)
|
||||
return res
|
||||
}
|
||||
|
||||
if (seen.length === 0) {
|
||||
// All inputVariables have fixed values specified
|
||||
const options = {
|
||||
...promptValues
|
||||
}
|
||||
const res = await chain.call(options)
|
||||
return res?.text
|
||||
} else if (seen.length === 1) {
|
||||
// If one inputVariable is not specify, use input (user's question) as value
|
||||
const lastValue = seen.pop()
|
||||
if (!lastValue) throw new Error('Please provide Prompt Values')
|
||||
const options = {
|
||||
...promptValues,
|
||||
[lastValue]: input
|
||||
}
|
||||
const res = await chain.call(options)
|
||||
return res?.text
|
||||
} else {
|
||||
throw new Error(`Please provide Prompt Values for: ${seen.join(', ')}`)
|
||||
}
|
||||
} else {
|
||||
const res = await chain.run(input)
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<any> {
|
||||
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<string> {
|
||||
const chain = nodeData.instance as VectorDBQAChain
|
||||
const obj = {
|
||||
query: input
|
||||
}
|
||||
const res = await chain.call(obj)
|
||||
return res?.text
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: VectorDBQAChain_Chains }
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-dna" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M14.828 14.828a4 4 0 1 0 -5.656 -5.656a4 4 0 0 0 5.656 5.656z"></path>
|
||||
<path d="M9.172 20.485a4 4 0 1 0 -5.657 -5.657"></path>
|
||||
<path d="M14.828 3.515a4 4 0 0 0 5.657 5.657"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 489 B |
@@ -1,4 +1,4 @@
|
||||
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { getBaseClasses } from '../../../src/utils'
|
||||
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from 'langchain/prompts'
|
||||
|
||||
@@ -25,15 +25,28 @@ class ChatPromptTemplate_Prompts implements INode {
|
||||
label: 'System Message',
|
||||
name: 'systemMessagePrompt',
|
||||
type: 'string',
|
||||
rows: 3,
|
||||
rows: 4,
|
||||
placeholder: `You are a helpful assistant that translates {input_language} to {output_language}.`
|
||||
},
|
||||
{
|
||||
label: 'Human Message',
|
||||
name: 'humanMessagePrompt',
|
||||
type: 'string',
|
||||
rows: 3,
|
||||
rows: 4,
|
||||
placeholder: `{text}`
|
||||
},
|
||||
{
|
||||
label: 'Format Prompt Values',
|
||||
name: 'promptValues',
|
||||
type: 'string',
|
||||
rows: 4,
|
||||
placeholder: `{
|
||||
"input_language": "English",
|
||||
"output_language": "French"
|
||||
}`,
|
||||
optional: true,
|
||||
acceptVariable: true,
|
||||
list: true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -41,11 +54,20 @@ class ChatPromptTemplate_Prompts implements INode {
|
||||
async init(nodeData: INodeData): Promise<any> {
|
||||
const systemMessagePrompt = nodeData.inputs?.systemMessagePrompt as string
|
||||
const humanMessagePrompt = nodeData.inputs?.humanMessagePrompt as string
|
||||
const promptValuesStr = nodeData.inputs?.promptValues as string
|
||||
|
||||
const prompt = ChatPromptTemplate.fromPromptMessages([
|
||||
SystemMessagePromptTemplate.fromTemplate(systemMessagePrompt),
|
||||
HumanMessagePromptTemplate.fromTemplate(humanMessagePrompt)
|
||||
])
|
||||
|
||||
let promptValues: ICommonObject = {}
|
||||
if (promptValuesStr) {
|
||||
promptValues = JSON.parse(promptValuesStr.replace(/\s/g, ''))
|
||||
}
|
||||
// @ts-ignore
|
||||
prompt.promptValues = promptValues
|
||||
|
||||
return prompt
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class FewShotPromptTemplate_Prompts implements INode {
|
||||
label: 'Examples',
|
||||
name: 'examples',
|
||||
type: 'string',
|
||||
rows: 5,
|
||||
rows: 4,
|
||||
placeholder: `[
|
||||
{ "word": "happy", "antonym": "sad" },
|
||||
{ "word": "tall", "antonym": "short" },
|
||||
@@ -42,14 +42,14 @@ class FewShotPromptTemplate_Prompts implements INode {
|
||||
label: 'Prefix',
|
||||
name: 'prefix',
|
||||
type: 'string',
|
||||
rows: 3,
|
||||
rows: 4,
|
||||
placeholder: `Give the antonym of every input`
|
||||
},
|
||||
{
|
||||
label: 'Suffix',
|
||||
name: 'suffix',
|
||||
type: 'string',
|
||||
rows: 3,
|
||||
rows: 4,
|
||||
placeholder: `Word: {input}\nAntonym:`
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { ICommonObject, INode, INodeData, INodeParams, PromptTemplate } from '../../../src/Interface'
|
||||
import { getBaseClasses, getInputVariables } from '../../../src/utils'
|
||||
import { PromptTemplate, PromptTemplateInput } from 'langchain/prompts'
|
||||
import { PromptTemplateInput } from 'langchain/prompts'
|
||||
|
||||
class PromptTemplate_Prompts implements INode {
|
||||
label: string
|
||||
@@ -19,20 +19,40 @@ class PromptTemplate_Prompts implements INode {
|
||||
this.icon = 'prompt.svg'
|
||||
this.category = 'Prompts'
|
||||
this.description = 'Schema to represent a basic prompt for an LLM'
|
||||
this.baseClasses = [this.type, ...getBaseClasses(PromptTemplate)]
|
||||
this.baseClasses = [...getBaseClasses(PromptTemplate)]
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Template',
|
||||
name: 'template',
|
||||
type: 'string',
|
||||
rows: 5,
|
||||
rows: 4,
|
||||
placeholder: `What is a good name for a company that makes {product}?`
|
||||
},
|
||||
{
|
||||
label: 'Format Prompt Values',
|
||||
name: 'promptValues',
|
||||
type: 'string',
|
||||
rows: 4,
|
||||
placeholder: `{
|
||||
"input_language": "English",
|
||||
"output_language": "French"
|
||||
}`,
|
||||
optional: true,
|
||||
acceptVariable: true,
|
||||
list: true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData): Promise<any> {
|
||||
const template = nodeData.inputs?.template as string
|
||||
const promptValuesStr = nodeData.inputs?.promptValues as string
|
||||
|
||||
let promptValues: ICommonObject = {}
|
||||
if (promptValuesStr) {
|
||||
promptValues = JSON.parse(promptValuesStr.replace(/\s/g, ''))
|
||||
}
|
||||
|
||||
const inputVariables = getInputVariables(template)
|
||||
|
||||
try {
|
||||
@@ -41,6 +61,7 @@ class PromptTemplate_Prompts implements INode {
|
||||
inputVariables
|
||||
}
|
||||
const prompt = new PromptTemplate(options)
|
||||
prompt.promptValues = promptValues
|
||||
return prompt
|
||||
} catch (e) {
|
||||
throw new Error(e)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { AIPluginTool } from 'langchain/tools'
|
||||
import { getBaseClasses } from '../../../src/utils'
|
||||
|
||||
class AIPlugin implements INode {
|
||||
label: string
|
||||
name: string
|
||||
description: string
|
||||
type: string
|
||||
icon: string
|
||||
category: string
|
||||
baseClasses: string[]
|
||||
inputs?: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'AI Plugin'
|
||||
this.name = 'aiPlugin'
|
||||
this.type = 'AIPlugin'
|
||||
this.icon = 'aiplugin.svg'
|
||||
this.category = 'Tools'
|
||||
this.description = 'Execute actions using ChatGPT Plugin Url'
|
||||
this.baseClasses = [this.type, ...getBaseClasses(AIPluginTool)]
|
||||
this.inputs = [
|
||||
{
|
||||
label: 'Plugin Url',
|
||||
name: 'pluginUrl',
|
||||
type: 'string',
|
||||
placeholder: 'https://www.klarna.com/.well-known/ai-plugin.json'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
async init(nodeData: INodeData): Promise<any> {
|
||||
const pluginUrl = nodeData.inputs?.pluginUrl as string
|
||||
const aiplugin = await AIPluginTool.fromPluginUrl(pluginUrl)
|
||||
|
||||
return aiplugin
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: AIPlugin }
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-plug" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M9.785 6l8.215 8.215l-2.054 2.054a5.81 5.81 0 1 1 -8.215 -8.215l2.054 -2.054z"></path>
|
||||
<path d="M4 20l3.5 -3.5"></path>
|
||||
<path d="M15 4l-3.5 3.5"></path>
|
||||
<path d="M20 9l-3.5 3.5"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 498 B |
@@ -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<any> {
|
||||
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 }
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-tool" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M7 10h3v-3l-3.5 -3.5a6 6 0 0 1 8 8l6 6a2 2 0 0 1 -3 3l-6 -6a6 6 0 0 1 -8 -8l3.5 3.5"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 396 B |
@@ -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<any> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<any> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<any> {
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<any> {
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -2,18 +2,7 @@
|
||||
* Types
|
||||
*/
|
||||
|
||||
export type NodeParamsType =
|
||||
| 'asyncOptions'
|
||||
| 'options'
|
||||
| 'string'
|
||||
| 'number'
|
||||
| 'boolean'
|
||||
| 'password'
|
||||
| 'json'
|
||||
| 'code'
|
||||
| 'date'
|
||||
| 'file'
|
||||
| 'folder'
|
||||
export type NodeParamsType = 'options' | 'string' | 'number' | 'boolean' | 'password' | 'json' | 'code' | 'date' | 'file' | 'folder'
|
||||
|
||||
export type CommonType = string | number | boolean | undefined | null
|
||||
|
||||
@@ -40,6 +29,13 @@ export interface INodeOptionsValue {
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface INodeOutputsValue {
|
||||
label: string
|
||||
name: string
|
||||
baseClasses: string[]
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface INodeParams {
|
||||
label: string
|
||||
name: string
|
||||
@@ -50,6 +46,7 @@ export interface INodeParams {
|
||||
optional?: boolean | INodeDisplay
|
||||
rows?: number
|
||||
list?: boolean
|
||||
acceptVariable?: boolean
|
||||
placeholder?: string
|
||||
fileType?: string
|
||||
}
|
||||
@@ -75,12 +72,15 @@ export interface INodeProperties {
|
||||
|
||||
export interface INode extends INodeProperties {
|
||||
inputs?: INodeParams[]
|
||||
getInstance?(nodeData: INodeData): Promise<string>
|
||||
output?: INodeOutputsValue[]
|
||||
init?(nodeData: INodeData, input: string, options?: ICommonObject): Promise<any>
|
||||
run?(nodeData: INodeData, input: string, options?: ICommonObject): Promise<string>
|
||||
}
|
||||
|
||||
export interface INodeData extends INodeProperties {
|
||||
id: string
|
||||
inputs?: ICommonObject
|
||||
outputs?: ICommonObject
|
||||
instance?: any
|
||||
}
|
||||
|
||||
@@ -88,3 +88,17 @@ export interface IMessage {
|
||||
message: string
|
||||
type: MessageType
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes
|
||||
*/
|
||||
|
||||
import { PromptTemplate as LangchainPromptTemplate, PromptTemplateInput } from 'langchain/prompts'
|
||||
|
||||
export class PromptTemplate extends LangchainPromptTemplate {
|
||||
promptValues: ICommonObject
|
||||
|
||||
constructor(input: PromptTemplateInput) {
|
||||
super(input)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
PORT=3000
|
||||
Regular → Executable
Regular → Executable
Regular → Executable
Regular → Executable
@@ -3,11 +3,11 @@
|
||||
"nodes": [
|
||||
{
|
||||
"width": 300,
|
||||
"height": 360,
|
||||
"height": 533,
|
||||
"id": "promptTemplate_0",
|
||||
"position": {
|
||||
"x": 294.38456937448433,
|
||||
"y": 66.5400435451831
|
||||
"x": 567,
|
||||
"y": 85
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
@@ -23,13 +23,26 @@
|
||||
"label": "Template",
|
||||
"name": "template",
|
||||
"type": "string",
|
||||
"rows": 5,
|
||||
"placeholder": "What is a good name for a company that makes {product}?"
|
||||
"rows": 4,
|
||||
"placeholder": "What is a good name for a company that makes {product}?",
|
||||
"id": "promptTemplate_0-input-template-string"
|
||||
},
|
||||
{
|
||||
"label": "Format Prompt Values",
|
||||
"name": "promptValues",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"placeholder": "{\n \"input_language\": \"English\",\n \"output_language\": \"French\"\n}",
|
||||
"optional": true,
|
||||
"acceptVariable": true,
|
||||
"list": true,
|
||||
"id": "promptTemplate_0-input-promptValues-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"template": "Word: {word}\\nAntonym: {antonym}\\n"
|
||||
"template": "Word: {word}\\nAntonym: {antonym}\\n",
|
||||
"promptValues": ""
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
@@ -39,22 +52,23 @@
|
||||
"type": "PromptTemplate | BaseStringPromptTemplate | BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"dragging": false,
|
||||
"positionAbsolute": {
|
||||
"x": 294.38456937448433,
|
||||
"y": 66.5400435451831
|
||||
},
|
||||
"dragging": false
|
||||
"x": 567,
|
||||
"y": 85
|
||||
}
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 886,
|
||||
"height": 955,
|
||||
"id": "fewShotPromptTemplate_0",
|
||||
"position": {
|
||||
"x": 719.2200337843097,
|
||||
"y": 67.20405755860693
|
||||
"x": 942.9569947740308,
|
||||
"y": 82.93222833361332
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
@@ -70,28 +84,32 @@
|
||||
"label": "Examples",
|
||||
"name": "examples",
|
||||
"type": "string",
|
||||
"rows": 5,
|
||||
"placeholder": "[\n { \"word\": \"happy\", \"antonym\": \"sad\" },\n { \"word\": \"tall\", \"antonym\": \"short\" },\n]"
|
||||
"rows": 4,
|
||||
"placeholder": "[\n { \"word\": \"happy\", \"antonym\": \"sad\" },\n { \"word\": \"tall\", \"antonym\": \"short\" },\n]",
|
||||
"id": "fewShotPromptTemplate_0-input-examples-string"
|
||||
},
|
||||
{
|
||||
"label": "Prefix",
|
||||
"name": "prefix",
|
||||
"type": "string",
|
||||
"rows": 3,
|
||||
"placeholder": "Give the antonym of every input"
|
||||
"rows": 4,
|
||||
"placeholder": "Give the antonym of every input",
|
||||
"id": "fewShotPromptTemplate_0-input-prefix-string"
|
||||
},
|
||||
{
|
||||
"label": "Suffix",
|
||||
"name": "suffix",
|
||||
"type": "string",
|
||||
"rows": 3,
|
||||
"placeholder": "Word: {input}\nAntonym:"
|
||||
"rows": 4,
|
||||
"placeholder": "Word: {input}\nAntonym:",
|
||||
"id": "fewShotPromptTemplate_0-input-suffix-string"
|
||||
},
|
||||
{
|
||||
"label": "Example Seperator",
|
||||
"name": "exampleSeparator",
|
||||
"type": "string",
|
||||
"placeholder": "\n\n"
|
||||
"placeholder": "\n\n",
|
||||
"id": "fewShotPromptTemplate_0-input-exampleSeparator-string"
|
||||
},
|
||||
{
|
||||
"label": "Template Format",
|
||||
@@ -107,7 +125,8 @@
|
||||
"name": "jinja-2"
|
||||
}
|
||||
],
|
||||
"default": "f-string"
|
||||
"default": "f-string",
|
||||
"id": "fewShotPromptTemplate_0-input-templateFormat-options"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
@@ -134,12 +153,13 @@
|
||||
"type": "FewShotPromptTemplate | BaseStringPromptTemplate | BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 719.2200337843097,
|
||||
"y": 67.20405755860693
|
||||
"x": 942.9569947740308,
|
||||
"y": 82.93222833361332
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
@@ -148,8 +168,8 @@
|
||||
"height": 472,
|
||||
"id": "openAI_0",
|
||||
"position": {
|
||||
"x": 1089.6434062122398,
|
||||
"y": 27.515288538129425
|
||||
"x": 1304.9299247555505,
|
||||
"y": 8.707397857674266
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
@@ -164,7 +184,8 @@
|
||||
{
|
||||
"label": "OpenAI Api Key",
|
||||
"name": "openAIApiKey",
|
||||
"type": "password"
|
||||
"type": "password",
|
||||
"id": "openAI_0-input-openAIApiKey-password"
|
||||
},
|
||||
{
|
||||
"label": "Model Name",
|
||||
@@ -189,20 +210,22 @@
|
||||
}
|
||||
],
|
||||
"default": "text-davinci-003",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"id": "openAI_0-input-modelName-options"
|
||||
},
|
||||
{
|
||||
"label": "Temperature",
|
||||
"name": "temperature",
|
||||
"type": "number",
|
||||
"default": 0.7,
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"id": "openAI_0-input-temperature-number"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"modelName": "text-davinci-003",
|
||||
"temperature": 0.7
|
||||
"temperature": "0"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
@@ -212,22 +235,23 @@
|
||||
"type": "OpenAI | BaseLLM | BaseLanguageModel"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1089.6434062122398,
|
||||
"y": 27.515288538129425
|
||||
"x": 1304.9299247555505,
|
||||
"y": 8.707397857674266
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 461,
|
||||
"height": 405,
|
||||
"id": "llmChain_0",
|
||||
"position": {
|
||||
"x": 1499.2654451385026,
|
||||
"y": 356.3275374721362
|
||||
"x": 1669.2177402155296,
|
||||
"y": 338.65158088371567
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
@@ -240,12 +264,12 @@
|
||||
"description": "Chain to run queries against LLMs",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Format Prompt Values",
|
||||
"name": "promptValues",
|
||||
"label": "Chain Name",
|
||||
"name": "chainName",
|
||||
"type": "string",
|
||||
"rows": 5,
|
||||
"placeholder": "{\n \"input_language\": \"English\",\n \"output_language\": \"French\"\n}",
|
||||
"optional": true
|
||||
"placeholder": "Name Your Chain",
|
||||
"optional": true,
|
||||
"id": "llmChain_0-input-chainName-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
@@ -265,38 +289,44 @@
|
||||
"inputs": {
|
||||
"model": "{{openAI_0.data.instance}}",
|
||||
"prompt": "{{fewShotPromptTemplate_0.data.instance}}",
|
||||
"promptValues": ""
|
||||
"chainName": ""
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "llmChain_0-output-llmChain-LLMChain|BaseChain",
|
||||
"name": "llmChain",
|
||||
"label": "LLMChain",
|
||||
"type": "LLMChain | BaseChain"
|
||||
"name": "output",
|
||||
"label": "Output",
|
||||
"type": "options",
|
||||
"options": [
|
||||
{
|
||||
"id": "llmChain_0-output-llmChain-LLMChain|BaseChain",
|
||||
"name": "llmChain",
|
||||
"label": "LLM Chain",
|
||||
"type": "LLMChain | BaseChain"
|
||||
},
|
||||
{
|
||||
"id": "llmChain_0-output-outputPrediction-string",
|
||||
"name": "outputPrediction",
|
||||
"label": "Output Prediction",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"default": "llmChain"
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"output": "llmChain"
|
||||
},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1499.2654451385026,
|
||||
"y": 356.3275374721362
|
||||
"x": 1669.2177402155296,
|
||||
"y": 338.65158088371567
|
||||
},
|
||||
"dragging": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "promptTemplate_0",
|
||||
"sourceHandle": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"target": "fewShotPromptTemplate_0",
|
||||
"targetHandle": "fewShotPromptTemplate_0-input-examplePrompt-PromptTemplate",
|
||||
"type": "buttonedge",
|
||||
"id": "promptTemplate_0-promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate-fewShotPromptTemplate_0-fewShotPromptTemplate_0-input-examplePrompt-PromptTemplate",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "openAI_0",
|
||||
"sourceHandle": "openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel",
|
||||
@@ -318,6 +348,17 @@
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "promptTemplate_0",
|
||||
"sourceHandle": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"target": "fewShotPromptTemplate_0",
|
||||
"targetHandle": "fewShotPromptTemplate_0-input-examplePrompt-PromptTemplate",
|
||||
"type": "buttonedge",
|
||||
"id": "promptTemplate_0-promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate-fewShotPromptTemplate_0-fewShotPromptTemplate_0-input-examplePrompt-PromptTemplate",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
{
|
||||
"description": "Use ChatGPT Plugins within LangChain abstractions with GET and POST Tools",
|
||||
"nodes": [
|
||||
{
|
||||
"width": 300,
|
||||
"height": 278,
|
||||
"id": "aiPlugin_0",
|
||||
"position": {
|
||||
"x": 992.9213747553727,
|
||||
"y": 115.80946637479596
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "aiPlugin_0",
|
||||
"label": "AI Plugin",
|
||||
"name": "aiPlugin",
|
||||
"type": "AIPlugin",
|
||||
"baseClasses": ["AIPlugin", "Tool"],
|
||||
"category": "Tools",
|
||||
"description": "Execute actions using ChatGPT Plugin Url",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Plugin Url",
|
||||
"name": "pluginUrl",
|
||||
"type": "string",
|
||||
"placeholder": "https://www.klarna.com/.well-known/ai-plugin.json"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"pluginUrl": "https://www.klarna.com/.well-known/ai-plugin.json"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "aiPlugin_0-output-aiPlugin-AIPlugin|Tool",
|
||||
"name": "aiPlugin",
|
||||
"label": "AIPlugin",
|
||||
"type": "AIPlugin | Tool"
|
||||
}
|
||||
],
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 992.9213747553727,
|
||||
"y": 115.80946637479596
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 143,
|
||||
"id": "requestsPost_0",
|
||||
"position": {
|
||||
"x": 638.2831241951309,
|
||||
"y": 294.0784991300699
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "requestsPost_0",
|
||||
"label": "Requests Post",
|
||||
"name": "requestsPost",
|
||||
"type": "RequestsPost",
|
||||
"baseClasses": ["RequestsPost", "Tool"],
|
||||
"category": "Tools",
|
||||
"description": "Execute HTTP POST requests",
|
||||
"inputParams": [],
|
||||
"inputAnchors": [],
|
||||
"inputs": {},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "requestsPost_0-output-requestsPost-RequestsPost|Tool",
|
||||
"name": "requestsPost",
|
||||
"label": "RequestsPost",
|
||||
"type": "RequestsPost | Tool"
|
||||
}
|
||||
],
|
||||
"selected": false
|
||||
},
|
||||
"positionAbsolute": {
|
||||
"x": 638.2831241951309,
|
||||
"y": 294.0784991300699
|
||||
},
|
||||
"selected": false,
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 143,
|
||||
"id": "requestsGet_0",
|
||||
"position": {
|
||||
"x": 703.0477667387721,
|
||||
"y": 476.8955204497346
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "requestsGet_0",
|
||||
"label": "Requests Get",
|
||||
"name": "requestsGet",
|
||||
"type": "RequestsGet",
|
||||
"baseClasses": ["RequestsGet", "Tool"],
|
||||
"category": "Tools",
|
||||
"description": "Execute HTTP GET requests",
|
||||
"inputParams": [],
|
||||
"inputAnchors": [],
|
||||
"inputs": {},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "requestsGet_0-output-requestsGet-RequestsGet|Tool",
|
||||
"name": "requestsGet",
|
||||
"label": "RequestsGet",
|
||||
"type": "RequestsGet | Tool"
|
||||
}
|
||||
],
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 703.0477667387721,
|
||||
"y": 476.8955204497346
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 280,
|
||||
"id": "mrklAgentChat_0",
|
||||
"position": {
|
||||
"x": 1363.057715565282,
|
||||
"y": 479.27393467974
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "mrklAgentChat_0",
|
||||
"label": "MRKL Agent for Chat Models",
|
||||
"name": "mrklAgentChat",
|
||||
"type": "AgentExecutor",
|
||||
"baseClasses": ["AgentExecutor", "BaseChain"],
|
||||
"category": "Agents",
|
||||
"description": "Agent that uses the ReAct Framework to decide what action to take, optimized to be used with Chat Models",
|
||||
"inputParams": [],
|
||||
"inputAnchors": [
|
||||
{
|
||||
"label": "Allowed Tools",
|
||||
"name": "tools",
|
||||
"type": "Tool",
|
||||
"list": true,
|
||||
"id": "mrklAgentChat_0-input-tools-Tool"
|
||||
},
|
||||
{
|
||||
"label": "Chat Model",
|
||||
"name": "model",
|
||||
"type": "BaseChatModel",
|
||||
"id": "mrklAgentChat_0-input-model-BaseChatModel"
|
||||
}
|
||||
],
|
||||
"inputs": {
|
||||
"tools": ["{{requestsGet_0.data.instance}}", "{{requestsPost_0.data.instance}}", "{{aiPlugin_0.data.instance}}"],
|
||||
"model": "{{chatOpenAI_0.data.instance}}"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "mrklAgentChat_0-output-mrklAgentChat-AgentExecutor|BaseChain",
|
||||
"name": "mrklAgentChat",
|
||||
"label": "AgentExecutor",
|
||||
"type": "AgentExecutor | BaseChain"
|
||||
}
|
||||
],
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1363.057715565282,
|
||||
"y": 479.27393467974
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 472,
|
||||
"id": "chatOpenAI_0",
|
||||
"position": {
|
||||
"x": 724.4534948088211,
|
||||
"y": 668.3578659651726
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "chatOpenAI_0",
|
||||
"label": "ChatOpenAI",
|
||||
"name": "chatOpenAI",
|
||||
"type": "ChatOpenAI",
|
||||
"baseClasses": ["ChatOpenAI", "BaseChatModel", "BaseLanguageModel"],
|
||||
"category": "Chat Models",
|
||||
"description": "Wrapper around OpenAI large language models that use the Chat endpoint",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "OpenAI Api Key",
|
||||
"name": "openAIApiKey",
|
||||
"type": "password"
|
||||
},
|
||||
{
|
||||
"label": "Model Name",
|
||||
"name": "modelName",
|
||||
"type": "options",
|
||||
"options": [
|
||||
{
|
||||
"label": "gpt-4",
|
||||
"name": "gpt-4"
|
||||
},
|
||||
{
|
||||
"label": "gpt-4-0314",
|
||||
"name": "gpt-4-0314"
|
||||
},
|
||||
{
|
||||
"label": "gpt-4-32k-0314",
|
||||
"name": "gpt-4-32k-0314"
|
||||
},
|
||||
{
|
||||
"label": "gpt-3.5-turbo",
|
||||
"name": "gpt-3.5-turbo"
|
||||
},
|
||||
{
|
||||
"label": "gpt-3.5-turbo-0301",
|
||||
"name": "gpt-3.5-turbo-0301"
|
||||
}
|
||||
],
|
||||
"default": "gpt-3.5-turbo",
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"label": "Temperature",
|
||||
"name": "temperature",
|
||||
"type": "number",
|
||||
"default": 0.9,
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"modelName": "gpt-3.5-turbo",
|
||||
"temperature": "0"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel",
|
||||
"name": "chatOpenAI",
|
||||
"label": "ChatOpenAI",
|
||||
"type": "ChatOpenAI | BaseChatModel | BaseLanguageModel"
|
||||
}
|
||||
],
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 724.4534948088211,
|
||||
"y": 668.3578659651726
|
||||
},
|
||||
"dragging": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "aiPlugin_0",
|
||||
"sourceHandle": "aiPlugin_0-output-aiPlugin-AIPlugin|Tool",
|
||||
"target": "mrklAgentChat_0",
|
||||
"targetHandle": "mrklAgentChat_0-input-tools-Tool",
|
||||
"type": "buttonedge",
|
||||
"id": "aiPlugin_0-aiPlugin_0-output-aiPlugin-AIPlugin|Tool-mrklAgentChat_0-mrklAgentChat_0-input-tools-Tool",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "requestsGet_0",
|
||||
"sourceHandle": "requestsGet_0-output-requestsGet-RequestsGet|Tool",
|
||||
"target": "mrklAgentChat_0",
|
||||
"targetHandle": "mrklAgentChat_0-input-tools-Tool",
|
||||
"type": "buttonedge",
|
||||
"id": "requestsGet_0-requestsGet_0-output-requestsGet-RequestsGet|Tool-mrklAgentChat_0-mrklAgentChat_0-input-tools-Tool",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "requestsPost_0",
|
||||
"sourceHandle": "requestsPost_0-output-requestsPost-RequestsPost|Tool",
|
||||
"target": "mrklAgentChat_0",
|
||||
"targetHandle": "mrklAgentChat_0-input-tools-Tool",
|
||||
"type": "buttonedge",
|
||||
"id": "requestsPost_0-requestsPost_0-output-requestsPost-RequestsPost|Tool-mrklAgentChat_0-mrklAgentChat_0-input-tools-Tool",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "chatOpenAI_0",
|
||||
"sourceHandle": "chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel",
|
||||
"target": "mrklAgentChat_0",
|
||||
"targetHandle": "mrklAgentChat_0-input-model-BaseChatModel",
|
||||
"type": "buttonedge",
|
||||
"id": "chatOpenAI_0-chatOpenAI_0-output-chatOpenAI-ChatOpenAI|BaseChatModel|BaseLanguageModel-mrklAgentChat_0-mrklAgentChat_0-input-model-BaseChatModel",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,508 @@
|
||||
{
|
||||
"description": "Use output from a chain as prompt for another chain",
|
||||
"nodes": [
|
||||
{
|
||||
"width": 300,
|
||||
"height": 533,
|
||||
"id": "promptTemplate_0",
|
||||
"position": {
|
||||
"x": 796.6293062501211,
|
||||
"y": 523.6130142453178
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "promptTemplate_0",
|
||||
"label": "Prompt Template",
|
||||
"name": "promptTemplate",
|
||||
"type": "PromptTemplate",
|
||||
"baseClasses": ["PromptTemplate", "BaseStringPromptTemplate", "BasePromptTemplate"],
|
||||
"category": "Prompts",
|
||||
"description": "Schema to represent a basic prompt for an LLM",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Template",
|
||||
"name": "template",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"placeholder": "What is a good name for a company that makes {product}?",
|
||||
"id": "promptTemplate_0-input-template-string"
|
||||
},
|
||||
{
|
||||
"label": "Format Prompt Values",
|
||||
"name": "promptValues",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"placeholder": "{\n \"input_language\": \"English\",\n \"output_language\": \"French\"\n}",
|
||||
"optional": true,
|
||||
"acceptVariable": true,
|
||||
"list": true,
|
||||
"id": "promptTemplate_0-input-promptValues-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"template": "You are an AI who performs one task based on the following objective: {objective}.\nRespond with how you would complete this task:",
|
||||
"promptValues": "{\n \"objective\": \"{{question}}\"\n}"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"name": "promptTemplate",
|
||||
"label": "PromptTemplate",
|
||||
"type": "PromptTemplate | BaseStringPromptTemplate | BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 796.6293062501211,
|
||||
"y": 523.6130142453178
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 405,
|
||||
"id": "llmChain_0",
|
||||
"position": {
|
||||
"x": 1239.1590462985343,
|
||||
"y": 477.999065568104
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "llmChain_0",
|
||||
"label": "LLM Chain",
|
||||
"name": "llmChain",
|
||||
"type": "LLMChain",
|
||||
"baseClasses": ["LLMChain", "BaseChain"],
|
||||
"category": "Chains",
|
||||
"description": "Chain to run queries against LLMs",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Chain Name",
|
||||
"name": "chainName",
|
||||
"type": "string",
|
||||
"placeholder": "Name Your Chain",
|
||||
"optional": true,
|
||||
"id": "llmChain_0-input-chainName-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
{
|
||||
"label": "Language Model",
|
||||
"name": "model",
|
||||
"type": "BaseLanguageModel",
|
||||
"id": "llmChain_0-input-model-BaseLanguageModel"
|
||||
},
|
||||
{
|
||||
"label": "Prompt",
|
||||
"name": "prompt",
|
||||
"type": "BasePromptTemplate",
|
||||
"id": "llmChain_0-input-prompt-BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"inputs": {
|
||||
"model": "{{openAI_0.data.instance}}",
|
||||
"prompt": "{{promptTemplate_0.data.instance}}",
|
||||
"chainName": "FirstChain"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"name": "output",
|
||||
"label": "Output",
|
||||
"type": "options",
|
||||
"options": [
|
||||
{
|
||||
"id": "llmChain_0-output-llmChain-LLMChain|BaseChain",
|
||||
"name": "llmChain",
|
||||
"label": "LLM Chain",
|
||||
"type": "LLMChain | BaseChain"
|
||||
},
|
||||
{
|
||||
"id": "llmChain_0-output-outputPrediction-string",
|
||||
"name": "outputPrediction",
|
||||
"label": "Output Prediction",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"default": "llmChain"
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"output": "outputPrediction"
|
||||
},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1239.1590462985343,
|
||||
"y": 477.999065568104
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 472,
|
||||
"id": "openAI_0",
|
||||
"position": {
|
||||
"x": 801.1835381596817,
|
||||
"y": 21.196316952440355
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "openAI_0",
|
||||
"label": "OpenAI",
|
||||
"name": "openAI",
|
||||
"type": "OpenAI",
|
||||
"baseClasses": ["OpenAI", "BaseLLM", "BaseLanguageModel"],
|
||||
"category": "LLMs",
|
||||
"description": "Wrapper around OpenAI large language models",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "OpenAI Api Key",
|
||||
"name": "openAIApiKey",
|
||||
"type": "password",
|
||||
"id": "openAI_0-input-openAIApiKey-password"
|
||||
},
|
||||
{
|
||||
"label": "Model Name",
|
||||
"name": "modelName",
|
||||
"type": "options",
|
||||
"options": [
|
||||
{
|
||||
"label": "text-davinci-003",
|
||||
"name": "text-davinci-003"
|
||||
},
|
||||
{
|
||||
"label": "text-davinci-002",
|
||||
"name": "text-davinci-002"
|
||||
},
|
||||
{
|
||||
"label": "text-curie-001",
|
||||
"name": "text-curie-001"
|
||||
},
|
||||
{
|
||||
"label": "text-babbage-001",
|
||||
"name": "text-babbage-001"
|
||||
}
|
||||
],
|
||||
"default": "text-davinci-003",
|
||||
"optional": true,
|
||||
"id": "openAI_0-input-modelName-options"
|
||||
},
|
||||
{
|
||||
"label": "Temperature",
|
||||
"name": "temperature",
|
||||
"type": "number",
|
||||
"default": 0.7,
|
||||
"optional": true,
|
||||
"id": "openAI_0-input-temperature-number"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"modelName": "text-davinci-003",
|
||||
"temperature": "0"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel",
|
||||
"name": "openAI",
|
||||
"label": "OpenAI",
|
||||
"type": "OpenAI | BaseLLM | BaseLanguageModel"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 801.1835381596817,
|
||||
"y": 21.196316952440355
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 405,
|
||||
"id": "llmChain_1",
|
||||
"position": {
|
||||
"x": 2078.2072357874076,
|
||||
"y": 476.5404337093371
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "llmChain_1",
|
||||
"label": "LLM Chain",
|
||||
"name": "llmChain",
|
||||
"type": "LLMChain",
|
||||
"baseClasses": ["LLMChain", "BaseChain"],
|
||||
"category": "Chains",
|
||||
"description": "Chain to run queries against LLMs",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Chain Name",
|
||||
"name": "chainName",
|
||||
"type": "string",
|
||||
"placeholder": "Name Your Chain",
|
||||
"optional": true,
|
||||
"id": "llmChain_1-input-chainName-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
{
|
||||
"label": "Language Model",
|
||||
"name": "model",
|
||||
"type": "BaseLanguageModel",
|
||||
"id": "llmChain_1-input-model-BaseLanguageModel"
|
||||
},
|
||||
{
|
||||
"label": "Prompt",
|
||||
"name": "prompt",
|
||||
"type": "BasePromptTemplate",
|
||||
"id": "llmChain_1-input-prompt-BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"inputs": {
|
||||
"model": "{{openAI_1.data.instance}}",
|
||||
"prompt": "{{promptTemplate_1.data.instance}}",
|
||||
"chainName": "LastChain"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"name": "output",
|
||||
"label": "Output",
|
||||
"type": "options",
|
||||
"options": [
|
||||
{
|
||||
"id": "llmChain_0-output-llmChain-LLMChain|BaseChain",
|
||||
"name": "llmChain",
|
||||
"label": "LLM Chain",
|
||||
"type": "LLMChain | BaseChain"
|
||||
},
|
||||
{
|
||||
"id": "llmChain_0-output-outputPrediction-string",
|
||||
"name": "outputPrediction",
|
||||
"label": "Output Prediction",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"default": "llmChain"
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"output": "llmChain"
|
||||
},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 2078.2072357874076,
|
||||
"y": 476.5404337093371
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 533,
|
||||
"id": "promptTemplate_1",
|
||||
"position": {
|
||||
"x": 1686.7296107958396,
|
||||
"y": 520.6957505277837
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "promptTemplate_1",
|
||||
"label": "Prompt Template",
|
||||
"name": "promptTemplate",
|
||||
"type": "PromptTemplate",
|
||||
"baseClasses": ["PromptTemplate", "BaseStringPromptTemplate", "BasePromptTemplate"],
|
||||
"category": "Prompts",
|
||||
"description": "Schema to represent a basic prompt for an LLM",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Template",
|
||||
"name": "template",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"placeholder": "What is a good name for a company that makes {product}?",
|
||||
"id": "promptTemplate_1-input-template-string"
|
||||
},
|
||||
{
|
||||
"label": "Format Prompt Values",
|
||||
"name": "promptValues",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"placeholder": "{\n \"input_language\": \"English\",\n \"output_language\": \"French\"\n}",
|
||||
"optional": true,
|
||||
"acceptVariable": true,
|
||||
"list": true,
|
||||
"id": "promptTemplate_1-input-promptValues-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"template": "You are a task creation AI that uses the result of an execution agent to create new tasks with the following objective: {objective}.\nThe last completed task has the result: {result}.\nBased on the result, create new tasks to be completed by the AI system that do not overlap with result.\nReturn the tasks as an array.",
|
||||
"promptValues": "{\n \"objective\": \"{{question}}\",\n \"result\": \"{{llmChain_0.data.instance}}\"\n}"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "promptTemplate_1-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"name": "promptTemplate",
|
||||
"label": "PromptTemplate",
|
||||
"type": "PromptTemplate | BaseStringPromptTemplate | BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1686.7296107958396,
|
||||
"y": 520.6957505277837
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 472,
|
||||
"id": "openAI_1",
|
||||
"position": {
|
||||
"x": 1688.3665789878662,
|
||||
"y": 16.528695004385895
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "openAI_1",
|
||||
"label": "OpenAI",
|
||||
"name": "openAI",
|
||||
"type": "OpenAI",
|
||||
"baseClasses": ["OpenAI", "BaseLLM", "BaseLanguageModel"],
|
||||
"category": "LLMs",
|
||||
"description": "Wrapper around OpenAI large language models",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "OpenAI Api Key",
|
||||
"name": "openAIApiKey",
|
||||
"type": "password",
|
||||
"id": "openAI_1-input-openAIApiKey-password"
|
||||
},
|
||||
{
|
||||
"label": "Model Name",
|
||||
"name": "modelName",
|
||||
"type": "options",
|
||||
"options": [
|
||||
{
|
||||
"label": "text-davinci-003",
|
||||
"name": "text-davinci-003"
|
||||
},
|
||||
{
|
||||
"label": "text-davinci-002",
|
||||
"name": "text-davinci-002"
|
||||
},
|
||||
{
|
||||
"label": "text-curie-001",
|
||||
"name": "text-curie-001"
|
||||
},
|
||||
{
|
||||
"label": "text-babbage-001",
|
||||
"name": "text-babbage-001"
|
||||
}
|
||||
],
|
||||
"default": "text-davinci-003",
|
||||
"optional": true,
|
||||
"id": "openAI_1-input-modelName-options"
|
||||
},
|
||||
{
|
||||
"label": "Temperature",
|
||||
"name": "temperature",
|
||||
"type": "number",
|
||||
"default": 0.7,
|
||||
"optional": true,
|
||||
"id": "openAI_1-input-temperature-number"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"modelName": "text-davinci-003",
|
||||
"temperature": "0"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "openAI_1-output-openAI-OpenAI|BaseLLM|BaseLanguageModel",
|
||||
"name": "openAI",
|
||||
"label": "OpenAI",
|
||||
"type": "OpenAI | BaseLLM | BaseLanguageModel"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1688.3665789878662,
|
||||
"y": 16.528695004385895
|
||||
},
|
||||
"dragging": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "promptTemplate_0",
|
||||
"sourceHandle": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"target": "llmChain_0",
|
||||
"targetHandle": "llmChain_0-input-prompt-BasePromptTemplate",
|
||||
"type": "buttonedge",
|
||||
"id": "promptTemplate_0-promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate-llmChain_0-llmChain_0-input-prompt-BasePromptTemplate",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "openAI_0",
|
||||
"sourceHandle": "openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel",
|
||||
"target": "llmChain_0",
|
||||
"targetHandle": "llmChain_0-input-model-BaseLanguageModel",
|
||||
"type": "buttonedge",
|
||||
"id": "openAI_0-openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel-llmChain_0-llmChain_0-input-model-BaseLanguageModel",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "promptTemplate_1",
|
||||
"sourceHandle": "promptTemplate_1-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"target": "llmChain_1",
|
||||
"targetHandle": "llmChain_1-input-prompt-BasePromptTemplate",
|
||||
"type": "buttonedge",
|
||||
"id": "promptTemplate_1-promptTemplate_1-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate-llmChain_1-llmChain_1-input-prompt-BasePromptTemplate",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "openAI_1",
|
||||
"sourceHandle": "openAI_1-output-openAI-OpenAI|BaseLLM|BaseLanguageModel",
|
||||
"target": "llmChain_1",
|
||||
"targetHandle": "llmChain_1-input-model-BaseLanguageModel",
|
||||
"type": "buttonedge",
|
||||
"id": "openAI_1-openAI_1-output-openAI-OpenAI|BaseLLM|BaseLanguageModel-llmChain_1-llmChain_1-input-model-BaseLanguageModel",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "llmChain_0",
|
||||
"sourceHandle": "llmChain_0-output-outputPrediction-string",
|
||||
"target": "promptTemplate_1",
|
||||
"targetHandle": "promptTemplate_1-input-promptValues-string",
|
||||
"type": "buttonedge",
|
||||
"id": "llmChain_0-llmChain_0-output-outputPrediction-string-promptTemplate_1-promptTemplate_1-input-promptValues-string",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
"height": 472,
|
||||
"id": "openAI_0",
|
||||
"position": {
|
||||
"x": 968.1753795547951,
|
||||
"y": -8.62176310944858
|
||||
"x": 618,
|
||||
"y": 97
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
@@ -22,7 +22,8 @@
|
||||
{
|
||||
"label": "OpenAI Api Key",
|
||||
"name": "openAIApiKey",
|
||||
"type": "password"
|
||||
"type": "password",
|
||||
"id": "openAI_0-input-openAIApiKey-password"
|
||||
},
|
||||
{
|
||||
"label": "Model Name",
|
||||
@@ -47,14 +48,16 @@
|
||||
}
|
||||
],
|
||||
"default": "text-davinci-003",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"id": "openAI_0-input-modelName-options"
|
||||
},
|
||||
{
|
||||
"label": "Temperature",
|
||||
"name": "temperature",
|
||||
"type": "number",
|
||||
"default": 0.7,
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"id": "openAI_0-input-temperature-number"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
@@ -70,69 +73,23 @@
|
||||
"type": "OpenAI | BaseLLM | BaseLanguageModel"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"dragging": false,
|
||||
"positionAbsolute": {
|
||||
"x": 968.1753795547951,
|
||||
"y": -8.62176310944858
|
||||
},
|
||||
"dragging": false
|
||||
"x": 618,
|
||||
"y": 97
|
||||
}
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 360,
|
||||
"id": "promptTemplate_0",
|
||||
"position": {
|
||||
"x": 970.576876549135,
|
||||
"y": 502.493937944275
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "promptTemplate_0",
|
||||
"label": "Prompt Template",
|
||||
"name": "promptTemplate",
|
||||
"type": "PromptTemplate",
|
||||
"baseClasses": ["PromptTemplate", "BaseStringPromptTemplate", "BasePromptTemplate"],
|
||||
"category": "Prompts",
|
||||
"description": "Schema to represent a basic prompt for an LLM",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Template",
|
||||
"name": "template",
|
||||
"type": "string",
|
||||
"rows": 5,
|
||||
"placeholder": "What is a good name for a company that makes {product}?"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"template": "What is a good name for a company that makes {product}?"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"name": "promptTemplate",
|
||||
"label": "PromptTemplate",
|
||||
"type": "PromptTemplate | BaseStringPromptTemplate | BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 970.576876549135,
|
||||
"y": 502.493937944275
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 461,
|
||||
"height": 405,
|
||||
"id": "llmChain_0",
|
||||
"position": {
|
||||
"x": 1414.1175742139496,
|
||||
"y": 340.4040954840462
|
||||
"x": 998.3768292410252,
|
||||
"y": 426.849642225371
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
@@ -145,12 +102,12 @@
|
||||
"description": "Chain to run queries against LLMs",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Format Prompt Values",
|
||||
"name": "promptValues",
|
||||
"label": "Chain Name",
|
||||
"name": "chainName",
|
||||
"type": "string",
|
||||
"rows": 5,
|
||||
"placeholder": "{\n \"input_language\": \"English\",\n \"output_language\": \"French\"\n}",
|
||||
"optional": true
|
||||
"placeholder": "Name Your Chain",
|
||||
"optional": true,
|
||||
"id": "llmChain_0-input-chainName-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
@@ -170,38 +127,105 @@
|
||||
"inputs": {
|
||||
"model": "{{openAI_0.data.instance}}",
|
||||
"prompt": "{{promptTemplate_0.data.instance}}",
|
||||
"promptValues": ""
|
||||
"chainName": ""
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "llmChain_0-output-llmChain-LLMChain|BaseChain",
|
||||
"name": "llmChain",
|
||||
"label": "LLMChain",
|
||||
"type": "LLMChain | BaseChain"
|
||||
"name": "output",
|
||||
"label": "Output",
|
||||
"type": "options",
|
||||
"options": [
|
||||
{
|
||||
"id": "llmChain_0-output-llmChain-LLMChain|BaseChain",
|
||||
"name": "llmChain",
|
||||
"label": "LLM Chain",
|
||||
"type": "LLMChain | BaseChain"
|
||||
},
|
||||
{
|
||||
"id": "llmChain_0-output-outputPrediction-string",
|
||||
"name": "outputPrediction",
|
||||
"label": "Output Prediction",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"default": "llmChain"
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"output": "llmChain"
|
||||
},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1414.1175742139496,
|
||||
"y": 340.4040954840462
|
||||
"x": 998.3768292410252,
|
||||
"y": 426.849642225371
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 533,
|
||||
"id": "promptTemplate_0",
|
||||
"position": {
|
||||
"x": 618.658978699234,
|
||||
"y": 589.2586352262571
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "promptTemplate_0",
|
||||
"label": "Prompt Template",
|
||||
"name": "promptTemplate",
|
||||
"type": "PromptTemplate",
|
||||
"baseClasses": ["PromptTemplate", "BaseStringPromptTemplate", "BasePromptTemplate"],
|
||||
"category": "Prompts",
|
||||
"description": "Schema to represent a basic prompt for an LLM",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "Template",
|
||||
"name": "template",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"placeholder": "What is a good name for a company that makes {product}?",
|
||||
"id": "promptTemplate_0-input-template-string"
|
||||
},
|
||||
{
|
||||
"label": "Format Prompt Values",
|
||||
"name": "promptValues",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"placeholder": "{\n \"input_language\": \"English\",\n \"output_language\": \"French\"\n}",
|
||||
"optional": true,
|
||||
"acceptVariable": true,
|
||||
"list": true,
|
||||
"id": "promptTemplate_0-input-promptValues-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"template": "What is a good name for a company that makes {product}?",
|
||||
"promptValues": ""
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"name": "promptTemplate",
|
||||
"label": "PromptTemplate",
|
||||
"type": "PromptTemplate | BaseStringPromptTemplate | BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 618.658978699234,
|
||||
"y": 589.2586352262571
|
||||
},
|
||||
"dragging": false
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "promptTemplate_0",
|
||||
"sourceHandle": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"target": "llmChain_0",
|
||||
"targetHandle": "llmChain_0-input-prompt-BasePromptTemplate",
|
||||
"type": "buttonedge",
|
||||
"id": "promptTemplate_0-promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate-llmChain_0-llmChain_0-input-prompt-BasePromptTemplate",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "openAI_0",
|
||||
"sourceHandle": "openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel",
|
||||
@@ -212,6 +236,17 @@
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "promptTemplate_0",
|
||||
"sourceHandle": "promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate",
|
||||
"target": "llmChain_0",
|
||||
"targetHandle": "llmChain_0-input-prompt-BasePromptTemplate",
|
||||
"type": "buttonedge",
|
||||
"id": "promptTemplate_0-promptTemplate_0-output-promptTemplate-PromptTemplate|BaseStringPromptTemplate|BasePromptTemplate-llmChain_0-llmChain_0-input-prompt-BasePromptTemplate",
|
||||
"data": {
|
||||
"label": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,66 +3,91 @@
|
||||
"nodes": [
|
||||
{
|
||||
"width": 300,
|
||||
"height": 460,
|
||||
"id": "chatPromptTemplate_0",
|
||||
"height": 405,
|
||||
"id": "llmChain_0",
|
||||
"position": {
|
||||
"x": 524,
|
||||
"y": 237
|
||||
"x": 1136.5578350285277,
|
||||
"y": 619.2492937692573
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "chatPromptTemplate_0",
|
||||
"label": "Chat Prompt Template",
|
||||
"name": "chatPromptTemplate",
|
||||
"type": "ChatPromptTemplate",
|
||||
"baseClasses": ["ChatPromptTemplate", "BaseChatPromptTemplate", "BasePromptTemplate"],
|
||||
"category": "Prompts",
|
||||
"description": "Schema to represent a chat prompt",
|
||||
"id": "llmChain_0",
|
||||
"label": "LLM Chain",
|
||||
"name": "llmChain",
|
||||
"type": "LLMChain",
|
||||
"baseClasses": ["LLMChain", "BaseChain"],
|
||||
"category": "Chains",
|
||||
"description": "Chain to run queries against LLMs",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "System Message",
|
||||
"name": "systemMessagePrompt",
|
||||
"label": "Chain Name",
|
||||
"name": "chainName",
|
||||
"type": "string",
|
||||
"rows": 3,
|
||||
"placeholder": "You are a helpful assistant that translates {input_language} to {output_language}."
|
||||
},
|
||||
{
|
||||
"label": "Human Message",
|
||||
"name": "humanMessagePrompt",
|
||||
"type": "string",
|
||||
"rows": 3,
|
||||
"placeholder": "{text}"
|
||||
"placeholder": "Name Your Chain",
|
||||
"optional": true,
|
||||
"id": "llmChain_0-input-chainName-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
{
|
||||
"label": "Language Model",
|
||||
"name": "model",
|
||||
"type": "BaseLanguageModel",
|
||||
"id": "llmChain_0-input-model-BaseLanguageModel"
|
||||
},
|
||||
{
|
||||
"label": "Prompt",
|
||||
"name": "prompt",
|
||||
"type": "BasePromptTemplate",
|
||||
"id": "llmChain_0-input-prompt-BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"systemMessagePrompt": "You are a helpful assistant that translates {input_language} to {output_language}.",
|
||||
"humanMessagePrompt": "{input}"
|
||||
"model": "{{chatOpenAI_0.data.instance}}",
|
||||
"prompt": "{{chatPromptTemplate_0.data.instance}}",
|
||||
"chainName": "Language Translation"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "chatPromptTemplate_0-output-chatPromptTemplate-ChatPromptTemplate|BaseChatPromptTemplate|BasePromptTemplate",
|
||||
"name": "chatPromptTemplate",
|
||||
"label": "ChatPromptTemplate",
|
||||
"type": "ChatPromptTemplate | BaseChatPromptTemplate | BasePromptTemplate"
|
||||
"name": "output",
|
||||
"label": "Output",
|
||||
"type": "options",
|
||||
"options": [
|
||||
{
|
||||
"id": "llmChain_0-output-llmChain-LLMChain|BaseChain",
|
||||
"name": "llmChain",
|
||||
"label": "LLM Chain",
|
||||
"type": "LLMChain | BaseChain"
|
||||
},
|
||||
{
|
||||
"id": "llmChain_0-output-outputPrediction-string",
|
||||
"name": "outputPrediction",
|
||||
"label": "Output Prediction",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"default": "llmChain"
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"output": "llmChain"
|
||||
},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"dragging": false,
|
||||
"positionAbsolute": {
|
||||
"x": 524,
|
||||
"y": 237
|
||||
}
|
||||
"x": 1136.5578350285277,
|
||||
"y": 619.2492937692573
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 472,
|
||||
"id": "chatOpenAI_0",
|
||||
"position": {
|
||||
"x": 855.1997276913991,
|
||||
"y": 24.090553068402556
|
||||
"x": 776.3729862229602,
|
||||
"y": 290.4580650723551
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
@@ -77,7 +102,8 @@
|
||||
{
|
||||
"label": "OpenAI Api Key",
|
||||
"name": "openAIApiKey",
|
||||
"type": "password"
|
||||
"type": "password",
|
||||
"id": "chatOpenAI_0-input-openAIApiKey-password"
|
||||
},
|
||||
{
|
||||
"label": "Model Name",
|
||||
@@ -106,20 +132,22 @@
|
||||
}
|
||||
],
|
||||
"default": "gpt-3.5-turbo",
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"id": "chatOpenAI_0-input-modelName-options"
|
||||
},
|
||||
{
|
||||
"label": "Temperature",
|
||||
"name": "temperature",
|
||||
"type": "number",
|
||||
"default": 0.9,
|
||||
"optional": true
|
||||
"optional": true,
|
||||
"id": "chatOpenAI_0-input-temperature-number"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"modelName": "gpt-3.5-turbo",
|
||||
"temperature": 0.9
|
||||
"temperature": "0"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
@@ -129,75 +157,83 @@
|
||||
"type": "ChatOpenAI | BaseChatModel | BaseLanguageModel"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 855.1997276913991,
|
||||
"y": 24.090553068402556
|
||||
"x": 776.3729862229602,
|
||||
"y": 290.4580650723551
|
||||
},
|
||||
"dragging": false
|
||||
},
|
||||
{
|
||||
"width": 300,
|
||||
"height": 461,
|
||||
"id": "llmChain_0",
|
||||
"height": 710,
|
||||
"id": "chatPromptTemplate_0",
|
||||
"position": {
|
||||
"x": 1192.2235692202612,
|
||||
"y": 361.71736677076257
|
||||
"x": 428.40848918154023,
|
||||
"y": 291.77611240963313
|
||||
},
|
||||
"type": "customNode",
|
||||
"data": {
|
||||
"id": "llmChain_0",
|
||||
"label": "LLM Chain",
|
||||
"name": "llmChain",
|
||||
"type": "LLMChain",
|
||||
"baseClasses": ["LLMChain", "BaseChain"],
|
||||
"category": "Chains",
|
||||
"description": "Chain to run queries against LLMs",
|
||||
"id": "chatPromptTemplate_0",
|
||||
"label": "Chat Prompt Template",
|
||||
"name": "chatPromptTemplate",
|
||||
"type": "ChatPromptTemplate",
|
||||
"baseClasses": ["ChatPromptTemplate", "BaseChatPromptTemplate", "BasePromptTemplate"],
|
||||
"category": "Prompts",
|
||||
"description": "Schema to represent a chat prompt",
|
||||
"inputParams": [
|
||||
{
|
||||
"label": "System Message",
|
||||
"name": "systemMessagePrompt",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"placeholder": "You are a helpful assistant that translates {input_language} to {output_language}.",
|
||||
"id": "chatPromptTemplate_0-input-systemMessagePrompt-string"
|
||||
},
|
||||
{
|
||||
"label": "Human Message",
|
||||
"name": "humanMessagePrompt",
|
||||
"type": "string",
|
||||
"rows": 4,
|
||||
"placeholder": "{text}",
|
||||
"id": "chatPromptTemplate_0-input-humanMessagePrompt-string"
|
||||
},
|
||||
{
|
||||
"label": "Format Prompt Values",
|
||||
"name": "promptValues",
|
||||
"type": "string",
|
||||
"rows": 5,
|
||||
"rows": 4,
|
||||
"placeholder": "{\n \"input_language\": \"English\",\n \"output_language\": \"French\"\n}",
|
||||
"optional": true
|
||||
}
|
||||
],
|
||||
"inputAnchors": [
|
||||
{
|
||||
"label": "Language Model",
|
||||
"name": "model",
|
||||
"type": "BaseLanguageModel",
|
||||
"id": "llmChain_0-input-model-BaseLanguageModel"
|
||||
},
|
||||
{
|
||||
"label": "Prompt",
|
||||
"name": "prompt",
|
||||
"type": "BasePromptTemplate",
|
||||
"id": "llmChain_0-input-prompt-BasePromptTemplate"
|
||||
"optional": true,
|
||||
"acceptVariable": true,
|
||||
"list": true,
|
||||
"id": "chatPromptTemplate_0-input-promptValues-string"
|
||||
}
|
||||
],
|
||||
"inputAnchors": [],
|
||||
"inputs": {
|
||||
"model": "{{chatOpenAI_0.data.instance}}",
|
||||
"prompt": "{{chatPromptTemplate_0.data.instance}}",
|
||||
"systemMessagePrompt": "You are a helpful assistant that translates {input_language} to {output_language}.",
|
||||
"humanMessagePrompt": "{input}",
|
||||
"promptValues": "{\n \"input_language\": \"English\",\n \"output_language\": \"French\"\n}"
|
||||
},
|
||||
"outputAnchors": [
|
||||
{
|
||||
"id": "llmChain_0-output-llmChain-LLMChain|BaseChain",
|
||||
"name": "llmChain",
|
||||
"label": "LLMChain",
|
||||
"type": "LLMChain | BaseChain"
|
||||
"id": "chatPromptTemplate_0-output-chatPromptTemplate-ChatPromptTemplate|BaseChatPromptTemplate|BasePromptTemplate",
|
||||
"name": "chatPromptTemplate",
|
||||
"label": "ChatPromptTemplate",
|
||||
"type": "ChatPromptTemplate | BaseChatPromptTemplate | BasePromptTemplate"
|
||||
}
|
||||
],
|
||||
"outputs": {},
|
||||
"selected": false
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1192.2235692202612,
|
||||
"y": 361.71736677076257
|
||||
"x": 428.40848918154023,
|
||||
"y": 291.77611240963313
|
||||
},
|
||||
"dragging": false
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { INodeData } from 'flowise-components'
|
||||
import { IActiveChatflows } from './Interface'
|
||||
import { IActiveChatflows, INodeData, IReactFlowNode } from './Interface'
|
||||
|
||||
/**
|
||||
* This pool is to keep track of active test triggers (event listeners),
|
||||
* so we can clear the event listeners whenever user refresh or exit page
|
||||
* This pool is to keep track of active chatflow pools
|
||||
* so we can prevent building langchain flow all over again
|
||||
*/
|
||||
export class ChatflowPool {
|
||||
activeChatflows: IActiveChatflows = {}
|
||||
@@ -12,9 +11,11 @@ export class ChatflowPool {
|
||||
* Add to the pool
|
||||
* @param {string} chatflowid
|
||||
* @param {INodeData} endingNodeData
|
||||
* @param {IReactFlowNode[]} startingNodes
|
||||
*/
|
||||
add(chatflowid: string, endingNodeData: INodeData) {
|
||||
add(chatflowid: string, endingNodeData: INodeData, startingNodes: IReactFlowNode[]) {
|
||||
this.activeChatflows[chatflowid] = {
|
||||
startingNodes,
|
||||
endingNodeData,
|
||||
inSync: true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { INode, INodeData } from 'flowise-components'
|
||||
import { INode, INodeData as INodeDataFromComponent, INodeParams } from 'flowise-components'
|
||||
|
||||
export type MessageType = 'apiMessage' | 'userMessage'
|
||||
|
||||
@@ -38,6 +38,12 @@ export interface INodeDirectedGraph {
|
||||
[key: string]: string[]
|
||||
}
|
||||
|
||||
export interface INodeData extends INodeDataFromComponent {
|
||||
inputAnchors: INodeParams[]
|
||||
inputParams: INodeParams[]
|
||||
outputAnchors: INodeParams[]
|
||||
}
|
||||
|
||||
export interface IReactFlowNode {
|
||||
id: string
|
||||
position: {
|
||||
@@ -111,6 +117,7 @@ export interface IncomingInput {
|
||||
|
||||
export interface IActiveChatflows {
|
||||
[key: string]: {
|
||||
startingNodes: IReactFlowNode[]
|
||||
endingNodeData: INodeData
|
||||
inSync: boolean
|
||||
}
|
||||
|
||||
@@ -4,15 +4,22 @@ import cors from 'cors'
|
||||
import http from 'http'
|
||||
import * as fs from 'fs'
|
||||
|
||||
import { IChatFlow, IncomingInput, IReactFlowNode, IReactFlowObject } from './Interface'
|
||||
import { getNodeModulesPackagePath, getStartingNodes, buildLangchain, getEndingNode, constructGraphs } from './utils'
|
||||
import { IChatFlow, IncomingInput, IReactFlowNode, IReactFlowObject, INodeData } from './Interface'
|
||||
import {
|
||||
getNodeModulesPackagePath,
|
||||
getStartingNodes,
|
||||
buildLangchain,
|
||||
getEndingNode,
|
||||
constructGraphs,
|
||||
resolveVariables,
|
||||
isStartNodeDependOnInput
|
||||
} from './utils'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { getDataSource } from './DataSource'
|
||||
import { NodesPool } from './NodesPool'
|
||||
import { ChatFlow } from './entity/ChatFlow'
|
||||
import { ChatMessage } from './entity/ChatMessage'
|
||||
import { ChatflowPool } from './ChatflowPool'
|
||||
import { INodeData } from 'flowise-components'
|
||||
|
||||
export class App {
|
||||
app: express.Application
|
||||
@@ -196,12 +203,19 @@ export class App {
|
||||
|
||||
let nodeToExecuteData: INodeData
|
||||
|
||||
/* Check if:
|
||||
* - Node Data already exists in pool
|
||||
* - Still in sync (i.e the flow has not been modified since)
|
||||
* - Flow doesn't start with nodes that depend on incomingInput.question
|
||||
***/
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(this.chatflowPool.activeChatflows, chatflowid) &&
|
||||
this.chatflowPool.activeChatflows[chatflowid].inSync
|
||||
this.chatflowPool.activeChatflows[chatflowid].inSync &&
|
||||
!isStartNodeDependOnInput(this.chatflowPool.activeChatflows[chatflowid].startingNodes)
|
||||
) {
|
||||
nodeToExecuteData = this.chatflowPool.activeChatflows[chatflowid].endingNodeData
|
||||
} else {
|
||||
/*** Get chatflows and prepare data ***/
|
||||
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||
id: chatflowid
|
||||
})
|
||||
@@ -209,33 +223,53 @@ export class App {
|
||||
|
||||
const flowData = chatflow.flowData
|
||||
const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
|
||||
const nodes = parsedFlowData.nodes
|
||||
const edges = parsedFlowData.edges
|
||||
|
||||
/*** Get Ending Node with Directed Graph ***/
|
||||
const { graph, nodeDependencies } = constructGraphs(parsedFlowData.nodes, parsedFlowData.edges)
|
||||
const { graph, nodeDependencies } = constructGraphs(nodes, 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 endingNodeData = nodes.find((nd) => nd.id === endingNodeId)?.data
|
||||
if (!endingNodeData) return res.status(500).send(`Ending node must be either a Chain or Agent`)
|
||||
|
||||
if (
|
||||
endingNodeData.outputs &&
|
||||
Object.keys(endingNodeData.outputs).length &&
|
||||
!Object.values(endingNodeData.outputs).includes(endingNodeData.name)
|
||||
) {
|
||||
return res
|
||||
.status(500)
|
||||
.send(
|
||||
`Output of ${endingNodeData.label} (${endingNodeData.id}) must be ${endingNodeData.label}, can't be an Output Prediction`
|
||||
)
|
||||
}
|
||||
|
||||
/*** Get Starting Nodes with Non-Directed Graph ***/
|
||||
const constructedObj = constructGraphs(parsedFlowData.nodes, parsedFlowData.edges, true)
|
||||
const constructedObj = constructGraphs(nodes, edges, true)
|
||||
const nonDirectedGraph = constructedObj.graph
|
||||
const { startingNodeIds, depthQueue } = getStartingNodes(nonDirectedGraph, endingNodeId)
|
||||
|
||||
/*** BFS to traverse from Starting Nodes to Ending Node ***/
|
||||
const reactFlowNodes = await buildLangchain(
|
||||
startingNodeIds,
|
||||
parsedFlowData.nodes,
|
||||
nodes,
|
||||
graph,
|
||||
depthQueue,
|
||||
this.nodesPool.componentNodes
|
||||
this.nodesPool.componentNodes,
|
||||
incomingInput.question
|
||||
)
|
||||
|
||||
const nodeToExecute = reactFlowNodes.find((node: IReactFlowNode) => node.id === endingNodeId)
|
||||
if (!nodeToExecute) return res.status(404).send(`Node ${endingNodeId} not found`)
|
||||
|
||||
nodeToExecuteData = nodeToExecute.data
|
||||
const reactFlowNodeData: INodeData = resolveVariables(nodeToExecute.data, reactFlowNodes, incomingInput.question)
|
||||
nodeToExecuteData = reactFlowNodeData
|
||||
|
||||
this.chatflowPool.add(chatflowid, nodeToExecuteData)
|
||||
const startingNodes = nodes.filter((nd) => startingNodeIds.includes(nd.id))
|
||||
this.chatflowPool.add(chatflowid, nodeToExecuteData, startingNodes)
|
||||
}
|
||||
|
||||
const nodeInstanceFilePath = this.nodesPool.componentNodes[nodeToExecuteData.name].filePath as string
|
||||
|
||||
@@ -8,10 +8,14 @@ import {
|
||||
INodeDirectedGraph,
|
||||
INodeQueue,
|
||||
IReactFlowEdge,
|
||||
IReactFlowNode
|
||||
IReactFlowNode,
|
||||
IVariableDict,
|
||||
INodeData
|
||||
} from '../Interface'
|
||||
import { cloneDeep, get } from 'lodash'
|
||||
import { ICommonObject, INodeData } from 'flowise-components'
|
||||
import { ICommonObject, getInputVariables } from 'flowise-components'
|
||||
|
||||
const QUESTION_VAR_PREFIX = 'question'
|
||||
|
||||
/**
|
||||
* Returns the home folder path of the user if
|
||||
@@ -166,13 +170,15 @@ export const getEndingNode = (nodeDependencies: INodeDependencies, graph: INodeD
|
||||
* @param {INodeDirectedGraph} graph
|
||||
* @param {IDepthQueue} depthQueue
|
||||
* @param {IComponentNodes} componentNodes
|
||||
* @param {string} question
|
||||
*/
|
||||
export const buildLangchain = async (
|
||||
startingNodeIds: string[],
|
||||
reactFlowNodes: IReactFlowNode[],
|
||||
graph: INodeDirectedGraph,
|
||||
depthQueue: IDepthQueue,
|
||||
componentNodes: IComponentNodes
|
||||
componentNodes: IComponentNodes,
|
||||
question: string
|
||||
) => {
|
||||
const flowNodes = cloneDeep(reactFlowNodes)
|
||||
|
||||
@@ -200,9 +206,9 @@ export const buildLangchain = async (
|
||||
const nodeModule = await import(nodeInstanceFilePath)
|
||||
const newNodeInstance = new nodeModule.nodeClass()
|
||||
|
||||
const reactFlowNodeData: INodeData = resolveVariables(reactFlowNode.data, flowNodes)
|
||||
const reactFlowNodeData: INodeData = resolveVariables(reactFlowNode.data, flowNodes, question)
|
||||
|
||||
flowNodes[nodeIndex].data.instance = await newNodeInstance.init(reactFlowNodeData)
|
||||
flowNodes[nodeIndex].data.instance = await newNodeInstance.init(reactFlowNodeData, question)
|
||||
} catch (e: any) {
|
||||
console.error(e)
|
||||
throw new Error(e)
|
||||
@@ -247,11 +253,14 @@ export const buildLangchain = async (
|
||||
* Get variable value from outputResponses.output
|
||||
* @param {string} paramValue
|
||||
* @param {IReactFlowNode[]} reactFlowNodes
|
||||
* @param {string} question
|
||||
* @param {boolean} isAcceptVariable
|
||||
* @returns {string}
|
||||
*/
|
||||
export const getVariableValue = (paramValue: string, reactFlowNodes: IReactFlowNode[]) => {
|
||||
export const getVariableValue = (paramValue: string, reactFlowNodes: IReactFlowNode[], question: string, isAcceptVariable = false) => {
|
||||
let returnVal = paramValue
|
||||
const variableStack = []
|
||||
const variableDict = {} as IVariableDict
|
||||
let startIdx = 0
|
||||
const endIdx = returnVal.length - 1
|
||||
|
||||
@@ -269,17 +278,36 @@ export const getVariableValue = (paramValue: string, reactFlowNodes: IReactFlowN
|
||||
const variableEndIdx = startIdx
|
||||
const variableFullPath = returnVal.substring(variableStartIdx, variableEndIdx)
|
||||
|
||||
if (isAcceptVariable && variableFullPath === QUESTION_VAR_PREFIX) {
|
||||
variableDict[`{{${variableFullPath}}}`] = question
|
||||
}
|
||||
|
||||
// Split by first occurence of '.' to get just nodeId
|
||||
const [variableNodeId, _] = variableFullPath.split('.')
|
||||
const executedNode = reactFlowNodes.find((nd) => nd.id === variableNodeId)
|
||||
if (executedNode) {
|
||||
const variableInstance = get(executedNode.data, 'instance')
|
||||
returnVal = variableInstance
|
||||
const variableValue = get(executedNode.data, 'instance')
|
||||
if (isAcceptVariable) {
|
||||
variableDict[`{{${variableFullPath}}}`] = variableValue
|
||||
} else {
|
||||
returnVal = variableValue
|
||||
}
|
||||
}
|
||||
variableStack.pop()
|
||||
}
|
||||
startIdx += 1
|
||||
}
|
||||
|
||||
if (isAcceptVariable) {
|
||||
const variablePaths = Object.keys(variableDict)
|
||||
variablePaths.sort() // Sort by length of variable path because longer path could possibly contains nested variable
|
||||
variablePaths.forEach((path) => {
|
||||
const variableValue = variableDict[path]
|
||||
// Replace all occurence
|
||||
returnVal = returnVal.split(path).join(variableValue)
|
||||
})
|
||||
return returnVal
|
||||
}
|
||||
return returnVal
|
||||
}
|
||||
|
||||
@@ -287,25 +315,26 @@ export const getVariableValue = (paramValue: string, reactFlowNodes: IReactFlowN
|
||||
* Loop through each inputs and resolve variable if neccessary
|
||||
* @param {INodeData} reactFlowNodeData
|
||||
* @param {IReactFlowNode[]} reactFlowNodes
|
||||
* @param {string} question
|
||||
* @returns {INodeData}
|
||||
*/
|
||||
export const resolveVariables = (reactFlowNodeData: INodeData, reactFlowNodes: IReactFlowNode[]): INodeData => {
|
||||
export const resolveVariables = (reactFlowNodeData: INodeData, reactFlowNodes: IReactFlowNode[], question: string): INodeData => {
|
||||
const flowNodeData = cloneDeep(reactFlowNodeData)
|
||||
const types = 'inputs'
|
||||
|
||||
const getParamValues = (paramsObj: ICommonObject) => {
|
||||
for (const key in paramsObj) {
|
||||
const paramValue: string = paramsObj[key]
|
||||
|
||||
if (Array.isArray(paramValue)) {
|
||||
const resolvedInstances = []
|
||||
for (const param of paramValue) {
|
||||
const resolvedInstance = getVariableValue(param, reactFlowNodes)
|
||||
const resolvedInstance = getVariableValue(param, reactFlowNodes, question)
|
||||
resolvedInstances.push(resolvedInstance)
|
||||
}
|
||||
paramsObj[key] = resolvedInstances
|
||||
} else {
|
||||
const resolvedInstance = getVariableValue(paramValue, reactFlowNodes)
|
||||
const isAcceptVariable = reactFlowNodeData.inputParams.find((param) => param.name === key)?.acceptVariable ?? false
|
||||
const resolvedInstance = getVariableValue(paramValue, reactFlowNodes, question, isAcceptVariable)
|
||||
paramsObj[key] = resolvedInstance
|
||||
}
|
||||
}
|
||||
@@ -317,3 +346,19 @@ export const resolveVariables = (reactFlowNodeData: INodeData, reactFlowNodes: I
|
||||
|
||||
return flowNodeData
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild flow if LLMChain has dependency on other chains
|
||||
* User Question => Prompt_0 => LLMChain_0 => Prompt-1 => LLMChain_1
|
||||
* @param {IReactFlowNode[]} startingNodes
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isStartNodeDependOnInput = (startingNodes: IReactFlowNode[]): boolean => {
|
||||
for (const node of startingNodes) {
|
||||
for (const inputName in node.data.inputs) {
|
||||
const inputVariables = getInputVariables(node.data.inputs[inputName])
|
||||
if (inputVariables.length > 0) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
PORT=8080
|
||||
@@ -25,6 +25,7 @@
|
||||
"prismjs": "^1.28.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^18.2.0",
|
||||
"react-code-blocks": "^0.0.9-0",
|
||||
"react-datepicker": "^4.8.0",
|
||||
"react-device-detect": "^1.17.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg class="mr-1.5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><rect width="32" height="32" rx="4" fill="#1683a5"></rect><path d="M6.71,14A5,5,0,0,1,8.82,9.29l2.64-2.2c1.67-1.37,2.52-1.41,4.6-1.41H21.7c1.19,0,2.45.27,2.45,1.79s-1.4,1.78-2.45,1.78H15.44a3.31,3.31,0,0,0-2,.89L11.24,12c-.55.44-1,.81-1,1.52v4.41c0,.7.41,1.07,1,1.52l2.16,1.82a3.34,3.34,0,0,0,2,.89H21.7c1.05,0,2.45.23,2.45,1.78s-1.26,1.78-2.45,1.78H16.06c-2.08,0-2.94,0-4.6-1.4L8.82,22.09A5.05,5.05,0,0,1,6.71,17.4Z" fill="#fff"></path></svg>
|
||||
|
After Width: | Height: | Size: 670 B |
@@ -0,0 +1 @@
|
||||
<svg class="mr-1.5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><rect width="32" height="32" fill="#f7df1e"></rect><path d="M21.5,25a3.27,3.27,0,0,0,3,1.83c1.25,0,2-.63,2-1.49,0-1-.81-1.39-2.19-2L23.56,23C21.39,22.1,20,20.94,20,18.49c0-2.25,1.72-4,4.41-4a4.44,4.44,0,0,1,4.27,2.41l-2.34,1.5a2,2,0,0,0-1.93-1.29,1.31,1.31,0,0,0-1.44,1.29c0,.9.56,1.27,1.85,1.83l.75.32c2.55,1.1,4,2.21,4,4.72,0,2.71-2.12,4.19-5,4.19a5.78,5.78,0,0,1-5.48-3.07Zm-10.63.26c.48.84.91,1.55,1.94,1.55s1.61-.39,1.61-1.89V14.69h3V25c0,3.11-1.83,4.53-4.49,4.53a4.66,4.66,0,0,1-4.51-2.75Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 737 B |
@@ -0,0 +1 @@
|
||||
<svg class="mr-1.5" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M15.84.5a16.4,16.4,0,0,0-3.57.32C9.1,1.39,8.53,2.53,8.53,4.64V7.48H16v1H5.77a4.73,4.73,0,0,0-4.7,3.74,14.82,14.82,0,0,0,0,7.54c.57,2.28,1.86,3.82,4,3.82h2.6V20.14a4.73,4.73,0,0,1,4.63-4.63h7.38a3.72,3.72,0,0,0,3.73-3.73V4.64A4.16,4.16,0,0,0,19.65.82,20.49,20.49,0,0,0,15.84.5ZM11.78,2.77a1.39,1.39,0,0,1,1.38,1.46,1.37,1.37,0,0,1-1.38,1.38A1.42,1.42,0,0,1,10.4,4.23,1.44,1.44,0,0,1,11.78,2.77Z" fill="#5a9fd4"></path><path d="M16.16,31.5a16.4,16.4,0,0,0,3.57-.32c3.17-.57,3.74-1.71,3.74-3.82V24.52H16v-1H26.23a4.73,4.73,0,0,0,4.7-3.74,14.82,14.82,0,0,0,0-7.54c-.57-2.28-1.86-3.82-4-3.82h-2.6v3.41a4.73,4.73,0,0,1-4.63,4.63H12.35a3.72,3.72,0,0,0-3.73,3.73v7.14a4.16,4.16,0,0,0,3.73,3.82A20.49,20.49,0,0,0,16.16,31.5Zm4.06-2.27a1.39,1.39,0,0,1-1.38-1.46,1.37,1.37,0,0,1,1.38-1.38,1.42,1.42,0,0,1,1.38,1.38A1.44,1.44,0,0,1,20.22,29.23Z" fill="#ffd43b"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -2,4 +2,5 @@
|
||||
export const gridSpacing = 3
|
||||
export const drawerWidth = 260
|
||||
export const appDrawerWidth = 320
|
||||
export const maxScroll = 100000
|
||||
export const baseURL = process.env.NODE_ENV === 'production' ? window.location.origin : window.location.origin.replace(':8080', ':3000')
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { createContext, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { getUniqueNodeId } from 'utils/genericHelper'
|
||||
import { cloneDeep } from 'lodash'
|
||||
|
||||
const initialValue = {
|
||||
reactFlowInstance: null,
|
||||
setReactFlowInstance: () => {},
|
||||
duplicateNode: () => {},
|
||||
deleteNode: () => {},
|
||||
deleteEdge: () => {}
|
||||
}
|
||||
@@ -40,9 +43,13 @@ export const ReactFlowContext = ({ children }) => {
|
||||
if (node.id === targetNodeId) {
|
||||
let value
|
||||
const inputAnchor = node.data.inputAnchors.find((ancr) => ancr.name === targetInput)
|
||||
const inputParam = node.data.inputParams.find((param) => param.name === targetInput)
|
||||
|
||||
if (inputAnchor && inputAnchor.list) {
|
||||
const values = node.data.inputs[targetInput] || []
|
||||
value = values.filter((item) => !item.includes(sourceNodeId))
|
||||
} else if (inputParam && inputParam.acceptVariable) {
|
||||
value = node.data.inputs[targetInput].replace(`{{${sourceNodeId}.data.instance}}`, '') || ''
|
||||
} else {
|
||||
value = ''
|
||||
}
|
||||
@@ -60,13 +67,53 @@ export const ReactFlowContext = ({ children }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const duplicateNode = (id) => {
|
||||
const nodes = reactFlowInstance.getNodes()
|
||||
const originalNode = nodes.find((n) => n.id === id)
|
||||
if (originalNode) {
|
||||
const newNodeId = getUniqueNodeId(originalNode.data, nodes)
|
||||
const clonedNode = cloneDeep(originalNode)
|
||||
|
||||
const duplicatedNode = {
|
||||
...clonedNode,
|
||||
id: newNodeId,
|
||||
position: {
|
||||
x: clonedNode.position.x + 400,
|
||||
y: clonedNode.position.y
|
||||
},
|
||||
positionAbsolute: {
|
||||
x: clonedNode.positionAbsolute.x + 400,
|
||||
y: clonedNode.positionAbsolute.y
|
||||
},
|
||||
data: {
|
||||
...clonedNode.data,
|
||||
id: newNodeId
|
||||
},
|
||||
selected: false
|
||||
}
|
||||
|
||||
const dataKeys = ['inputParams', 'inputAnchors', 'outputAnchors']
|
||||
|
||||
for (const key of dataKeys) {
|
||||
for (const item of duplicatedNode.data[key]) {
|
||||
if (item.id) {
|
||||
item.id = item.id.replace(id, newNodeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reactFlowInstance.setNodes([...nodes, duplicatedNode])
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<flowContext.Provider
|
||||
value={{
|
||||
reactFlowInstance,
|
||||
setReactFlowInstance,
|
||||
deleteNode,
|
||||
deleteEdge
|
||||
deleteEdge,
|
||||
duplicateNode
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -80,8 +80,6 @@ export default function themePalette(theme) {
|
||||
main: theme.customization.isDarkMode ? theme.colors?.darkPrimary800 : theme.colors?.grey50
|
||||
},
|
||||
canvasHeader: {
|
||||
executionLight: theme.colors?.successLight,
|
||||
executionDark: theme.colors?.successDark,
|
||||
deployLight: theme.colors?.primaryLight,
|
||||
deployDark: theme.colors?.primaryDark,
|
||||
saveLight: theme.colors?.secondaryLight,
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Tabs, Tab, Dialog, DialogContent, DialogTitle, Box } from '@mui/material'
|
||||
import { CopyBlock, atomOneDark } from 'react-code-blocks'
|
||||
import { baseURL } from 'store/constant'
|
||||
import pythonSVG from 'assets/images/python.svg'
|
||||
import javascriptSVG from 'assets/images/javascript.svg'
|
||||
import cURLSVG from 'assets/images/cURL.svg'
|
||||
|
||||
function TabPanel(props) {
|
||||
const { children, value, index, ...other } = props
|
||||
return (
|
||||
<div
|
||||
role='tabpanel'
|
||||
hidden={value !== index}
|
||||
id={`attachment-tabpanel-${index}`}
|
||||
aria-labelledby={`attachment-tab-${index}`}
|
||||
{...other}
|
||||
>
|
||||
{value === index && <Box sx={{ p: 1 }}>{children}</Box>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
TabPanel.propTypes = {
|
||||
children: PropTypes.node,
|
||||
index: PropTypes.number.isRequired,
|
||||
value: PropTypes.number.isRequired
|
||||
}
|
||||
|
||||
function a11yProps(index) {
|
||||
return {
|
||||
id: `attachment-tab-${index}`,
|
||||
'aria-controls': `attachment-tabpanel-${index}`
|
||||
}
|
||||
}
|
||||
|
||||
const APICodeDialog = ({ show, dialogProps, onCancel }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
const codes = ['Python', 'JavaScript', 'cURL']
|
||||
const [value, setValue] = useState(0)
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
setValue(newValue)
|
||||
}
|
||||
|
||||
const getCode = (codeLang) => {
|
||||
if (codeLang === 'Python') {
|
||||
return `import requests
|
||||
|
||||
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}"
|
||||
|
||||
def query(payload):
|
||||
response = requests.post(API_URL, json=payload)
|
||||
return response.json()
|
||||
|
||||
output = query({
|
||||
"question": "Hey, how are you?",
|
||||
})
|
||||
`
|
||||
} else if (codeLang === 'JavaScript') {
|
||||
return `async function query(data) {
|
||||
const response = await fetch(
|
||||
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}",
|
||||
{
|
||||
method: "POST",
|
||||
body: {
|
||||
"question": "Hey, how are you?"
|
||||
},
|
||||
}
|
||||
);
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
`
|
||||
} else if (codeLang === 'cURL') {
|
||||
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
|
||||
-X POST \\
|
||||
-d '{"question": "Hey, how are you?"}'`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
const getLang = (codeLang) => {
|
||||
if (codeLang === 'Python') {
|
||||
return 'python'
|
||||
} else if (codeLang === 'JavaScript') {
|
||||
return 'javascript'
|
||||
} else if (codeLang === 'cURL') {
|
||||
return 'bash'
|
||||
}
|
||||
return 'python'
|
||||
}
|
||||
|
||||
const getSVG = (codeLang) => {
|
||||
if (codeLang === 'Python') {
|
||||
return pythonSVG
|
||||
} else if (codeLang === 'JavaScript') {
|
||||
return javascriptSVG
|
||||
} else if (codeLang === 'cURL') {
|
||||
return cURLSVG
|
||||
}
|
||||
return pythonSVG
|
||||
}
|
||||
|
||||
const component = show ? (
|
||||
<Dialog
|
||||
open={show}
|
||||
fullWidth
|
||||
maxWidth='md'
|
||||
onClose={onCancel}
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
||||
{dialogProps.title}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<Tabs value={value} onChange={handleChange} aria-label='tabs'>
|
||||
{codes.map((codeLang, index) => (
|
||||
<Tab
|
||||
icon={
|
||||
<img
|
||||
style={{ objectFit: 'cover', height: 'auto', width: 'auto', marginLeft: 10 }}
|
||||
src={getSVG(codeLang)}
|
||||
alt='code'
|
||||
/>
|
||||
}
|
||||
iconPosition='left'
|
||||
key={index}
|
||||
label={codeLang}
|
||||
{...a11yProps(index)}
|
||||
></Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
<div style={{ marginTop: 10 }}></div>
|
||||
{codes.map((codeLang, index) => (
|
||||
<TabPanel key={index} value={value} index={index}>
|
||||
<CopyBlock
|
||||
theme={atomOneDark}
|
||||
text={getCode(codeLang)}
|
||||
language={getLang(codeLang)}
|
||||
showLineNumbers={false}
|
||||
wrapLines
|
||||
/>
|
||||
</TabPanel>
|
||||
))}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
APICodeDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func
|
||||
}
|
||||
|
||||
export default APICodeDialog
|
||||
@@ -0,0 +1,6 @@
|
||||
.editor__textarea {
|
||||
outline: 0;
|
||||
}
|
||||
.editor__textarea::placeholder {
|
||||
color: rgba(120, 120, 120, 0.5);
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
Box,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
Typography,
|
||||
Stack
|
||||
} from '@mui/material'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
import { StyledButton } from 'ui-component/button/StyledButton'
|
||||
import { DarkCodeEditor } from 'ui-component/editor/DarkCodeEditor'
|
||||
import { LightCodeEditor } from 'ui-component/editor/LightCodeEditor'
|
||||
|
||||
import './EditPromptValuesDialog.css'
|
||||
import { baseURL } from 'store/constant'
|
||||
|
||||
const EditPromptValuesDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
const languageType = 'json'
|
||||
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [inputParam, setInputParam] = useState(null)
|
||||
const [textCursorPosition, setTextCursorPosition] = useState({})
|
||||
|
||||
useEffect(() => {
|
||||
if (dialogProps.value) setInputValue(dialogProps.value)
|
||||
if (dialogProps.inputParam) setInputParam(dialogProps.inputParam)
|
||||
|
||||
return () => {
|
||||
setInputValue('')
|
||||
setInputParam(null)
|
||||
setTextCursorPosition({})
|
||||
}
|
||||
}, [dialogProps])
|
||||
|
||||
const onMouseUp = (e) => {
|
||||
if (e.target && e.target.selectionEnd && e.target.value) {
|
||||
const cursorPosition = e.target.selectionEnd
|
||||
const textBeforeCursorPosition = e.target.value.substring(0, cursorPosition)
|
||||
const textAfterCursorPosition = e.target.value.substring(cursorPosition, e.target.value.length)
|
||||
const body = {
|
||||
textBeforeCursorPosition,
|
||||
textAfterCursorPosition
|
||||
}
|
||||
setTextCursorPosition(body)
|
||||
} else {
|
||||
setTextCursorPosition({})
|
||||
}
|
||||
}
|
||||
|
||||
const onSelectOutputResponseClick = (node, isUserQuestion = false) => {
|
||||
let variablePath = isUserQuestion ? `question` : `${node.id}.data.instance`
|
||||
if (textCursorPosition) {
|
||||
let newInput = ''
|
||||
if (textCursorPosition.textBeforeCursorPosition === undefined && textCursorPosition.textAfterCursorPosition === undefined)
|
||||
newInput = `${inputValue}${`{{${variablePath}}}`}`
|
||||
else newInput = `${textCursorPosition.textBeforeCursorPosition}{{${variablePath}}}${textCursorPosition.textAfterCursorPosition}`
|
||||
setInputValue(newInput)
|
||||
}
|
||||
}
|
||||
|
||||
const component = show ? (
|
||||
<Dialog open={show} fullWidth maxWidth='md' aria-labelledby='alert-dialog-title' aria-describedby='alert-dialog-description'>
|
||||
<DialogContent>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
{inputParam && inputParam.type === 'string' && (
|
||||
<div style={{ flex: 70 }}>
|
||||
<Typography sx={{ mb: 2, ml: 1 }} variant='h4'>
|
||||
{inputParam.label}
|
||||
</Typography>
|
||||
<PerfectScrollbar
|
||||
style={{
|
||||
border: '1px solid',
|
||||
borderColor: theme.palette.grey['500'],
|
||||
borderRadius: '12px',
|
||||
height: '100%',
|
||||
maxHeight: 'calc(100vh - 220px)',
|
||||
overflowX: 'hidden',
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
{customization.isDarkMode ? (
|
||||
<DarkCodeEditor
|
||||
disabled={dialogProps.disabled}
|
||||
value={inputValue}
|
||||
onValueChange={(code) => setInputValue(code)}
|
||||
placeholder={inputParam.placeholder}
|
||||
type={languageType}
|
||||
onMouseUp={(e) => onMouseUp(e)}
|
||||
onBlur={(e) => onMouseUp(e)}
|
||||
style={{
|
||||
fontSize: '0.875rem',
|
||||
minHeight: 'calc(100vh - 220px)',
|
||||
width: '100%'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<LightCodeEditor
|
||||
disabled={dialogProps.disabled}
|
||||
value={inputValue}
|
||||
onValueChange={(code) => setInputValue(code)}
|
||||
placeholder={inputParam.placeholder}
|
||||
type={languageType}
|
||||
onMouseUp={(e) => onMouseUp(e)}
|
||||
onBlur={(e) => onMouseUp(e)}
|
||||
style={{
|
||||
fontSize: '0.875rem',
|
||||
minHeight: 'calc(100vh - 220px)',
|
||||
width: '100%'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</PerfectScrollbar>
|
||||
</div>
|
||||
)}
|
||||
{!dialogProps.disabled && inputParam && inputParam.acceptVariable && (
|
||||
<div style={{ flex: 30 }}>
|
||||
<Stack flexDirection='row' sx={{ mb: 1, ml: 2 }}>
|
||||
<Typography variant='h4'>Select Variable</Typography>
|
||||
</Stack>
|
||||
<PerfectScrollbar style={{ height: '100%', maxHeight: 'calc(100vh - 220px)', overflowX: 'hidden' }}>
|
||||
<Box sx={{ pl: 2, pr: 2 }}>
|
||||
<List>
|
||||
<ListItemButton
|
||||
sx={{
|
||||
p: 0,
|
||||
borderRadius: `${customization.borderRadius}px`,
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
|
||||
mb: 1
|
||||
}}
|
||||
disabled={dialogProps.disabled}
|
||||
onClick={() => onSelectOutputResponseClick(null, true)}
|
||||
>
|
||||
<ListItem alignItems='center'>
|
||||
<ListItemAvatar>
|
||||
<div
|
||||
style={{
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: 10,
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
alt='AI'
|
||||
src='https://raw.githubusercontent.com/zahidkhawaja/langchain-chat-nextjs/main/public/parroticon.png'
|
||||
/>
|
||||
</div>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
sx={{ ml: 1 }}
|
||||
primary='question'
|
||||
secondary={`User's question from chatbox`}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
{dialogProps.availableNodesForVariable &&
|
||||
dialogProps.availableNodesForVariable.length > 0 &&
|
||||
dialogProps.availableNodesForVariable.map((node, index) => {
|
||||
const selectedOutputAnchor = node.data.outputAnchors[0].options.find(
|
||||
(ancr) => ancr.name === node.data.outputs['output']
|
||||
)
|
||||
return (
|
||||
<ListItemButton
|
||||
key={index}
|
||||
sx={{
|
||||
p: 0,
|
||||
borderRadius: `${customization.borderRadius}px`,
|
||||
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
|
||||
mb: 1
|
||||
}}
|
||||
disabled={dialogProps.disabled}
|
||||
onClick={() => onSelectOutputResponseClick(node)}
|
||||
>
|
||||
<ListItem alignItems='center'>
|
||||
<ListItemAvatar>
|
||||
<div
|
||||
style={{
|
||||
width: 50,
|
||||
height: 50,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: 10,
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
alt={node.data.name}
|
||||
src={`${baseURL}/api/v1/node-icon/${node.data.name}`}
|
||||
/>
|
||||
</div>
|
||||
</ListItemAvatar>
|
||||
<ListItemText
|
||||
sx={{ ml: 1 }}
|
||||
primary={
|
||||
node.data.inputs.chainName ? node.data.inputs.chainName : node.data.id
|
||||
}
|
||||
secondary={`${selectedOutputAnchor?.label ?? 'output'} from ${
|
||||
node.data.label
|
||||
}`}
|
||||
/>
|
||||
</ListItem>
|
||||
</ListItemButton>
|
||||
)
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
</PerfectScrollbar>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>{dialogProps.cancelButtonName}</Button>
|
||||
<StyledButton disabled={dialogProps.disabled} variant='contained' onClick={() => onConfirm(inputValue, inputParam.name)}>
|
||||
{dialogProps.confirmButtonName}
|
||||
</StyledButton>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
EditPromptValuesDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func,
|
||||
onConfirm: PropTypes.func
|
||||
}
|
||||
|
||||
export default EditPromptValuesDialog
|
||||
@@ -18,7 +18,7 @@ const StyledPopper = styled(Popper)({
|
||||
}
|
||||
})
|
||||
|
||||
export const Dropdown = ({ name, value, options, onSelect, disabled = false }) => {
|
||||
export const Dropdown = ({ name, value, options, onSelect, disabled = false, disableClearable = false }) => {
|
||||
const customization = useSelector((state) => state.customization)
|
||||
const findMatchingOptions = (options = [], value) => options.find((option) => option.name === value)
|
||||
const getDefaultOptionValue = () => ''
|
||||
@@ -29,6 +29,7 @@ export const Dropdown = ({ name, value, options, onSelect, disabled = false }) =
|
||||
<Autocomplete
|
||||
id={name}
|
||||
disabled={disabled}
|
||||
disableClearable={disableClearable}
|
||||
size='small'
|
||||
options={options || []}
|
||||
value={findMatchingOptions(options, internalValue) || getDefaultOptionValue()}
|
||||
@@ -59,5 +60,6 @@ Dropdown.propTypes = {
|
||||
value: PropTypes.string,
|
||||
options: PropTypes.array,
|
||||
onSelect: PropTypes.func,
|
||||
disabled: PropTypes.bool
|
||||
disabled: PropTypes.bool,
|
||||
disableClearable: PropTypes.bool
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ import './prism-dark.css'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
|
||||
export const DarkCodeEditor = ({ value, placeholder, type, style, onValueChange, onMouseUp, onBlur }) => {
|
||||
export const DarkCodeEditor = ({ value, placeholder, disabled = false, type, style, onValueChange, onMouseUp, onBlur }) => {
|
||||
const theme = useTheme()
|
||||
|
||||
return (
|
||||
<Editor
|
||||
disabled={disabled}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
highlight={(code) => highlight(code, type === 'json' ? languages.json : languages.js)}
|
||||
@@ -32,6 +33,7 @@ export const DarkCodeEditor = ({ value, placeholder, type, style, onValueChange,
|
||||
DarkCodeEditor.propTypes = {
|
||||
value: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
type: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
onValueChange: PropTypes.func,
|
||||
|
||||
@@ -8,11 +8,12 @@ import './prism-light.css'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
|
||||
export const LightCodeEditor = ({ value, placeholder, type, style, onValueChange, onMouseUp, onBlur }) => {
|
||||
export const LightCodeEditor = ({ value, placeholder, disabled = false, type, style, onValueChange, onMouseUp, onBlur }) => {
|
||||
const theme = useTheme()
|
||||
|
||||
return (
|
||||
<Editor
|
||||
disabled={disabled}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
highlight={(code) => highlight(code, type === 'json' ? languages.json : languages.js)}
|
||||
@@ -32,6 +33,7 @@ export const LightCodeEditor = ({ value, placeholder, type, style, onValueChange
|
||||
LightCodeEditor.propTypes = {
|
||||
value: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
type: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
onValueChange: PropTypes.func,
|
||||
|
||||
@@ -1,28 +1,58 @@
|
||||
import { useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { FormControl, OutlinedInput } from '@mui/material'
|
||||
import EditPromptValuesDialog from 'ui-component/dialog/EditPromptValuesDialog'
|
||||
|
||||
export const Input = ({ inputParam, value, onChange, disabled = false }) => {
|
||||
export const Input = ({ inputParam, value, onChange, disabled = false, showDialog, dialogProps, onDialogCancel, onDialogConfirm }) => {
|
||||
const [myValue, setMyValue] = useState(value ?? '')
|
||||
|
||||
const getInputType = (type) => {
|
||||
switch (type) {
|
||||
case 'string':
|
||||
return 'text'
|
||||
case 'password':
|
||||
return 'password'
|
||||
case 'number':
|
||||
return 'number'
|
||||
default:
|
||||
return 'text'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
|
||||
<OutlinedInput
|
||||
id={inputParam.name}
|
||||
size='small'
|
||||
disabled={disabled}
|
||||
type={inputParam.type === 'string' ? 'text' : inputParam.type}
|
||||
placeholder={inputParam.placeholder}
|
||||
multiline={!!inputParam.rows}
|
||||
maxRows={inputParam.rows || 0}
|
||||
minRows={inputParam.rows || 0}
|
||||
value={myValue}
|
||||
name={inputParam.name}
|
||||
onChange={(e) => {
|
||||
setMyValue(e.target.value)
|
||||
onChange(e.target.value)
|
||||
<>
|
||||
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
|
||||
<OutlinedInput
|
||||
id={inputParam.name}
|
||||
size='small'
|
||||
disabled={disabled}
|
||||
type={getInputType(inputParam.type)}
|
||||
placeholder={inputParam.placeholder}
|
||||
multiline={!!inputParam.rows}
|
||||
rows={inputParam.rows ?? 1}
|
||||
value={myValue}
|
||||
name={inputParam.name}
|
||||
onChange={(e) => {
|
||||
setMyValue(e.target.value)
|
||||
onChange(e.target.value)
|
||||
}}
|
||||
inputProps={{
|
||||
style: {
|
||||
height: inputParam.rows ? '90px' : 'inherit'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<EditPromptValuesDialog
|
||||
show={showDialog}
|
||||
dialogProps={dialogProps}
|
||||
onCancel={onDialogCancel}
|
||||
onConfirm={(newValue, inputParamName) => {
|
||||
setMyValue(newValue)
|
||||
onDialogConfirm(newValue, inputParamName)
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
></EditPromptValuesDialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,5 +60,9 @@ Input.propTypes = {
|
||||
inputParam: PropTypes.object,
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
disabled: PropTypes.bool
|
||||
disabled: PropTypes.bool,
|
||||
showDialog: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onDialogCancel: PropTypes.func,
|
||||
onDialogConfirm: PropTypes.func
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<FormControl sx={{ mt: 1, width: '100%' }} size='small'>
|
||||
<Switch
|
||||
disabled={disabled}
|
||||
checked={myValue}
|
||||
onChange={(event) => {
|
||||
setMyValue(event.target.checked)
|
||||
onChange(event.target.checked)
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
SwitchInput.propTypes = {
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
disabled: PropTypes.bool
|
||||
}
|
||||
@@ -9,13 +9,9 @@ export const TooltipWithParser = ({ title }) => {
|
||||
|
||||
return (
|
||||
<Tooltip title={parser(title)} placement='right'>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<IconButton sx={{ height: 25, width: 25 }}>
|
||||
<Info
|
||||
style={{ background: 'transparent', color: customization.isDarkMode ? 'white' : 'inherit', height: 18, width: 18 }}
|
||||
/>
|
||||
</IconButton>
|
||||
</div>
|
||||
<IconButton sx={{ height: 25, width: 25 }}>
|
||||
<Info style={{ background: 'transparent', color: customization.isDarkMode ? 'white' : 'inherit', height: 18, width: 18 }} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,23 +22,12 @@ export const getUniqueNodeId = (nodeData, nodes) => {
|
||||
return nodeId
|
||||
}
|
||||
|
||||
export const initializeNodeData = (nodeParams) => {
|
||||
export const initializeDefaultNodeData = (nodeParams) => {
|
||||
const initialValues = {}
|
||||
|
||||
for (let i = 0; i < nodeParams.length; i += 1) {
|
||||
const input = nodeParams[i]
|
||||
|
||||
// Load from nodeParams default values
|
||||
initialValues[input.name] = input.default || ''
|
||||
|
||||
// Special case for array, always initialize the item if default is not set
|
||||
if (input.type === 'array' && !input.default) {
|
||||
const newObj = {}
|
||||
for (let j = 0; j < input.array.length; j += 1) {
|
||||
newObj[input.array[j].name] = input.array[j].default || ''
|
||||
}
|
||||
initialValues[input.name] = [newObj]
|
||||
}
|
||||
}
|
||||
|
||||
return initialValues
|
||||
@@ -46,62 +35,119 @@ export const initializeNodeData = (nodeParams) => {
|
||||
|
||||
export const initNode = (nodeData, newNodeId) => {
|
||||
const inputAnchors = []
|
||||
const inputParams = []
|
||||
const incoming = nodeData.inputs ? nodeData.inputs.length : 0
|
||||
const outgoing = 1
|
||||
|
||||
const whitelistTypes = ['asyncOptions', 'options', 'string', 'number', 'boolean', 'password', 'json', 'code', 'date', 'file', 'folder']
|
||||
const whitelistTypes = ['options', 'string', 'number', 'boolean', 'password', 'json', 'code', 'date', 'file', 'folder']
|
||||
|
||||
for (let i = 0; i < incoming; i += 1) {
|
||||
if (whitelistTypes.includes(nodeData.inputs[i].type)) continue
|
||||
const newInput = {
|
||||
...nodeData.inputs[i],
|
||||
id: `${newNodeId}-input-${nodeData.inputs[i].name}-${nodeData.inputs[i].type}`
|
||||
}
|
||||
inputAnchors.push(newInput)
|
||||
if (whitelistTypes.includes(nodeData.inputs[i].type)) {
|
||||
inputParams.push(newInput)
|
||||
} else {
|
||||
inputAnchors.push(newInput)
|
||||
}
|
||||
}
|
||||
|
||||
const outputAnchors = []
|
||||
for (let i = 0; i < outgoing; i += 1) {
|
||||
const newOutput = {
|
||||
id: `${newNodeId}-output-${nodeData.name}-${nodeData.baseClasses.join('|')}`,
|
||||
name: nodeData.name,
|
||||
label: nodeData.type,
|
||||
type: nodeData.baseClasses.join(' | ')
|
||||
if (nodeData.outputs && nodeData.outputs.length) {
|
||||
const options = []
|
||||
for (let j = 0; j < nodeData.outputs.length; j += 1) {
|
||||
let baseClasses = ''
|
||||
let type = ''
|
||||
|
||||
const outputBaseClasses = nodeData.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: `${newNodeId}-output-${nodeData.outputs[j].name}-${baseClasses}`,
|
||||
name: nodeData.outputs[j].name,
|
||||
label: nodeData.outputs[j].label,
|
||||
type
|
||||
}
|
||||
options.push(newOutputOption)
|
||||
}
|
||||
const newOutput = {
|
||||
name: 'output',
|
||||
label: 'Output',
|
||||
type: 'options',
|
||||
options,
|
||||
default: nodeData.outputs[0].name
|
||||
}
|
||||
outputAnchors.push(newOutput)
|
||||
} else {
|
||||
const newOutput = {
|
||||
id: `${newNodeId}-output-${nodeData.name}-${nodeData.baseClasses.join('|')}`,
|
||||
name: nodeData.name,
|
||||
label: nodeData.type,
|
||||
type: nodeData.baseClasses.join(' | ')
|
||||
}
|
||||
outputAnchors.push(newOutput)
|
||||
}
|
||||
outputAnchors.push(newOutput)
|
||||
}
|
||||
|
||||
nodeData.id = newNodeId
|
||||
nodeData.inputAnchors = inputAnchors
|
||||
nodeData.outputAnchors = outputAnchors
|
||||
|
||||
/*
|
||||
Initial inputs = [
|
||||
/* Initial
|
||||
inputs = [
|
||||
{
|
||||
label: 'field_label',
|
||||
name: 'field'
|
||||
label: 'field_label_1',
|
||||
name: 'string'
|
||||
},
|
||||
{
|
||||
label: 'field_label_2',
|
||||
name: 'CustomType'
|
||||
}
|
||||
]
|
||||
|
||||
// Turn into inputs object with default values
|
||||
Converted inputs = { 'field': 'defaultvalue' }
|
||||
=> Convert to inputs, inputParams, inputAnchors
|
||||
|
||||
=> inputs = { 'field': 'defaultvalue' } // Turn into inputs object with default values
|
||||
|
||||
// Move remaining inputs that are not part of inputAnchors to inputParams
|
||||
inputParams = [
|
||||
{
|
||||
label: 'field_label',
|
||||
name: 'field'
|
||||
}
|
||||
]
|
||||
=> // For inputs that are part of whitelistTypes
|
||||
inputParams = [
|
||||
{
|
||||
label: 'field_label_1',
|
||||
name: 'string'
|
||||
}
|
||||
]
|
||||
|
||||
=> // For inputs that are not part of whitelistTypes
|
||||
inputAnchors = [
|
||||
{
|
||||
label: 'field_label_2',
|
||||
name: 'CustomType'
|
||||
}
|
||||
]
|
||||
*/
|
||||
if (nodeData.inputs) {
|
||||
nodeData.inputParams = nodeData.inputs.filter(({ name }) => !nodeData.inputAnchors.some((exclude) => exclude.name === name))
|
||||
nodeData.inputs = initializeNodeData(nodeData.inputs)
|
||||
nodeData.inputAnchors = inputAnchors
|
||||
nodeData.inputParams = inputParams
|
||||
nodeData.inputs = initializeDefaultNodeData(nodeData.inputs)
|
||||
} else {
|
||||
nodeData.inputAnchors = []
|
||||
nodeData.inputParams = []
|
||||
nodeData.inputs = {}
|
||||
}
|
||||
|
||||
if (nodeData.outputs) {
|
||||
nodeData.outputs = initializeDefaultNodeData(outputAnchors)
|
||||
} else {
|
||||
nodeData.outputs = {}
|
||||
}
|
||||
|
||||
nodeData.outputAnchors = outputAnchors
|
||||
nodeData.id = newNodeId
|
||||
|
||||
return nodeData
|
||||
}
|
||||
|
||||
@@ -133,7 +179,9 @@ export const isValidConnection = (connection, reactFlowInstance) => {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
const targetNodeInputAnchor = targetNode.data.inputAnchors.find((ancr) => ancr.id === targetHandle)
|
||||
const targetNodeInputAnchor =
|
||||
targetNode.data.inputAnchors.find((ancr) => ancr.id === targetHandle) ||
|
||||
targetNode.data.inputParams.find((ancr) => ancr.id === targetHandle)
|
||||
if (
|
||||
(targetNodeInputAnchor &&
|
||||
!targetNodeInputAnchor?.list &&
|
||||
@@ -144,7 +192,6 @@ export const isValidConnection = (connection, reactFlowInstance) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -200,6 +247,7 @@ export const generateExportFlowData = (flowData) => {
|
||||
inputAnchors: node.data.inputAnchors,
|
||||
inputs: {},
|
||||
outputAnchors: node.data.outputAnchors,
|
||||
outputs: node.data.outputs,
|
||||
selected: false
|
||||
}
|
||||
|
||||
@@ -225,11 +273,33 @@ export const generateExportFlowData = (flowData) => {
|
||||
return exportJson
|
||||
}
|
||||
|
||||
export const copyToClipboard = (e) => {
|
||||
const src = e.src
|
||||
if (Array.isArray(src) || typeof src === 'object') {
|
||||
navigator.clipboard.writeText(JSON.stringify(src, null, ' '))
|
||||
} else {
|
||||
navigator.clipboard.writeText(src)
|
||||
export const getAvailableNodesForVariable = (nodes, edges, target, targetHandle) => {
|
||||
// example edge id = "llmChain_0-llmChain_0-output-outputPrediction-string-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)
|
||||
}
|
||||
}
|
||||
return parentNodes
|
||||
}
|
||||
|
||||
export const rearrangeToolsOrdering = (newValues, sourceNodeId) => {
|
||||
// RequestsGet and RequestsPost have to be in order before other tools
|
||||
newValues.push(`{{${sourceNodeId}.data.instance}}`)
|
||||
|
||||
const sortKey = (item) => {
|
||||
if (item.includes('requestsGet')) {
|
||||
return 0
|
||||
} else if (item.includes('requestsPost')) {
|
||||
return 1
|
||||
} else {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
newValues.sort((a, b) => sortKey(a) - sortKey(b))
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ import { useTheme } from '@mui/material/styles'
|
||||
import { Avatar, Box, ButtonBase, Typography, Stack, TextField } from '@mui/material'
|
||||
|
||||
// icons
|
||||
import { IconSettings, IconChevronLeft, IconDeviceFloppy, IconPencil, IconCheck, IconX } from '@tabler/icons'
|
||||
import { IconSettings, IconChevronLeft, IconDeviceFloppy, IconPencil, IconCheck, IconX, IconWorldWww } from '@tabler/icons'
|
||||
|
||||
// project imports
|
||||
import Settings from 'views/settings'
|
||||
import SaveChatflowDialog from 'ui-component/dialog/SaveChatflowDialog'
|
||||
import APICodeDialog from 'ui-component/dialog/APICodeDialog'
|
||||
|
||||
// API
|
||||
import chatflowsApi from 'api/chatflows'
|
||||
@@ -35,6 +36,8 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
|
||||
const [flowName, setFlowName] = useState('')
|
||||
const [isSettingsOpen, setSettingsOpen] = useState(false)
|
||||
const [flowDialogOpen, setFlowDialogOpen] = useState(false)
|
||||
const [apiDialogOpen, setAPIDialogOpen] = useState(false)
|
||||
const [apiDialogProps, setAPIDialogProps] = useState({})
|
||||
|
||||
const updateChatflowApi = useApi(chatflowsApi.updateChatflow)
|
||||
const canvas = useSelector((state) => state.canvas)
|
||||
@@ -76,6 +79,14 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
|
||||
}
|
||||
}
|
||||
|
||||
const onAPIDialogClick = () => {
|
||||
setAPIDialogProps({
|
||||
title: 'Use this chatflow with API',
|
||||
chatflowid: chatflow.id
|
||||
})
|
||||
setAPIDialogOpen(true)
|
||||
}
|
||||
|
||||
const onSaveChatflowClick = () => {
|
||||
if (chatflow.id) handleSaveFlow(chatflow.name)
|
||||
else setFlowDialogOpen(true)
|
||||
@@ -219,6 +230,26 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
<ButtonBase title='API Endpoint' sx={{ borderRadius: '50%', mr: 2 }}>
|
||||
<Avatar
|
||||
variant='rounded'
|
||||
sx={{
|
||||
...theme.typography.commonAvatar,
|
||||
...theme.typography.mediumAvatar,
|
||||
transition: 'all .2s ease-in-out',
|
||||
background: theme.palette.canvasHeader.deployLight,
|
||||
color: theme.palette.canvasHeader.deployDark,
|
||||
'&:hover': {
|
||||
background: theme.palette.canvasHeader.deployDark,
|
||||
color: theme.palette.canvasHeader.deployLight
|
||||
}
|
||||
}}
|
||||
color='inherit'
|
||||
onClick={onAPIDialogClick}
|
||||
>
|
||||
<IconWorldWww stroke={1.5} size='1.3rem' />
|
||||
</Avatar>
|
||||
</ButtonBase>
|
||||
<ButtonBase title='Save Chatflow' sx={{ borderRadius: '50%', mr: 2 }}>
|
||||
<Avatar
|
||||
variant='rounded'
|
||||
@@ -277,6 +308,7 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
|
||||
onCancel={() => setFlowDialogOpen(false)}
|
||||
onConfirm={onConfirmSaveName}
|
||||
/>
|
||||
<APICodeDialog show={apiDialogOpen} dialogProps={apiDialogProps} onCancel={() => setAPIDialogOpen(false)} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import NodeOutputHandler from './NodeOutputHandler'
|
||||
|
||||
// const
|
||||
import { baseURL } from 'store/constant'
|
||||
import { IconTrash } from '@tabler/icons'
|
||||
import { IconTrash, IconCopy } from '@tabler/icons'
|
||||
import { flowContext } from 'store/context/ReactFlowContext'
|
||||
|
||||
const CardWrapper = styled(MainCard)(({ theme }) => ({
|
||||
@@ -33,7 +33,7 @@ const CardWrapper = styled(MainCard)(({ theme }) => ({
|
||||
|
||||
const CanvasNode = ({ data }) => {
|
||||
const theme = useTheme()
|
||||
const { deleteNode } = useContext(flowContext)
|
||||
const { deleteNode, duplicateNode } = useContext(flowContext)
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -76,10 +76,22 @@ const CanvasNode = ({ data }) => {
|
||||
</Box>
|
||||
<div style={{ flexGrow: 1 }}></div>
|
||||
<IconButton
|
||||
title='Duplicate'
|
||||
onClick={() => {
|
||||
duplicateNode(data.id)
|
||||
}}
|
||||
sx={{ height: 35, width: 35, '&:hover': { color: theme?.palette.primary.main } }}
|
||||
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
|
||||
>
|
||||
<IconCopy />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title='Delete'
|
||||
onClick={() => {
|
||||
deleteNode(data.id)
|
||||
}}
|
||||
sx={{ height: 35, width: 35, mr: 1 }}
|
||||
sx={{ height: 35, width: 35, mr: 1, '&:hover': { color: 'red' } }}
|
||||
color={theme?.customization?.isDarkMode ? theme.colors?.paper : 'inherit'}
|
||||
>
|
||||
<IconTrash />
|
||||
</IconButton>
|
||||
|
||||
@@ -4,13 +4,17 @@ import { useEffect, useRef, useState, useContext } from 'react'
|
||||
|
||||
// material-ui
|
||||
import { useTheme, styled } from '@mui/material/styles'
|
||||
import { Box, Typography, Tooltip } from '@mui/material'
|
||||
import { Box, Typography, Tooltip, IconButton } from '@mui/material'
|
||||
import { tooltipClasses } from '@mui/material/Tooltip'
|
||||
import { IconArrowsMaximize } from '@tabler/icons'
|
||||
|
||||
// project import
|
||||
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 } from 'utils/genericHelper'
|
||||
import { isValidConnection, getAvailableNodesForVariable } from 'utils/genericHelper'
|
||||
|
||||
const CustomWidthTooltip = styled(({ className, ...props }) => <Tooltip {...props} classes={{ popper: className }} />)({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
@@ -23,9 +27,35 @@ const CustomWidthTooltip = styled(({ className, ...props }) => <Tooltip {...prop
|
||||
const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false }) => {
|
||||
const theme = useTheme()
|
||||
const ref = useRef(null)
|
||||
const { reactFlowInstance } = useContext(flowContext)
|
||||
const updateNodeInternals = useUpdateNodeInternals()
|
||||
const [position, setPosition] = useState(0)
|
||||
const { reactFlowInstance } = useContext(flowContext)
|
||||
const [showExpandDialog, setShowExpandDialog] = useState(false)
|
||||
const [expandDialogProps, setExpandDialogProps] = useState({})
|
||||
|
||||
const onExpandDialogClicked = (value, inputParam) => {
|
||||
const dialogProp = {
|
||||
value,
|
||||
inputParam,
|
||||
disabled,
|
||||
confirmButtonName: 'Save',
|
||||
cancelButtonName: 'Cancel'
|
||||
}
|
||||
|
||||
if (!disabled) {
|
||||
const nodes = reactFlowInstance.getNodes()
|
||||
const edges = reactFlowInstance.getEdges()
|
||||
const nodesForVariable = inputParam.acceptVariable ? getAvailableNodesForVariable(nodes, edges, data.id, inputParam.id) : []
|
||||
dialogProp.availableNodesForVariable = nodesForVariable
|
||||
}
|
||||
setExpandDialogProps(dialogProp)
|
||||
setShowExpandDialog(true)
|
||||
}
|
||||
|
||||
const onExpandDialogSave = (newValue, inputParamName) => {
|
||||
setShowExpandDialog(false)
|
||||
data.inputs[inputParamName] = newValue
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current && ref.current.offsetTop && ref.current.clientHeight) {
|
||||
@@ -68,11 +98,47 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false }) =
|
||||
|
||||
{inputParam && (
|
||||
<>
|
||||
{inputParam.acceptVariable && (
|
||||
<CustomWidthTooltip placement='left' title={inputParam.type}>
|
||||
<Handle
|
||||
type='target'
|
||||
position={Position.Left}
|
||||
key={inputParam.id}
|
||||
id={inputParam.id}
|
||||
isValidConnection={(connection) => isValidConnection(connection, reactFlowInstance)}
|
||||
style={{
|
||||
height: 10,
|
||||
width: 10,
|
||||
backgroundColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary,
|
||||
top: position
|
||||
}}
|
||||
/>
|
||||
</CustomWidthTooltip>
|
||||
)}
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Typography>
|
||||
{inputParam.label}
|
||||
{!inputParam.optional && <span style={{ color: 'red' }}> *</span>}
|
||||
</Typography>
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<Typography>
|
||||
{inputParam.label}
|
||||
{!inputParam.optional && <span style={{ color: 'red' }}> *</span>}
|
||||
</Typography>
|
||||
<div style={{ flexGrow: 1 }}></div>
|
||||
{inputParam.type === 'string' && inputParam.rows && (
|
||||
<IconButton
|
||||
size='small'
|
||||
sx={{
|
||||
height: 25,
|
||||
width: 25
|
||||
}}
|
||||
title='Expand'
|
||||
color='primary'
|
||||
onClick={() =>
|
||||
onExpandDialogClicked(data.inputs[inputParam.name] ?? inputParam.default ?? '', inputParam)
|
||||
}
|
||||
>
|
||||
<IconArrowsMaximize />
|
||||
</IconButton>
|
||||
)}
|
||||
</div>
|
||||
{inputParam.type === 'file' && (
|
||||
<File
|
||||
disabled={disabled}
|
||||
@@ -81,12 +147,23 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false }) =
|
||||
value={data.inputs[inputParam.name] ?? inputParam.default ?? 'Choose a file to upload'}
|
||||
/>
|
||||
)}
|
||||
{inputParam.type === 'boolean' && (
|
||||
<SwitchInput
|
||||
disabled={disabled}
|
||||
onChange={(newValue) => (data.inputs[inputParam.name] = newValue)}
|
||||
value={data.inputs[inputParam.name] ?? inputParam.default ?? false}
|
||||
/>
|
||||
)}
|
||||
{(inputParam.type === 'string' || inputParam.type === 'password' || inputParam.type === 'number') && (
|
||||
<Input
|
||||
disabled={disabled}
|
||||
inputParam={inputParam}
|
||||
onChange={(newValue) => (data.inputs[inputParam.name] = newValue)}
|
||||
value={data.inputs[inputParam.name] ?? inputParam.default ?? ''}
|
||||
showDialog={showExpandDialog}
|
||||
dialogProps={expandDialogProps}
|
||||
onDialogCancel={() => setShowExpandDialog(false)}
|
||||
onDialogConfirm={(newValue, inputParamName) => onExpandDialogSave(newValue, inputParamName)}
|
||||
/>
|
||||
)}
|
||||
{inputParam.type === 'options' && (
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Box, Typography, Tooltip } from '@mui/material'
|
||||
import { tooltipClasses } from '@mui/material/Tooltip'
|
||||
import { flowContext } from 'store/context/ReactFlowContext'
|
||||
import { isValidConnection } from 'utils/genericHelper'
|
||||
import { Dropdown } from 'ui-component/dropdown/Dropdown'
|
||||
|
||||
const CustomWidthTooltip = styled(({ className, ...props }) => <Tooltip {...props} classes={{ popper: className }} />)({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
@@ -17,11 +18,12 @@ const CustomWidthTooltip = styled(({ className, ...props }) => <Tooltip {...prop
|
||||
|
||||
// ===========================|| NodeOutputHandler ||=========================== //
|
||||
|
||||
const NodeOutputHandler = ({ outputAnchor, data }) => {
|
||||
const NodeOutputHandler = ({ outputAnchor, data, disabled = false }) => {
|
||||
const theme = useTheme()
|
||||
const ref = useRef(null)
|
||||
const updateNodeInternals = useUpdateNodeInternals()
|
||||
const [position, setPosition] = useState(0)
|
||||
const [dropdownValue, setDropdownValue] = useState(null)
|
||||
const { reactFlowInstance } = useContext(flowContext)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -39,33 +41,82 @@ const NodeOutputHandler = ({ outputAnchor, data }) => {
|
||||
}, 0)
|
||||
}, [data.id, position, updateNodeInternals])
|
||||
|
||||
useEffect(() => {
|
||||
if (dropdownValue) {
|
||||
setTimeout(() => {
|
||||
updateNodeInternals(data.id)
|
||||
}, 0)
|
||||
}
|
||||
}, [data.id, dropdownValue, updateNodeInternals])
|
||||
|
||||
return (
|
||||
<div ref={ref}>
|
||||
<CustomWidthTooltip placement='right' title={outputAnchor.type}>
|
||||
<Handle
|
||||
type='source'
|
||||
position={Position.Right}
|
||||
key={outputAnchor.id}
|
||||
id={outputAnchor.id}
|
||||
isValidConnection={(connection) => isValidConnection(connection, reactFlowInstance)}
|
||||
style={{
|
||||
height: 10,
|
||||
width: 10,
|
||||
backgroundColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary,
|
||||
top: position
|
||||
}}
|
||||
/>
|
||||
</CustomWidthTooltip>
|
||||
<Box sx={{ p: 2, textAlign: 'end' }}>
|
||||
<Typography>{outputAnchor.label}</Typography>
|
||||
</Box>
|
||||
{outputAnchor.type !== 'options' && !outputAnchor.options && (
|
||||
<>
|
||||
<CustomWidthTooltip placement='right' title={outputAnchor.type}>
|
||||
<Handle
|
||||
type='source'
|
||||
position={Position.Right}
|
||||
key={outputAnchor.id}
|
||||
id={outputAnchor.id}
|
||||
isValidConnection={(connection) => isValidConnection(connection, reactFlowInstance)}
|
||||
style={{
|
||||
height: 10,
|
||||
width: 10,
|
||||
backgroundColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary,
|
||||
top: position
|
||||
}}
|
||||
/>
|
||||
</CustomWidthTooltip>
|
||||
<Box sx={{ p: 2, textAlign: 'end' }}>
|
||||
<Typography>{outputAnchor.label}</Typography>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
{outputAnchor.type === 'options' && outputAnchor.options && outputAnchor.options.length > 0 && (
|
||||
<>
|
||||
<CustomWidthTooltip
|
||||
placement='right'
|
||||
title={
|
||||
outputAnchor.options.find((opt) => opt.name === data.outputs?.[outputAnchor.name])?.type ?? outputAnchor.type
|
||||
}
|
||||
>
|
||||
<Handle
|
||||
type='source'
|
||||
position={Position.Right}
|
||||
id={outputAnchor.options.find((opt) => opt.name === data.outputs?.[outputAnchor.name])?.id ?? ''}
|
||||
isValidConnection={(connection) => isValidConnection(connection, reactFlowInstance)}
|
||||
style={{
|
||||
height: 10,
|
||||
width: 10,
|
||||
backgroundColor: data.selected ? theme.palette.primary.main : theme.palette.text.secondary,
|
||||
top: position
|
||||
}}
|
||||
/>
|
||||
</CustomWidthTooltip>
|
||||
<Box sx={{ p: 2, textAlign: 'end' }}>
|
||||
<Dropdown
|
||||
disabled={disabled}
|
||||
disableClearable={true}
|
||||
name={outputAnchor.name}
|
||||
options={outputAnchor.options}
|
||||
onSelect={(newValue) => {
|
||||
setDropdownValue(newValue)
|
||||
data.outputs[outputAnchor.name] = newValue
|
||||
}}
|
||||
value={data.outputs[outputAnchor.name] ?? outputAnchor.default ?? 'choose an option'}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
NodeOutputHandler.propTypes = {
|
||||
outputAnchor: PropTypes.object,
|
||||
data: PropTypes.object
|
||||
data: PropTypes.object,
|
||||
disabled: PropTypes.bool
|
||||
}
|
||||
|
||||
export default NodeOutputHandler
|
||||
|
||||
@@ -38,7 +38,7 @@ import useConfirm from 'hooks/useConfirm'
|
||||
import { IconX } from '@tabler/icons'
|
||||
|
||||
// utils
|
||||
import { getUniqueNodeId, initNode, getEdgeLabelName } from 'utils/genericHelper'
|
||||
import { getUniqueNodeId, initNode, getEdgeLabelName, rearrangeToolsOrdering } from 'utils/genericHelper'
|
||||
import useNotifier from 'utils/useNotifier'
|
||||
|
||||
const nodeTypes = { customNode: CanvasNode }
|
||||
@@ -108,10 +108,18 @@ const Canvas = () => {
|
||||
setTimeout(() => setDirty(), 0)
|
||||
let value
|
||||
const inputAnchor = node.data.inputAnchors.find((ancr) => ancr.name === targetInput)
|
||||
const inputParam = node.data.inputParams.find((param) => param.name === targetInput)
|
||||
|
||||
if (inputAnchor && inputAnchor.list) {
|
||||
const newValues = node.data.inputs[targetInput] || []
|
||||
newValues.push(`{{${sourceNodeId}.data.instance}}`)
|
||||
if (targetInput === 'tools') {
|
||||
rearrangeToolsOrdering(newValues, sourceNodeId)
|
||||
} else {
|
||||
newValues.push(`{{${sourceNodeId}.data.instance}}`)
|
||||
}
|
||||
value = newValues
|
||||
} else if (inputParam && inputParam.acceptVariable) {
|
||||
value = node.data.inputs[targetInput] || ''
|
||||
} else {
|
||||
value = `{{${sourceNodeId}.data.instance}}`
|
||||
}
|
||||
@@ -489,6 +497,7 @@ const Canvas = () => {
|
||||
onConnect={onConnect}
|
||||
onInit={setReactFlowInstance}
|
||||
fitView
|
||||
minZoom={0.1}
|
||||
>
|
||||
<Controls
|
||||
style={{
|
||||
|
||||
@@ -34,6 +34,8 @@ import useApi from 'hooks/useApi'
|
||||
import useConfirm from 'hooks/useConfirm'
|
||||
import useNotifier from 'utils/useNotifier'
|
||||
|
||||
import { maxScroll } from 'store/constant'
|
||||
|
||||
export const ChatMessage = ({ chatflowid }) => {
|
||||
const theme = useTheme()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
@@ -116,7 +118,7 @@ export const ChatMessage = ({ chatflowid }) => {
|
||||
|
||||
const scrollToBottom = () => {
|
||||
if (ps.current) {
|
||||
ps.current.scrollTo({ top: Number.MAX_SAFE_INTEGER, behavior: 'smooth' })
|
||||
ps.current.scrollTo({ top: maxScroll, behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ const MarketplaceCanvas = () => {
|
||||
nodeTypes={nodeTypes}
|
||||
edgeTypes={edgeTypes}
|
||||
fitView
|
||||
minZoom={0.1}
|
||||
>
|
||||
<Controls
|
||||
style={{
|
||||
|
||||
@@ -88,7 +88,7 @@ const MarketplaceCanvasNode = ({ data }) => {
|
||||
</>
|
||||
)}
|
||||
{data.inputAnchors.map((inputAnchor, index) => (
|
||||
<NodeInputHandler key={index} inputAnchor={inputAnchor} data={data} />
|
||||
<NodeInputHandler disabled={true} key={index} inputAnchor={inputAnchor} data={data} />
|
||||
))}
|
||||
{data.inputParams.map((inputParam, index) => (
|
||||
<NodeInputHandler disabled={true} key={index} inputParam={inputParam} data={data} />
|
||||
@@ -108,7 +108,7 @@ const MarketplaceCanvasNode = ({ data }) => {
|
||||
<Divider />
|
||||
|
||||
{data.outputAnchors.map((outputAnchor, index) => (
|
||||
<NodeOutputHandler key={index} outputAnchor={outputAnchor} data={data} />
|
||||
<NodeOutputHandler disabled={true} key={index} outputAnchor={outputAnchor} data={data} />
|
||||
))}
|
||||
</Box>
|
||||
</CardWrapper>
|
||||
|
||||
Reference in New Issue
Block a user