mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-24 05:00:38 +03:00
Merge branch 'main' into feature/RateLimit
This commit is contained in:
@@ -156,10 +156,6 @@ npx flowise start --PORT=3000 --DEBUG=true
|
||||
|
||||
A member of the FlowiseAI team will automatically be notified/assigned when you open a pull request. You can also reach out to us on [Discord](https://discord.gg/jbaHfsRVBW).
|
||||
|
||||
## 📃 Contributor License Agreement
|
||||
|
||||
Before we can merge your contribution you have to sign our [Contributor License Agreement (CLA)](https://cla-assistant.io/FlowiseAI/Flowise). The CLA contains the terms and conditions under which the contribution is submitted. You need to do this only once for your first pull request. Keep in mind that without a signed CLA we cannot merge your contribution.
|
||||
|
||||
## 📜 Code of Conduct
|
||||
|
||||
This project and everyone participating in it are governed by the Code of Conduct which can be found in the [file](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to hello@flowiseai.com.
|
||||
|
||||
@@ -2,8 +2,15 @@ import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||
import { BaseChatModel } from 'langchain/chat_models/base'
|
||||
import { AutoGPT } from 'langchain/experimental/autogpt'
|
||||
import { Tool } from 'langchain/tools'
|
||||
import { AIMessage, HumanMessage, SystemMessage } from 'langchain/schema'
|
||||
import { VectorStoreRetriever } from 'langchain/vectorstores/base'
|
||||
import { flatten } from 'lodash'
|
||||
import { StructuredTool } from 'langchain/tools'
|
||||
import { LLMChain } from 'langchain/chains'
|
||||
import { PromptTemplate } from 'langchain/prompts'
|
||||
|
||||
type ObjectTool = StructuredTool
|
||||
const FINISH_NAME = 'finish'
|
||||
|
||||
class AutoGPT_Agents implements INode {
|
||||
label: string
|
||||
@@ -88,13 +95,107 @@ class AutoGPT_Agents implements INode {
|
||||
|
||||
async run(nodeData: INodeData, input: string): Promise<string> {
|
||||
const executor = nodeData.instance as AutoGPT
|
||||
const model = nodeData.inputs?.model as BaseChatModel
|
||||
|
||||
try {
|
||||
let totalAssistantReply = ''
|
||||
executor.run = async (goals: string[]): Promise<string | undefined> => {
|
||||
const user_input = 'Determine which next command to use, and respond using the format specified above:'
|
||||
let loopCount = 0
|
||||
while (loopCount < executor.maxIterations) {
|
||||
loopCount += 1
|
||||
|
||||
const { text: assistantReply } = await executor.chain.call({
|
||||
goals,
|
||||
user_input,
|
||||
memory: executor.memory,
|
||||
messages: executor.fullMessageHistory
|
||||
})
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('\x1b[92m\x1b[1m\n*****AutoGPT*****\n\x1b[0m\x1b[0m')
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(assistantReply)
|
||||
totalAssistantReply += assistantReply + '\n'
|
||||
executor.fullMessageHistory.push(new HumanMessage(user_input))
|
||||
executor.fullMessageHistory.push(new AIMessage(assistantReply))
|
||||
|
||||
const action = await executor.outputParser.parse(assistantReply)
|
||||
const tools = executor.tools.reduce((acc, tool) => ({ ...acc, [tool.name]: tool }), {} as { [key: string]: ObjectTool })
|
||||
if (action.name === FINISH_NAME) {
|
||||
return action.args.response
|
||||
}
|
||||
let result: string
|
||||
if (action.name in tools) {
|
||||
const tool = tools[action.name]
|
||||
let observation
|
||||
try {
|
||||
observation = await tool.call(action.args)
|
||||
} catch (e) {
|
||||
observation = `Error in args: ${e}`
|
||||
}
|
||||
result = `Command ${tool.name} returned: ${observation}`
|
||||
} else if (action.name === 'ERROR') {
|
||||
result = `Error: ${action.args}. `
|
||||
} else {
|
||||
result = `Unknown command '${action.name}'. Please refer to the 'COMMANDS' list for available commands and only respond in the specified JSON format.`
|
||||
}
|
||||
|
||||
let memoryToAdd = `Assistant Reply: ${assistantReply}\nResult: ${result} `
|
||||
if (executor.feedbackTool) {
|
||||
const feedback = `\n${await executor.feedbackTool.call('Input: ')}`
|
||||
if (feedback === 'q' || feedback === 'stop') {
|
||||
return 'EXITING'
|
||||
}
|
||||
memoryToAdd += feedback
|
||||
}
|
||||
|
||||
const documents = await executor.textSplitter.createDocuments([memoryToAdd])
|
||||
await executor.memory.addDocuments(documents)
|
||||
executor.fullMessageHistory.push(new SystemMessage(result))
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
const res = await executor.run([input])
|
||||
return res || 'I have completed all my tasks.'
|
||||
|
||||
if (!res) {
|
||||
const sentence = `Unfortunately I was not able to complete all the task. Here is the chain of thoughts:`
|
||||
return `${await rephraseString(sentence, model)}\n\`\`\`javascript\n${totalAssistantReply}\n\`\`\`\n`
|
||||
}
|
||||
|
||||
const sentence = `I have completed all my tasks. Here is the chain of thoughts:`
|
||||
let writeFilePath = ''
|
||||
const writeTool = executor.tools.find((tool) => tool.name === 'write_file')
|
||||
if (executor.tools.length && writeTool) {
|
||||
writeFilePath = (writeTool as any).store.basePath
|
||||
}
|
||||
return `${await rephraseString(
|
||||
sentence,
|
||||
model
|
||||
)}\n\`\`\`javascript\n${totalAssistantReply}\n\`\`\`\nAnd the final result:\n\`\`\`javascript\n${res}\n\`\`\`\n${
|
||||
writeFilePath
|
||||
? await rephraseString(
|
||||
`You can download the final result displayed above, or see if a new file has been successfully written to \`${writeFilePath}\``,
|
||||
model
|
||||
)
|
||||
: ''
|
||||
}`
|
||||
} catch (e) {
|
||||
throw new Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rephraseString = async (sentence: string, model: BaseChatModel) => {
|
||||
const promptTemplate = new PromptTemplate({
|
||||
template: 'You are a helpful Assistant that rephrase a sentence: {sentence}',
|
||||
inputVariables: ['sentence']
|
||||
})
|
||||
const chain = new LLMChain({ llm: model, prompt: promptTemplate })
|
||||
const res = await chain.call({ sentence })
|
||||
return res?.text
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: AutoGPT_Agents }
|
||||
|
||||
+8
-7
@@ -2,7 +2,7 @@ import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Inter
|
||||
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
|
||||
import { ChatOpenAI, OpenAIChatInput } from 'langchain/chat_models/openai'
|
||||
|
||||
class ChatOpenAIFineTuned_ChatModels implements INode {
|
||||
class ChatOpenAICustom_ChatModels implements INode {
|
||||
label: string
|
||||
name: string
|
||||
version: number
|
||||
@@ -15,19 +15,20 @@ class ChatOpenAIFineTuned_ChatModels implements INode {
|
||||
inputs: INodeParams[]
|
||||
|
||||
constructor() {
|
||||
this.label = 'ChatOpenAI Fine-Tuned'
|
||||
this.name = 'chatOpenAIFineTuned'
|
||||
this.label = 'ChatOpenAI Custom'
|
||||
this.name = 'chatOpenAICustom'
|
||||
this.version = 1.0
|
||||
this.type = 'ChatOpenAI-FineTuned'
|
||||
this.type = 'ChatOpenAI-Custom'
|
||||
this.icon = 'openai.png'
|
||||
this.category = 'Chat Models'
|
||||
this.description = 'Wrapper around fine-tuned OpenAI LLM that use the Chat endpoint'
|
||||
this.description = 'Custom/FineTuned model using OpenAI Chat compatible API'
|
||||
this.baseClasses = [this.type, ...getBaseClasses(ChatOpenAI)]
|
||||
this.credential = {
|
||||
label: 'Connect Credential',
|
||||
name: 'credential',
|
||||
type: 'credential',
|
||||
credentialNames: ['openAIApi']
|
||||
credentialNames: ['openAIApi'],
|
||||
optional: true
|
||||
}
|
||||
this.inputs = [
|
||||
{
|
||||
@@ -146,4 +147,4 @@ class ChatOpenAIFineTuned_ChatModels implements INode {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { nodeClass: ChatOpenAIFineTuned_ChatModels }
|
||||
module.exports = { nodeClass: ChatOpenAICustom_ChatModels }
|
||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
@@ -18,7 +18,7 @@ class Github_DocumentLoaders implements INode {
|
||||
constructor() {
|
||||
this.label = 'Github'
|
||||
this.name = 'github'
|
||||
this.version = 1.0
|
||||
this.version = 2.0
|
||||
this.type = 'Document'
|
||||
this.icon = 'github.png'
|
||||
this.category = 'Document Loaders'
|
||||
@@ -51,6 +51,34 @@ class Github_DocumentLoaders implements INode {
|
||||
type: 'boolean',
|
||||
optional: true
|
||||
},
|
||||
{
|
||||
label: 'Max Concurrency',
|
||||
name: 'maxConcurrency',
|
||||
type: 'number',
|
||||
step: 1,
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Ignore Paths',
|
||||
name: 'ignorePath',
|
||||
description: 'An array of paths to be ignored',
|
||||
placeholder: `["*.md"]`,
|
||||
type: 'string',
|
||||
rows: 4,
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Max Retries',
|
||||
name: 'maxRetries',
|
||||
description:
|
||||
'The maximum number of retries that can be made for a single call, with an exponential backoff between each attempt. Defaults to 2.',
|
||||
type: 'number',
|
||||
step: 1,
|
||||
optional: true,
|
||||
additionalParams: true
|
||||
},
|
||||
{
|
||||
label: 'Text Splitter',
|
||||
name: 'textSplitter',
|
||||
@@ -73,6 +101,9 @@ class Github_DocumentLoaders implements INode {
|
||||
const recursive = nodeData.inputs?.recursive as boolean
|
||||
const textSplitter = nodeData.inputs?.textSplitter as TextSplitter
|
||||
const metadata = nodeData.inputs?.metadata
|
||||
const maxConcurrency = nodeData.inputs?.maxConcurrency as string
|
||||
const maxRetries = nodeData.inputs?.maxRetries as string
|
||||
const ignorePath = nodeData.inputs?.ignorePath as string
|
||||
|
||||
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
|
||||
const accessToken = getCredentialParam('accessToken', credentialData, nodeData)
|
||||
@@ -84,6 +115,9 @@ class Github_DocumentLoaders implements INode {
|
||||
}
|
||||
|
||||
if (accessToken) githubOptions.accessToken = accessToken
|
||||
if (maxConcurrency) githubOptions.maxConcurrency = parseInt(maxConcurrency, 10)
|
||||
if (maxRetries) githubOptions.maxRetries = parseInt(maxRetries, 10)
|
||||
if (ignorePath) githubOptions.ignorePaths = JSON.parse(ignorePath)
|
||||
|
||||
const loader = new GithubRepoLoader(repoLink, githubOptions)
|
||||
const docs = textSplitter ? await loader.loadAndSplit(textSplitter) : await loader.load()
|
||||
|
||||
@@ -432,6 +432,10 @@ export const getCredentialData = async (selectedCredentialId: string, options: I
|
||||
const databaseEntities = options.databaseEntities as IDatabaseEntity
|
||||
|
||||
try {
|
||||
if (!selectedCredentialId) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const credential = await appDataSource.getRepository(databaseEntities['Credential']).findOneBy({
|
||||
id: selectedCredentialId
|
||||
})
|
||||
|
||||
@@ -265,7 +265,7 @@
|
||||
"id": "github_0",
|
||||
"label": "Github",
|
||||
"name": "github",
|
||||
"version": 1,
|
||||
"version": 2,
|
||||
"type": "Document",
|
||||
"baseClasses": ["Document"],
|
||||
"category": "Document Loaders",
|
||||
@@ -301,6 +301,35 @@
|
||||
"optional": true,
|
||||
"id": "github_0-input-recursive-boolean"
|
||||
},
|
||||
{
|
||||
"label": "Max Concurrency",
|
||||
"name": "maxConcurrency",
|
||||
"type": "number",
|
||||
"step": 1,
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "github_0-input-maxConcurrency-number"
|
||||
},
|
||||
{
|
||||
"label": "Ignore Paths",
|
||||
"name": "ignorePath",
|
||||
"type": "string",
|
||||
"description": "An array of paths to be ignored",
|
||||
"placeholder": "[\"*.md\"]",
|
||||
"rows": 4,
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "github_0-input-ignorePath-string"
|
||||
},
|
||||
{
|
||||
"label": "Max Retries",
|
||||
"name": "maxRetries",
|
||||
"description": "The maximum number of retries that can be made for a single call, with an exponential backoff between each attempt. Defaults to 2.",
|
||||
"type": "number",
|
||||
"optional": true,
|
||||
"additionalParams": true,
|
||||
"id": "github_0-input-maxRetries-number"
|
||||
},
|
||||
{
|
||||
"label": "Metadata",
|
||||
"name": "metadata",
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "todays_date_time",
|
||||
"description": "Useful to get todays day, date and time.",
|
||||
"color": "linear-gradient(rgb(117,118,129), rgb(230,10,250))",
|
||||
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/javascript.svg",
|
||||
"schema": "[]",
|
||||
"func": "const timeZone = 'Australia/Sydney';\nconst options = {\n timeZone: timeZone,\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n weekday: 'long',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: true\n};\nconst today = new Date();\nconst formattedDate = today.toLocaleString('en-GB', options);\nconst result = {\n \"formattedDate\": formattedDate,\n \"timezone\": timeZone\n};\nreturn JSON.stringify(result);\n"
|
||||
}
|
||||
@@ -481,7 +481,7 @@ export const isStartNodeDependOnInput = (startingNodes: IReactFlowNode[], nodes:
|
||||
if (inputVariables.length > 0) return true
|
||||
}
|
||||
}
|
||||
const whitelistNodeNames = ['vectorStoreToDocument']
|
||||
const whitelistNodeNames = ['vectorStoreToDocument', 'autoGPT']
|
||||
for (const node of nodes) {
|
||||
if (whitelistNodeNames.includes(node.data.name)) return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user