Feature: Custom Templates (#3169)

* New Feature: Custom Templates in the marketplace.

* New Feature: Custom Templates in the marketplace.

* Custom Template Delete and Shortcut in the dropdown menu

* auto detect framework

* minor ui fixes

* adding custom template feature for tools

* ui tool dialog save template

---------

Co-authored-by: Henry <hzj94@hotmail.com>
This commit is contained in:
Vinod Kiran
2024-09-16 19:14:39 +05:30
committed by GitHub
parent 44b70ca7e2
commit b02bdc74ad
23 changed files with 1217 additions and 170 deletions
@@ -4,6 +4,11 @@ import { StatusCodes } from 'http-status-codes'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { getErrorMessage } from '../../errors/utils'
import { IReactFlowEdge, IReactFlowNode } from '../../Interface'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { DeleteResult } from 'typeorm'
import { CustomTemplate } from '../../database/entities/CustomTemplate'
import chatflowsService from '../chatflows'
type ITemplate = {
badge: string
@@ -96,6 +101,148 @@ const getAllTemplates = async () => {
}
}
export default {
getAllTemplates
const deleteCustomTemplate = async (templateId: string): Promise<DeleteResult> => {
try {
const appServer = getRunningExpressApp()
return await appServer.AppDataSource.getRepository(CustomTemplate).delete({ id: templateId })
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: marketplacesService.deleteCustomTemplate - ${getErrorMessage(error)}`
)
}
}
const getAllCustomTemplates = async (): Promise<any> => {
try {
const appServer = getRunningExpressApp()
const templates: any[] = await appServer.AppDataSource.getRepository(CustomTemplate).find()
templates.map((template) => {
template.usecases = template.usecases ? JSON.parse(template.usecases) : ''
if (template.type === 'Tool') {
template.flowData = JSON.parse(template.flowData)
template.iconSrc = template.flowData.iconSrc
template.schema = template.flowData.schema
template.func = template.flowData.func
template.categories = []
template.flowData = undefined
} else {
template.categories = getCategories(JSON.parse(template.flowData))
}
if (!template.badge) {
template.badge = ''
}
if (!template.framework) {
template.framework = ''
}
})
return templates
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: marketplacesService.getAllCustomTemplates - ${getErrorMessage(error)}`
)
}
}
const saveCustomTemplate = async (body: any): Promise<any> => {
try {
const appServer = getRunningExpressApp()
let flowDataStr = ''
let derivedFramework = ''
const customTemplate = new CustomTemplate()
Object.assign(customTemplate, body)
if (body.chatflowId) {
const chatflow = await chatflowsService.getChatflowById(body.chatflowId)
const flowData = JSON.parse(chatflow.flowData)
const { framework, exportJson } = _generateExportFlowData(flowData)
flowDataStr = JSON.stringify(exportJson)
customTemplate.framework = framework
} else if (body.tool) {
const flowData = {
iconSrc: body.tool.iconSrc,
schema: body.tool.schema,
func: body.tool.func
}
customTemplate.framework = ''
customTemplate.type = 'Tool'
flowDataStr = JSON.stringify(flowData)
}
customTemplate.framework = derivedFramework
if (customTemplate.usecases) {
customTemplate.usecases = JSON.stringify(customTemplate.usecases)
}
const entity = appServer.AppDataSource.getRepository(CustomTemplate).create(customTemplate)
entity.flowData = flowDataStr
const flowTemplate = await appServer.AppDataSource.getRepository(CustomTemplate).save(entity)
return flowTemplate
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: marketplacesService.saveCustomTemplate - ${getErrorMessage(error)}`
)
}
}
const _generateExportFlowData = (flowData: any) => {
const nodes = flowData.nodes
const edges = flowData.edges
let framework = 'Langchain'
for (let i = 0; i < nodes.length; i += 1) {
nodes[i].selected = false
const node = nodes[i]
const newNodeData = {
id: node.data.id,
label: node.data.label,
version: node.data.version,
name: node.data.name,
type: node.data.type,
baseClasses: node.data.baseClasses,
tags: node.data.tags,
category: node.data.category,
description: node.data.description,
inputParams: node.data.inputParams,
inputAnchors: node.data.inputAnchors,
inputs: {},
outputAnchors: node.data.outputAnchors,
outputs: node.data.outputs,
selected: false
}
if (node.data.tags && node.data.tags.length) {
if (node.data.tags.includes('LlamaIndex')) {
framework = 'LlamaIndex'
}
}
// Remove password, file & folder
if (node.data.inputs && Object.keys(node.data.inputs).length) {
const nodeDataInputs: any = {}
for (const input in node.data.inputs) {
const inputParam = node.data.inputParams.find((inp: any) => inp.name === input)
if (inputParam && inputParam.type === 'password') continue
if (inputParam && inputParam.type === 'file') continue
if (inputParam && inputParam.type === 'folder') continue
nodeDataInputs[input] = node.data.inputs[input]
}
newNodeData.inputs = nodeDataInputs
}
nodes[i].data = newNodeData
}
const exportJson = {
nodes,
edges
}
return { exportJson, framework }
}
export default {
getAllTemplates,
getAllCustomTemplates,
saveCustomTemplate,
deleteCustomTemplate
}