Merge branch 'main' into feature/RateLimit

This commit is contained in:
chungyau97
2023-09-01 18:42:16 +08:00
9 changed files with 188 additions and 15 deletions
-4
View File
@@ -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 }
@@ -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()
+4
View File
@@ -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"
}
+1 -1
View File
@@ -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
}