removed flow-config endpoint and resort to just prediction endpoint

This commit is contained in:
Henry
2023-05-10 01:06:53 +01:00
parent a1539edadf
commit 601b0b0f5b
8 changed files with 255 additions and 162 deletions
+24 -28
View File
@@ -222,37 +222,12 @@ export class App {
return res.json(availableConfigs) return res.json(availableConfigs)
}) })
this.app.post('/api/v1/flow-config/:id', upload.array('files'), async (req: Request, res: Response) => {
const chatflow = await this.AppDataSource.getRepository(ChatFlow).findOneBy({
id: req.params.id
})
if (!chatflow) return res.status(404).send(`Chatflow ${req.params.id} not found`)
await this.validateKey(req, res, chatflow)
const overrideConfig: ICommonObject = { ...req.body }
const files = req.files as any[]
if (!files || !files.length) return
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])
}
}
return res.json(overrideConfig)
})
// ---------------------------------------- // ----------------------------------------
// Prediction // Prediction
// ---------------------------------------- // ----------------------------------------
// Send input message and get prediction result (External) // Send input message and get prediction result (External)
this.app.post('/api/v1/prediction/:id', async (req: Request, res: Response) => { this.app.post('/api/v1/prediction/:id', upload.array('files'), async (req: Request, res: Response) => {
await this.processPrediction(req, res) await this.processPrediction(req, res)
}) })
@@ -346,7 +321,7 @@ export class App {
async processPrediction(req: Request, res: Response, isInternal = false) { async processPrediction(req: Request, res: Response, isInternal = false) {
try { try {
const chatflowid = req.params.id const chatflowid = req.params.id
const incomingInput: IncomingInput = req.body let incomingInput: IncomingInput = req.body
let nodeToExecuteData: INodeData let nodeToExecuteData: INodeData
@@ -359,6 +334,28 @@ export class App {
await this.validateKey(req, res, chatflow) await this.validateKey(req, res, chatflow)
} }
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: []
}
}
/* Don't rebuild the flow (to avoid duplicated upsert, recomputation) when all these conditions met: /* Don't rebuild the flow (to avoid duplicated upsert, recomputation) when all these conditions met:
* - Node Data already exists in pool * - Node Data already exists in pool
* - Still in sync (i.e the flow has not been modified since) * - Still in sync (i.e the flow has not been modified since)
@@ -378,7 +375,6 @@ export class App {
nodeToExecuteData = this.chatflowPool.activeChatflows[chatflowid].endingNodeData nodeToExecuteData = this.chatflowPool.activeChatflows[chatflowid].endingNodeData
} else { } else {
/*** Get chatflows and prepare data ***/ /*** Get chatflows and prepare data ***/
const flowData = chatflow.flowData const flowData = chatflow.flowData
const parsedFlowData: IReactFlowObject = JSON.parse(flowData) const parsedFlowData: IReactFlowObject = JSON.parse(flowData)
const nodes = parsedFlowData.nodes const nodes = parsedFlowData.nodes
+7 -6
View File
@@ -353,6 +353,12 @@ export const resolveVariables = (reactFlowNodeData: INodeData, reactFlowNodes: I
return flowNodeData return flowNodeData
} }
/**
* Loop through each inputs and replace their value with override config values
* @param {INodeData} flowNodeData
* @param {ICommonObject} overrideConfig
* @returns {INodeData}
*/
export const replaceInputsWithConfig = (flowNodeData: INodeData, overrideConfig: ICommonObject) => { export const replaceInputsWithConfig = (flowNodeData: INodeData, overrideConfig: ICommonObject) => {
const types = 'inputs' const types = 'inputs'
@@ -560,12 +566,7 @@ export const findAvailableConfigs = (reactFlowNodes: IReactFlowNode[]) => {
for (const inputParam of flowNode.data.inputParams) { for (const inputParam of flowNode.data.inputParams) {
let obj: IOverrideConfig let obj: IOverrideConfig
if (inputParam.type === 'password' || inputParam.type === 'options') { if (inputParam.type === 'password' || inputParam.type === 'options') {
obj = { continue
node: flowNode.data.label,
label: inputParam.label,
name: inputParam.name,
type: 'string'
}
} else if (inputParam.type === 'file') { } else if (inputParam.type === 'file') {
obj = { obj = {
node: flowNode.data.label, node: flowNode.data.label,
+9 -2
View File
@@ -1,8 +1,8 @@
// assets // assets
import { IconTrash, IconFileUpload, IconFileExport } from '@tabler/icons' import { IconTrash, IconFileUpload, IconFileExport, IconCopy } from '@tabler/icons'
// constant // constant
const icons = { IconTrash, IconFileUpload, IconFileExport } const icons = { IconTrash, IconFileUpload, IconFileExport, IconCopy }
// ==============================|| SETTINGS MENU ITEMS ||============================== // // ==============================|| SETTINGS MENU ITEMS ||============================== //
@@ -11,6 +11,13 @@ const settings = {
title: '', title: '',
type: 'group', type: 'group',
children: [ children: [
{
id: 'duplicateChatflow',
title: 'Duplicate Chatflow',
type: 'item',
url: '',
icon: icons.IconCopy
},
{ {
id: 'loadChatflow', id: 'loadChatflow',
title: 'Load Chatflow', title: 'Load Chatflow',
+1
View File
@@ -4,3 +4,4 @@ export const drawerWidth = 260
export const appDrawerWidth = 320 export const appDrawerWidth = 320
export const maxScroll = 100000 export const maxScroll = 100000
export const baseURL = process.env.NODE_ENV === 'production' ? window.location.origin : window.location.origin.replace(':8080', ':3000') export const baseURL = process.env.NODE_ENV === 'production' ? window.location.origin : window.location.origin.replace(':8080', ':3000')
export const uiBaseURL = window.location.origin
@@ -66,7 +66,7 @@ const unshiftFiles = (configData) => {
return configData return configData
} }
const getFormDataExamplesForJS = (configData) => { const getConfigExamplesForJS = (configData, bodyType) => {
let finalStr = '' let finalStr = ''
configData = unshiftFiles(configData) configData = unshiftFiles(configData)
const loop = Math.min(configData.length, 4) const loop = Math.min(configData.length, 4)
@@ -77,12 +77,13 @@ const getFormDataExamplesForJS = (configData) => {
else if (config.type === 'boolean') exampleVal = `true` else if (config.type === 'boolean') exampleVal = `true`
else if (config.type === 'number') exampleVal = `1` else if (config.type === 'number') exampleVal = `1`
else if (config.name === 'files') exampleVal = `input.files[0]` else if (config.name === 'files') exampleVal = `input.files[0]`
finalStr += `formData.append("${config.name}", ${exampleVal})\n` finalStr += bodyType === 'json' ? `\n "${config.name}": ${exampleVal},` : `formData.append("${config.name}", ${exampleVal})\n`
if (i === loop - 1 && bodyType !== 'json') `formData.append("question", "Hey, how are you?")\n`
} }
return finalStr return finalStr
} }
const getFormDataExamplesForPython = (configData) => { const getConfigExamplesForPython = (configData, bodyType) => {
let finalStr = '' let finalStr = ''
configData = unshiftFiles(configData) configData = unshiftFiles(configData)
const loop = Math.min(configData.length, 4) const loop = Math.min(configData.length, 4)
@@ -93,26 +94,26 @@ const getFormDataExamplesForPython = (configData) => {
else if (config.type === 'boolean') exampleVal = `true` else if (config.type === 'boolean') exampleVal = `true`
else if (config.type === 'number') exampleVal = `1` else if (config.type === 'number') exampleVal = `1`
else if (config.name === 'files') exampleVal = `('example${config.type}', open('example${config.type}', 'rb'))` else if (config.name === 'files') exampleVal = `('example${config.type}', open('example${config.type}', 'rb'))`
finalStr += `\n "${config.name}": ${exampleVal}` finalStr += bodyType === 'json' ? `\n "${config.name}": ${exampleVal},` : `\n "${config.name}": ${exampleVal},`
if (i === loop - 1) finalStr += `\n` if (i === loop - 1 && bodyType !== 'json') finalStr += `\n "question": "Hey, how are you?"\n`
} }
return finalStr return finalStr
} }
const getFormDataExamplesForCurl = (configData) => { const getConfigExamplesForCurl = (configData, bodyType) => {
let finalStr = '' let finalStr = ''
configData = unshiftFiles(configData) configData = unshiftFiles(configData)
const loop = Math.min(configData.length, 4) const loop = Math.min(configData.length, 4)
for (let i = 0; i < loop; i += 1) { for (let i = 0; i < loop; i += 1) {
const config = configData[i] const config = configData[i]
let exampleVal = `example` let exampleVal = `example`
if (config.type === 'string') exampleVal = `example` if (config.type === 'string') exampleVal = bodyType === 'json' ? `"example"` : `example`
else if (config.type === 'boolean') exampleVal = `true` else if (config.type === 'boolean') exampleVal = `true`
else if (config.type === 'number') exampleVal = `1` else if (config.type === 'number') exampleVal = `1`
else if (config.name === 'files') exampleVal = `@/home/user1/Desktop/example${config.type}` else if (config.name === 'files') exampleVal = `@/home/user1/Desktop/example${config.type}`
finalStr += `\n -F "${config.name}=${exampleVal}"` finalStr += bodyType === 'json' ? `"${config.name}": ${exampleVal}` : `\n -F "${config.name}=${exampleVal}"`
if (i === loop - 1) finalStr += `)\n` if (i === loop - 1) finalStr += bodyType === 'json' ? ` }` : ` \\\n -F "question=Hey, how are you?"`
else finalStr += ` \\` else finalStr += bodyType === 'json' ? `, ` : ` \\`
} }
return finalStr return finalStr
} }
@@ -178,19 +179,21 @@ output = query({
}) })
` `
} else if (codeLang === 'JavaScript') { } else if (codeLang === 'JavaScript') {
return `async function query() { return `async function query(data) {
const response = await fetch( const response = await fetch(
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}",
{ {
method: "POST", method: "POST",
body: { body: data
"question": "Hey, how are you?"
},
} }
); );
const result = await response.json(); const result = await response.json();
return result; return result;
} }
query({"question": "Hey, how are you?"}).then((response) => {
console.log(response);
});
` `
} else if (codeLang === 'cURL') { } else if (codeLang === 'cURL') {
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
@@ -222,14 +225,16 @@ output = query({
{ {
headers: { Authorization: "Bearer ${selectedApiKey?.apiKey}" }, headers: { Authorization: "Bearer ${selectedApiKey?.apiKey}" },
method: "POST", method: "POST",
body: { body: data
"question": "Hey, how are you?"
},
} }
); );
const result = await response.json(); const result = await response.json();
return result; return result;
} }
query({"question": "Hey, how are you?"}).then((response) => {
console.log(response);
});
` `
} else if (codeLang === 'cURL') { } else if (codeLang === 'cURL') {
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
@@ -262,143 +267,190 @@ output = query({
return pythonSVG return pythonSVG
} }
const getConfigCode = (codeLang, configData) => { // ----------------------------CONFIG FORM DATA --------------------------//
const getConfigCodeWithFormData = (codeLang, configData) => {
if (codeLang === 'Python') { if (codeLang === 'Python') {
return `import requests return `import requests
form_data = {${getFormDataExamplesForPython(configData)}}
def setConfig(): API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}"
response = requests.post("${baseURL}/api/v1/flow-config/${dialogProps.chatflowid}", files=form_data)
# use form data to upload files
form_data = {${getConfigExamplesForPython(configData, 'formData')}}
def query(form_data):
response = requests.post(API_URL, files=form_data)
return response.json() return response.json()
def query(payload): output = query(form_data)
response = requests.post("${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", json=payload)
return response.json()
# Set initial config
config = setConfig()
# Run prediction with config
output = query({
"question": "Hey, how are you?",
"overrideConfig": config
})
` `
} else if (codeLang === 'JavaScript') { } else if (codeLang === 'JavaScript') {
return `let formData = new FormData(); return `// use FormData to upload files
${getFormDataExamplesForJS(configData)} let formData = new FormData();
async function setConfig() { ${getConfigExamplesForJS(configData, 'formData')}
const response = await fetch( async function query(formData) {
"${baseURL}/api/v1/flow-config/${dialogProps.chatflowid}",
{
method: "POST",
body: formData
}
);
const config = await response.json();
return config; //Returns a config object
}
async function query(config) {
const response = await fetch( const response = await fetch(
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}",
{ {
method: "POST", method: "POST",
body: { body: formData
"question": "Hey, how are you?",
"overrideConfig": config
},
} }
); );
const result = await response.json(); const result = await response.json();
return result; return result;
} }
// Set initial config query(formData).then((response) => {
const config = await setConfig() console.log(response);
});
// Run prediction with config
const res = await query(config)
` `
} else if (codeLang === 'cURL') { } else if (codeLang === 'cURL') {
return `CONFIG=$(curl ${baseURL}/api/v1/flow-config/${dialogProps.chatflowid} \\ return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
-X POST \\${getFormDataExamplesForCurl(configData)} -X POST \\${getConfigExamplesForCurl(configData, 'formData')}`
curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
-X POST \\
-d '{"question": "Hey, how are you?", "overrideConfig": $CONFIG}'`
} }
return '' return ''
} }
const getConfigCodeWithAuthorization = (codeLang, configData) => { // ----------------------------CONFIG FORM DATA with AUTH--------------------------//
const getConfigCodeWithFormDataWithAuth = (codeLang, configData) => {
if (codeLang === 'Python') { if (codeLang === 'Python') {
return `import requests return `import requests
form_data = {${getFormDataExamplesForPython(configData)}}
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}"
headers = {"Authorization": "Bearer ${selectedApiKey?.apiKey}"} headers = {"Authorization": "Bearer ${selectedApiKey?.apiKey}"}
def setConfig(): # use form data to upload files
response = requests.post("${baseURL}/api/v1/flow-config/${dialogProps.chatflowid}", headers=headers, files=form_data) form_data = {${getConfigExamplesForPython(configData, 'formData')}}
def query(form_data):
response = requests.post(API_URL, headers=headers, files=form_data)
return response.json() return response.json()
def query(payload): output = query(form_data)
response = requests.post("${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", headers=headers, json=payload)
return response.json()
# Set initial config
config = setConfig()
# Run prediction with config
output = query({
"question": "Hey, how are you?",
"overrideConfig": config
})
` `
} else if (codeLang === 'JavaScript') { } else if (codeLang === 'JavaScript') {
return `let formData = new FormData(); return `// use FormData to upload files
${getFormDataExamplesForJS(configData)} let formData = new FormData();
async function setConfig() { ${getConfigExamplesForJS(configData, 'formData')}
async function query(formData) {
const response = await fetch( const response = await fetch(
"${baseURL}/api/v1/flow-config/${dialogProps.chatflowid}", "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}",
{ {
headers: { Authorization: "Bearer ${selectedApiKey?.apiKey}" }, headers: { Authorization: "Bearer ${selectedApiKey?.apiKey}" },
method: "POST", method: "POST",
body: formData body: formData
} }
); );
const config = await response.json(); const result = await response.json();
return config; //Returns a config object return result;
} }
async function query(config) { query(formData).then((response) => {
console.log(response);
});
`
} else if (codeLang === 'cURL') {
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
-X POST \\${getConfigExamplesForCurl(configData, 'formData')} \\
-H "Authorization: Bearer ${selectedApiKey?.apiKey}"`
}
return ''
}
// ----------------------------CONFIG JSON--------------------------//
const getConfigCode = (codeLang, configData) => {
if (codeLang === 'Python') {
return `import requests
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}"
def query(payload):
response = requests.post(API_URL, json=payload)
return response.json()
output = query({
"question": "Hey, how are you?",
"overrideConfig": {${getConfigExamplesForPython(configData, 'json')}
}
})
`
} else if (codeLang === 'JavaScript') {
return `async function query(data) {
const response = await fetch( const response = await fetch(
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}", "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}",
{ {
headers: { Authorization: "Bearer ${selectedApiKey?.apiKey}" },
method: "POST", method: "POST",
body: { body: data
"question": "Hey, how are you?",
"overrideConfig": config
},
} }
); );
const result = await response.json(); const result = await response.json();
return result; return result;
} }
// Set initial config query({
const config = await setConfig() "question": "Hey, how are you?",
"overrideConfig": {${getConfigExamplesForJS(configData, 'json')}
// Run prediction with config }
const res = await query(config) }).then((response) => {
console.log(response);
});
` `
} else if (codeLang === 'cURL') { } else if (codeLang === 'cURL') {
return `CONFIG=$(curl ${baseURL}/api/v1/flow-config/${dialogProps.chatflowid} \\ return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
-X POST \\ -X POST \\
-H "Authorization: Bearer ${selectedApiKey?.apiKey}"\\${getFormDataExamplesForCurl(configData)} -d '{"question": "Hey, how are you?", "overrideConfig": {${getConfigExamplesForCurl(configData, 'json')}}'`
curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\ }
return ''
}
// ----------------------------CONFIG JSON with AUTH--------------------------//
const getConfigCodeWithAuthorization = (codeLang, configData) => {
if (codeLang === 'Python') {
return `import requests
API_URL = "${baseURL}/api/v1/prediction/${dialogProps.chatflowid}"
headers = {"Authorization": "Bearer ${selectedApiKey?.apiKey}"}
def query(payload):
response = requests.post(API_URL, headers=headers, json=payload)
return response.json()
output = query({
"question": "Hey, how are you?",
"overrideConfig": {${getConfigExamplesForPython(configData, 'json')}
}
})
`
} else if (codeLang === 'JavaScript') {
return `async function query(data) {
const response = await fetch(
"${baseURL}/api/v1/prediction/${dialogProps.chatflowid}",
{
headers: { Authorization: "Bearer ${selectedApiKey?.apiKey}" },
method: "POST",
body: data
}
);
const result = await response.json();
return result;
}
query({
"question": "Hey, how are you?",
"overrideConfig": {${getConfigExamplesForJS(configData, 'json')}
}
}).then((response) => {
console.log(response);
});
`
} else if (codeLang === 'cURL') {
return `curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
-X POST \\ -X POST \\
-H "Authorization: Bearer ${selectedApiKey?.apiKey}" -d '{"question": "Hey, how are you?", "overrideConfig": {${getConfigExamplesForCurl(configData, 'json')}}' \\
-d '{"question": "Hey, how are you?", "overrideConfig": $CONFIG}'` -H "Authorization: Bearer ${selectedApiKey?.apiKey}"`
} }
return '' return ''
} }
@@ -500,7 +552,11 @@ curl ${baseURL}/api/v1/prediction/${dialogProps.chatflowid} \\
theme={atomOneDark} theme={atomOneDark}
text={ text={
chatflowApiKeyId chatflowApiKeyId
? getConfigCodeWithAuthorization(codeLang, getConfigApi.data) ? dialogProps.isFormDataRequired
? getConfigCodeWithFormDataWithAuth(codeLang, getConfigApi.data)
: getConfigCodeWithAuthorization(codeLang, getConfigApi.data)
: dialogProps.isFormDataRequired
? getConfigCodeWithFormData(codeLang, getConfigApi.data)
: getConfigCode(codeLang, getConfigApi.data) : getConfigCode(codeLang, getConfigApi.data)
} }
language={getLang(codeLang)} language={getLang(codeLang)}
+30 -2
View File
@@ -23,6 +23,7 @@ import useApi from 'hooks/useApi'
// utils // utils
import { generateExportFlowData } from 'utils/genericHelper' import { generateExportFlowData } from 'utils/genericHelper'
import { uiBaseURL } from 'store/constant'
// ==============================|| CANVAS HEADER ||============================== // // ==============================|| CANVAS HEADER ||============================== //
@@ -47,6 +48,13 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
if (setting === 'deleteChatflow') { if (setting === 'deleteChatflow') {
handleDeleteFlow() handleDeleteFlow()
} else if (setting === 'duplicateChatflow') {
try {
localStorage.setItem('duplicatedFlowData', chatflow.flowData)
window.open(`${uiBaseURL}/canvas`, '_blank')
} catch (e) {
console.error(e)
}
} else if (setting === 'exportChatflow') { } else if (setting === 'exportChatflow') {
try { try {
const flowData = JSON.parse(chatflow.flowData) const flowData = JSON.parse(chatflow.flowData)
@@ -80,10 +88,26 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
} }
const onAPIDialogClick = () => { const onAPIDialogClick = () => {
let isFormDataRequired = false
try {
const flowData = JSON.parse(chatflow.flowData)
const nodes = flowData.nodes
for (const node of nodes) {
if (node.data.inputParams.find((param) => param.type === 'file')) {
isFormDataRequired = true
break
}
}
} catch (e) {
console.error(e)
}
setAPIDialogProps({ setAPIDialogProps({
title: 'Use this chatflow with API', title: 'Use this chatflow with API',
chatflowid: chatflow.id, chatflowid: chatflow.id,
chatflowApiKeyId: chatflow.apikeyid chatflowApiKeyId: chatflow.apikeyid,
isFormDataRequired
}) })
setAPIDialogOpen(true) setAPIDialogOpen(true)
} }
@@ -131,7 +155,9 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
} }
}} }}
color='inherit' color='inherit'
onClick={() => navigate(-1)} onClick={() =>
window.history.state && window.history.state.idx > 0 ? navigate(-1) : navigate('/', { replace: true })
}
> >
<IconChevronLeft stroke={1.5} size='1.3rem' /> <IconChevronLeft stroke={1.5} size='1.3rem' />
</Avatar> </Avatar>
@@ -231,6 +257,7 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
)} )}
</Box> </Box>
<Box> <Box>
{chatflow?.id && (
<ButtonBase title='API Endpoint' sx={{ borderRadius: '50%', mr: 2 }}> <ButtonBase title='API Endpoint' sx={{ borderRadius: '50%', mr: 2 }}>
<Avatar <Avatar
variant='rounded' variant='rounded'
@@ -251,6 +278,7 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
<IconCode stroke={1.5} size='1.3rem' /> <IconCode stroke={1.5} size='1.3rem' />
</Avatar> </Avatar>
</ButtonBase> </ButtonBase>
)}
<ButtonBase title='Save Chatflow' sx={{ borderRadius: '50%', mr: 2 }}> <ButtonBase title='Save Chatflow' sx={{ borderRadius: '50%', mr: 2 }}>
<Avatar <Avatar
variant='rounded' variant='rounded'
+5
View File
@@ -406,9 +406,14 @@ const Canvas = () => {
useEffect(() => { useEffect(() => {
if (chatflowId) { if (chatflowId) {
getSpecificChatflowApi.request(chatflowId) getSpecificChatflowApi.request(chatflowId)
} else {
if (localStorage.getItem('duplicatedFlowData')) {
handleLoadFlow(localStorage.getItem('duplicatedFlowData'))
setTimeout(() => localStorage.removeItem('duplicatedFlowData'), 0)
} else { } else {
setNodes([]) setNodes([])
setEdges([]) setEdges([])
}
dispatch({ dispatch({
type: SET_CHATFLOW, type: SET_CHATFLOW,
chatflow: { chatflow: {
@@ -46,7 +46,6 @@ const MarketplaceCanvas = () => {
}, [flowData]) }, [flowData])
const onChatflowCopy = (flowData) => { const onChatflowCopy = (flowData) => {
//navigator.clipboard.writeText(JSON.stringify(flowData))
const templateFlowData = JSON.stringify(flowData) const templateFlowData = JSON.stringify(flowData)
navigate(`/canvas`, { state: { templateFlowData } }) navigate(`/canvas`, { state: { templateFlowData } })
} }