Feature/OpenAI Assistant V2 (#2258)

* add gpt4 turbo to assistant

* OpenAI Assistant V2

* update langfuse handler
This commit is contained in:
Henry Heng
2024-04-25 20:14:04 +01:00
committed by GitHub
parent 4782c0f6fc
commit 7360d1d9a6
25 changed files with 23422 additions and 17637 deletions
@@ -0,0 +1,201 @@
import { Request, Response, NextFunction } from 'express'
import { StatusCodes } from 'http-status-codes'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import openAIAssistantVectorStoreService from '../../services/openai-assistants-vector-store'
const getAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params === 'undefined' || !req.params.id) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.getAssistantVectorStore - id not provided!`
)
}
if (typeof req.query === 'undefined' || !req.query.credential) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.getAssistantVectorStore - credential not provided!`
)
}
const apiResponse = await openAIAssistantVectorStoreService.getAssistantVectorStore(req.query.credential as string, req.params.id)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const listAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.query === 'undefined' || !req.query.credential) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.listAssistantVectorStore - credential not provided!`
)
}
const apiResponse = await openAIAssistantVectorStoreService.listAssistantVectorStore(req.query.credential as string)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const createAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => {
try {
if (!req.body) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.createAssistantVectorStore - body not provided!`
)
}
if (typeof req.query === 'undefined' || !req.query.credential) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.createAssistantVectorStore - credential not provided!`
)
}
const apiResponse = await openAIAssistantVectorStoreService.createAssistantVectorStore(req.query.credential as string, req.body)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const updateAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params === 'undefined' || !req.params.id) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.updateAssistantVectorStore - id not provided!`
)
}
if (typeof req.query === 'undefined' || !req.query.credential) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.updateAssistantVectorStore - credential not provided!`
)
}
if (!req.body) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.updateAssistantVectorStore - body not provided!`
)
}
const apiResponse = await openAIAssistantVectorStoreService.updateAssistantVectorStore(
req.query.credential as string,
req.params.id,
req.body
)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const deleteAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.params === 'undefined' || !req.params.id) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.deleteAssistantVectorStore - id not provided!`
)
}
if (typeof req.query === 'undefined' || !req.query.credential) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.updateAssistantVectorStore - credential not provided!`
)
}
const apiResponse = await openAIAssistantVectorStoreService.deleteAssistantVectorStore(
req.query.credential as string,
req.params.id as string
)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const uploadFilesToAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => {
try {
if (!req.body) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore - body not provided!`
)
}
if (typeof req.params === 'undefined' || !req.params.id) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore - id not provided!`
)
}
if (typeof req.query === 'undefined' || !req.query.credential) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore - credential not provided!`
)
}
const files = req.files ?? []
const uploadFiles: { filePath: string; fileName: string }[] = []
if (Array.isArray(files)) {
for (const file of files) {
uploadFiles.push({
filePath: file.path,
fileName: file.originalname
})
}
}
const apiResponse = await openAIAssistantVectorStoreService.uploadFilesToAssistantVectorStore(
req.query.credential as string,
req.params.id as string,
uploadFiles
)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
const deleteFilesFromAssistantVectorStore = async (req: Request, res: Response, next: NextFunction) => {
try {
if (!req.body) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.deleteFilesFromAssistantVectorStore - body not provided!`
)
}
if (typeof req.params === 'undefined' || !req.params.id) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.deleteFilesFromAssistantVectorStore - id not provided!`
)
}
if (typeof req.query === 'undefined' || !req.query.credential) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.deleteFilesFromAssistantVectorStore - credential not provided!`
)
}
const apiResponse = await openAIAssistantVectorStoreService.deleteFilesFromAssistantVectorStore(
req.query.credential as string,
req.params.id as string,
req.body.file_ids
)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getAssistantVectorStore,
listAssistantVectorStore,
createAssistantVectorStore,
updateAssistantVectorStore,
deleteAssistantVectorStore,
uploadFilesToAssistantVectorStore,
deleteFilesFromAssistantVectorStore
}
@@ -1,11 +1,10 @@
import { Request, Response, NextFunction } from 'express'
import path from 'path'
import * as fs from 'fs'
import openaiAssistantsService from '../../services/openai-assistants'
import { getUserHome } from '../../utils'
import contentDisposition from 'content-disposition'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { StatusCodes } from 'http-status-codes'
import { streamStorageFile } from 'flowise-components'
// List available assistants
const getAllOpenaiAssistants = async (req: Request, res: Response, next: NextFunction) => {
@@ -48,27 +47,57 @@ const getSingleOpenaiAssistant = async (req: Request, res: Response, next: NextF
// Download file from assistant
const getFileFromAssistant = async (req: Request, res: Response, next: NextFunction) => {
try {
const filePath = path.join(getUserHome(), '.flowise', 'openai-assistant', req.body.fileName)
//raise error if file path is not absolute
if (!path.isAbsolute(filePath)) return res.status(500).send(`Invalid file path`)
//raise error if file path contains '..'
if (filePath.includes('..')) return res.status(500).send(`Invalid file path`)
//only return from the .flowise openai-assistant folder
if (!(filePath.includes('.flowise') && filePath.includes('openai-assistant'))) return res.status(500).send(`Invalid file path`)
if (fs.existsSync(filePath)) {
res.setHeader('Content-Disposition', contentDisposition(path.basename(filePath)))
const fileStream = fs.createReadStream(filePath)
if (!req.body.chatflowId || !req.body.chatId || !req.body.fileName) {
return res.status(500).send(`Invalid file path`)
}
const chatflowId = req.body.chatflowId as string
const chatId = req.body.chatId as string
const fileName = req.body.fileName as string
res.setHeader('Content-Disposition', contentDisposition(fileName))
const fileStream = await streamStorageFile(chatflowId, chatId, fileName)
if (!fileStream) throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: getFileFromAssistant`)
if (fileStream instanceof fs.ReadStream && fileStream?.pipe) {
fileStream.pipe(res)
} else {
return res.status(404).send(`File ${req.body.fileName} not found`)
res.send(fileStream)
}
} catch (error) {
next(error)
}
}
const uploadAssistantFiles = async (req: Request, res: Response, next: NextFunction) => {
try {
if (typeof req.query === 'undefined' || !req.query.credential) {
throw new InternalFlowiseError(
StatusCodes.PRECONDITION_FAILED,
`Error: openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore - credential not provided!`
)
}
const files = req.files ?? []
const uploadFiles: { filePath: string; fileName: string }[] = []
if (Array.isArray(files)) {
for (const file of files) {
uploadFiles.push({
filePath: file.path,
fileName: file.originalname
})
}
}
const apiResponse = await openaiAssistantsService.uploadFilesToAssistant(req.query.credential as string, uploadFiles)
return res.json(apiResponse)
} catch (error) {
next(error)
}
}
export default {
getAllOpenaiAssistants,
getSingleOpenaiAssistant,
getFileFromAssistant
getFileFromAssistant,
uploadAssistantFiles
}
+1 -1
View File
@@ -135,7 +135,7 @@ export class App {
'/api/v1/components-credentials-icon/',
'/api/v1/chatflows-streaming',
'/api/v1/chatflows-uploads',
'/api/v1/openai-assistants-file',
'/api/v1/openai-assistants-file/download',
'/api/v1/feedback',
'/api/v1/get-upload-file',
'/api/v1/ip'
+2
View File
@@ -25,6 +25,7 @@ import nodeLoadMethodRouter from './node-load-methods'
import nodesRouter from './nodes'
import openaiAssistantsRouter from './openai-assistants'
import openaiAssistantsFileRouter from './openai-assistants-files'
import openaiAssistantsVectorStoreRouter from './openai-assistants-vector-store'
import predictionRouter from './predictions'
import promptListsRouter from './prompts-lists'
import publicChatbotRouter from './public-chatbots'
@@ -65,6 +66,7 @@ router.use('/node-load-method', nodeLoadMethodRouter)
router.use('/nodes', nodesRouter)
router.use('/openai-assistants', openaiAssistantsRouter)
router.use('/openai-assistants-file', openaiAssistantsFileRouter)
router.use('/openai-assistants-vector-store', openaiAssistantsVectorStoreRouter)
router.use('/prediction', predictionRouter)
router.use('/prompts-list', promptListsRouter)
router.use('/public-chatbotConfig', publicChatbotRouter)
@@ -1,8 +1,12 @@
import express from 'express'
import multer from 'multer'
import path from 'path'
import openaiAssistantsController from '../../controllers/openai-assistants'
const router = express.Router()
// CREATE
router.post('/', openaiAssistantsController.getFileFromAssistant)
const router = express.Router()
const upload = multer({ dest: `${path.join(__dirname, '..', '..', '..', 'uploads')}/` })
router.post('/download/', openaiAssistantsController.getFileFromAssistant)
router.post('/upload/', upload.array('files'), openaiAssistantsController.uploadAssistantFiles)
export default router
@@ -0,0 +1,30 @@
import express from 'express'
import multer from 'multer'
import path from 'path'
import openaiAssistantsVectorStoreController from '../../controllers/openai-assistants-vector-store'
const router = express.Router()
const upload = multer({ dest: `${path.join(__dirname, '..', '..', '..', 'uploads')}/` })
// CREATE
router.post('/', openaiAssistantsVectorStoreController.createAssistantVectorStore)
// READ
router.get('/:id', openaiAssistantsVectorStoreController.getAssistantVectorStore)
// LIST
router.get('/', openaiAssistantsVectorStoreController.listAssistantVectorStore)
// UPDATE
router.put(['/', '/:id'], openaiAssistantsVectorStoreController.updateAssistantVectorStore)
// DELETE
router.delete(['/', '/:id'], openaiAssistantsVectorStoreController.deleteAssistantVectorStore)
// POST
router.post('/:id', upload.array('files'), openaiAssistantsVectorStoreController.uploadFilesToAssistantVectorStore)
// DELETE
router.patch(['/', '/:id'], openaiAssistantsVectorStoreController.deleteFilesFromAssistantVectorStore)
export default router
@@ -1,12 +1,10 @@
import OpenAI from 'openai'
import path from 'path'
import * as fs from 'fs'
import { StatusCodes } from 'http-status-codes'
import { uniqWith, isEqual } from 'lodash'
import { uniqWith, isEqual, cloneDeep } from 'lodash'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { Assistant } from '../../database/entities/Assistant'
import { Credential } from '../../database/entities/Credential'
import { getUserHome, decryptCredentialData, getAppVersion } from '../../utils'
import { decryptCredentialData, getAppVersion } from '../../utils'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { getErrorMessage } from '../../errors/utils'
@@ -34,6 +32,7 @@ const createAssistant = async (requestBody: any): Promise<any> => {
}
const openai = new OpenAI({ apiKey: openAIApiKey })
// Prepare tools
let tools = []
if (assistantDetails.tools) {
for (const tool of assistantDetails.tools ?? []) {
@@ -43,40 +42,25 @@ const createAssistant = async (requestBody: any): Promise<any> => {
}
}
if (assistantDetails.uploadFiles) {
// Base64 strings
let files: string[] = []
const fileBase64 = assistantDetails.uploadFiles
if (fileBase64.startsWith('[') && fileBase64.endsWith(']')) {
files = JSON.parse(fileBase64)
} else {
files = [fileBase64]
}
// Save tool_resources to be stored later into database
const savedToolResources = cloneDeep(assistantDetails.tool_resources)
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(path.join(getUserHome(), '.flowise', 'openai-assistant'))) {
fs.mkdirSync(path.dirname(filePath), { recursive: true })
// Cleanup tool_resources for creating assistant
if (assistantDetails.tool_resources) {
for (const toolResource in assistantDetails.tool_resources) {
if (toolResource === 'file_search') {
assistantDetails.tool_resources['file_search'] = {
vector_store_ids: assistantDetails.tool_resources['file_search'].vector_store_ids
}
} else if (toolResource === 'code_interpreter') {
assistantDetails.tool_resources['code_interpreter'] = {
file_ids: assistantDetails.tool_resources['code_interpreter'].file_ids
}
}
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 the assistant doesn't exist, create a new one
if (!assistantDetails.id) {
const newAssistant = await openai.beta.assistants.create({
name: assistantDetails.name,
@@ -84,12 +68,15 @@ const createAssistant = async (requestBody: any): Promise<any> => {
instructions: assistantDetails.instructions,
model: assistantDetails.model,
tools,
file_ids: (assistantDetails.files ?? []).map((file: OpenAI.Files.FileObject) => file.id)
tool_resources: assistantDetails.tool_resources,
temperature: assistantDetails.temperature,
top_p: assistantDetails.top_p
})
assistantDetails.id = newAssistant.id
} else {
const retrievedAssistant = await openai.beta.assistants.retrieve(assistantDetails.id)
let filteredTools = uniqWith([...retrievedAssistant.tools, ...tools], isEqual)
let filteredTools = uniqWith([...retrievedAssistant.tools.filter((tool) => tool.type === 'function'), ...tools], isEqual)
// Remove empty functions
filteredTools = filteredTools.filter((tool) => !(tool.type === 'function' && !(tool as any).function))
await openai.beta.assistants.update(assistantDetails.id, {
@@ -98,17 +85,16 @@ const createAssistant = async (requestBody: any): Promise<any> => {
instructions: assistantDetails.instructions ?? '',
model: assistantDetails.model,
tools: filteredTools,
file_ids: uniqWith(
[...retrievedAssistant.file_ids, ...(assistantDetails.files ?? []).map((file: OpenAI.Files.FileObject) => file.id)],
isEqual
)
tool_resources: assistantDetails.tool_resources,
temperature: assistantDetails.temperature,
top_p: assistantDetails.top_p
})
}
const newAssistantDetails = {
...assistantDetails
}
if (newAssistantDetails.uploadFiles) delete newAssistantDetails.uploadFiles
if (savedToolResources) newAssistantDetails.tool_resources = savedToolResources
requestBody.details = JSON.stringify(newAssistantDetails)
} catch (error) {
@@ -117,7 +103,7 @@ const createAssistant = async (requestBody: any): Promise<any> => {
const newAssistant = new Assistant()
Object.assign(newAssistant, requestBody)
const assistant = await appServer.AppDataSource.getRepository(Assistant).create(newAssistant)
const assistant = appServer.AppDataSource.getRepository(Assistant).create(newAssistant)
const dbResponse = await appServer.AppDataSource.getRepository(Assistant).save(assistant)
await appServer.telemetry.sendTelemetry('assistant_created', {
@@ -249,42 +235,26 @@ const updateAssistant = async (assistantId: string, requestBody: any): Promise<a
}
}
if (assistantDetails.uploadFiles) {
// Base64 strings
let files: string[] = []
const fileBase64 = assistantDetails.uploadFiles
if (fileBase64.startsWith('[') && fileBase64.endsWith(']')) {
files = JSON.parse(fileBase64)
} else {
files = [fileBase64]
}
// Save tool_resources to be stored later into database
const savedToolResources = cloneDeep(assistantDetails.tool_resources)
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(path.join(getUserHome(), '.flowise', 'openai-assistant'))) {
fs.mkdirSync(path.dirname(filePath), { recursive: true })
// Cleanup tool_resources before updating
if (assistantDetails.tool_resources) {
for (const toolResource in assistantDetails.tool_resources) {
if (toolResource === 'file_search') {
assistantDetails.tool_resources['file_search'] = {
vector_store_ids: assistantDetails.tool_resources['file_search'].vector_store_ids
}
} else if (toolResource === 'code_interpreter') {
assistantDetails.tool_resources['code_interpreter'] = {
file_ids: assistantDetails.tool_resources['code_interpreter'].file_ids
}
}
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]
}
const retrievedAssistant = await openai.beta.assistants.retrieve(openAIAssistantId)
let filteredTools = uniqWith([...retrievedAssistant.tools, ...tools], isEqual)
let filteredTools = uniqWith([...retrievedAssistant.tools.filter((tool) => tool.type === 'function'), ...tools], isEqual)
filteredTools = filteredTools.filter((tool) => !(tool.type === 'function' && !(tool as any).function))
await openai.beta.assistants.update(openAIAssistantId, {
@@ -293,23 +263,22 @@ const updateAssistant = async (assistantId: string, requestBody: any): Promise<a
instructions: assistantDetails.instructions,
model: assistantDetails.model,
tools: filteredTools,
file_ids: uniqWith(
[...retrievedAssistant.file_ids, ...(assistantDetails.files ?? []).map((file: OpenAI.Files.FileObject) => file.id)],
isEqual
)
tool_resources: assistantDetails.tool_resources,
temperature: assistantDetails.temperature,
top_p: assistantDetails.top_p
})
const newAssistantDetails = {
...assistantDetails,
id: openAIAssistantId
}
if (newAssistantDetails.uploadFiles) delete newAssistantDetails.uploadFiles
if (savedToolResources) newAssistantDetails.tool_resources = savedToolResources
const updateAssistant = new Assistant()
body.details = JSON.stringify(newAssistantDetails)
Object.assign(updateAssistant, body)
await appServer.AppDataSource.getRepository(Assistant).merge(assistant, updateAssistant)
appServer.AppDataSource.getRepository(Assistant).merge(assistant, updateAssistant)
const dbResponse = await appServer.AppDataSource.getRepository(Assistant).save(assistant)
return dbResponse
} catch (error) {
@@ -0,0 +1,257 @@
import OpenAI from 'openai'
import { StatusCodes } from 'http-status-codes'
import fs from 'fs'
import { Credential } from '../../database/entities/Credential'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { getErrorMessage } from '../../errors/utils'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { decryptCredentialData } from '../../utils'
const getAssistantVectorStore = async (credentialId: string, vectorStoreId: string) => {
try {
const appServer = getRunningExpressApp()
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
id: credentialId
})
if (!credential) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`)
}
// Decrpyt credentialData
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
const openAIApiKey = decryptedCredentialData['openAIApiKey']
if (!openAIApiKey) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`)
}
const openai = new OpenAI({ apiKey: openAIApiKey })
const dbResponse = await openai.beta.vectorStores.retrieve(vectorStoreId)
return dbResponse
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: openaiAssistantsVectorStoreService.getAssistantVectorStore - ${getErrorMessage(error)}`
)
}
}
const listAssistantVectorStore = async (credentialId: string) => {
try {
const appServer = getRunningExpressApp()
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
id: credentialId
})
if (!credential) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`)
}
// Decrpyt credentialData
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
const openAIApiKey = decryptedCredentialData['openAIApiKey']
if (!openAIApiKey) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`)
}
const openai = new OpenAI({ apiKey: openAIApiKey })
const dbResponse = await openai.beta.vectorStores.list()
return dbResponse.data
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: openaiAssistantsVectorStoreService.listAssistantVectorStore - ${getErrorMessage(error)}`
)
}
}
const createAssistantVectorStore = async (credentialId: string, obj: OpenAI.Beta.VectorStores.VectorStoreCreateParams) => {
try {
const appServer = getRunningExpressApp()
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
id: credentialId
})
if (!credential) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`)
}
// Decrpyt credentialData
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
const openAIApiKey = decryptedCredentialData['openAIApiKey']
if (!openAIApiKey) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`)
}
const openai = new OpenAI({ apiKey: openAIApiKey })
const dbResponse = await openai.beta.vectorStores.create(obj)
return dbResponse
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: openaiAssistantsVectorStoreService.createAssistantVectorStore - ${getErrorMessage(error)}`
)
}
}
const updateAssistantVectorStore = async (
credentialId: string,
vectorStoreId: string,
obj: OpenAI.Beta.VectorStores.VectorStoreUpdateParams
) => {
try {
const appServer = getRunningExpressApp()
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
id: credentialId
})
if (!credential) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`)
}
// Decrpyt credentialData
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
const openAIApiKey = decryptedCredentialData['openAIApiKey']
if (!openAIApiKey) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`)
}
const openai = new OpenAI({ apiKey: openAIApiKey })
const dbResponse = await openai.beta.vectorStores.update(vectorStoreId, obj)
const vectorStoreFiles = await openai.beta.vectorStores.files.list(vectorStoreId)
if (vectorStoreFiles.data?.length) {
const files = []
for (const file of vectorStoreFiles.data) {
const fileData = await openai.files.retrieve(file.id)
files.push(fileData)
}
;(dbResponse as any).files = files
}
return dbResponse
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: openaiAssistantsVectorStoreService.updateAssistantVectorStore - ${getErrorMessage(error)}`
)
}
}
const deleteAssistantVectorStore = async (credentialId: string, vectorStoreId: string) => {
try {
const appServer = getRunningExpressApp()
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
id: credentialId
})
if (!credential) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`)
}
// Decrpyt credentialData
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
const openAIApiKey = decryptedCredentialData['openAIApiKey']
if (!openAIApiKey) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`)
}
const openai = new OpenAI({ apiKey: openAIApiKey })
const dbResponse = await openai.beta.vectorStores.del(vectorStoreId)
return dbResponse
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: openaiAssistantsVectorStoreService.deleteAssistantVectorStore - ${getErrorMessage(error)}`
)
}
}
const uploadFilesToAssistantVectorStore = async (
credentialId: string,
vectorStoreId: string,
files: { filePath: string; fileName: string }[]
): Promise<any> => {
try {
const appServer = getRunningExpressApp()
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
id: credentialId
})
if (!credential) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`)
}
// Decrpyt credentialData
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
const openAIApiKey = decryptedCredentialData['openAIApiKey']
if (!openAIApiKey) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`)
}
const openai = new OpenAI({ apiKey: openAIApiKey })
const uploadedFiles = []
for (const file of files) {
const createdFile = await openai.files.create({
file: new File([new Blob([fs.readFileSync(file.filePath)])], file.fileName),
purpose: 'assistants'
})
uploadedFiles.push(createdFile)
fs.unlinkSync(file.filePath)
}
const file_ids = [...uploadedFiles.map((file) => file.id)]
const res = await openai.beta.vectorStores.fileBatches.createAndPoll(vectorStoreId, {
file_ids
})
if (res.status === 'completed' && res.file_counts.completed === uploadedFiles.length) return uploadedFiles
else if (res.status === 'failed')
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
'Error: openaiAssistantsVectorStoreService.uploadFilesToAssistantVectorStore - Upload failed!'
)
else
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
'Error: openaiAssistantsVectorStoreService.uploadFilesToAssistantVectorStore - Upload cancelled!'
)
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: openaiAssistantsVectorStoreService.uploadFilesToAssistantVectorStore - ${getErrorMessage(error)}`
)
}
}
const deleteFilesFromAssistantVectorStore = async (credentialId: string, vectorStoreId: string, file_ids: string[]) => {
try {
const appServer = getRunningExpressApp()
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
id: credentialId
})
if (!credential) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`)
}
// Decrpyt credentialData
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
const openAIApiKey = decryptedCredentialData['openAIApiKey']
if (!openAIApiKey) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`)
}
const openai = new OpenAI({ apiKey: openAIApiKey })
const deletedFileIds = []
let count = 0
for (const file of file_ids) {
const res = await openai.beta.vectorStores.files.del(vectorStoreId, file)
if (res.deleted) {
deletedFileIds.push(file)
count += 1
}
}
return { deletedFileIds, count }
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: openaiAssistantsVectorStoreService.uploadFilesToAssistantVectorStore - ${getErrorMessage(error)}`
)
}
}
export default {
getAssistantVectorStore,
listAssistantVectorStore,
createAssistantVectorStore,
updateAssistantVectorStore,
deleteAssistantVectorStore,
uploadFilesToAssistantVectorStore,
deleteFilesFromAssistantVectorStore
}
@@ -1,4 +1,5 @@
import OpenAI from 'openai'
import fs from 'fs'
import { StatusCodes } from 'http-status-codes'
import { decryptCredentialData } from '../../utils'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
@@ -59,8 +60,18 @@ const getSingleOpenaiAssistant = async (credentialId: string, assistantId: strin
const dbResponse = await openai.beta.assistants.retrieve(assistantId)
const resp = await openai.files.list()
const existingFiles = resp.data ?? []
if (dbResponse.file_ids && dbResponse.file_ids.length) {
;(dbResponse as any).files = existingFiles.filter((file) => dbResponse.file_ids.includes(file.id))
if (dbResponse.tool_resources?.code_interpreter?.file_ids?.length) {
;(dbResponse.tool_resources.code_interpreter as any).files = [
...existingFiles.filter((file) => dbResponse.tool_resources?.code_interpreter?.file_ids?.includes(file.id))
]
}
if (dbResponse.tool_resources?.file_search?.vector_store_ids?.length) {
// Since there can only be 1 vector store per assistant
const vectorStoreId = dbResponse.tool_resources.file_search.vector_store_ids[0]
const vectorStoreFiles = await openai.beta.vectorStores.files.list(vectorStoreId)
const fileIds = vectorStoreFiles.data?.map((file) => file.id) ?? []
;(dbResponse.tool_resources.file_search as any).files = [...existingFiles.filter((file) => fileIds.includes(file.id))]
;(dbResponse.tool_resources.file_search as any).vector_store_object = await openai.beta.vectorStores.retrieve(vectorStoreId)
}
return dbResponse
} catch (error) {
@@ -71,7 +82,38 @@ const getSingleOpenaiAssistant = async (credentialId: string, assistantId: strin
}
}
const uploadFilesToAssistant = async (credentialId: string, files: { filePath: string; fileName: string }[]) => {
const appServer = getRunningExpressApp()
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
id: credentialId
})
if (!credential) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `Credential ${credentialId} not found in the database!`)
}
// Decrpyt credentialData
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
const openAIApiKey = decryptedCredentialData['openAIApiKey']
if (!openAIApiKey) {
throw new InternalFlowiseError(StatusCodes.NOT_FOUND, `OpenAI ApiKey not found`)
}
const openai = new OpenAI({ apiKey: openAIApiKey })
const uploadedFiles = []
for (const file of files) {
const createdFile = await openai.files.create({
file: new File([new Blob([fs.readFileSync(file.filePath)])], file.fileName),
purpose: 'assistants'
})
uploadedFiles.push(createdFile)
fs.unlinkSync(file.filePath)
}
return uploadedFiles
}
export default {
getAllOpenaiAssistants,
getSingleOpenaiAssistant
getSingleOpenaiAssistant,
uploadFilesToAssistant
}
+3
View File
@@ -1066,6 +1066,9 @@ export const isFlowValidForStream = (reactFlowNodes: IReactFlowNode[], endingNod
return false
}
}
// If agent is openAIAssistant, streaming is enabled
if (endingNodeData.name === 'openAIAssistant') return true
} else if (endingNodeData.category === 'Engine') {
// Engines that are available to stream
const whitelistEngine = ['contextChatEngine', 'simpleChatEngine', 'queryEngine', 'subQuestionQueryEngine']