Skip to content

Commit

Permalink
Feature/OpenAI Assistant V2 (#2258)
Browse files Browse the repository at this point in the history
* add gpt4 turbo to assistant

* OpenAI Assistant V2

* update langfuse handler
  • Loading branch information
HenryHengZJ committed Apr 25, 2024
1 parent 4782c0f commit 7360d1d
Show file tree
Hide file tree
Showing 25 changed files with 21,405 additions and 15,620 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
},
"resolutions": {
"@qdrant/openapi-typescript-fetch": "1.2.1",
"@google/generative-ai": "^0.7.0"
"@google/generative-ai": "^0.7.0",
"openai": "4.38.3"
}
}
313 changes: 265 additions & 48 deletions packages/components/nodes/agents/OpenAIAssistant/OpenAIAssistant.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"node-html-markdown": "^1.3.0",
"notion-to-md": "^3.1.1",
"object-hash": "^3.0.0",
"openai": "^4.32.1",
"openai": "^4.38.3",
"pdf-parse": "^1.1.1",
"pdfjs-dist": "^3.7.107",
"pg": "^8.11.2",
Expand Down
21 changes: 21 additions & 0 deletions packages/components/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ export class AnalyticHandler {
}

if (langfuseTraceClient) {
langfuseTraceClient.update({
input: {
text: input
}
})
const span = langfuseTraceClient.span({
name,
input: {
Expand Down Expand Up @@ -472,6 +477,14 @@ export class AnalyticHandler {
span.end({
output
})
const langfuseTraceClient = this.handlers['langFuse'].trace[returnIds['langFuse'].trace]
if (langfuseTraceClient) {
langfuseTraceClient.update({
output: {
output
}
})
}
if (shutdown) {
const langfuse: Langfuse = this.handlers['langFuse'].client
await langfuse.shutdownAsync()
Expand Down Expand Up @@ -513,6 +526,14 @@ export class AnalyticHandler {
error
}
})
const langfuseTraceClient = this.handlers['langFuse'].trace[returnIds['langFuse'].trace]
if (langfuseTraceClient) {
langfuseTraceClient.update({
output: {
error
}
})
}
if (shutdown) {
const langfuse: Langfuse = this.handlers['langFuse'].client
await langfuse.shutdownAsync()
Expand Down
6 changes: 4 additions & 2 deletions packages/components/src/storageUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ export const addBase64FilesToStorage = async (file: string, chatflowid: string,
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
const mime = splitDataURI[0].split(':')[1].split(';')[0]

const key = chatflowid + '/' + filename
const Key = chatflowid + '/' + filename
const putObjCmd = new PutObjectCommand({
Bucket,
Key: key,
Key,
ContentEncoding: 'base64', // required for binary data
ContentType: mime,
Body: bf
Expand Down Expand Up @@ -61,6 +61,7 @@ export const addFileToStorage = async (mime: string, bf: Buffer, fileName: strin
Body: bf
})
await s3Client.send(putObjCmd)
return 'FILE-STORAGE::' + fileName
} else {
const dir = path.join(getStoragePath(), ...paths)
if (!fs.existsSync(dir)) {
Expand All @@ -69,6 +70,7 @@ export const addFileToStorage = async (mime: string, bf: Buffer, fileName: strin

const filePath = path.join(dir, fileName)
fs.writeFileSync(filePath, bf)
return 'FILE-STORAGE::' + fileName
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
57 changes: 43 additions & 14 deletions packages/server/src/controllers/openai-assistants/index.ts
Original file line number Diff line number Diff line change
@@ -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) => {
Expand Down Expand Up @@ -48,20 +47,49 @@ 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)
}
Expand All @@ -70,5 +98,6 @@ const getFileFromAssistant = async (req: Request, res: Response, next: NextFunct
export default {
getAllOpenaiAssistants,
getSingleOpenaiAssistant,
getFileFromAssistant
getFileFromAssistant,
uploadAssistantFiles
}
2 changes: 1 addition & 1 deletion packages/server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions packages/server/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 7360d1d

Please sign in to comment.