feature: modularized express routes for reusability, testability, composability and performance (#2030)
* transition GET /api/v1/apikey * transition POST /api/v1/apikey * transition PUT /api/v1/apikey/:id * transition DELETE /api/v1/apikey/:id * Enable e2e tests for api/v1/apikey routes * remove unused addChatflowsCount * Enable e2e tests for api/v1/variables routes * Enable Cypress in GitHub Action * Update main.yml * Update main.yml * Transition GET /api/v1/variables * Enable cypress on github workflow * Transition POST /api/v1/variables * Transition PUT /api/v1/variables * Transition DELETE /api/v1/variables * Transition GET /api/v1/variables * Transition GET /api/v1/chatflows * Transition GET /api/v1/chatflows/:id * Transition POST /api/v1/chatflows * Transition DELETE /api/v1/chatflows/:id * Transition PUT /api/v1/chatflows/:id * Transition GET /api/v1/chatflows/apikey/:apiKey * Transition GET /api/v1/credentials * Transition POST /api/v1/credentials * Transition GET /api/v1/credentials/:id * Transition PUT /api/v1/credentials/:id * Transition DELETE /api/v1/credentials/:id * Transition GET /api/v1/tools * Transition GET /api/v1/tools/:id * Transition POST /api/v1/tools * Transition PUT & DELETE /api/v1/tools/:id * Transition /api/v1/assistants routes * Transition /api/v1/nodes routes * Transition GET /api/v1/chatflows-streaming/:id & GET /api/v1/chatflows-uploads/:id * wip-all-routes * Transition GET /api/v1/public-chatflows/:id & /api/v1/public-chatbotConfig/:id * Remove ts-ignore annotations * Transition GET /api/v1/chatmessage/:id * Transition POST /api/v1/chatmessage/:id * delete /api/v1/chatmessage/:id * transition /api/v1/feedback/:id routes * transition /api/v1/stats/:id * Transition GET /api/v1/openai-assistants/:id * Transition GET /api/v1/openai-assistants * Transition POST /api/v1/openai-assistants-file * transition GET /api/v1/get-upload-path * transition GET /api/v1/get-upload-file * transition GET /api/v1/flow-config/:id * transition POST /api/v1/node-config * transition GET /api/v1/version * transition GET /api/v1/fetch-links * transition POST /api/v1/vector/upsert/:id * transition POST /api/v1/vector/internal-upsert/:id * transition POST /api/v1/load-prompt * Update index.ts * transition POST /api/v1/prompts-list * transition predictions * Update index.ts * transition GET /api/v1/marketplaces/templates * Router update modularity cleanup * extend request interface - express namespace * Update index.ts * add errorMiddleware * Add custom application error handler * Fix pnpm lock file * prediction return and vector upsert * Move the getUploadsConfig into its own file * Remove lint warnings * fix undefined variable value * Fix node-load-method api call * standardize the error message display * Apply review comment bugfixes * Update index.ts * standardize error message display in snack notifications * Error message standard in the UI * Rename flowXpressApp to appServer * Upload middleware fix and axios update * fix async await --------- Co-authored-by: Henry <hzj94@hotmail.com>
This commit is contained in:
parent
ea255db15d
commit
957694a912
|
|
@ -1,17 +1,13 @@
|
||||||
name: Node CI
|
name: Node CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
|
|
@ -31,12 +27,22 @@ jobs:
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
check-latest: false
|
check-latest: false
|
||||||
cache: 'pnpm'
|
|
||||||
|
|
||||||
- run: npm i -g pnpm
|
- run: npm i -g pnpm
|
||||||
|
|
||||||
- run: pnpm install
|
- run: pnpm install
|
||||||
|
- run: ./node_modules/.bin/cypress install
|
||||||
- run: pnpm lint
|
- run: pnpm lint
|
||||||
|
|
||||||
- run: pnpm build
|
- run: pnpm build
|
||||||
|
- name: Install dependencies
|
||||||
|
uses: cypress-io/github-action@v6
|
||||||
|
with:
|
||||||
|
working-directory: ./
|
||||||
|
runTests: false
|
||||||
|
- name: Cypress test
|
||||||
|
uses: cypress-io/github-action@v6
|
||||||
|
with:
|
||||||
|
install: false
|
||||||
|
working-directory: packages/server
|
||||||
|
start: pnpm start
|
||||||
|
wait-on: 'http://localhost:3000'
|
||||||
|
wait-on-timeout: 120
|
||||||
|
browser: chrome
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,19 @@ You can also specify the env variables when using `npx`. For example:
|
||||||
npx flowise start --PORT=3000 --DEBUG=true
|
npx flowise start --PORT=3000 --DEBUG=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 📖 Tests
|
||||||
|
|
||||||
|
We use [Cypress](https://github.com/cypress-io) for our e2e testing. If you want to run the test suite in dev mode please follow this guide:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd Flowise/packages/server
|
||||||
|
pnpm install
|
||||||
|
./node_modules/.bin/cypress install
|
||||||
|
pnpm build
|
||||||
|
#Only for writting new tests on local dev -> pnpm run cypress:open
|
||||||
|
pnpm run e2e
|
||||||
|
```
|
||||||
|
|
||||||
## 📖 Documentation
|
## 📖 Documentation
|
||||||
|
|
||||||
[Flowise Docs](https://docs.flowiseai.com/)
|
[Flowise Docs](https://docs.flowiseai.com/)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { defineConfig } from 'cypress'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
e2e: {
|
||||||
|
setupNodeEvents() {
|
||||||
|
// implement node event listeners here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
describe('E2E suite for api/v1/apikey API endpoint', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/apikey')
|
||||||
|
})
|
||||||
|
|
||||||
|
// DEFAULT TEST ON PAGE LOAD
|
||||||
|
it('displays 1 api key by default', () => {
|
||||||
|
cy.get('table.MuiTable-root tbody tr').should('have.length', 1)
|
||||||
|
cy.get('table.MuiTable-root tbody tr td').first().should('have.text', 'DefaultKey')
|
||||||
|
})
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
it('can add new api key', () => {
|
||||||
|
const newApiKeyItem = 'MafiKey'
|
||||||
|
cy.get('#btn_createApiKey').click()
|
||||||
|
cy.get('#keyName').type(`${newApiKeyItem}`)
|
||||||
|
cy.get('#btn_confirmAddingApiKey').click()
|
||||||
|
cy.get('table.MuiTable-root tbody tr').should('have.length', 2)
|
||||||
|
cy.get('table.MuiTable-root tbody tr').last().find('td').first().should('have.text', newApiKeyItem)
|
||||||
|
})
|
||||||
|
|
||||||
|
// READ
|
||||||
|
it('can retrieve all api keys', () => {
|
||||||
|
cy.get('table.MuiTable-root tbody tr').should('have.length', 2)
|
||||||
|
cy.get('table.MuiTable-root tbody tr').first().find('td').first().should('have.text', 'DefaultKey')
|
||||||
|
cy.get('table.MuiTable-root tbody tr').last().find('td').first().should('have.text', 'MafiKey')
|
||||||
|
})
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
it('can update new api key', () => {
|
||||||
|
const UpdatedApiKeyItem = 'UpsertCloudKey'
|
||||||
|
cy.get('table.MuiTable-root tbody tr').last().find('td').eq(4).find('button').click()
|
||||||
|
cy.get('#keyName').clear().type(`${UpdatedApiKeyItem}`)
|
||||||
|
cy.get('#btn_confirmEditingApiKey').click()
|
||||||
|
cy.get('table.MuiTable-root tbody tr').should('have.length', 2)
|
||||||
|
cy.get('table.MuiTable-root tbody tr').last().find('td').first().should('have.text', UpdatedApiKeyItem)
|
||||||
|
})
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
it('can delete new api key', () => {
|
||||||
|
cy.get('table.MuiTable-root tbody tr').last().find('td').eq(5).find('button').click()
|
||||||
|
cy.get('.MuiDialog-scrollPaper .MuiDialogActions-spacing button').last().click()
|
||||||
|
cy.get('table.MuiTable-root tbody tr').should('have.length', 1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
describe('E2E suite for api/v1/variables API endpoint', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.visit('http://localhost:3000/variables')
|
||||||
|
})
|
||||||
|
|
||||||
|
// DEFAULT TEST ON PAGE LOAD
|
||||||
|
it('displays no variables by default', () => {
|
||||||
|
cy.get('.MuiCardContent-root .MuiStack-root').last().find('div').last().should('have.text', 'No Variables Yet')
|
||||||
|
})
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
it('can add new variable', () => {
|
||||||
|
const newVariableName = 'MafiVariable'
|
||||||
|
const newVariableValue = 'shh!!! secret value'
|
||||||
|
cy.get('#btn_createVariable').click()
|
||||||
|
cy.get('#txtInput_variableName').type(`${newVariableName}`)
|
||||||
|
cy.get('#txtInput_variableValue').type(`${newVariableValue}`)
|
||||||
|
cy.get('.MuiDialogActions-spacing button').click()
|
||||||
|
cy.get('.MuiTable-root tbody tr').should('have.length', 1)
|
||||||
|
cy.get('.MuiTable-root tbody tr').last().find('th').first().find('div').first().should('have.text', newVariableName)
|
||||||
|
})
|
||||||
|
|
||||||
|
// READ
|
||||||
|
it('can retrieve all api keys', () => {
|
||||||
|
const newVariableName = 'MafiVariable'
|
||||||
|
cy.get('.MuiTable-root tbody tr').should('have.length', 1)
|
||||||
|
cy.get('.MuiTable-root tbody tr').last().find('th').first().find('div').first().should('have.text', newVariableName)
|
||||||
|
})
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
it('can update new api key', () => {
|
||||||
|
const updatedVariableName = 'PichiVariable'
|
||||||
|
const updatedVariableValue = 'silence shh! value'
|
||||||
|
cy.get('.MuiTable-root tbody tr').last().find('td').eq(4).find('button').click()
|
||||||
|
cy.get('#txtInput_variableName').clear().type(`${updatedVariableName}`)
|
||||||
|
cy.get('#txtInput_variableValue').clear().type(`${updatedVariableValue}`)
|
||||||
|
cy.get('.MuiDialogActions-spacing button').click()
|
||||||
|
cy.get('.MuiTable-root tbody tr').should('have.length', 1)
|
||||||
|
cy.get('.MuiTable-root tbody tr').last().find('th').first().find('div').first().should('have.text', updatedVariableName)
|
||||||
|
})
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
it('can delete new api key', () => {
|
||||||
|
cy.get('.MuiTable-root tbody tr').last().find('td').eq(5).find('button').click()
|
||||||
|
cy.get('.MuiDialog-scrollPaper .MuiDialogActions-spacing button').last().click()
|
||||||
|
cy.get('.MuiTable-root tbody tr').should('have.length', 0)
|
||||||
|
cy.get('.MuiCardContent-root .MuiStack-root').last().find('div').last().should('have.text', 'No Variables Yet')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
// ***********************************************
|
||||||
|
// This example commands.ts shows you how to
|
||||||
|
// create various custom commands and overwrite
|
||||||
|
// existing commands.
|
||||||
|
//
|
||||||
|
// For more comprehensive examples of custom
|
||||||
|
// commands please read more here:
|
||||||
|
// https://on.cypress.io/custom-commands
|
||||||
|
// ***********************************************
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a parent command --
|
||||||
|
// Cypress.Commands.add('login', (email, password) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a child command --
|
||||||
|
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This is a dual command --
|
||||||
|
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// -- This will overwrite an existing command --
|
||||||
|
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||||
|
//
|
||||||
|
// declare global {
|
||||||
|
// namespace Cypress {
|
||||||
|
// interface Chainable {
|
||||||
|
// login(email: string, password: string): Chainable<void>
|
||||||
|
// drag(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
|
||||||
|
// dismiss(subject: string, options?: Partial<TypeOptions>): Chainable<Element>
|
||||||
|
// visit(originalFn: CommandOriginalFn, url: string, options: Partial<VisitOptions>): Chainable<Element>
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// ***********************************************************
|
||||||
|
// This example support/e2e.ts is processed and
|
||||||
|
// loaded automatically before your test files.
|
||||||
|
//
|
||||||
|
// This is a great place to put global configuration and
|
||||||
|
// behavior that modifies Cypress.
|
||||||
|
//
|
||||||
|
// You can change the location of this file or turn off
|
||||||
|
// automatically serving support files with the
|
||||||
|
// 'supportFile' configuration option.
|
||||||
|
//
|
||||||
|
// You can read more here:
|
||||||
|
// https://on.cypress.io/configuration
|
||||||
|
// ***********************************************************
|
||||||
|
|
||||||
|
// Import commands.js using ES2015 syntax:
|
||||||
|
import './commands'
|
||||||
|
|
||||||
|
// Alternatively you can use CommonJS syntax:
|
||||||
|
// require('./commands')
|
||||||
|
|
@ -36,7 +36,11 @@
|
||||||
"typeorm:migration-generate": "pnpm typeorm migration:generate -d ./src/utils/typeormDataSource.ts",
|
"typeorm:migration-generate": "pnpm typeorm migration:generate -d ./src/utils/typeormDataSource.ts",
|
||||||
"typeorm:migration-run": "pnpm typeorm migration:run -d ./src/utils/typeormDataSource.ts",
|
"typeorm:migration-run": "pnpm typeorm migration:run -d ./src/utils/typeormDataSource.ts",
|
||||||
"watch": "tsc --watch",
|
"watch": "tsc --watch",
|
||||||
"version": "oclif readme && git add README.md"
|
"version": "oclif readme && git add README.md",
|
||||||
|
"cypress:open": "cypress open",
|
||||||
|
"cypress:run": "cypress run",
|
||||||
|
"e2e": "start-server-and-test dev http://localhost:3000 cypress:run",
|
||||||
|
"cypress:ci": "START_SERVER_AND_TEST_INSECURE=1 start-server-and-test start https-get://localhost:3000 cypress:run"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"homepage": "https://flowiseai.com",
|
"homepage": "https://flowiseai.com",
|
||||||
|
|
@ -63,6 +67,8 @@
|
||||||
"express-rate-limit": "^6.9.0",
|
"express-rate-limit": "^6.9.0",
|
||||||
"flowise-components": "workspace:^",
|
"flowise-components": "workspace:^",
|
||||||
"flowise-ui": "workspace:^",
|
"flowise-ui": "workspace:^",
|
||||||
|
"http-errors": "^2.0.0",
|
||||||
|
"http-status-codes": "^2.3.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"moment": "^2.29.3",
|
"moment": "^2.29.3",
|
||||||
"moment-timezone": "^0.5.34",
|
"moment-timezone": "^0.5.34",
|
||||||
|
|
@ -86,11 +92,13 @@
|
||||||
"@types/multer": "^1.4.7",
|
"@types/multer": "^1.4.7",
|
||||||
"@types/sanitize-html": "^2.9.5",
|
"@types/sanitize-html": "^2.9.5",
|
||||||
"concurrently": "^7.1.0",
|
"concurrently": "^7.1.0",
|
||||||
|
"cypress": "^13.7.1",
|
||||||
"nodemon": "^2.0.22",
|
"nodemon": "^2.0.22",
|
||||||
"oclif": "^3",
|
"oclif": "^3",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"run-script-os": "^1.1.6",
|
"run-script-os": "^1.1.6",
|
||||||
"shx": "^0.3.3",
|
"shx": "^0.3.3",
|
||||||
|
"start-server-and-test": "^2.0.3",
|
||||||
"ts-node": "^10.7.0",
|
"ts-node": "^10.7.0",
|
||||||
"tsc-watch": "^6.0.4",
|
"tsc-watch": "^6.0.4",
|
||||||
"typescript": "^4.8.4"
|
"typescript": "^4.8.4"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import { StatusCodes } from 'http-status-codes'
|
||||||
|
import { ApiError } from '../../errors/apiError'
|
||||||
|
import apikeyService from '../../services/apikey'
|
||||||
|
|
||||||
|
// Get api keys
|
||||||
|
const getAllApiKeys = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await apikeyService.getAllApiKeys()
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createApiKey = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body.keyName === 'undefined' || req.body.keyName === '') {
|
||||||
|
throw new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.createApiKey - keyName not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await apikeyService.createApiKey(req.body.keyName)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update api key
|
||||||
|
const updateApiKey = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.updateApiKey - id not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.body.keyName === 'undefined' || req.body.keyName === '') {
|
||||||
|
new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.updateApiKey - keyName not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await apikeyService.updateApiKey(req.params.id, req.body.keyName)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete api key
|
||||||
|
const deleteApiKey = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.deleteApiKey - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await apikeyService.deleteApiKey(req.params.id)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify api key
|
||||||
|
const verifyApiKey = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.apiKey === 'undefined' || req.params.apiKey === '') {
|
||||||
|
new ApiError(StatusCodes.PRECONDITION_FAILED, `Error: apikeyController.verifyApiKey - apiKey not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await apikeyService.verifyApiKey(req.params.apiKey)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createApiKey,
|
||||||
|
deleteApiKey,
|
||||||
|
getAllApiKeys,
|
||||||
|
updateApiKey,
|
||||||
|
verifyApiKey
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import assistantsService from '../../services/assistants'
|
||||||
|
|
||||||
|
const creatAssistant = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: assistantsController.creatAssistant - body not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await assistantsService.creatAssistant(req.body)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteAssistant = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: assistantsController.deleteAssistant - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await assistantsService.deleteAssistant(req.params.id, req.query.isDeleteBoth)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllAssistants = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await assistantsService.getAllAssistants()
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAssistantById = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: assistantsController.getAssistantById - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await assistantsService.getAssistantById(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateAssistant = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: assistantsController.updateAssistant - id not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: assistantsController.updateAssistant - body not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await assistantsService.updateAssistant(req.params.id, req.body)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
creatAssistant,
|
||||||
|
deleteAssistant,
|
||||||
|
getAllAssistants,
|
||||||
|
getAssistantById,
|
||||||
|
updateAssistant
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import { chatType, IReactFlowObject } from '../../Interface'
|
||||||
|
import chatflowsService from '../../services/chatflows'
|
||||||
|
import chatMessagesService from '../../services/chat-messages'
|
||||||
|
import { clearSessionMemory } from '../../utils'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import { FindOptionsWhere } from 'typeorm'
|
||||||
|
import { ChatMessage } from '../../database/entities/ChatMessage'
|
||||||
|
|
||||||
|
const createChatMessage = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error('Error: chatMessagesController.createChatMessage - request body not provided!')
|
||||||
|
}
|
||||||
|
const apiResponse = await chatMessagesService.createChatMessage(req.body)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllChatMessages = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
let chatTypeFilter = req.query?.chatType as chatType | undefined
|
||||||
|
if (chatTypeFilter) {
|
||||||
|
try {
|
||||||
|
const chatTypeFilterArray = JSON.parse(chatTypeFilter)
|
||||||
|
if (chatTypeFilterArray.includes(chatType.EXTERNAL) && chatTypeFilterArray.includes(chatType.INTERNAL)) {
|
||||||
|
chatTypeFilter = undefined
|
||||||
|
} else if (chatTypeFilterArray.includes(chatType.EXTERNAL)) {
|
||||||
|
chatTypeFilter = chatType.EXTERNAL
|
||||||
|
} else if (chatTypeFilterArray.includes(chatType.INTERNAL)) {
|
||||||
|
chatTypeFilter = chatType.INTERNAL
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return res.status(500).send(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const sortOrder = req.query?.order as string | undefined
|
||||||
|
const chatId = req.query?.chatId as string | undefined
|
||||||
|
const memoryType = req.query?.memoryType as string | undefined
|
||||||
|
const sessionId = req.query?.sessionId as string | undefined
|
||||||
|
const messageId = req.query?.messageId as string | undefined
|
||||||
|
const startDate = req.query?.startDate as string | undefined
|
||||||
|
const endDate = req.query?.endDate as string | undefined
|
||||||
|
const feedback = req.query?.feedback as boolean | undefined
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: chatMessageController.getAllChatMessages - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await chatMessagesService.getAllChatMessages(
|
||||||
|
req.params.id,
|
||||||
|
chatTypeFilter,
|
||||||
|
sortOrder,
|
||||||
|
chatId,
|
||||||
|
memoryType,
|
||||||
|
sessionId,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
messageId,
|
||||||
|
feedback
|
||||||
|
)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllInternalChatMessages = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const sortOrder = req.query?.order as string | undefined
|
||||||
|
const chatId = req.query?.chatId as string | undefined
|
||||||
|
const memoryType = req.query?.memoryType as string | undefined
|
||||||
|
const sessionId = req.query?.sessionId as string | undefined
|
||||||
|
const messageId = req.query?.messageId as string | undefined
|
||||||
|
const startDate = req.query?.startDate as string | undefined
|
||||||
|
const endDate = req.query?.endDate as string | undefined
|
||||||
|
const feedback = req.query?.feedback as boolean | undefined
|
||||||
|
const apiResponse = await chatMessagesService.getAllInternalChatMessages(
|
||||||
|
req.params.id,
|
||||||
|
chatType.INTERNAL,
|
||||||
|
sortOrder,
|
||||||
|
chatId,
|
||||||
|
memoryType,
|
||||||
|
sessionId,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
messageId,
|
||||||
|
feedback
|
||||||
|
)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Delete all chatmessages from chatId
|
||||||
|
const removeAllChatMessages = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error('Error: chatMessagesController.removeAllChatMessages - id not provided!')
|
||||||
|
}
|
||||||
|
const chatflowid = req.params.id
|
||||||
|
const chatflow = await chatflowsService.getChatflowById(req.params.id)
|
||||||
|
if (!chatflow) {
|
||||||
|
return res.status(404).send(`Chatflow ${req.params.id} not found`)
|
||||||
|
}
|
||||||
|
const chatId = req.query?.chatId as string
|
||||||
|
const memoryType = req.query?.memoryType as string | undefined
|
||||||
|
const sessionId = req.query?.sessionId as string | undefined
|
||||||
|
const chatType = req.query?.chatType as string | undefined
|
||||||
|
const isClearFromViewMessageDialog = req.query?.isClearFromViewMessageDialog as string | undefined
|
||||||
|
const flowData = chatflow.flowData
|
||||||
|
const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
|
||||||
|
const nodes = parsedFlowData.nodes
|
||||||
|
try {
|
||||||
|
await clearSessionMemory(
|
||||||
|
nodes,
|
||||||
|
appServer.nodesPool.componentNodes,
|
||||||
|
chatId,
|
||||||
|
appServer.AppDataSource,
|
||||||
|
sessionId,
|
||||||
|
memoryType,
|
||||||
|
isClearFromViewMessageDialog
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
return res.status(500).send('Error clearing chat messages')
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOptions: FindOptionsWhere<ChatMessage> = { chatflowid }
|
||||||
|
if (chatId) deleteOptions.chatId = chatId
|
||||||
|
if (memoryType) deleteOptions.memoryType = memoryType
|
||||||
|
if (sessionId) deleteOptions.sessionId = sessionId
|
||||||
|
if (chatType) deleteOptions.chatType = chatType
|
||||||
|
const apiResponse = await chatMessagesService.removeAllChatMessages(chatId, chatflowid, deleteOptions)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createChatMessage,
|
||||||
|
getAllChatMessages,
|
||||||
|
getAllInternalChatMessages,
|
||||||
|
removeAllChatMessages
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import chatflowsService from '../../services/chatflows'
|
||||||
|
import { ChatFlow } from '../../database/entities/ChatFlow'
|
||||||
|
import { createRateLimiter } from '../../utils/rateLimit'
|
||||||
|
import { getApiKey } from '../../utils/apiKey'
|
||||||
|
|
||||||
|
const checkIfChatflowIsValidForStreaming = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: chatflowsRouter.checkIfChatflowIsValidForStreaming - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await chatflowsService.checkIfChatflowIsValidForStreaming(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkIfChatflowIsValidForUploads = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: chatflowsRouter.checkIfChatflowIsValidForUploads - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await chatflowsService.checkIfChatflowIsValidForUploads(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteChatflow = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: chatflowsRouter.deleteChatflow - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await chatflowsService.deleteChatflow(req.params.id)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllChatflows = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await chatflowsService.getAllChatflows()
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get specific chatflow via api key
|
||||||
|
const getChatflowByApiKey = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.apiKey === 'undefined' || req.params.apiKey === '') {
|
||||||
|
throw new Error(`Error: chatflowsRouter.getChatflowById - apiKey not provided!`)
|
||||||
|
}
|
||||||
|
const apiKey = await getApiKey(req.params.apiKey)
|
||||||
|
if (!apiKey) {
|
||||||
|
return res.status(401).send('Unauthorized')
|
||||||
|
}
|
||||||
|
const apiResponse = await chatflowsService.getChatflowByApiKey(apiKey.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getChatflowById = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: chatflowsRouter.getChatflowById - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await chatflowsService.getChatflowById(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveChatflow = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: chatflowsRouter.saveChatflow - body not provided!`)
|
||||||
|
}
|
||||||
|
const body = req.body
|
||||||
|
const newChatFlow = new ChatFlow()
|
||||||
|
Object.assign(newChatFlow, body)
|
||||||
|
const apiResponse = await chatflowsService.saveChatflow(newChatFlow)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateChatflow = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: chatflowsRouter.updateChatflow - id not provided!`)
|
||||||
|
}
|
||||||
|
const chatflow = await chatflowsService.getChatflowById(req.params.id)
|
||||||
|
if (!chatflow) {
|
||||||
|
return res.status(404).send(`Chatflow ${req.params.id} not found`)
|
||||||
|
}
|
||||||
|
const body = req.body
|
||||||
|
const updateChatFlow = new ChatFlow()
|
||||||
|
Object.assign(updateChatFlow, body)
|
||||||
|
updateChatFlow.id = chatflow.id
|
||||||
|
createRateLimiter(updateChatFlow)
|
||||||
|
const apiResponse = await chatflowsService.updateChatflow(chatflow, updateChatFlow)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSinglePublicChatflow = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: chatflowsRouter.updateChatflow - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await chatflowsService.getSinglePublicChatflow(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSinglePublicChatbotConfig = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: chatflowsRouter.getSinglePublicChatbotConfig - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await chatflowsService.getSinglePublicChatbotConfig(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
checkIfChatflowIsValidForStreaming,
|
||||||
|
checkIfChatflowIsValidForUploads,
|
||||||
|
deleteChatflow,
|
||||||
|
getAllChatflows,
|
||||||
|
getChatflowByApiKey,
|
||||||
|
getChatflowById,
|
||||||
|
saveChatflow,
|
||||||
|
updateChatflow,
|
||||||
|
getSinglePublicChatflow,
|
||||||
|
getSinglePublicChatbotConfig
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import componentsCredentialsService from '../../services/components-credentials'
|
||||||
|
|
||||||
|
// Get all component credentials
|
||||||
|
const getAllComponentsCredentials = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await componentsCredentialsService.getAllComponentsCredentials()
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get component credential via name
|
||||||
|
const getComponentByName = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.name === 'undefined' || req.params.name === '') {
|
||||||
|
throw new Error(`Error: componentsCredentialsController.getComponentByName - name not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await componentsCredentialsService.getComponentByName(req.params.name)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns specific component credential icon via name
|
||||||
|
const getSingleComponentsCredentialIcon = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.name === 'undefined' || req.params.name === '') {
|
||||||
|
throw new Error(`Error: componentsCredentialsController.getSingleComponentsCredentialIcon - name not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await componentsCredentialsService.getSingleComponentsCredentialIcon(req.params.name)
|
||||||
|
return res.sendFile(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllComponentsCredentials,
|
||||||
|
getComponentByName,
|
||||||
|
getSingleComponentsCredentialIcon
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import credentialsService from '../../services/credentials'
|
||||||
|
|
||||||
|
const createCredential = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: credentialsController.createCredential - body not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await credentialsService.createCredential(req.body)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteCredentials = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: credentialsController.deleteCredentials - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await credentialsService.deleteCredentials(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllCredentials = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await credentialsService.getAllCredentials(req.query.credentialName)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCredentialById = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: credentialsController.getCredentialById - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await credentialsService.getCredentialById(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateCredential = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: credentialsController.updateCredential - id not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: credentialsController.updateCredential - body not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await credentialsService.updateCredential(req.params.id, req.body)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createCredential,
|
||||||
|
deleteCredentials,
|
||||||
|
getAllCredentials,
|
||||||
|
getCredentialById,
|
||||||
|
updateCredential
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import feedbackService from '../../services/feedback'
|
||||||
|
|
||||||
|
const getAllChatMessageFeedback = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: feedbackController.getAllChatMessageFeedback - id not provided!`)
|
||||||
|
}
|
||||||
|
const chatflowid = req.params.id
|
||||||
|
const chatId = req.query?.chatId as string | undefined
|
||||||
|
const sortOrder = req.query?.order as string | undefined
|
||||||
|
const startDate = req.query?.startDate as string | undefined
|
||||||
|
const endDate = req.query?.endDate as string | undefined
|
||||||
|
const apiResponse = await feedbackService.getAllChatMessageFeedback(chatflowid, chatId, sortOrder, startDate, endDate)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createChatMessageFeedbackForChatflow = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: feedbackController.createChatMessageFeedbackForChatflow - body not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await feedbackService.createChatMessageFeedbackForChatflow(req.body)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateChatMessageFeedbackForChatflow = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: feedbackController.updateChatMessageFeedbackForChatflow - body not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: feedbackController.updateChatMessageFeedbackForChatflow - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await feedbackService.updateChatMessageFeedbackForChatflow(req.params.id, req.body)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllChatMessageFeedback,
|
||||||
|
createChatMessageFeedbackForChatflow,
|
||||||
|
updateChatMessageFeedbackForChatflow
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import fetchLinksService from '../../services/fetch-links'
|
||||||
|
|
||||||
|
const getAllLinks = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.query.url === 'undefined' || req.query.url === '') {
|
||||||
|
throw new Error(`Error: fetchLinksController.getAllLinks - url not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.query.relativeLinksMethod === 'undefined' || req.query.relativeLinksMethod === '') {
|
||||||
|
throw new Error(`Error: fetchLinksController.getAllLinks - relativeLinksMethod not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.query.limit === 'undefined' || req.query.limit === '') {
|
||||||
|
throw new Error(`Error: fetchLinksController.getAllLinks - limit not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await fetchLinksService.getAllLinks(
|
||||||
|
req.query.url as string,
|
||||||
|
req.query.relativeLinksMethod as string,
|
||||||
|
req.query.limit as string
|
||||||
|
)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllLinks
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import flowConfigsService from '../../services/flow-configs'
|
||||||
|
|
||||||
|
const getSingleFlowConfig = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: flowConfigsController.getSingleFlowConfig - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await flowConfigsService.getSingleFlowConfig(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getSingleFlowConfig
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import path from 'path'
|
||||||
|
import contentDisposition from 'content-disposition'
|
||||||
|
import { getStoragePath } from 'flowise-components'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
|
||||||
|
const streamUploadedImage = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (!req.query.chatflowId || !req.query.chatId || !req.query.fileName) {
|
||||||
|
return res.status(500).send(`Invalid file path`)
|
||||||
|
}
|
||||||
|
const chatflowId = req.query.chatflowId as string
|
||||||
|
const chatId = req.query.chatId as string
|
||||||
|
const fileName = req.query.fileName as string
|
||||||
|
const filePath = path.join(getStoragePath(), chatflowId, chatId, 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 storage folder
|
||||||
|
if (!filePath.startsWith(getStoragePath())) 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)
|
||||||
|
fileStream.pipe(res)
|
||||||
|
} else {
|
||||||
|
return res.status(404).send(`File ${fileName} not found`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
streamUploadedImage
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import { getStoragePath } from 'flowise-components'
|
||||||
|
|
||||||
|
const getPathForUploads = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = {
|
||||||
|
storagePath: getStoragePath()
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getPathForUploads
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import { utilBuildChatflow } from '../../utils/buildChatflow'
|
||||||
|
|
||||||
|
// Send input message and get prediction result (Internal)
|
||||||
|
const createInternalPrediction = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await utilBuildChatflow(req, req.io, true)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createInternalPrediction
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
|
||||||
|
// Configure number of proxies in Host Environment
|
||||||
|
const configureProxyNrInHostEnv = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.ip === 'undefined' || req.ip) {
|
||||||
|
throw new Error(`Error: ipController.configureProxyNrInHostEnv - ip not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = {
|
||||||
|
ip: req.ip,
|
||||||
|
msg: `Check returned IP address in the response. If it matches your current IP address ( which you can get by going to http://ip.nfriedly.com/ or https://api.ipify.org/ ), then the number of proxies is correct and the rate limiter should now work correctly. If not, increase the number of proxies by 1 and restart Cloud-Hosted Flowise until the IP address matches your own. Visit https://docs.flowiseai.com/configuration/rate-limit#cloud-hosted-rate-limit-setup-guide for more information.`
|
||||||
|
}
|
||||||
|
return res.send(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
configureProxyNrInHostEnv
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import loadPromptsService from '../../services/load-prompts'
|
||||||
|
|
||||||
|
const createPrompt = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || typeof req.body.promptName === 'undefined' || req.body.promptName === '') {
|
||||||
|
throw new Error(`Error: loadPromptsController.createPrompt - promptName not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await loadPromptsService.createPrompt(req.body.promptName as string)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createPrompt
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import marketplacesService from '../../services/marketplaces'
|
||||||
|
|
||||||
|
// Get all templates for marketplaces
|
||||||
|
const getAllTemplates = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await marketplacesService.getAllTemplates()
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllTemplates
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import nodeConfigsService from '../../services/node-configs'
|
||||||
|
|
||||||
|
const getAllNodeConfigs = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: nodeConfigsController.getAllNodeConfigs - body not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await nodeConfigsService.getAllNodeConfigs(req.body)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllNodeConfigs
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
|
||||||
|
// Returns specific component node icon via name
|
||||||
|
const getSingleNodeIcon = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentNodes, req.params.name)) {
|
||||||
|
const nodeInstance = appServer.nodesPool.componentNodes[req.params.name]
|
||||||
|
if (nodeInstance.icon === undefined) {
|
||||||
|
throw new Error(`Error: nodeIconController.getSingleNodeIcon - Node ${req.params.name} icon not found`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeInstance.icon.endsWith('.svg') || nodeInstance.icon.endsWith('.png') || nodeInstance.icon.endsWith('.jpg')) {
|
||||||
|
const filepath = nodeInstance.icon
|
||||||
|
res.sendFile(filepath)
|
||||||
|
} else {
|
||||||
|
throw new Error(`Error: nodeIconController.getSingleNodeIcon - Node ${req.params.name} icon is missing icon`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Error: nodeIconController.getSingleNodeIcon - Node ${req.params.name} not found`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getSingleNodeIcon
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import nodesService from '../../services/nodes'
|
||||||
|
|
||||||
|
const getAllNodes = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await nodesService.getAllNodes()
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getNodeByName = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.name === 'undefined' || req.params.name === '') {
|
||||||
|
throw new Error(`Error: nodesController.getNodeByName - name not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await nodesService.getNodeByName(req.params.name)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSingleNodeIcon = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.name === 'undefined' || req.params.name === '') {
|
||||||
|
throw new Error(`Error: nodesController.getSingleNodeIcon - name not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await nodesService.getSingleNodeIcon(req.params.name)
|
||||||
|
return res.sendFile(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSingleNodeAsyncOptions = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: nodesController.getSingleNodeAsyncOptions - body not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.params.name === 'undefined' || req.params.name === '') {
|
||||||
|
throw new Error(`Error: nodesController.getSingleNodeAsyncOptions - name not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await nodesService.getSingleNodeAsyncOptions(req.params.name, req.body)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const executeCustomFunction = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: nodesController.executeCustomFunction - body not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await nodesService.executeCustomFunction(req.body)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllNodes,
|
||||||
|
getNodeByName,
|
||||||
|
getSingleNodeIcon,
|
||||||
|
getSingleNodeAsyncOptions,
|
||||||
|
executeCustomFunction
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
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'
|
||||||
|
|
||||||
|
// List available assistants
|
||||||
|
const getAllOpenaiAssistants = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.query.credential === 'undefined' || req.query.credential === '') {
|
||||||
|
throw new Error(`Error: openaiAssistantsController.getAllOpenaiAssistants - credential not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await openaiAssistantsService.getAllOpenaiAssistants(req.query.credential as string)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get assistant object
|
||||||
|
const getSingleOpenaiAssistant = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: openaiAssistantsController.getSingleOpenaiAssistant - id not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.query.credential === 'undefined' || req.query.credential === '') {
|
||||||
|
throw new Error(`Error: openaiAssistantsController.getSingleOpenaiAssistant - credential not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await openaiAssistantsService.getSingleOpenaiAssistant(req.query.credential as string, req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
fileStream.pipe(res)
|
||||||
|
} else {
|
||||||
|
return res.status(404).send(`File ${req.body.fileName} not found`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllOpenaiAssistants,
|
||||||
|
getSingleOpenaiAssistant,
|
||||||
|
getFileFromAssistant
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import { getRateLimiter } from '../../utils/rateLimit'
|
||||||
|
import chatflowsService from '../../services/chatflows'
|
||||||
|
import logger from '../../utils/logger'
|
||||||
|
import { utilBuildChatflow } from '../../utils/buildChatflow'
|
||||||
|
|
||||||
|
// Send input message and get prediction result (External)
|
||||||
|
const createPrediction = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: predictionsController.createPrediction - id not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: predictionsController.createPrediction - body not provided!`)
|
||||||
|
}
|
||||||
|
const chatflow = await chatflowsService.getChatflowById(req.params.id)
|
||||||
|
if (!chatflow) {
|
||||||
|
return res.status(404).send(`Chatflow ${req.params.id} not found`)
|
||||||
|
}
|
||||||
|
let isDomainAllowed = true
|
||||||
|
logger.info(`[server]: Request originated from ${req.headers.origin}`)
|
||||||
|
if (chatflow.chatbotConfig) {
|
||||||
|
const parsedConfig = JSON.parse(chatflow.chatbotConfig)
|
||||||
|
// check whether the first one is not empty. if it is empty that means the user set a value and then removed it.
|
||||||
|
const isValidAllowedOrigins = parsedConfig.allowedOrigins?.length && parsedConfig.allowedOrigins[0] !== ''
|
||||||
|
if (isValidAllowedOrigins) {
|
||||||
|
const originHeader = req.headers.origin as string
|
||||||
|
const origin = new URL(originHeader).host
|
||||||
|
isDomainAllowed =
|
||||||
|
parsedConfig.allowedOrigins.filter((domain: string) => {
|
||||||
|
try {
|
||||||
|
const allowedOrigin = new URL(domain).host
|
||||||
|
return origin === allowedOrigin
|
||||||
|
} catch (e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}).length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDomainAllowed) {
|
||||||
|
const apiResponse = await utilBuildChatflow(req, req.io)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} else {
|
||||||
|
return res.status(401).send(`This site is not allowed to access this chatbot`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getRateLimiterMiddleware = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
return getRateLimiter(req, res, next)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createPrediction,
|
||||||
|
getRateLimiterMiddleware
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import promptsListsService from '../../services/prompts-lists'
|
||||||
|
|
||||||
|
// Prompt from Hub
|
||||||
|
const createPromptsList = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await promptsListsService.createPromptsList(req.body)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createPromptsList
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import statsService from '../../services/stats'
|
||||||
|
import { chatType } from '../../Interface'
|
||||||
|
|
||||||
|
const getChatflowStats = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: statsController.getChatflowStats - id not provided!`)
|
||||||
|
}
|
||||||
|
const chatflowid = req.params.id
|
||||||
|
let chatTypeFilter = req.query?.chatType as chatType | undefined
|
||||||
|
const startDate = req.query?.startDate as string | undefined
|
||||||
|
const endDate = req.query?.endDate as string | undefined
|
||||||
|
if (chatTypeFilter) {
|
||||||
|
try {
|
||||||
|
const chatTypeFilterArray = JSON.parse(chatTypeFilter)
|
||||||
|
if (chatTypeFilterArray.includes(chatType.EXTERNAL) && chatTypeFilterArray.includes(chatType.INTERNAL)) {
|
||||||
|
chatTypeFilter = undefined
|
||||||
|
} else if (chatTypeFilterArray.includes(chatType.EXTERNAL)) {
|
||||||
|
chatTypeFilter = chatType.EXTERNAL
|
||||||
|
} else if (chatTypeFilterArray.includes(chatType.INTERNAL)) {
|
||||||
|
chatTypeFilter = chatType.INTERNAL
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return res.status(500).send(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const apiResponse = await statsService.getChatflowStats(chatflowid, chatTypeFilter, startDate, endDate, '', true)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getChatflowStats
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import toolsService from '../../services/tools'
|
||||||
|
|
||||||
|
const creatTool = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: toolsController.creatTool - body not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await toolsService.creatTool(req.body)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTool = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: toolsController.updateTool - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await toolsService.deleteTool(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllTools = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await toolsService.getAllTools()
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getToolById = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: toolsController.getToolById - id not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await toolsService.getToolById(req.params.id)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTool = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error(`Error: toolsController.updateTool - id not provided!`)
|
||||||
|
}
|
||||||
|
if (typeof req.body === 'undefined' || req.body === '') {
|
||||||
|
throw new Error(`Error: toolsController.deleteTool - body not provided!`)
|
||||||
|
}
|
||||||
|
const apiResponse = await toolsService.updateTool(req.params.id, req.body)
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
creatTool,
|
||||||
|
deleteTool,
|
||||||
|
getAllTools,
|
||||||
|
getToolById,
|
||||||
|
updateTool
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import variablesService from '../../services/variables'
|
||||||
|
import { Variable } from '../../database/entities/Variable'
|
||||||
|
|
||||||
|
const createVariable = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.body === 'undefined') {
|
||||||
|
throw new Error(`Error: variablesController.createVariable - body not provided!`)
|
||||||
|
}
|
||||||
|
const body = req.body
|
||||||
|
const newVariable = new Variable()
|
||||||
|
Object.assign(newVariable, body)
|
||||||
|
const apiResponse = await variablesService.createVariable(newVariable)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteVariable = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error('Error: variablesController.deleteVariable - id not provided!')
|
||||||
|
}
|
||||||
|
const apiResponse = await variablesService.deleteVariable(req.params.id)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllVariables = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await variablesService.getAllVariables()
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateVariable = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
if (typeof req.params.id === 'undefined' || req.params.id === '') {
|
||||||
|
throw new Error('Error: variablesController.updateVariable - id not provided!')
|
||||||
|
}
|
||||||
|
if (typeof req.body === 'undefined') {
|
||||||
|
throw new Error('Error: variablesController.updateVariable - body not provided!')
|
||||||
|
}
|
||||||
|
const variable = await variablesService.getVariableById(req.params.id)
|
||||||
|
if (!variable) {
|
||||||
|
return res.status(404).send(`Variable ${req.params.id} not found in the database`)
|
||||||
|
}
|
||||||
|
const body = req.body
|
||||||
|
const updatedVariable = new Variable()
|
||||||
|
Object.assign(updatedVariable, body)
|
||||||
|
const apiResponse = await variablesService.updateVariable(variable, updatedVariable)
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createVariable,
|
||||||
|
deleteVariable,
|
||||||
|
getAllVariables,
|
||||||
|
updateVariable
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import vectorsService from '../../services/vectors'
|
||||||
|
import { getRateLimiter } from '../../utils/rateLimit'
|
||||||
|
|
||||||
|
const getRateLimiterMiddleware = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
return getRateLimiter(req, res, next)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const upsertVectorMiddleware = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
return await vectorsService.upsertVectorMiddleware(req, res)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createInternalUpsert = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
return await vectorsService.upsertVectorMiddleware(req, res, true)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
upsertVectorMiddleware,
|
||||||
|
createInternalUpsert,
|
||||||
|
getRateLimiterMiddleware
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Request, Response, NextFunction } from 'express'
|
||||||
|
import versionsService from '../../services/versions'
|
||||||
|
|
||||||
|
const getVersion = async (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const apiResponse = await versionsService.getVersion()
|
||||||
|
if (apiResponse.executionError) {
|
||||||
|
return res.status(apiResponse.status).send(apiResponse.msg)
|
||||||
|
}
|
||||||
|
return res.json(apiResponse)
|
||||||
|
} catch (error) {
|
||||||
|
next(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getVersion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
export class ApiError extends Error {
|
||||||
|
statusCode: number
|
||||||
|
constructor(statusCode: number, message: string) {
|
||||||
|
super(message)
|
||||||
|
this.statusCode = statusCode
|
||||||
|
// capture the stack trace of the error from anywhere in the application
|
||||||
|
Error.captureStackTrace(this, this.constructor)
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { NextFunction, Request, Response } from 'express'
|
||||||
|
import { StatusCodes } from 'http-status-codes'
|
||||||
|
import { ApiError } from '../../errors/apiError'
|
||||||
|
|
||||||
|
// we need eslint because we have to pass next arg for the error middleware
|
||||||
|
// eslint-disable-next-line
|
||||||
|
async function errorHandlerMiddleware(err: ApiError, req: Request, res: Response, next: NextFunction) {
|
||||||
|
let displayedError = {
|
||||||
|
statusCode: err.statusCode || StatusCodes.INTERNAL_SERVER_ERROR,
|
||||||
|
success: false,
|
||||||
|
message: err.message,
|
||||||
|
// Provide error stack trace only in development
|
||||||
|
stack: process.env.NODE_ENV === 'development' ? err.stack : {}
|
||||||
|
}
|
||||||
|
res.setHeader('Content-Type', 'application/json')
|
||||||
|
res.status(displayedError.statusCode).json(displayedError)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default errorHandlerMiddleware
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import express from 'express'
|
||||||
|
import apikeyController from '../../controllers/apikey'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', apikeyController.createApiKey)
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', apikeyController.getAllApiKeys)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
router.put('/:id', apikeyController.updateApiKey)
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
router.delete('/:id', apikeyController.deleteApiKey)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import express from 'express'
|
||||||
|
import assistantsController from '../../controllers/assistants'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', assistantsController.creatAssistant)
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', assistantsController.getAllAssistants)
|
||||||
|
router.get('/:id', assistantsController.getAssistantById)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
router.put('/:id', assistantsController.updateAssistant)
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
router.delete('/:id', assistantsController.deleteAssistant)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import express from 'express'
|
||||||
|
import chatMessageController from '../../controllers/chat-messages'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/:id', chatMessageController.createChatMessage)
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:id', chatMessageController.getAllChatMessages)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
router.delete('/:id', chatMessageController.removeAllChatMessages)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import express from 'express'
|
||||||
|
import chatflowsController from '../../controllers/chatflows'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:id', chatflowsController.checkIfChatflowIsValidForStreaming)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import express from 'express'
|
||||||
|
import chatflowsController from '../../controllers/chatflows'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:id', chatflowsController.checkIfChatflowIsValidForUploads)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import express from 'express'
|
||||||
|
import chatflowsController from '../../controllers/chatflows'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', chatflowsController.saveChatflow)
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', chatflowsController.getAllChatflows)
|
||||||
|
router.get('/:id', chatflowsController.getChatflowById)
|
||||||
|
router.get('/apikey/:apikey', chatflowsController.getChatflowByApiKey)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
router.put('/:id', chatflowsController.updateChatflow)
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
router.delete('/:id', chatflowsController.deleteChatflow)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import componentsCredentialsController from '../../controllers/components-credentials'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:name', componentsCredentialsController.getSingleComponentsCredentialIcon)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import express from 'express'
|
||||||
|
import componentsCredentialsController from '../../controllers/components-credentials'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', componentsCredentialsController.getAllComponentsCredentials)
|
||||||
|
router.get('/:name', componentsCredentialsController.getComponentByName)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import express from 'express'
|
||||||
|
import credentialsController from '../../controllers/credentials'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', credentialsController.createCredential)
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', credentialsController.getAllCredentials)
|
||||||
|
router.get('/:id', credentialsController.getCredentialById)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
router.put('/:id', credentialsController.updateCredential)
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
router.delete('/:id', credentialsController.deleteCredentials)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import feedbackController from '../../controllers/feedback'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/:id', feedbackController.createChatMessageFeedbackForChatflow)
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:id', feedbackController.getAllChatMessageFeedback)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
router.put('/:id', feedbackController.updateChatMessageFeedbackForChatflow)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import fetchLinksController from '../../controllers/fetch-links'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', fetchLinksController.getAllLinks)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import flowConfigsController from '../../controllers/flow-configs'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:id', flowConfigsController.getSingleFlowConfig)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import getUploadFileController from '../../controllers/get-upload-file'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', getUploadFileController.streamUploadedImage)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import getUploadPathController from '../../controllers/get-upload-path'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', getUploadPathController.getPathForUploads)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
import express from 'express'
|
||||||
|
import apikeyRouter from './apikey'
|
||||||
|
import assistantsRouter from './assistants'
|
||||||
|
import chatflowsRouter from './chatflows'
|
||||||
|
import chatflowsStreamingRouter from './chatflows-streaming'
|
||||||
|
import chatflowsUploadsRouter from './chatflows-uploads'
|
||||||
|
import chatMessageRouter from './chat-messages'
|
||||||
|
import componentsCredentialsRouter from './components-credentials'
|
||||||
|
import componentsCredentialsIconRouter from './components-credentials-icon'
|
||||||
|
import credentialsRouter from './credentials'
|
||||||
|
import feedbackRouter from './feedback'
|
||||||
|
import fetchLinksRouter from './fetch-links'
|
||||||
|
import flowConfigRouter from './flow-config'
|
||||||
|
import internalChatmessagesRouter from './internal-chat-messages'
|
||||||
|
import internalPredictionRouter from './internal-predictions'
|
||||||
|
import ipRouter from './ip'
|
||||||
|
import getUploadFileRouter from './get-upload-file'
|
||||||
|
import getUploadPathRouter from './get-upload-path'
|
||||||
|
import loadPromptRouter from './load-prompts'
|
||||||
|
import marketplacesRouter from './marketplaces'
|
||||||
|
import nodeConfigRouter from './node-configs'
|
||||||
|
import nodeCustomFunctionRouter from './node-custom-functions'
|
||||||
|
import nodeIconRouter from './node-icons'
|
||||||
|
import nodeLoadMethodRouter from './node-load-methods'
|
||||||
|
import nodesRouter from './nodes'
|
||||||
|
import openaiAssistantsRouter from './openai-assistants'
|
||||||
|
import openaiAssistantsFileRouter from './openai-assistants-files'
|
||||||
|
import predictionRouter from './predictions'
|
||||||
|
import promptListsRouter from './prompts-lists'
|
||||||
|
import publicChatbotRouter from './public-chatbots'
|
||||||
|
import publicChatflowsRouter from './public-chatflows'
|
||||||
|
import statsRouter from './stats'
|
||||||
|
import toolsRouter from './tools'
|
||||||
|
import variablesRouter from './variables'
|
||||||
|
import vectorRouter from './vectors'
|
||||||
|
import verifyRouter from './verify'
|
||||||
|
import versionRouter from './versions'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
router.use('/apikey', apikeyRouter)
|
||||||
|
router.use('/assistants', assistantsRouter)
|
||||||
|
router.use('/chatflows', chatflowsRouter)
|
||||||
|
router.use('/chatflows-streaming', chatflowsStreamingRouter)
|
||||||
|
router.use('/chatmessage', chatMessageRouter)
|
||||||
|
router.use('/components-credentials', componentsCredentialsRouter)
|
||||||
|
router.use('/components-credentials-icon', componentsCredentialsIconRouter)
|
||||||
|
router.use('/chatflows-uploads', chatflowsUploadsRouter)
|
||||||
|
router.use('/credentials', credentialsRouter)
|
||||||
|
router.use('/feedback', feedbackRouter)
|
||||||
|
router.use('/fetch-links', fetchLinksRouter)
|
||||||
|
router.use('/flow-config', flowConfigRouter)
|
||||||
|
router.use('/internal-chatmessage', internalChatmessagesRouter)
|
||||||
|
router.use('/internal-prediction', internalPredictionRouter)
|
||||||
|
router.use('/ip', ipRouter)
|
||||||
|
router.use('/get-upload-file', getUploadFileRouter)
|
||||||
|
router.use('/get-upload-path', getUploadPathRouter)
|
||||||
|
router.use('/load-prompt', loadPromptRouter)
|
||||||
|
router.use('/marketplaces', marketplacesRouter)
|
||||||
|
router.use('/node-config', nodeConfigRouter)
|
||||||
|
router.use('/node-custom-function', nodeCustomFunctionRouter)
|
||||||
|
router.use('/node-icon', nodeIconRouter)
|
||||||
|
router.use('/node-load-method', nodeLoadMethodRouter)
|
||||||
|
router.use('/nodes', nodesRouter)
|
||||||
|
router.use('/openai-assistants', openaiAssistantsRouter)
|
||||||
|
router.use('/openai-assistants-file', openaiAssistantsFileRouter)
|
||||||
|
router.use('/prediction', predictionRouter)
|
||||||
|
router.use('/prompts-list', promptListsRouter)
|
||||||
|
router.use('/public-chatbotConfig', publicChatbotRouter)
|
||||||
|
router.use('/public-chatflows', publicChatflowsRouter)
|
||||||
|
router.use('/stats', statsRouter)
|
||||||
|
router.use('/tools', toolsRouter)
|
||||||
|
router.use('/variables', variablesRouter)
|
||||||
|
router.use('/vector', vectorRouter)
|
||||||
|
router.use('/verify', verifyRouter)
|
||||||
|
router.use('/version', versionRouter)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import chatMessagesController from '../../controllers/chat-messages'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:id', chatMessagesController.getAllInternalChatMessages)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import internalPredictionsController from '../../controllers/internal-predictions'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/:id', internalPredictionsController.createInternalPrediction)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import ipController from '../../controllers/ip'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', ipController.configureProxyNrInHostEnv)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import loadPromptsController from '../../controllers/load-prompts'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', loadPromptsController.createPrompt)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import marketplacesController from '../../controllers/marketplaces'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/templates', marketplacesController.getAllTemplates)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import nodeConfigsController from '../../controllers/node-configs'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', nodeConfigsController.getAllNodeConfigs)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import nodesRouter from '../../controllers/nodes'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.post('/', nodesRouter.executeCustomFunction)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import nodesController from '../../controllers/nodes'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:name', nodesController.getSingleNodeIcon)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import express from 'express'
|
||||||
|
import nodesRouter from '../../controllers/nodes'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
router.post('/:name', nodesRouter.getSingleNodeAsyncOptions)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import express from 'express'
|
||||||
|
import nodesController from '../../controllers/nodes'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', nodesController.getAllNodes)
|
||||||
|
router.get('/:name', nodesController.getNodeByName)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import openaiAssistantsController from '../../controllers/openai-assistants'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', openaiAssistantsController.getFileFromAssistant)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import express from 'express'
|
||||||
|
import openaiAssistantsController from '../../controllers/openai-assistants'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', openaiAssistantsController.getAllOpenaiAssistants)
|
||||||
|
router.get('/:id', openaiAssistantsController.getSingleOpenaiAssistant)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import express from 'express'
|
||||||
|
import multer from 'multer'
|
||||||
|
import path from 'path'
|
||||||
|
import predictionsController from '../../controllers/predictions'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
const upload = multer({ dest: `${path.join(__dirname, '..', '..', '..', 'uploads')}/` })
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/:id', upload.array('files'), predictionsController.getRateLimiterMiddleware, predictionsController.createPrediction)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import promptsListController from '../../controllers/prompts-lists'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', promptsListController.createPromptsList)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import chatflowsController from '../../controllers/chatflows'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:id', chatflowsController.getSinglePublicChatbotConfig)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import chatflowsController from '../../controllers/chatflows'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:id', chatflowsController.getSinglePublicChatflow)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import express from 'express'
|
||||||
|
import statsController from '../../controllers/stats'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/:id', statsController.getChatflowStats)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import express from 'express'
|
||||||
|
import toolsController from '../../controllers/tools'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', toolsController.creatTool)
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', toolsController.getAllTools)
|
||||||
|
router.get('/:id', toolsController.getToolById)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
router.put('/:id', toolsController.updateTool)
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
router.delete('/:id', toolsController.deleteTool)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import express from 'express'
|
||||||
|
import variablesController from '../../controllers/variables'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/', variablesController.createVariable)
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', variablesController.getAllVariables)
|
||||||
|
|
||||||
|
// UPDATE
|
||||||
|
router.put('/:id', variablesController.updateVariable)
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
router.delete('/:id', variablesController.deleteVariable)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import express from 'express'
|
||||||
|
import multer from 'multer'
|
||||||
|
import path from 'path'
|
||||||
|
import vectorsController from '../../controllers/vectors'
|
||||||
|
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
const upload = multer({ dest: `${path.join(__dirname, '..', '..', '..', 'uploads')}/` })
|
||||||
|
|
||||||
|
// CREATE
|
||||||
|
router.post('/upsert/:id', upload.array('files'), vectorsController.getRateLimiterMiddleware, vectorsController.upsertVectorMiddleware)
|
||||||
|
router.post('/internal-upsert/:id', vectorsController.createInternalUpsert)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import apikeyController from '../../controllers/apikey'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/apikey/:apikey', apikeyController.verifyApiKey)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
import express from 'express'
|
||||||
|
import versionsController from '../../controllers/versions'
|
||||||
|
const router = express.Router()
|
||||||
|
|
||||||
|
// READ
|
||||||
|
router.get('/', versionsController.getVersion)
|
||||||
|
|
||||||
|
export default router
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { addAPIKey, deleteAPIKey, getAPIKeys, updateAPIKey } from '../../utils/apiKey'
|
||||||
|
import { addChatflowsCount } from '../../utils/addChatflowsCount'
|
||||||
|
import { getApiKey } from '../../utils/apiKey'
|
||||||
|
|
||||||
|
const getAllApiKeys = async () => {
|
||||||
|
try {
|
||||||
|
const keys = await getAPIKeys()
|
||||||
|
const dbResponse = await addChatflowsCount(keys)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: apikeyService.getAllApiKeys - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createApiKey = async (keyName: string) => {
|
||||||
|
try {
|
||||||
|
const keys = await addAPIKey(keyName)
|
||||||
|
const dbResponse = await addChatflowsCount(keys)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: apikeyService.createApiKey - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update api key
|
||||||
|
const updateApiKey = async (id: string, keyName: string) => {
|
||||||
|
try {
|
||||||
|
const keys = await updateAPIKey(id, keyName)
|
||||||
|
const dbResponse = await addChatflowsCount(keys)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: apikeyService.updateApiKey - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteApiKey = async (id: string) => {
|
||||||
|
try {
|
||||||
|
const keys = await deleteAPIKey(id)
|
||||||
|
const dbResponse = await addChatflowsCount(keys)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: apikeyService.deleteApiKey - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyApiKey = async (paramApiKey: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const apiKey = await getApiKey(paramApiKey)
|
||||||
|
if (!apiKey) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 401,
|
||||||
|
msg: `Unauthorized`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const dbResponse = 'OK'
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: apikeyService.verifyApiKey - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createApiKey,
|
||||||
|
deleteApiKey,
|
||||||
|
getAllApiKeys,
|
||||||
|
updateApiKey,
|
||||||
|
verifyApiKey
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,366 @@
|
||||||
|
import OpenAI from 'openai'
|
||||||
|
import path from 'path'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
import { uniqWith, isEqual } 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'
|
||||||
|
|
||||||
|
const creatAssistant = async (requestBody: any): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
if (!requestBody.details) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Invalid request body`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const assistantDetails = JSON.parse(requestBody.details)
|
||||||
|
try {
|
||||||
|
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
|
id: requestBody.credential
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!credential) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Credential ${requestBody.credential} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrpyt credentialData
|
||||||
|
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
||||||
|
const openAIApiKey = decryptedCredentialData['openAIApiKey']
|
||||||
|
if (!openAIApiKey) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `OpenAI ApiKey not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||||
|
|
||||||
|
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(path.join(getUserHome(), '.flowise', 'openai-assistant'))) {
|
||||||
|
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
||||||
|
}
|
||||||
|
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,
|
||||||
|
file_ids: (assistantDetails.files ?? []).map((file: OpenAI.Files.FileObject) => file.id)
|
||||||
|
})
|
||||||
|
assistantDetails.id = newAssistant.id
|
||||||
|
} else {
|
||||||
|
const retrievedAssistant = await openai.beta.assistants.retrieve(assistantDetails.id)
|
||||||
|
let filteredTools = uniqWith([...retrievedAssistant.tools, ...tools], isEqual)
|
||||||
|
filteredTools = filteredTools.filter((tool) => !(tool.type === 'function' && !(tool as any).function))
|
||||||
|
|
||||||
|
await openai.beta.assistants.update(assistantDetails.id, {
|
||||||
|
name: assistantDetails.name,
|
||||||
|
description: assistantDetails.description ?? '',
|
||||||
|
instructions: assistantDetails.instructions ?? '',
|
||||||
|
model: assistantDetails.model,
|
||||||
|
tools: filteredTools,
|
||||||
|
file_ids: uniqWith(
|
||||||
|
[...retrievedAssistant.file_ids, ...(assistantDetails.files ?? []).map((file: OpenAI.Files.FileObject) => file.id)],
|
||||||
|
isEqual
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAssistantDetails = {
|
||||||
|
...assistantDetails
|
||||||
|
}
|
||||||
|
if (newAssistantDetails.uploadFiles) delete newAssistantDetails.uploadFiles
|
||||||
|
|
||||||
|
requestBody.details = JSON.stringify(newAssistantDetails)
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Error creating new assistant: ${error}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const newAssistant = new Assistant()
|
||||||
|
Object.assign(newAssistant, requestBody)
|
||||||
|
|
||||||
|
const assistant = await appServer.AppDataSource.getRepository(Assistant).create(newAssistant)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Assistant).save(assistant)
|
||||||
|
|
||||||
|
await appServer.telemetry.sendTelemetry('assistant_created', {
|
||||||
|
version: await getAppVersion(),
|
||||||
|
assistantId: dbResponse.id
|
||||||
|
})
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: assistantsService.creatTool - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteAssistant = async (assistantId: string, isDeleteBoth: any): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const assistant = await appServer.AppDataSource.getRepository(Assistant).findOneBy({
|
||||||
|
id: assistantId
|
||||||
|
})
|
||||||
|
if (!assistant) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Assistant ${assistantId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const assistantDetails = JSON.parse(assistant.details)
|
||||||
|
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
|
id: assistant.credential
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!credential) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Credential ${assistant.credential} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrpyt credentialData
|
||||||
|
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
||||||
|
const openAIApiKey = decryptedCredentialData['openAIApiKey']
|
||||||
|
if (!openAIApiKey) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `OpenAI ApiKey not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Assistant).delete({ id: assistantId })
|
||||||
|
if (isDeleteBoth) await openai.beta.assistants.del(assistantDetails.id)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error: any) {
|
||||||
|
if (error.status === 404 && error.type === 'invalid_request_error') {
|
||||||
|
return 'OK'
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Error deleting assistant: ${error}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: assistantsService.deleteTool - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllAssistants = async (): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Assistant).find()
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: assistantsService.getAllAssistants - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAssistantById = async (assistantId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Assistant).findOneBy({
|
||||||
|
id: assistantId
|
||||||
|
})
|
||||||
|
if (!dbResponse) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Assistant ${assistantId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: assistantsService.getAssistantById - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateAssistant = async (assistantId: string, requestBody: any): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const assistant = await appServer.AppDataSource.getRepository(Assistant).findOneBy({
|
||||||
|
id: assistantId
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!assistant) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Assistant ${assistantId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const openAIAssistantId = JSON.parse(assistant.details)?.id
|
||||||
|
const body = requestBody
|
||||||
|
const assistantDetails = JSON.parse(body.details)
|
||||||
|
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
|
id: body.credential
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!credential) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Credential ${body.credential} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrpyt credentialData
|
||||||
|
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
||||||
|
const openAIApiKey = decryptedCredentialData['openAIApiKey']
|
||||||
|
if (!openAIApiKey) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `OpenAI ApiKey not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||||
|
|
||||||
|
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(path.join(getUserHome(), '.flowise', 'openai-assistant'))) {
|
||||||
|
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
filteredTools = filteredTools.filter((tool) => !(tool.type === 'function' && !(tool as any).function))
|
||||||
|
|
||||||
|
await openai.beta.assistants.update(openAIAssistantId, {
|
||||||
|
name: assistantDetails.name,
|
||||||
|
description: assistantDetails.description,
|
||||||
|
instructions: assistantDetails.instructions,
|
||||||
|
model: assistantDetails.model,
|
||||||
|
tools: filteredTools,
|
||||||
|
file_ids: uniqWith(
|
||||||
|
[...retrievedAssistant.file_ids, ...(assistantDetails.files ?? []).map((file: OpenAI.Files.FileObject) => file.id)],
|
||||||
|
isEqual
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const newAssistantDetails = {
|
||||||
|
...assistantDetails,
|
||||||
|
id: openAIAssistantId
|
||||||
|
}
|
||||||
|
if (newAssistantDetails.uploadFiles) delete newAssistantDetails.uploadFiles
|
||||||
|
|
||||||
|
const updateAssistant = new Assistant()
|
||||||
|
body.details = JSON.stringify(newAssistantDetails)
|
||||||
|
Object.assign(updateAssistant, body)
|
||||||
|
|
||||||
|
await appServer.AppDataSource.getRepository(Assistant).merge(assistant, updateAssistant)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Assistant).save(assistant)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Error updating assistant: ${error}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: assistantsService.updateAssistant - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
creatAssistant,
|
||||||
|
deleteAssistant,
|
||||||
|
getAllAssistants,
|
||||||
|
getAssistantById,
|
||||||
|
updateAssistant
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
import { FindOptionsWhere } from 'typeorm'
|
||||||
|
import path from 'path'
|
||||||
|
import { chatType, IChatMessage } from '../../Interface'
|
||||||
|
import { utilGetChatMessage } from '../../utils/getChatMessage'
|
||||||
|
import { utilAddChatMessage } from '../../utils/addChatMesage'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback'
|
||||||
|
import { getStoragePath } from 'flowise-components'
|
||||||
|
import { deleteFolderRecursive } from '../../utils'
|
||||||
|
import logger from '../../utils/logger'
|
||||||
|
import { ChatMessage } from '../../database/entities/ChatMessage'
|
||||||
|
|
||||||
|
// Add chatmessages for chatflowid
|
||||||
|
const createChatMessage = async (chatMessage: Partial<IChatMessage>) => {
|
||||||
|
try {
|
||||||
|
const dbResponse = await utilAddChatMessage(chatMessage)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatMessagesService.createChatMessage - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all chatmessages from chatflowid
|
||||||
|
const getAllChatMessages = async (
|
||||||
|
chatflowId: string,
|
||||||
|
chatTypeFilter: chatType | undefined,
|
||||||
|
sortOrder: string = 'ASC',
|
||||||
|
chatId?: string,
|
||||||
|
memoryType?: string,
|
||||||
|
sessionId?: string,
|
||||||
|
startDate?: string,
|
||||||
|
endDate?: string,
|
||||||
|
messageId?: string,
|
||||||
|
feedback?: boolean
|
||||||
|
): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const dbResponse = await utilGetChatMessage(
|
||||||
|
chatflowId,
|
||||||
|
chatTypeFilter,
|
||||||
|
sortOrder,
|
||||||
|
chatId,
|
||||||
|
memoryType,
|
||||||
|
sessionId,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
messageId,
|
||||||
|
feedback
|
||||||
|
)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatMessagesService.getAllChatMessages - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get internal chatmessages from chatflowid
|
||||||
|
const getAllInternalChatMessages = async (
|
||||||
|
chatflowId: string,
|
||||||
|
chatTypeFilter: chatType | undefined,
|
||||||
|
sortOrder: string = 'ASC',
|
||||||
|
chatId?: string,
|
||||||
|
memoryType?: string,
|
||||||
|
sessionId?: string,
|
||||||
|
startDate?: string,
|
||||||
|
endDate?: string,
|
||||||
|
messageId?: string,
|
||||||
|
feedback?: boolean
|
||||||
|
): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const dbResponse = await utilGetChatMessage(
|
||||||
|
chatflowId,
|
||||||
|
chatTypeFilter,
|
||||||
|
sortOrder,
|
||||||
|
chatId,
|
||||||
|
memoryType,
|
||||||
|
sessionId,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
messageId,
|
||||||
|
feedback
|
||||||
|
)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatMessagesService.getAllInternalChatMessages - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeAllChatMessages = async (chatId: string, chatflowid: string, deleteOptions: FindOptionsWhere<ChatMessage>): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
|
||||||
|
// remove all related feedback records
|
||||||
|
const feedbackDeleteOptions: FindOptionsWhere<ChatMessageFeedback> = { chatId }
|
||||||
|
await appServer.AppDataSource.getRepository(ChatMessageFeedback).delete(feedbackDeleteOptions)
|
||||||
|
|
||||||
|
// Delete all uploads corresponding to this chatflow/chatId
|
||||||
|
if (chatId) {
|
||||||
|
try {
|
||||||
|
const directory = path.join(getStoragePath(), chatflowid, chatId)
|
||||||
|
deleteFolderRecursive(directory)
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(`[server]: Error deleting file storage for chatflow ${chatflowid}, chatId ${chatId}: ${e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatMessage).delete(deleteOptions)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatMessagesService.removeAllChatMessages - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createChatMessage,
|
||||||
|
getAllChatMessages,
|
||||||
|
getAllInternalChatMessages,
|
||||||
|
removeAllChatMessages
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,278 @@
|
||||||
|
import path from 'path'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import { IChatFlow } from '../../Interface'
|
||||||
|
import { ChatFlow } from '../../database/entities/ChatFlow'
|
||||||
|
import {
|
||||||
|
getAppVersion,
|
||||||
|
getTelemetryFlowObj,
|
||||||
|
deleteFolderRecursive,
|
||||||
|
isFlowValidForStream,
|
||||||
|
constructGraphs,
|
||||||
|
getEndingNodes
|
||||||
|
} from '../../utils'
|
||||||
|
import logger from '../../utils/logger'
|
||||||
|
import { getStoragePath } from 'flowise-components'
|
||||||
|
import { IReactFlowObject } from '../../Interface'
|
||||||
|
import { utilGetUploadsConfig } from '../../utils/getUploadsConfig'
|
||||||
|
|
||||||
|
// Check if chatflow valid for streaming
|
||||||
|
const checkIfChatflowIsValidForStreaming = async (chatflowId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
//**
|
||||||
|
const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
|
id: chatflowId
|
||||||
|
})
|
||||||
|
if (!chatflow) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Chatflow ${chatflowId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Get Ending Node with Directed Graph ***/
|
||||||
|
const flowData = chatflow.flowData
|
||||||
|
const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
|
||||||
|
const nodes = parsedFlowData.nodes
|
||||||
|
const edges = parsedFlowData.edges
|
||||||
|
const { graph, nodeDependencies } = constructGraphs(nodes, edges)
|
||||||
|
|
||||||
|
const endingNodeIds = getEndingNodes(nodeDependencies, graph)
|
||||||
|
if (!endingNodeIds.length) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Ending nodes not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const endingNodes = nodes.filter((nd) => endingNodeIds.includes(nd.id))
|
||||||
|
|
||||||
|
let isStreaming = false
|
||||||
|
let isEndingNodeExists = endingNodes.find((node) => node.data?.outputs?.output === 'EndingNode')
|
||||||
|
|
||||||
|
for (const endingNode of endingNodes) {
|
||||||
|
const endingNodeData = endingNode.data
|
||||||
|
if (!endingNodeData) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Ending node ${endingNode.id} data not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isEndingNode = endingNodeData?.outputs?.output === 'EndingNode'
|
||||||
|
|
||||||
|
if (!isEndingNode) {
|
||||||
|
if (
|
||||||
|
endingNodeData &&
|
||||||
|
endingNodeData.category !== 'Chains' &&
|
||||||
|
endingNodeData.category !== 'Agents' &&
|
||||||
|
endingNodeData.category !== 'Engine'
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Ending node must be either a Chain or Agent`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isStreaming = isEndingNode ? false : isFlowValidForStream(nodes, endingNodeData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once custom function ending node exists, flow is always unavailable to stream
|
||||||
|
const dbResponse = { isStreaming: isEndingNodeExists ? false : isStreaming }
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.checkIfChatflowIsValidForStreaming - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if chatflow valid for uploads
|
||||||
|
const checkIfChatflowIsValidForUploads = async (chatflowId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const dbResponse = await utilGetUploadsConfig(chatflowId)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.checkIfChatflowIsValidForUploads - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteChatflow = async (chatflowId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).delete({ id: chatflowId })
|
||||||
|
try {
|
||||||
|
// Delete all uploads corresponding to this chatflow
|
||||||
|
const directory = path.join(getStoragePath(), chatflowId)
|
||||||
|
deleteFolderRecursive(directory)
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(`[server]: Error deleting file storage for chatflow ${chatflowId}: ${e}`)
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.getAllChatflows - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllChatflows = async (): Promise<IChatFlow[]> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).find()
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.getAllChatflows - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getChatflowByApiKey = async (apiKeyId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow)
|
||||||
|
.createQueryBuilder('cf')
|
||||||
|
.where('cf.apikeyid = :apikeyid', { apikeyid: apiKeyId })
|
||||||
|
.orWhere('cf.apikeyid IS NULL')
|
||||||
|
.orWhere('cf.apikeyid = ""')
|
||||||
|
.orderBy('cf.name', 'ASC')
|
||||||
|
.getMany()
|
||||||
|
if (dbResponse.length < 1) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Chatflow not found in the database!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.getChatflowByApiKey - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getChatflowById = async (chatflowId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
|
id: chatflowId
|
||||||
|
})
|
||||||
|
if (!dbResponse) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Chatflow ${chatflowId} not found in the database!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.getAllChatflows - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const saveChatflow = async (newChatFlow: ChatFlow): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const newDbChatflow = await appServer.AppDataSource.getRepository(ChatFlow).create(newChatFlow)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(newDbChatflow)
|
||||||
|
await appServer.telemetry.sendTelemetry('chatflow_created', {
|
||||||
|
version: await getAppVersion(),
|
||||||
|
chatflowId: dbResponse.id,
|
||||||
|
flowGraph: getTelemetryFlowObj(JSON.parse(dbResponse.flowData)?.nodes, JSON.parse(dbResponse.flowData)?.edges)
|
||||||
|
})
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.saveChatflow - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateChatflow = async (chatflow: ChatFlow, updateChatFlow: ChatFlow): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const newDbChatflow = await appServer.AppDataSource.getRepository(ChatFlow).merge(chatflow, updateChatFlow)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(newDbChatflow)
|
||||||
|
// chatFlowPool is initialized only when a flow is opened
|
||||||
|
// if the user attempts to rename/update category without opening any flow, chatFlowPool will be undefined
|
||||||
|
if (appServer.chatflowPool) {
|
||||||
|
// Update chatflowpool inSync to false, to build flow from scratch again because data has been changed
|
||||||
|
appServer.chatflowPool.updateInSync(chatflow.id, false)
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.updateChatflow - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get specific chatflow via id (PUBLIC endpoint, used when sharing chatbot link)
|
||||||
|
const getSinglePublicChatflow = async (chatflowId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
|
id: chatflowId
|
||||||
|
})
|
||||||
|
if (dbResponse && dbResponse.isPublic) {
|
||||||
|
return dbResponse
|
||||||
|
} else if (dbResponse && !dbResponse.isPublic) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 401,
|
||||||
|
msg: `Unauthorized`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Chatflow ${chatflowId} not found`
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.getSinglePublicChatflow - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get specific chatflow chatbotConfig via id (PUBLIC endpoint, used to retrieve config for embedded chat)
|
||||||
|
// Safe as public endpoint as chatbotConfig doesn't contain sensitive credential
|
||||||
|
const getSinglePublicChatbotConfig = async (chatflowId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
|
id: chatflowId
|
||||||
|
})
|
||||||
|
if (!dbResponse) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Chatflow ${chatflowId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const uploadsConfig = await utilGetUploadsConfig(chatflowId)
|
||||||
|
// even if chatbotConfig is not set but uploads are enabled
|
||||||
|
// send uploadsConfig to the chatbot
|
||||||
|
if (dbResponse.chatbotConfig || uploadsConfig) {
|
||||||
|
try {
|
||||||
|
const parsedConfig = dbResponse.chatbotConfig ? JSON.parse(dbResponse.chatbotConfig) : {}
|
||||||
|
return { ...parsedConfig, uploads: uploadsConfig }
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Error parsing Chatbot Config for Chatflow ${chatflowId}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'OK'
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: chatflowsService.getSinglePublicChatbotConfig - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
checkIfChatflowIsValidForStreaming,
|
||||||
|
checkIfChatflowIsValidForUploads,
|
||||||
|
deleteChatflow,
|
||||||
|
getAllChatflows,
|
||||||
|
getChatflowByApiKey,
|
||||||
|
getChatflowById,
|
||||||
|
saveChatflow,
|
||||||
|
updateChatflow,
|
||||||
|
getSinglePublicChatflow,
|
||||||
|
getSinglePublicChatbotConfig
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
|
||||||
|
// Get all component credentials
|
||||||
|
const getAllComponentsCredentials = async (): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = []
|
||||||
|
for (const credName in appServer.nodesPool.componentCredentials) {
|
||||||
|
const clonedCred = cloneDeep(appServer.nodesPool.componentCredentials[credName])
|
||||||
|
dbResponse.push(clonedCred)
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: componentsCredentialsService.getAllComponentsCredentials - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getComponentByName = async (credentialName: string) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
if (!credentialName.includes('&')) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentCredentials, credentialName)) {
|
||||||
|
return appServer.nodesPool.componentCredentials[credentialName]
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`Error: componentsCredentialsService.getSingleComponentsCredential - Credential ${credentialName} not found`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const dbResponse = []
|
||||||
|
for (const name of credentialName.split('&')) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentCredentials, name)) {
|
||||||
|
dbResponse.push(appServer.nodesPool.componentCredentials[name])
|
||||||
|
} else {
|
||||||
|
throw new Error(`Error: componentsCredentialsService.getSingleComponentsCredential - Credential ${name} not found`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: componentsCredentialsService.getSingleComponentsCredential - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns specific component credential icon via name
|
||||||
|
const getSingleComponentsCredentialIcon = async (credentialName: string) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentCredentials, credentialName)) {
|
||||||
|
const credInstance = appServer.nodesPool.componentCredentials[credentialName]
|
||||||
|
if (credInstance.icon === undefined) {
|
||||||
|
throw new Error(`Credential ${credentialName} icon not found`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (credInstance.icon.endsWith('.svg') || credInstance.icon.endsWith('.png') || credInstance.icon.endsWith('.jpg')) {
|
||||||
|
const filepath = credInstance.icon
|
||||||
|
return filepath
|
||||||
|
} else {
|
||||||
|
throw new Error(`Credential ${credentialName} icon is missing icon`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Credential ${credentialName} not found`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: componentsCredentialsService.getSingleComponentsCredentialIcon - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllComponentsCredentials,
|
||||||
|
getComponentByName,
|
||||||
|
getSingleComponentsCredentialIcon
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
import { omit } from 'lodash'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import { Credential } from '../../database/entities/Credential'
|
||||||
|
import { transformToCredentialEntity, decryptCredentialData } from '../../utils'
|
||||||
|
import { ICredentialReturnResponse } from '../../Interface'
|
||||||
|
|
||||||
|
const createCredential = async (requestBody: any) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const newCredential = await transformToCredentialEntity(requestBody)
|
||||||
|
const credential = await appServer.AppDataSource.getRepository(Credential).create(newCredential)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Credential).save(credential)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: credentialsService.createCredential - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all credentials from chatflowid
|
||||||
|
const deleteCredentials = async (credentialId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Credential).delete({ id: credentialId })
|
||||||
|
if (!dbResponse) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Credential ${credentialId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: credentialsService.deleteCredential - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllCredentials = async (paramCredentialName: any) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
let dbResponse = []
|
||||||
|
if (paramCredentialName) {
|
||||||
|
if (Array.isArray(paramCredentialName)) {
|
||||||
|
for (let i = 0; i < paramCredentialName.length; i += 1) {
|
||||||
|
const name = paramCredentialName[i] as string
|
||||||
|
const credentials = await appServer.AppDataSource.getRepository(Credential).findBy({
|
||||||
|
credentialName: name
|
||||||
|
})
|
||||||
|
dbResponse.push(...credentials)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const credentials = await appServer.AppDataSource.getRepository(Credential).findBy({
|
||||||
|
credentialName: paramCredentialName as string
|
||||||
|
})
|
||||||
|
dbResponse = [...credentials]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const credentials = await appServer.AppDataSource.getRepository(Credential).find()
|
||||||
|
for (const credential of credentials) {
|
||||||
|
dbResponse.push(omit(credential, ['encryptedData']))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: credentialsService.getAllCredentials - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getCredentialById = async (credentialId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
|
id: credentialId
|
||||||
|
})
|
||||||
|
if (!credential) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Credential ${credentialId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decrpyt credentialData
|
||||||
|
const decryptedCredentialData = await decryptCredentialData(
|
||||||
|
credential.encryptedData,
|
||||||
|
credential.credentialName,
|
||||||
|
appServer.nodesPool.componentCredentials
|
||||||
|
)
|
||||||
|
const returnCredential: ICredentialReturnResponse = {
|
||||||
|
...credential,
|
||||||
|
plainDataObj: decryptedCredentialData
|
||||||
|
}
|
||||||
|
const dbResponse = omit(returnCredential, ['encryptedData'])
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: credentialsService.createCredential - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateCredential = async (credentialId: string, requestBody: any): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
|
id: credentialId
|
||||||
|
})
|
||||||
|
if (!credential) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Credential ${credentialId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const updateCredential = await transformToCredentialEntity(requestBody)
|
||||||
|
await appServer.AppDataSource.getRepository(Credential).merge(credential, updateCredential)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Credential).save(credential)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: credentialsService.updateCredential - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createCredential,
|
||||||
|
deleteCredentials,
|
||||||
|
getAllCredentials,
|
||||||
|
getCredentialById,
|
||||||
|
updateCredential
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { utilGetChatMessageFeedback } from '../../utils/getChatMessageFeedback'
|
||||||
|
import { utilAddChatMessageFeedback } from '../../utils/addChatMessageFeedback'
|
||||||
|
import { utilUpdateChatMessageFeedback } from '../../utils/updateChatMessageFeedback'
|
||||||
|
import { IChatMessageFeedback } from '../../Interface'
|
||||||
|
|
||||||
|
// Get all chatmessage feedback from chatflowid
|
||||||
|
const getAllChatMessageFeedback = async (
|
||||||
|
chatflowid: string,
|
||||||
|
chatId: string | undefined,
|
||||||
|
sortOrder: string | undefined,
|
||||||
|
startDate: string | undefined,
|
||||||
|
endDate: string | undefined
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const dbResponse = await utilGetChatMessageFeedback(chatflowid, chatId, sortOrder, startDate, endDate)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: feedbackService.getAllChatMessageFeedback - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add chatmessage feedback for chatflowid
|
||||||
|
const createChatMessageFeedbackForChatflow = async (requestBody: Partial<IChatMessageFeedback>): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const dbResponse = await utilAddChatMessageFeedback(requestBody)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: feedbackService.createChatMessageFeedbackForChatflow - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add chatmessage feedback for chatflowid
|
||||||
|
const updateChatMessageFeedbackForChatflow = async (chatflowId: string, requestBody: Partial<IChatMessageFeedback>): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const dbResponse = await utilUpdateChatMessageFeedback(chatflowId, requestBody)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: feedbackService.updateChatMessageFeedbackForChatflow - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllChatMessageFeedback,
|
||||||
|
createChatMessageFeedbackForChatflow,
|
||||||
|
updateChatMessageFeedbackForChatflow
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { webCrawl, xmlScrape } from 'flowise-components'
|
||||||
|
|
||||||
|
const getAllLinks = async (requestUrl: string, relativeLinksMethod: string, queryLimit: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const url = decodeURIComponent(requestUrl)
|
||||||
|
if (!relativeLinksMethod) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Please choose a Relative Links Method in Additional Parameters!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const limit = parseInt(queryLimit)
|
||||||
|
if (process.env.DEBUG === 'true') console.info(`Start ${relativeLinksMethod}`)
|
||||||
|
const links: string[] = relativeLinksMethod === 'webCrawl' ? await webCrawl(url, limit) : await xmlScrape(url, limit)
|
||||||
|
if (process.env.DEBUG === 'true') console.info(`Finish ${relativeLinksMethod}`)
|
||||||
|
const dbResponse = {
|
||||||
|
status: 'OK',
|
||||||
|
links
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: fetchLinksService.getAllLinks - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllLinks
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { findAvailableConfigs } from '../../utils'
|
||||||
|
import { IReactFlowObject } from '../../Interface'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import chatflowsService from '../chatflows'
|
||||||
|
|
||||||
|
const getSingleFlowConfig = async (chatflowId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const chatflow = await chatflowsService.getChatflowById(chatflowId)
|
||||||
|
if (!chatflow) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Chatflow ${chatflowId} not found in the database!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const flowData = chatflow.flowData
|
||||||
|
const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
|
||||||
|
const nodes = parsedFlowData.nodes
|
||||||
|
const dbResponse = findAvailableConfigs(nodes, appServer.nodesPool.componentCredentials)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: flowConfigService.getSingleFlowConfig - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getSingleFlowConfig
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Client } from 'langchainhub'
|
||||||
|
import { parsePrompt } from '../../utils/hub'
|
||||||
|
|
||||||
|
const createPrompt = async (promptName: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
let hub = new Client()
|
||||||
|
const prompt = await hub.pull(promptName)
|
||||||
|
const templates = parsePrompt(prompt)
|
||||||
|
const dbResponse = {
|
||||||
|
status: 'OK',
|
||||||
|
prompt: promptName,
|
||||||
|
templates: templates
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: loadPromptsService.createPrompt - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createPrompt
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
import path from 'path'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
|
||||||
|
// Get all templates for marketplaces
|
||||||
|
const getAllTemplates = async () => {
|
||||||
|
try {
|
||||||
|
let marketplaceDir = path.join(__dirname, '..', '..', '..', 'marketplaces', 'chatflows')
|
||||||
|
let jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
|
||||||
|
let templates: any[] = []
|
||||||
|
jsonsInDir.forEach((file, index) => {
|
||||||
|
const filePath = path.join(__dirname, '..', '..', '..', 'marketplaces', 'chatflows', file)
|
||||||
|
const fileData = fs.readFileSync(filePath)
|
||||||
|
const fileDataObj = JSON.parse(fileData.toString())
|
||||||
|
const template = {
|
||||||
|
id: index,
|
||||||
|
templateName: file.split('.json')[0],
|
||||||
|
flowData: fileData.toString(),
|
||||||
|
badge: fileDataObj?.badge,
|
||||||
|
framework: fileDataObj?.framework,
|
||||||
|
categories: fileDataObj?.categories,
|
||||||
|
type: 'Chatflow',
|
||||||
|
description: fileDataObj?.description || ''
|
||||||
|
}
|
||||||
|
templates.push(template)
|
||||||
|
})
|
||||||
|
|
||||||
|
marketplaceDir = path.join(__dirname, '..', '..', '..', 'marketplaces', 'tools')
|
||||||
|
jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
|
||||||
|
jsonsInDir.forEach((file, index) => {
|
||||||
|
const filePath = path.join(__dirname, '..', '..', '..', 'marketplaces', 'tools', file)
|
||||||
|
const fileData = fs.readFileSync(filePath)
|
||||||
|
const fileDataObj = JSON.parse(fileData.toString())
|
||||||
|
const template = {
|
||||||
|
...fileDataObj,
|
||||||
|
id: index,
|
||||||
|
type: 'Tool',
|
||||||
|
framework: fileDataObj?.framework,
|
||||||
|
badge: fileDataObj?.badge,
|
||||||
|
categories: '',
|
||||||
|
templateName: file.split('.json')[0]
|
||||||
|
}
|
||||||
|
templates.push(template)
|
||||||
|
})
|
||||||
|
const sortedTemplates = templates.sort((a, b) => a.templateName.localeCompare(b.templateName))
|
||||||
|
const FlowiseDocsQnAIndex = sortedTemplates.findIndex((tmp) => tmp.templateName === 'Flowise Docs QnA')
|
||||||
|
if (FlowiseDocsQnAIndex > 0) {
|
||||||
|
sortedTemplates.unshift(sortedTemplates.splice(FlowiseDocsQnAIndex, 1)[0])
|
||||||
|
}
|
||||||
|
const dbResponse = sortedTemplates
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: marketplacesService.getAllTemplates - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllTemplates
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { findAvailableConfigs } from '../../utils'
|
||||||
|
import { IReactFlowNode } from '../../Interface'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
|
||||||
|
const getAllNodeConfigs = async (requestBody: any) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const nodes = [{ data: requestBody }] as IReactFlowNode[]
|
||||||
|
const dbResponse = findAvailableConfigs(nodes, appServer.nodesPool.componentCredentials)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: nodeConfigsService.getAllNodeConfigs - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllNodeConfigs
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,142 @@
|
||||||
|
import { cloneDeep } from 'lodash'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import { INodeData } from '../../Interface'
|
||||||
|
import { INodeOptionsValue, ICommonObject, handleEscapeCharacters } from 'flowise-components'
|
||||||
|
import { databaseEntities } from '../../utils'
|
||||||
|
import logger from '../../utils/logger'
|
||||||
|
|
||||||
|
// Get all component nodes
|
||||||
|
const getAllNodes = async () => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = []
|
||||||
|
for (const nodeName in appServer.nodesPool.componentNodes) {
|
||||||
|
const clonedNode = cloneDeep(appServer.nodesPool.componentNodes[nodeName])
|
||||||
|
dbResponse.push(clonedNode)
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: nodesService.getAllNodes - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get specific component node via name
|
||||||
|
const getNodeByName = async (nodeName: string) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentNodes, nodeName)) {
|
||||||
|
const dbResponse = appServer.nodesPool.componentNodes[nodeName]
|
||||||
|
return dbResponse
|
||||||
|
} else {
|
||||||
|
throw new Error(`Node ${nodeName} not found`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: nodesService.getAllNodes - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns specific component node icon via name
|
||||||
|
const getSingleNodeIcon = async (nodeName: string) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentNodes, nodeName)) {
|
||||||
|
const nodeInstance = appServer.nodesPool.componentNodes[nodeName]
|
||||||
|
if (nodeInstance.icon === undefined) {
|
||||||
|
throw new Error(`Node ${nodeName} icon not found`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nodeInstance.icon.endsWith('.svg') || nodeInstance.icon.endsWith('.png') || nodeInstance.icon.endsWith('.jpg')) {
|
||||||
|
const filepath = nodeInstance.icon
|
||||||
|
return filepath
|
||||||
|
} else {
|
||||||
|
throw new Error(`Node ${nodeName} icon is missing icon`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Node ${nodeName} not found`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: nodesService.getSingleNodeIcon - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSingleNodeAsyncOptions = async (nodeName: string, requestBody: any): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const nodeData: INodeData = requestBody
|
||||||
|
if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentNodes, nodeName)) {
|
||||||
|
try {
|
||||||
|
const nodeInstance = appServer.nodesPool.componentNodes[nodeName]
|
||||||
|
const methodName = nodeData.loadMethod || ''
|
||||||
|
|
||||||
|
const dbResponse: INodeOptionsValue[] = await nodeInstance.loadMethods![methodName]!.call(nodeInstance, nodeData, {
|
||||||
|
appDataSource: appServer.AppDataSource,
|
||||||
|
databaseEntities: databaseEntities
|
||||||
|
})
|
||||||
|
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Node ${nodeName} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: nodesService.getSingleNodeAsyncOptions - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute custom function node
|
||||||
|
const executeCustomFunction = async (requestBody: any) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const body = requestBody
|
||||||
|
const functionInputVariables = Object.fromEntries(
|
||||||
|
[...(body?.javascriptFunction ?? '').matchAll(/\$([a-zA-Z0-9_]+)/g)].map((g) => [g[1], undefined])
|
||||||
|
)
|
||||||
|
const nodeData = { inputs: { functionInputVariables, ...body } }
|
||||||
|
if (Object.prototype.hasOwnProperty.call(appServer.nodesPool.componentNodes, 'customFunction')) {
|
||||||
|
try {
|
||||||
|
const nodeInstanceFilePath = appServer.nodesPool.componentNodes['customFunction'].filePath as string
|
||||||
|
const nodeModule = await import(nodeInstanceFilePath)
|
||||||
|
const newNodeInstance = new nodeModule.nodeClass()
|
||||||
|
|
||||||
|
const options: ICommonObject = {
|
||||||
|
appDataSource: appServer.AppDataSource,
|
||||||
|
databaseEntities,
|
||||||
|
logger
|
||||||
|
}
|
||||||
|
|
||||||
|
const returnData = await newNodeInstance.init(nodeData, '', options)
|
||||||
|
const dbResponse = typeof returnData === 'string' ? handleEscapeCharacters(returnData, true) : returnData
|
||||||
|
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Error running custom function: ${error}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Node customFunction not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: nodesService.executeCustomFunction - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllNodes,
|
||||||
|
getNodeByName,
|
||||||
|
getSingleNodeIcon,
|
||||||
|
getSingleNodeAsyncOptions,
|
||||||
|
executeCustomFunction
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
import OpenAI from 'openai'
|
||||||
|
import { decryptCredentialData } from '../../utils'
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import { Credential } from '../../database/entities/Credential'
|
||||||
|
|
||||||
|
// ----------------------------------------
|
||||||
|
// Assistants
|
||||||
|
// ----------------------------------------
|
||||||
|
|
||||||
|
// List available assistants
|
||||||
|
const getAllOpenaiAssistants = async (credentialId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
|
id: credentialId
|
||||||
|
})
|
||||||
|
if (!credential) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Credential ${credentialId} not found in the database!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decrpyt credentialData
|
||||||
|
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
||||||
|
const openAIApiKey = decryptedCredentialData['openAIApiKey']
|
||||||
|
if (!openAIApiKey) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `OpenAI ApiKey not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||||
|
const retrievedAssistants = await openai.beta.assistants.list()
|
||||||
|
const dbResponse = retrievedAssistants.data
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: openaiAssistantsService.getAllOpenaiAssistants - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get assistant object
|
||||||
|
const getSingleOpenaiAssistant = async (credentialId: string, assistantId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const credential = await appServer.AppDataSource.getRepository(Credential).findOneBy({
|
||||||
|
id: credentialId
|
||||||
|
})
|
||||||
|
if (!credential) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Credential ${credentialId} not found in the database!`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Decrpyt credentialData
|
||||||
|
const decryptedCredentialData = await decryptCredentialData(credential.encryptedData)
|
||||||
|
const openAIApiKey = decryptedCredentialData['openAIApiKey']
|
||||||
|
if (!openAIApiKey) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `OpenAI ApiKey not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openai = new OpenAI({ apiKey: openAIApiKey })
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: openaiAssistantsService.getSingleOpenaiAssistant - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getAllOpenaiAssistants,
|
||||||
|
getSingleOpenaiAssistant
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const createPromptsList = async (requestBody: any) => {
|
||||||
|
try {
|
||||||
|
const tags = requestBody.tags ? `tags=${requestBody.tags}` : ''
|
||||||
|
// Default to 100, TODO: add pagination and use offset & limit
|
||||||
|
const url = `https://api.hub.langchain.com/repos/?limit=100&${tags}has_commits=true&sort_field=num_likes&sort_direction=desc&is_archived=false`
|
||||||
|
const resp = await axios.get(url)
|
||||||
|
if (resp.data.repos) {
|
||||||
|
return {
|
||||||
|
status: 'OK',
|
||||||
|
repos: resp.data.repos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return { status: 'ERROR', repos: [] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createPromptsList
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { chatType } from '../../Interface'
|
||||||
|
import { ChatMessage } from '../../database/entities/ChatMessage'
|
||||||
|
import { utilGetChatMessage } from '../../utils/getChatMessage'
|
||||||
|
import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback'
|
||||||
|
|
||||||
|
// get stats for showing in chatflow
|
||||||
|
const getChatflowStats = async (
|
||||||
|
chatflowid: string,
|
||||||
|
chatTypeFilter: chatType | undefined,
|
||||||
|
startDate?: string,
|
||||||
|
endDate?: string,
|
||||||
|
messageId?: string,
|
||||||
|
feedback?: boolean
|
||||||
|
): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const chatmessages = (await utilGetChatMessage(
|
||||||
|
chatflowid,
|
||||||
|
chatTypeFilter,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
messageId,
|
||||||
|
feedback
|
||||||
|
)) as Array<ChatMessage & { feedback?: ChatMessageFeedback }>
|
||||||
|
const totalMessages = chatmessages.length
|
||||||
|
const totalFeedback = chatmessages.filter((message) => message?.feedback).length
|
||||||
|
const positiveFeedback = chatmessages.filter((message) => message?.feedback?.rating === 'THUMBS_UP').length
|
||||||
|
const dbResponse = {
|
||||||
|
totalMessages,
|
||||||
|
totalFeedback,
|
||||||
|
positiveFeedback
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: statsService.getChatflowStats - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getChatflowStats
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
|
||||||
|
const createEvent = async (eventInfo: any) => {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
await appServer.telemetry.sendTelemetry(eventInfo.name, eventInfo.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createEvent
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import { Tool } from '../../database/entities/Tool'
|
||||||
|
import { getAppVersion } from '../../utils'
|
||||||
|
|
||||||
|
const creatTool = async (requestBody: any): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const newTool = new Tool()
|
||||||
|
Object.assign(newTool, requestBody)
|
||||||
|
const tool = await appServer.AppDataSource.getRepository(Tool).create(newTool)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Tool).save(tool)
|
||||||
|
await appServer.telemetry.sendTelemetry('tool_created', {
|
||||||
|
version: await getAppVersion(),
|
||||||
|
toolId: dbResponse.id,
|
||||||
|
toolName: dbResponse.name
|
||||||
|
})
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: toolsService.creatTool - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTool = async (toolId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Tool).delete({
|
||||||
|
id: toolId
|
||||||
|
})
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: toolsService.deleteTool - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllTools = async (): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Tool).find()
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: toolsService.getAllTools - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getToolById = async (toolId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Tool).findOneBy({
|
||||||
|
id: toolId
|
||||||
|
})
|
||||||
|
if (!dbResponse) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Tool ${toolId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: toolsService.getToolById - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTool = async (toolId: string, toolBody: any): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const tool = await appServer.AppDataSource.getRepository(Tool).findOneBy({
|
||||||
|
id: toolId
|
||||||
|
})
|
||||||
|
if (!tool) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Tool ${toolId} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const updateTool = new Tool()
|
||||||
|
Object.assign(updateTool, toolBody)
|
||||||
|
await appServer.AppDataSource.getRepository(Tool).merge(tool, updateTool)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Tool).save(tool)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: toolsService.getToolById - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
creatTool,
|
||||||
|
deleteTool,
|
||||||
|
getAllTools,
|
||||||
|
getToolById,
|
||||||
|
updateTool
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
|
||||||
|
import { Variable } from '../../database/entities/Variable'
|
||||||
|
|
||||||
|
const createVariable = async (newVariable: Variable) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const variable = await appServer.AppDataSource.getRepository(Variable).create(newVariable)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Variable).save(variable)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: variablesServices.createVariable - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteVariable = async (variableId: string): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Variable).delete({ id: variableId })
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: variablesServices.createVariable - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllVariables = async () => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Variable).find()
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: variablesServices.getAllVariables - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getVariableById = async (variableId: string) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Variable).findOneBy({
|
||||||
|
id: variableId
|
||||||
|
})
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: variablesServices.getVariableById - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateVariable = async (variable: Variable, updatedVariable: Variable) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const tmpUpdatedVariable = await appServer.AppDataSource.getRepository(Variable).merge(variable, updatedVariable)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(Variable).save(tmpUpdatedVariable)
|
||||||
|
return dbResponse
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: variablesServices.updateVariable - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
createVariable,
|
||||||
|
deleteVariable,
|
||||||
|
getAllVariables,
|
||||||
|
getVariableById,
|
||||||
|
updateVariable
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Request, Response } from 'express'
|
||||||
|
import { upsertVector } from '../../utils/upsertVector'
|
||||||
|
|
||||||
|
const upsertVectorMiddleware = async (req: Request, res: Response, isInternal: boolean = false) => {
|
||||||
|
try {
|
||||||
|
await upsertVector(req, res, isInternal)
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: vectorsService.getRateLimiter - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
upsertVectorMiddleware
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import path from 'path'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
|
||||||
|
const getVersion = async () => {
|
||||||
|
try {
|
||||||
|
const getPackageJsonPath = (): string => {
|
||||||
|
const checkPaths = [
|
||||||
|
path.join(__dirname, '..', 'package.json'),
|
||||||
|
path.join(__dirname, '..', '..', 'package.json'),
|
||||||
|
path.join(__dirname, '..', '..', '..', 'package.json'),
|
||||||
|
path.join(__dirname, '..', '..', '..', '..', 'package.json'),
|
||||||
|
path.join(__dirname, '..', '..', '..', '..', '..', 'package.json')
|
||||||
|
]
|
||||||
|
for (const checkPath of checkPaths) {
|
||||||
|
if (fs.existsSync(checkPath)) {
|
||||||
|
return checkPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const packagejsonPath = getPackageJsonPath()
|
||||||
|
if (!packagejsonPath) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: 'Version not found'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const content = await fs.promises.readFile(packagejsonPath, 'utf8')
|
||||||
|
const parsedContent = JSON.parse(content)
|
||||||
|
return {
|
||||||
|
version: parsedContent.version
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Version not found: ${error}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: versionService.getVersion - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getVersion
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { ChatMessage } from '../database/entities/ChatMessage'
|
||||||
|
import { IChatMessage } from '../Interface'
|
||||||
|
import { getRunningExpressApp } from '../utils/getRunningExpressApp'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that add chat messages.
|
||||||
|
* @param {Partial<IChatMessage>} chatMessage
|
||||||
|
*/
|
||||||
|
export const utilAddChatMessage = async (chatMessage: Partial<IChatMessage>): Promise<ChatMessage> => {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const newChatMessage = new ChatMessage()
|
||||||
|
Object.assign(newChatMessage, chatMessage)
|
||||||
|
if (!newChatMessage.createdDate) {
|
||||||
|
newChatMessage.createdDate = new Date()
|
||||||
|
}
|
||||||
|
const chatmessage = await appServer.AppDataSource.getRepository(ChatMessage).create(newChatMessage)
|
||||||
|
const dbResponse = await appServer.AppDataSource.getRepository(ChatMessage).save(chatmessage)
|
||||||
|
return dbResponse
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { ChatMessageFeedback } from '../database/entities/ChatMessageFeedback'
|
||||||
|
import { IChatMessageFeedback } from '../Interface'
|
||||||
|
import { getRunningExpressApp } from '../utils/getRunningExpressApp'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that add chat message feedback.
|
||||||
|
* @param {Partial<IChatMessageFeedback>} chatMessageFeedback
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const utilAddChatMessageFeedback = async (chatMessageFeedback: Partial<IChatMessageFeedback>): Promise<ChatMessageFeedback> => {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const newChatMessageFeedback = new ChatMessageFeedback()
|
||||||
|
Object.assign(newChatMessageFeedback, chatMessageFeedback)
|
||||||
|
const feedback = await appServer.AppDataSource.getRepository(ChatMessageFeedback).create(newChatMessageFeedback)
|
||||||
|
return await appServer.AppDataSource.getRepository(ChatMessageFeedback).save(feedback)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { ChatFlow } from '../database/entities/ChatFlow'
|
||||||
|
import { getRunningExpressApp } from '../utils/getRunningExpressApp'
|
||||||
|
|
||||||
|
export const addChatflowsCount = async (keys: any) => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
let tmpResult = keys
|
||||||
|
if (typeof keys !== 'undefined' && keys.length > 0) {
|
||||||
|
const updatedKeys: any[] = []
|
||||||
|
//iterate through keys and get chatflows
|
||||||
|
for (const key of keys) {
|
||||||
|
const chatflows = await appServer.AppDataSource.getRepository(ChatFlow)
|
||||||
|
.createQueryBuilder('cf')
|
||||||
|
.where('cf.apikeyid = :apikeyid', { apikeyid: key.id })
|
||||||
|
.getMany()
|
||||||
|
const linkedChatFlows: any[] = []
|
||||||
|
chatflows.map((cf) => {
|
||||||
|
linkedChatFlows.push({
|
||||||
|
flowName: cf.name,
|
||||||
|
category: cf.category,
|
||||||
|
updatedDate: cf.updatedDate
|
||||||
|
})
|
||||||
|
})
|
||||||
|
key.chatFlows = linkedChatFlows
|
||||||
|
updatedKeys.push(key)
|
||||||
|
}
|
||||||
|
tmpResult = updatedKeys
|
||||||
|
}
|
||||||
|
return tmpResult
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Error: addChatflowsCount - ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,427 @@
|
||||||
|
import { Request } from 'express'
|
||||||
|
import { IFileUpload, getStoragePath, convertSpeechToText, ICommonObject } from 'flowise-components'
|
||||||
|
import { StatusCodes } from 'http-status-codes'
|
||||||
|
import { IncomingInput, IMessage, INodeData, IReactFlowObject, IReactFlowNode, IDepthQueue, chatType, IChatMessage } from '../Interface'
|
||||||
|
import path from 'path'
|
||||||
|
import { ChatFlow } from '../database/entities/ChatFlow'
|
||||||
|
import { Server } from 'socket.io'
|
||||||
|
import { getRunningExpressApp } from '../utils/getRunningExpressApp'
|
||||||
|
import {
|
||||||
|
mapMimeTypeToInputField,
|
||||||
|
isFlowValidForStream,
|
||||||
|
buildFlow,
|
||||||
|
getTelemetryFlowObj,
|
||||||
|
getAppVersion,
|
||||||
|
resolveVariables,
|
||||||
|
getSessionChatHistory,
|
||||||
|
findMemoryNode,
|
||||||
|
replaceInputsWithConfig,
|
||||||
|
getStartingNodes,
|
||||||
|
isStartNodeDependOnInput,
|
||||||
|
getMemorySessionId,
|
||||||
|
isSameOverrideConfig,
|
||||||
|
getEndingNodes,
|
||||||
|
constructGraphs
|
||||||
|
} from '../utils'
|
||||||
|
import { utilValidateKey } from './validateKey'
|
||||||
|
import { databaseEntities } from '.'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import { omit } from 'lodash'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
import logger from './logger'
|
||||||
|
import { utilAddChatMessage } from './addChatMesage'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build Chatflow
|
||||||
|
* @param {Request} req
|
||||||
|
* @param {Server} socketIO
|
||||||
|
* @param {boolean} isInternal
|
||||||
|
* @param {boolean} isUpsert
|
||||||
|
*/
|
||||||
|
export const utilBuildChatflow = async (req: Request, socketIO?: Server, isInternal: boolean = false): Promise<any> => {
|
||||||
|
try {
|
||||||
|
const appServer = getRunningExpressApp()
|
||||||
|
const chatflowid = req.params.id
|
||||||
|
let incomingInput: IncomingInput = req.body
|
||||||
|
let nodeToExecuteData: INodeData
|
||||||
|
const chatflow = await appServer.AppDataSource.getRepository(ChatFlow).findOneBy({
|
||||||
|
id: chatflowid
|
||||||
|
})
|
||||||
|
if (!chatflow) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: StatusCodes.NOT_FOUND,
|
||||||
|
msg: `Chatflow ${chatflowid} not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const chatId = incomingInput.chatId ?? incomingInput.overrideConfig?.sessionId ?? uuidv4()
|
||||||
|
const userMessageDateTime = new Date()
|
||||||
|
|
||||||
|
if (!isInternal) {
|
||||||
|
const isKeyValidated = await utilValidateKey(req, chatflow)
|
||||||
|
if (!isKeyValidated) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: StatusCodes.UNAUTHORIZED,
|
||||||
|
msg: `Unauthorized`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let fileUploads: IFileUpload[] = []
|
||||||
|
if (incomingInput.uploads) {
|
||||||
|
fileUploads = incomingInput.uploads
|
||||||
|
for (let i = 0; i < fileUploads.length; i += 1) {
|
||||||
|
const upload = fileUploads[i]
|
||||||
|
if ((upload.type === 'file' || upload.type === 'audio') && upload.data) {
|
||||||
|
const filename = upload.name
|
||||||
|
const dir = path.join(getStoragePath(), chatflowid, chatId)
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true })
|
||||||
|
}
|
||||||
|
const filePath = path.join(dir, filename)
|
||||||
|
const splitDataURI = upload.data.split(',')
|
||||||
|
const bf = Buffer.from(splitDataURI.pop() || '', 'base64')
|
||||||
|
fs.writeFileSync(filePath, bf)
|
||||||
|
|
||||||
|
// Omit upload.data since we don't store the content in database
|
||||||
|
upload.type = 'stored-file'
|
||||||
|
fileUploads[i] = omit(upload, ['data'])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run Speech to Text conversion
|
||||||
|
if (upload.mime === 'audio/webm') {
|
||||||
|
let speechToTextConfig: ICommonObject = {}
|
||||||
|
if (chatflow.speechToText) {
|
||||||
|
const speechToTextProviders = JSON.parse(chatflow.speechToText)
|
||||||
|
for (const provider in speechToTextProviders) {
|
||||||
|
const providerObj = speechToTextProviders[provider]
|
||||||
|
if (providerObj.status) {
|
||||||
|
speechToTextConfig = providerObj
|
||||||
|
speechToTextConfig['name'] = provider
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (speechToTextConfig) {
|
||||||
|
const options: ICommonObject = {
|
||||||
|
chatId,
|
||||||
|
chatflowid,
|
||||||
|
appDataSource: appServer.AppDataSource,
|
||||||
|
databaseEntities: databaseEntities
|
||||||
|
}
|
||||||
|
const speechToTextResult = await convertSpeechToText(upload, speechToTextConfig, options)
|
||||||
|
if (speechToTextResult) {
|
||||||
|
incomingInput.question = speechToTextResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let isStreamValid = false
|
||||||
|
|
||||||
|
const files = (req.files as any[]) || []
|
||||||
|
|
||||||
|
if (files.length) {
|
||||||
|
const overrideConfig: ICommonObject = { ...req.body }
|
||||||
|
for (const file of files) {
|
||||||
|
const fileData = fs.readFileSync(file.path, { encoding: 'base64' })
|
||||||
|
const dataBase64String = `data:${file.mimetype};base64,${fileData},filename:${file.filename}`
|
||||||
|
|
||||||
|
const fileInputField = mapMimeTypeToInputField(file.mimetype)
|
||||||
|
if (overrideConfig[fileInputField]) {
|
||||||
|
overrideConfig[fileInputField] = JSON.stringify([...JSON.parse(overrideConfig[fileInputField]), dataBase64String])
|
||||||
|
} else {
|
||||||
|
overrideConfig[fileInputField] = JSON.stringify([dataBase64String])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
incomingInput = {
|
||||||
|
question: req.body.question ?? 'hello',
|
||||||
|
overrideConfig,
|
||||||
|
history: [],
|
||||||
|
socketIOClientId: req.body.socketIOClientId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Get chatflows and prepare data ***/
|
||||||
|
const flowData = chatflow.flowData
|
||||||
|
const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
|
||||||
|
const nodes = parsedFlowData.nodes
|
||||||
|
const edges = parsedFlowData.edges
|
||||||
|
|
||||||
|
// Get session ID
|
||||||
|
const memoryNode = findMemoryNode(nodes, edges)
|
||||||
|
const memoryType = memoryNode?.data.label
|
||||||
|
let sessionId = undefined
|
||||||
|
if (memoryNode) sessionId = getMemorySessionId(memoryNode, incomingInput, chatId, isInternal)
|
||||||
|
|
||||||
|
/* Reuse the flow without having to rebuild (to avoid duplicated upsert, recomputation, reinitialization of memory) when all these conditions met:
|
||||||
|
* - Node Data already exists in pool
|
||||||
|
* - Still in sync (i.e the flow has not been modified since)
|
||||||
|
* - Existing overrideConfig and new overrideConfig are the same
|
||||||
|
* - Flow doesn't start with/contain nodes that depend on incomingInput.question
|
||||||
|
* TODO: convert overrideConfig to hash when we no longer store base64 string but filepath
|
||||||
|
***/
|
||||||
|
const isFlowReusable = () => {
|
||||||
|
return (
|
||||||
|
Object.prototype.hasOwnProperty.call(appServer.chatflowPool.activeChatflows, chatflowid) &&
|
||||||
|
appServer.chatflowPool.activeChatflows[chatflowid].inSync &&
|
||||||
|
appServer.chatflowPool.activeChatflows[chatflowid].endingNodeData &&
|
||||||
|
isSameOverrideConfig(
|
||||||
|
isInternal,
|
||||||
|
appServer.chatflowPool.activeChatflows[chatflowid].overrideConfig,
|
||||||
|
incomingInput.overrideConfig
|
||||||
|
) &&
|
||||||
|
!isStartNodeDependOnInput(appServer.chatflowPool.activeChatflows[chatflowid].startingNodes, nodes)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFlowReusable()) {
|
||||||
|
nodeToExecuteData = appServer.chatflowPool.activeChatflows[chatflowid].endingNodeData as INodeData
|
||||||
|
isStreamValid = isFlowValidForStream(nodes, nodeToExecuteData)
|
||||||
|
logger.debug(
|
||||||
|
`[server]: Reuse existing chatflow ${chatflowid} with ending node ${nodeToExecuteData.label} (${nodeToExecuteData.id})`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
/*** Get Ending Node with Directed Graph ***/
|
||||||
|
const { graph, nodeDependencies } = constructGraphs(nodes, edges)
|
||||||
|
const directedGraph = graph
|
||||||
|
const endingNodeIds = getEndingNodes(nodeDependencies, directedGraph)
|
||||||
|
if (!endingNodeIds.length) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Ending nodes not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const endingNodes = nodes.filter((nd) => endingNodeIds.includes(nd.id))
|
||||||
|
|
||||||
|
let isEndingNodeExists = endingNodes.find((node) => node.data?.outputs?.output === 'EndingNode')
|
||||||
|
|
||||||
|
for (const endingNode of endingNodes) {
|
||||||
|
const endingNodeData = endingNode.data
|
||||||
|
if (!endingNodeData) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Ending node ${endingNode.id} data not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isEndingNode = endingNodeData?.outputs?.output === 'EndingNode'
|
||||||
|
|
||||||
|
if (!isEndingNode) {
|
||||||
|
if (
|
||||||
|
endingNodeData &&
|
||||||
|
endingNodeData.category !== 'Chains' &&
|
||||||
|
endingNodeData.category !== 'Agents' &&
|
||||||
|
endingNodeData.category !== 'Engine'
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Ending node must be either a Chain or Agent`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
endingNodeData.outputs &&
|
||||||
|
Object.keys(endingNodeData.outputs).length &&
|
||||||
|
!Object.values(endingNodeData.outputs ?? {}).includes(endingNodeData.name)
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: `Output of ${endingNodeData.label} (${endingNodeData.id}) must be ${endingNodeData.label}, can't be an Output Prediction`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isStreamValid = isFlowValidForStream(nodes, endingNodeData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once custom function ending node exists, flow is always unavailable to stream
|
||||||
|
isStreamValid = isEndingNodeExists ? false : isStreamValid
|
||||||
|
|
||||||
|
let chatHistory: IMessage[] = incomingInput.history ?? []
|
||||||
|
|
||||||
|
// When {{chat_history}} is used in Prompt Template, fetch the chat conversations from memory node
|
||||||
|
for (const endingNode of endingNodes) {
|
||||||
|
const endingNodeData = endingNode.data
|
||||||
|
|
||||||
|
if (!endingNodeData.inputs?.memory) continue
|
||||||
|
|
||||||
|
const memoryNodeId = endingNodeData.inputs?.memory.split('.')[0].replace('{{', '')
|
||||||
|
const memoryNode = nodes.find((node) => node.data.id === memoryNodeId)
|
||||||
|
|
||||||
|
if (!memoryNode) continue
|
||||||
|
|
||||||
|
if (!chatHistory.length && (incomingInput.chatId || incomingInput.overrideConfig?.sessionId)) {
|
||||||
|
chatHistory = await getSessionChatHistory(
|
||||||
|
memoryNode,
|
||||||
|
appServer.nodesPool.componentNodes,
|
||||||
|
incomingInput,
|
||||||
|
appServer.AppDataSource,
|
||||||
|
databaseEntities,
|
||||||
|
logger
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Get Starting Nodes with Reversed Graph ***/
|
||||||
|
const constructedObj = constructGraphs(nodes, edges, { isReversed: true })
|
||||||
|
const nonDirectedGraph = constructedObj.graph
|
||||||
|
let startingNodeIds: string[] = []
|
||||||
|
let depthQueue: IDepthQueue = {}
|
||||||
|
for (const endingNodeId of endingNodeIds) {
|
||||||
|
const resx = getStartingNodes(nonDirectedGraph, endingNodeId)
|
||||||
|
startingNodeIds.push(...resx.startingNodeIds)
|
||||||
|
depthQueue = Object.assign(depthQueue, resx.depthQueue)
|
||||||
|
}
|
||||||
|
startingNodeIds = [...new Set(startingNodeIds)]
|
||||||
|
|
||||||
|
const startingNodes = nodes.filter((nd) => startingNodeIds.includes(nd.id))
|
||||||
|
|
||||||
|
logger.debug(`[server]: Start building chatflow ${chatflowid}`)
|
||||||
|
/*** BFS to traverse from Starting Nodes to Ending Node ***/
|
||||||
|
const reactFlowNodes = await buildFlow(
|
||||||
|
startingNodeIds,
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
graph,
|
||||||
|
depthQueue,
|
||||||
|
appServer.nodesPool.componentNodes,
|
||||||
|
incomingInput.question,
|
||||||
|
chatHistory,
|
||||||
|
chatId,
|
||||||
|
sessionId ?? '',
|
||||||
|
chatflowid,
|
||||||
|
appServer.AppDataSource,
|
||||||
|
incomingInput?.overrideConfig,
|
||||||
|
appServer.cachePool,
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
incomingInput.uploads
|
||||||
|
)
|
||||||
|
|
||||||
|
const nodeToExecute =
|
||||||
|
endingNodeIds.length === 1
|
||||||
|
? reactFlowNodes.find((node: IReactFlowNode) => endingNodeIds[0] === node.id)
|
||||||
|
: reactFlowNodes[reactFlowNodes.length - 1]
|
||||||
|
if (!nodeToExecute) {
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 404,
|
||||||
|
msg: `Node not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (incomingInput.overrideConfig) {
|
||||||
|
nodeToExecute.data = replaceInputsWithConfig(nodeToExecute.data, incomingInput.overrideConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
const reactFlowNodeData: INodeData = resolveVariables(nodeToExecute.data, reactFlowNodes, incomingInput.question, chatHistory)
|
||||||
|
nodeToExecuteData = reactFlowNodeData
|
||||||
|
|
||||||
|
appServer.chatflowPool.add(chatflowid, nodeToExecuteData, startingNodes, incomingInput?.overrideConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`[server]: Running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`)
|
||||||
|
|
||||||
|
const nodeInstanceFilePath = appServer.nodesPool.componentNodes[nodeToExecuteData.name].filePath as string
|
||||||
|
const nodeModule = await import(nodeInstanceFilePath)
|
||||||
|
const nodeInstance = new nodeModule.nodeClass({ sessionId })
|
||||||
|
|
||||||
|
let result = isStreamValid
|
||||||
|
? await nodeInstance.run(nodeToExecuteData, incomingInput.question, {
|
||||||
|
chatId,
|
||||||
|
chatflowid,
|
||||||
|
chatHistory: incomingInput.history,
|
||||||
|
logger,
|
||||||
|
appDataSource: appServer.AppDataSource,
|
||||||
|
databaseEntities,
|
||||||
|
analytic: chatflow.analytic,
|
||||||
|
uploads: incomingInput.uploads,
|
||||||
|
socketIO,
|
||||||
|
socketIOClientId: incomingInput.socketIOClientId
|
||||||
|
})
|
||||||
|
: await nodeInstance.run(nodeToExecuteData, incomingInput.question, {
|
||||||
|
chatId,
|
||||||
|
chatflowid,
|
||||||
|
chatHistory: incomingInput.history,
|
||||||
|
logger,
|
||||||
|
appDataSource: appServer.AppDataSource,
|
||||||
|
databaseEntities,
|
||||||
|
analytic: chatflow.analytic,
|
||||||
|
uploads: incomingInput.uploads
|
||||||
|
})
|
||||||
|
result = typeof result === 'string' ? { text: result } : result
|
||||||
|
|
||||||
|
// Retrieve threadId from assistant if exists
|
||||||
|
if (typeof result === 'object' && result.assistant) {
|
||||||
|
sessionId = result.assistant.threadId
|
||||||
|
}
|
||||||
|
|
||||||
|
const userMessage: Omit<IChatMessage, 'id'> = {
|
||||||
|
role: 'userMessage',
|
||||||
|
content: incomingInput.question,
|
||||||
|
chatflowid,
|
||||||
|
chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL,
|
||||||
|
chatId,
|
||||||
|
memoryType,
|
||||||
|
sessionId,
|
||||||
|
createdDate: userMessageDateTime,
|
||||||
|
fileUploads: incomingInput.uploads ? JSON.stringify(fileUploads) : undefined
|
||||||
|
}
|
||||||
|
await utilAddChatMessage(userMessage)
|
||||||
|
|
||||||
|
let resultText = ''
|
||||||
|
if (result.text) resultText = result.text
|
||||||
|
else if (result.json) resultText = '```json\n' + JSON.stringify(result.json, null, 2)
|
||||||
|
else resultText = JSON.stringify(result, null, 2)
|
||||||
|
|
||||||
|
const apiMessage: Omit<IChatMessage, 'id' | 'createdDate'> = {
|
||||||
|
role: 'apiMessage',
|
||||||
|
content: resultText,
|
||||||
|
chatflowid,
|
||||||
|
chatType: isInternal ? chatType.INTERNAL : chatType.EXTERNAL,
|
||||||
|
chatId,
|
||||||
|
memoryType,
|
||||||
|
sessionId
|
||||||
|
}
|
||||||
|
if (result?.sourceDocuments) apiMessage.sourceDocuments = JSON.stringify(result.sourceDocuments)
|
||||||
|
if (result?.usedTools) apiMessage.usedTools = JSON.stringify(result.usedTools)
|
||||||
|
if (result?.fileAnnotations) apiMessage.fileAnnotations = JSON.stringify(result.fileAnnotations)
|
||||||
|
const chatMessage = await utilAddChatMessage(apiMessage)
|
||||||
|
|
||||||
|
logger.debug(`[server]: Finished running ${nodeToExecuteData.label} (${nodeToExecuteData.id})`)
|
||||||
|
await appServer.telemetry.sendTelemetry('prediction_sent', {
|
||||||
|
version: await getAppVersion(),
|
||||||
|
chatflowId: chatflowid,
|
||||||
|
chatId,
|
||||||
|
type: isInternal ? chatType.INTERNAL : chatType.EXTERNAL,
|
||||||
|
flowGraph: getTelemetryFlowObj(nodes, edges)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Prepare response
|
||||||
|
// return the question in the response
|
||||||
|
// this is used when input text is empty but question is in audio format
|
||||||
|
result.question = incomingInput.question
|
||||||
|
result.chatId = chatId
|
||||||
|
result.chatMessageId = chatMessage.id
|
||||||
|
if (sessionId) result.sessionId = sessionId
|
||||||
|
if (memoryType) result.memoryType = memoryType
|
||||||
|
|
||||||
|
return result
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.error('[server]: Error:', e)
|
||||||
|
return {
|
||||||
|
executionError: true,
|
||||||
|
status: 500,
|
||||||
|
msg: e.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue