mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 15:00:57 +03:00
add upload files and tool features
This commit is contained in:
@@ -29,6 +29,7 @@ export interface IChatMessage {
|
||||
content: string
|
||||
chatflowid: string
|
||||
sourceDocuments?: string
|
||||
usedTools?: string
|
||||
chatType: string
|
||||
chatId: string
|
||||
memoryType?: string
|
||||
|
||||
@@ -69,6 +69,10 @@ export default class Start extends Command {
|
||||
logger.error('uncaughtException: ', err)
|
||||
})
|
||||
|
||||
process.on('unhandledRejection', (err) => {
|
||||
logger.error('unhandledRejection: ', err)
|
||||
})
|
||||
|
||||
const { flags } = await this.parse(Start)
|
||||
|
||||
if (flags.PORT) process.env.PORT = flags.PORT
|
||||
|
||||
@@ -20,6 +20,9 @@ export class ChatMessage implements IChatMessage {
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
sourceDocuments?: string
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
usedTools?: string
|
||||
|
||||
@Column()
|
||||
chatType: string
|
||||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddUsedToolsToChatMessage1699481607341 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
const columnExists = await queryRunner.hasColumn('chat_message', 'usedTools')
|
||||
if (!columnExists) queryRunner.query(`ALTER TABLE \`chat_message\` ADD COLUMN \`usedTools\` TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`chat_message\` DROP COLUMN \`usedTools\`;`)
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { AddApiConfig1694099200729 } from './1694099200729-AddApiConfig'
|
||||
import { AddAnalytic1694432361423 } from './1694432361423-AddAnalytic'
|
||||
import { AddChatHistory1694658767766 } from './1694658767766-AddChatHistory'
|
||||
import { AddAssistantEntity1699325775451 } from './1699325775451-AddAssistantEntity'
|
||||
import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedToolsToChatMessage'
|
||||
|
||||
export const mysqlMigrations = [
|
||||
Init1693840429259,
|
||||
@@ -17,5 +18,6 @@ export const mysqlMigrations = [
|
||||
AddApiConfig1694099200729,
|
||||
AddAnalytic1694432361423,
|
||||
AddChatHistory1694658767766,
|
||||
AddAssistantEntity1699325775451
|
||||
AddAssistantEntity1699325775451,
|
||||
AddUsedToolsToChatMessage1699481607341
|
||||
]
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddUsedToolsToChatMessage1699481607341 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" ADD COLUMN IF NOT EXISTS "usedTools" TEXT;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "chat_flow" DROP COLUMN "usedTools";`)
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { AddApiConfig1694099183389 } from './1694099183389-AddApiConfig'
|
||||
import { AddAnalytic1694432361423 } from './1694432361423-AddAnalytic'
|
||||
import { AddChatHistory1694658756136 } from './1694658756136-AddChatHistory'
|
||||
import { AddAssistantEntity1699325775451 } from './1699325775451-AddAssistantEntity'
|
||||
import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedToolsToChatMessage'
|
||||
|
||||
export const postgresMigrations = [
|
||||
Init1693891895163,
|
||||
@@ -17,5 +18,6 @@ export const postgresMigrations = [
|
||||
AddApiConfig1694099183389,
|
||||
AddAnalytic1694432361423,
|
||||
AddChatHistory1694658756136,
|
||||
AddAssistantEntity1699325775451
|
||||
AddAssistantEntity1699325775451,
|
||||
AddUsedToolsToChatMessage1699481607341
|
||||
]
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddUsedToolsToChatMessage1699481607341 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "temp_chat_message" ("id" varchar PRIMARY KEY NOT NULL, "role" varchar NOT NULL, "chatflowid" varchar NOT NULL, "content" text NOT NULL, "sourceDocuments" text, "usedTools" text, "createdDate" datetime NOT NULL DEFAULT (datetime('now')), "chatType" VARCHAR NOT NULL DEFAULT 'INTERNAL', "chatId" VARCHAR NOT NULL, "memoryType" VARCHAR, "sessionId" VARCHAR);`
|
||||
)
|
||||
await queryRunner.query(
|
||||
`INSERT INTO "temp_chat_message" ("id", "role", "chatflowid", "content", "sourceDocuments", "createdDate", "chatType", "chatId", "memoryType", "sessionId") SELECT "id", "role", "chatflowid", "content", "sourceDocuments", "createdDate", "chatType", "chatId", "memoryType", "sessionId" FROM "chat_message";`
|
||||
)
|
||||
await queryRunner.query(`DROP TABLE "chat_message";`)
|
||||
await queryRunner.query(`ALTER TABLE "temp_chat_message" RENAME TO "chat_message";`)
|
||||
await queryRunner.query(`CREATE INDEX "IDX_e574527322272fd838f4f0f3d3" ON "chat_message" ("chatflowid") ;`)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE IF EXISTS "temp_chat_message";`)
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" DROP COLUMN "usedTools";`)
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { AddApiConfig1694090982460 } from './1694090982460-AddApiConfig'
|
||||
import { AddAnalytic1694432361423 } from './1694432361423-AddAnalytic'
|
||||
import { AddChatHistory1694657778173 } from './1694657778173-AddChatHistory'
|
||||
import { AddAssistantEntity1699325775451 } from './1699325775451-AddAssistantEntity'
|
||||
import { AddUsedToolsToChatMessage1699481607341 } from './1699481607341-AddUsedToolsToChatMessage'
|
||||
|
||||
export const sqliteMigrations = [
|
||||
Init1693835579790,
|
||||
@@ -17,5 +18,6 @@ export const sqliteMigrations = [
|
||||
AddApiConfig1694090982460,
|
||||
AddAnalytic1694432361423,
|
||||
AddChatHistory1694657778173,
|
||||
AddAssistantEntity1699325775451
|
||||
AddAssistantEntity1699325775451,
|
||||
AddUsedToolsToChatMessage1699481607341
|
||||
]
|
||||
|
||||
+121
-36
@@ -49,7 +49,8 @@ import {
|
||||
replaceInputsWithConfig,
|
||||
getEncryptionKey,
|
||||
checkMemorySessionId,
|
||||
clearSessionMemoryFromViewMessageDialog
|
||||
clearSessionMemoryFromViewMessageDialog,
|
||||
getUserHome
|
||||
} from './utils'
|
||||
import { cloneDeep, omit } from 'lodash'
|
||||
import { getDataSource } from './DataSource'
|
||||
@@ -670,6 +671,15 @@ export class App {
|
||||
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||
const retrievedAssistant = await openai.beta.assistants.retrieve(req.params.id)
|
||||
|
||||
if (retrievedAssistant.file_ids && retrievedAssistant.file_ids.length) {
|
||||
const files = []
|
||||
for (const file_id of retrievedAssistant.file_ids) {
|
||||
const file = await openai.files.retrieve(file_id)
|
||||
files.push(file)
|
||||
}
|
||||
;(retrievedAssistant as any).files = files
|
||||
}
|
||||
|
||||
return res.json(retrievedAssistant)
|
||||
})
|
||||
|
||||
@@ -701,46 +711,87 @@ export class App {
|
||||
|
||||
const assistantDetails = JSON.parse(body.details)
|
||||
|
||||
if (!assistantDetails.id) {
|
||||
try {
|
||||
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
||||
id: body.credential
|
||||
})
|
||||
try {
|
||||
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
||||
id: body.credential
|
||||
})
|
||||
|
||||
if (!credential) return res.status(404).send(`Credential ${body.credential} not found`)
|
||||
if (!credential) return res.status(404).send(`Credential ${body.credential} not found`)
|
||||
|
||||
// Decrpyt credentialData
|
||||
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
||||
const openAIApiKey = decryptedCredentialData['openAIApiKey']
|
||||
if (!openAIApiKey) return res.status(404).send(`OpenAI ApiKey not found`)
|
||||
// Decrpyt credentialData
|
||||
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
||||
const openAIApiKey = decryptedCredentialData['openAIApiKey']
|
||||
if (!openAIApiKey) return res.status(404).send(`OpenAI ApiKey not found`)
|
||||
|
||||
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||
|
||||
let tools = []
|
||||
if (assistantDetails.tools) {
|
||||
for (const tool of assistantDetails.tools ?? []) {
|
||||
tools.push({
|
||||
type: tool
|
||||
})
|
||||
}
|
||||
let tools = []
|
||||
if (assistantDetails.tools) {
|
||||
for (const tool of assistantDetails.tools ?? []) {
|
||||
tools.push({
|
||||
type: tool
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (assistantDetails.uploadFiles) {
|
||||
// Base64 strings
|
||||
let files: string[] = []
|
||||
const fileBase64 = assistantDetails.uploadFiles
|
||||
if (fileBase64.startsWith('[') && fileBase64.endsWith(']')) {
|
||||
files = JSON.parse(fileBase64)
|
||||
} else {
|
||||
files = [fileBase64]
|
||||
}
|
||||
|
||||
const uploadedFiles = []
|
||||
for (const file of files) {
|
||||
const splitDataURI = file.split(',')
|
||||
const filename = splitDataURI.pop()?.split(':')[1] ?? ''
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
const filePath = path.join(getUserHome(), '.flowise', 'openai-assistant', filename)
|
||||
if (!fs.existsSync(filePath)) fs.writeFileSync(filePath, bf)
|
||||
|
||||
const createdFile = await openai.files.create({
|
||||
file: fs.createReadStream(filePath),
|
||||
purpose: 'assistants'
|
||||
})
|
||||
uploadedFiles.push(createdFile)
|
||||
|
||||
fs.unlinkSync(filePath)
|
||||
}
|
||||
assistantDetails.files = [...assistantDetails.files, ...uploadedFiles]
|
||||
}
|
||||
|
||||
if (!assistantDetails.id) {
|
||||
const newAssistant = await openai.beta.assistants.create({
|
||||
name: assistantDetails.name,
|
||||
description: assistantDetails.description,
|
||||
instructions: assistantDetails.instructions,
|
||||
model: assistantDetails.model,
|
||||
tools
|
||||
tools,
|
||||
file_ids: (assistantDetails.files ?? []).map((file: OpenAI.Files.FileObject) => file.id)
|
||||
})
|
||||
assistantDetails.id = newAssistant.id
|
||||
} else {
|
||||
await openai.beta.assistants.update(assistantDetails.id, {
|
||||
name: assistantDetails.name,
|
||||
description: assistantDetails.description,
|
||||
instructions: assistantDetails.instructions,
|
||||
model: assistantDetails.model,
|
||||
tools,
|
||||
file_ids: (assistantDetails.files ?? []).map((file: OpenAI.Files.FileObject) => file.id)
|
||||
})
|
||||
|
||||
const newAssistantDetails = {
|
||||
...assistantDetails,
|
||||
id: newAssistant.id
|
||||
}
|
||||
|
||||
body.details = JSON.stringify(newAssistantDetails)
|
||||
} catch (error) {
|
||||
return res.status(500).send(`Error creating new assistant: ${error}`)
|
||||
}
|
||||
|
||||
const newAssistantDetails = {
|
||||
...assistantDetails
|
||||
}
|
||||
if (newAssistantDetails.uploadFiles) delete newAssistantDetails.uploadFiles
|
||||
|
||||
body.details = JSON.stringify(newAssistantDetails)
|
||||
} catch (error) {
|
||||
return res.status(500).send(`Error creating new assistant: ${error}`)
|
||||
}
|
||||
|
||||
const newAssistant = new Assistant()
|
||||
@@ -790,18 +841,50 @@ export class App {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (assistantDetails.uploadFiles) {
|
||||
// Base64 strings
|
||||
let files: string[] = []
|
||||
const fileBase64 = assistantDetails.uploadFiles
|
||||
if (fileBase64.startsWith('[') && fileBase64.endsWith(']')) {
|
||||
files = JSON.parse(fileBase64)
|
||||
} else {
|
||||
files = [fileBase64]
|
||||
}
|
||||
|
||||
const uploadedFiles = []
|
||||
for (const file of files) {
|
||||
const splitDataURI = file.split(',')
|
||||
const filename = splitDataURI.pop()?.split(':')[1] ?? ''
|
||||
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||
const filePath = path.join(getUserHome(), '.flowise', 'openai-assistant', filename)
|
||||
if (!fs.existsSync(filePath)) fs.writeFileSync(filePath, bf)
|
||||
|
||||
const createdFile = await openai.files.create({
|
||||
file: fs.createReadStream(filePath),
|
||||
purpose: 'assistants'
|
||||
})
|
||||
uploadedFiles.push(createdFile)
|
||||
|
||||
fs.unlinkSync(filePath)
|
||||
}
|
||||
assistantDetails.files = [...assistantDetails.files, ...uploadedFiles]
|
||||
}
|
||||
|
||||
await openai.beta.assistants.update(openAIAssistantId, {
|
||||
name: assistantDetails.name,
|
||||
description: assistantDetails.description,
|
||||
instructions: assistantDetails.instructions,
|
||||
model: assistantDetails.model,
|
||||
tools
|
||||
tools,
|
||||
file_ids: (assistantDetails.files ?? []).map((file: OpenAI.Files.FileObject) => file.id)
|
||||
})
|
||||
|
||||
const newAssistantDetails = {
|
||||
...assistantDetails,
|
||||
id: openAIAssistantId
|
||||
}
|
||||
if (newAssistantDetails.uploadFiles) delete newAssistantDetails.uploadFiles
|
||||
|
||||
const updateAssistant = new Assistant()
|
||||
body.details = JSON.stringify(newAssistantDetails)
|
||||
@@ -828,14 +911,13 @@ export class App {
|
||||
}
|
||||
|
||||
try {
|
||||
const body = req.body
|
||||
const assistantDetails = JSON.parse(body.details)
|
||||
const assistantDetails = JSON.parse(assistant.details)
|
||||
|
||||
const credential = await this.AppDataSource.getRepository(Credential).findOneBy({
|
||||
id: body.credential
|
||||
id: assistant.credential
|
||||
})
|
||||
|
||||
if (!credential) return res.status(404).send(`Credential ${body.credential} not found`)
|
||||
if (!credential) return res.status(404).send(`Credential ${assistant.credential} not found`)
|
||||
|
||||
// Decrpyt credentialData
|
||||
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
||||
@@ -844,11 +926,13 @@ export class App {
|
||||
|
||||
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||
|
||||
const results = await this.AppDataSource.getRepository(Assistant).delete({ id: req.params.id })
|
||||
|
||||
await openai.beta.assistants.del(assistantDetails.id)
|
||||
|
||||
const results = await this.AppDataSource.getRepository(Assistant).delete({ id: req.params.id })
|
||||
return res.json(results)
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
if (error.status === 404 && error.type === 'invalid_request_error') return res.send('OK')
|
||||
return res.status(500).send(`Error deleting assistant: ${error}`)
|
||||
}
|
||||
})
|
||||
@@ -1389,6 +1473,7 @@ export class App {
|
||||
sessionId
|
||||
}
|
||||
if (result?.sourceDocuments) apiMessage.sourceDocuments = JSON.stringify(result.sourceDocuments)
|
||||
if (result?.usedTools) apiMessage.usedTools = JSON.stringify(result.usedTools)
|
||||
await this.addChatMessage(apiMessage)
|
||||
|
||||
logger.debug(`[server]: Finished running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`)
|
||||
|
||||
Reference in New Issue
Block a user