add tools marketplace
This commit is contained in:
parent
4cadd7aa37
commit
c8ba8e2aee
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "add_contact_hubspot",
|
||||||
|
"description": "Add new contact to Hubspot",
|
||||||
|
"color": "linear-gradient(rgb(85,198,123), rgb(0,230,99))",
|
||||||
|
"iconSrc": "https://cdn.worldvectorlogo.com/logos/hubspot-1.svg",
|
||||||
|
"schema": "[{\"id\":1,\"property\":\"email\",\"description\":\"email address of contact\",\"type\":\"string\",\"required\":true},{\"id\":2,\"property\":\"firstname\",\"description\":\"first name of contact\",\"type\":\"string\",\"required\":false},{\"id\":3,\"property\":\"lastname\",\"description\":\"last name of contact\",\"type\":\"string\",\"required\":false}]",
|
||||||
|
"func": "const fetch = require('node-fetch');\nconst url = 'https://api.hubapi.com/crm/v3/objects/contacts'\nconst token = 'YOUR-TOKEN';\n\nconst body = {\n\t\"properties\": {\n\t \"email\": $email\n\t}\n};\n\nif ($firstname) body.properties.firstname = $firstname;\nif ($lastname) body.properties.lastname = $lastname;\n\nconst options = {\n\tmethod: 'POST',\n\theaders: {\n\t 'Authorization': `Bearer ${token}`,\n\t\t'Content-Type': 'application/json'\n\t},\n\tbody: JSON.stringify(body)\n};\n\ntry {\n\tconst response = await fetch(url, options);\n\tconst text = await response.text();\n\treturn text;\n} catch (error) {\n\tconsole.error(error);\n\treturn '';\n}"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "add_airtable",
|
||||||
|
"description": "Add column1, column2 to Airtable",
|
||||||
|
"color": "linear-gradient(rgb(125,71,222), rgb(128,102,23))",
|
||||||
|
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/airtable.svg",
|
||||||
|
"schema": "[{\"id\":0,\"property\":\"column1\",\"description\":\"this is column1\",\"type\":\"string\",\"required\":true},{\"id\":1,\"property\":\"column2\",\"description\":\"this is column2\",\"type\":\"string\",\"required\":true}]",
|
||||||
|
"func": "const fetch = require('node-fetch');\nconst baseId = 'YOUR-BASE-ID';\nconst tableId = 'YOUR-TABLE-ID';\nconst token = 'YOUR-TOKEN';\n\nconst body = {\n\t\"records\": [\n\t\t{\n\t\t\t\"fields\": {\n\t\t\t\t\"column1\": $column1,\n\t\t\t\t\"column2\": $column2,\n\t\t\t}\n\t\t}\n\t]\n};\n\nconst options = {\n\tmethod: 'POST',\n\theaders: {\n\t\t'Authorization': `Bearer ${token}`,\n\t\t'Content-Type': 'application/json'\n\t},\n\tbody: JSON.stringify(body)\n};\n\nconst url = `https://api.airtable.com/v0/${baseId}/${tableId}`\n\ntry {\n\tconst response = await fetch(url, options);\n\tconst text = await response.text();\n\treturn text;\n} catch (error) {\n\tconsole.error(error);\n\treturn '';\n}"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "get_stock_movers",
|
||||||
|
"description": "Get the stocks that has biggest price/volume moves, e.g. actives, gainers, losers, etc.",
|
||||||
|
"iconSrc": "https://rapidapi.com/cdn/images?url=https://rapidapi-prod-apis.s3.amazonaws.com/9c/e743343bdd41edad39a3fdffd5b974/016c33699f51603ae6fe4420c439124b.png",
|
||||||
|
"color": "linear-gradient(rgb(191,202,167), rgb(143,202,246))",
|
||||||
|
"schema": "[]",
|
||||||
|
"func": "const fetch = require('node-fetch');\nconst url = 'https://morning-star.p.rapidapi.com/market/v2/get-movers';\nconst options = {\n\tmethod: 'GET',\n\theaders: {\n\t\t'X-RapidAPI-Key': 'YOUR-API-KEY',\n\t\t'X-RapidAPI-Host': 'morning-star.p.rapidapi.com'\n\t}\n};\n\ntry {\n\tconst response = await fetch(url, options);\n\tconst result = await response.text();\n\tconsole.log(result);\n\treturn result;\n} catch (error) {\n\tconsole.error(error);\n\treturn '';\n}"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "send_message_to_discord_channel",
|
||||||
|
"description": "Send message to Discord channel",
|
||||||
|
"color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))",
|
||||||
|
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/discord-icon.svg",
|
||||||
|
"schema": "[{\"id\":1,\"property\":\"content\",\"description\":\"message to send\",\"type\":\"string\",\"required\":true}]",
|
||||||
|
"func": "const fetch = require('node-fetch');\nconst webhookUrl = 'YOUR-WEBHOOK-URL'\n\nconst body = {\n\t\"content\": $content\n};\n\nconst options = {\n\tmethod: 'POST',\n\theaders: {\n\t\t'Content-Type': 'application/json'\n\t},\n\tbody: JSON.stringify(body)\n};\n\nconst url = `${webhookUrl}?wait=true`\n\ntry {\n\tconst response = await fetch(url, options);\n\tconst text = await response.text();\n\treturn text;\n} catch (error) {\n\tconsole.error(error);\n\treturn '';\n}"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "send_message_to_slack_channel",
|
||||||
|
"description": "Send message to Slack channel",
|
||||||
|
"color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))",
|
||||||
|
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/slack-icon.svg",
|
||||||
|
"schema": "[{\"id\":1,\"property\":\"text\",\"description\":\"message to send\",\"type\":\"string\",\"required\":true}]",
|
||||||
|
"func": "const fetch = require('node-fetch');\nconst webhookUrl = 'YOUR-WEBHOOK-URL'\n\nconst body = {\n\t\"text\": $text\n};\n\nconst options = {\n\tmethod: 'POST',\n\theaders: {\n\t\t'Content-Type': 'application/json'\n\t},\n\tbody: JSON.stringify(body)\n};\n\nconst url = `${webhookUrl}`\n\ntry {\n\tconst response = await fetch(url, options);\n\tconst text = await response.text();\n\treturn text;\n} catch (error) {\n\tconsole.error(error);\n\treturn '';\n}"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "send_message_to_teams_channel",
|
||||||
|
"description": "Send message to Teams channel",
|
||||||
|
"color": "linear-gradient(rgb(155,190,84), rgb(176,69,245))",
|
||||||
|
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/microsoft-teams.svg",
|
||||||
|
"schema": "[{\"id\":1,\"property\":\"content\",\"description\":\"message to send\",\"type\":\"string\",\"required\":true}]",
|
||||||
|
"func": "const fetch = require('node-fetch');\nconst webhookUrl = 'YOUR-WEBHOOK-URL'\n\nconst body = {\n\t\"content\": $content\n};\n\nconst options = {\n\tmethod: 'POST',\n\theaders: {\n\t\t'Content-Type': 'application/json'\n\t},\n\tbody: JSON.stringify(body)\n};\n\nconst url = `${webhookUrl}?wait=true`\n\ntry {\n\tconst response = await fetch(url, options);\n\tconst text = await response.text();\n\treturn text;\n} catch (error) {\n\tconsole.error(error);\n\treturn '';\n}"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"name": "sendgrid_email",
|
||||||
|
"description": "Send email using SendGrid",
|
||||||
|
"color": "linear-gradient(rgb(230,108,70), rgb(222,4,98))",
|
||||||
|
"iconSrc": "https://raw.githubusercontent.com/gilbarbara/logos/main/logos/sendgrid-icon.svg",
|
||||||
|
"schema": "[{\"id\":0,\"property\":\"fromEmail\",\"description\":\"Email address used to send the message\",\"type\":\"string\",\"required\":true},{\"id\":1,\"property\":\"toEmail \",\"description\":\"The intended recipient's email address\",\"type\":\"string\",\"required\":true},{\"id\":2,\"property\":\"subject\",\"description\":\"The subject of email\",\"type\":\"string\",\"required\":true},{\"id\":3,\"property\":\"content\",\"description\":\"Content of email\",\"type\":\"string\",\"required\":true}]",
|
||||||
|
"func": "const fetch = require('node-fetch');\nconst url = 'https://api.sendgrid.com/v3/mail/send';\nconst api_key = 'YOUR-API-KEY';\n\nconst body = {\n \"personalizations\": [\n {\n \"to\": [{ \"email\": $toEmail }]\n }\n ],\n\t\"from\": {\n\t \"email\": $fromEmail\n\t},\n\t\"subject\": $subject,\n\t\"content\": [\n\t {\n\t \"type\": 'text/plain',\n\t \"value\": $content\n\t }\n\t]\n};\n\nconst options = {\n\tmethod: 'POST',\n\theaders: {\n\t 'Authorization': `Bearer ${api_key}`,\n\t\t'Content-Type': 'application/json'\n\t},\n\tbody: JSON.stringify(body)\n};\n\ntry {\n\tconst response = await fetch(url, options);\n\tconst text = await response.text();\n\treturn text;\n} catch (error) {\n\tconsole.error(error);\n\treturn '';\n}"
|
||||||
|
}
|
||||||
|
|
@ -30,6 +30,7 @@ export interface ITool {
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
color: string
|
color: string
|
||||||
|
iconSrc?: string
|
||||||
schema?: string
|
schema?: string
|
||||||
func?: string
|
func?: string
|
||||||
updatedDate: Date
|
updatedDate: Date
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ export class Tool implements ITool {
|
||||||
@Column()
|
@Column()
|
||||||
color: string
|
color: string
|
||||||
|
|
||||||
|
@Column({ nullable: true })
|
||||||
|
iconSrc?: string
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
schema?: string
|
schema?: string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -462,12 +462,12 @@ export class App {
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
||||||
// Get all chatflows for marketplaces
|
// Get all chatflows for marketplaces
|
||||||
this.app.get('/api/v1/marketplaces', async (req: Request, res: Response) => {
|
this.app.get('/api/v1/marketplaces/chatflows', async (req: Request, res: Response) => {
|
||||||
const marketplaceDir = path.join(__dirname, '..', 'marketplaces')
|
const marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'chatflows')
|
||||||
const jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
|
const jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
|
||||||
const templates: any[] = []
|
const templates: any[] = []
|
||||||
jsonsInDir.forEach((file, index) => {
|
jsonsInDir.forEach((file, index) => {
|
||||||
const filePath = path.join(__dirname, '..', 'marketplaces', file)
|
const filePath = path.join(__dirname, '..', 'marketplaces', 'chatflows', file)
|
||||||
const fileData = fs.readFileSync(filePath)
|
const fileData = fs.readFileSync(filePath)
|
||||||
const fileDataObj = JSON.parse(fileData.toString())
|
const fileDataObj = JSON.parse(fileData.toString())
|
||||||
const template = {
|
const template = {
|
||||||
|
|
@ -481,6 +481,25 @@ export class App {
|
||||||
return res.json(templates)
|
return res.json(templates)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Get all tools for marketplaces
|
||||||
|
this.app.get('/api/v1/marketplaces/tools', async (req: Request, res: Response) => {
|
||||||
|
const marketplaceDir = path.join(__dirname, '..', 'marketplaces', 'tools')
|
||||||
|
const jsonsInDir = fs.readdirSync(marketplaceDir).filter((file) => path.extname(file) === '.json')
|
||||||
|
const templates: any[] = []
|
||||||
|
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,
|
||||||
|
templateName: file.split('.json')[0]
|
||||||
|
}
|
||||||
|
templates.push(template)
|
||||||
|
})
|
||||||
|
return res.json(templates)
|
||||||
|
})
|
||||||
|
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
// API Keys
|
// API Keys
|
||||||
// ----------------------------------------
|
// ----------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Flowise - LangchainJS UI</title>
|
<title>Flowise - Low-code LLM apps builder</title>
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<!-- Meta Tags-->
|
<!-- Meta Tags-->
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#2296f3" />
|
<meta name="theme-color" content="#2296f3" />
|
||||||
<meta name="title" content="Flowise - LangchainJS UI" />
|
<meta name="title" content="Flowise - Low-code LLM apps builder" />
|
||||||
<meta name="description" content="Flowise helps you to better integrate Web3 with existing Web2 applications" />
|
<meta name="description" content="Flowise helps you to better integrate Web3 with existing Web2 applications" />
|
||||||
<meta name="keywords" content="react, material-ui, reactjs, reactjs, workflow automation, web3, web2, blockchain" />
|
<meta name="keywords" content="react, material-ui, reactjs, reactjs, workflow automation, web3, web2, blockchain" />
|
||||||
<meta name="author" content="CodedThemes" />
|
<meta name="author" content="CodedThemes" />
|
||||||
|
|
@ -17,13 +17,13 @@
|
||||||
<meta property="og:url" content="https://flowiseai.com/" />
|
<meta property="og:url" content="https://flowiseai.com/" />
|
||||||
<meta property="og:site_name" content="flowiseai.com" />
|
<meta property="og:site_name" content="flowiseai.com" />
|
||||||
<meta property="article:publisher" content="https://www.facebook.com/codedthemes" />
|
<meta property="article:publisher" content="https://www.facebook.com/codedthemes" />
|
||||||
<meta property="og:title" content="Flowise - LangchainJS UI" />
|
<meta property="og:title" content="Flowise - Low-code LLM apps builder" />
|
||||||
<meta property="og:description" content="Flowise helps you to better build LLM flows using Langchain in simple GUI" />
|
<meta property="og:description" content="Flowise helps you to better build LLM flows using Langchain in simple GUI" />
|
||||||
<meta property="og:image" content="https://flowiseai.com/og-image/og-facebook.png" />
|
<meta property="og:image" content="https://flowiseai.com/og-image/og-facebook.png" />
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
<meta property="twitter:card" content="summary_large_image" />
|
<meta property="twitter:card" content="summary_large_image" />
|
||||||
<meta property="twitter:url" content="https://flowiseai.com" />
|
<meta property="twitter:url" content="https://flowiseai.com" />
|
||||||
<meta property="twitter:title" content="Flowise - LangchainJS UI" />
|
<meta property="twitter:title" content="Flowise - Low-code LLM apps builder" />
|
||||||
<meta property="twitter:description" content="Flowise helps you to better build LLM flows using Langchain in simple GUI" />
|
<meta property="twitter:description" content="Flowise helps you to better build LLM flows using Langchain in simple GUI" />
|
||||||
<meta property="twitter:image" content="https://flowiseai.com/og-image/og-twitter.png" />
|
<meta property="twitter:image" content="https://flowiseai.com/og-image/og-twitter.png" />
|
||||||
<meta name="twitter:creator" content="@codedthemes" />
|
<meta name="twitter:creator" content="@codedthemes" />
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import client from './client'
|
import client from './client'
|
||||||
|
|
||||||
const getAllMarketplaces = () => client.get('/marketplaces')
|
const getAllChatflowsMarketplaces = () => client.get('/marketplaces/chatflows')
|
||||||
|
const getAllToolsMarketplaces = () => client.get('/marketplaces/tools')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getAllMarketplaces
|
getAllChatflowsMarketplaces,
|
||||||
|
getAllToolsMarketplaces
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,9 @@ export default function componentStyleOverrides(theme) {
|
||||||
'&::placeholder': {
|
'&::placeholder': {
|
||||||
color: theme.darkTextSecondary,
|
color: theme.darkTextSecondary,
|
||||||
fontSize: '0.875rem'
|
fontSize: '0.875rem'
|
||||||
|
},
|
||||||
|
'&.Mui-disabled': {
|
||||||
|
WebkitTextFillColor: theme?.customization?.isDarkMode ? theme.colors?.grey500 : theme.darkTextSecondary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ const CardWrapper = styled(MainCard)(({ theme }) => ({
|
||||||
|
|
||||||
// ===========================|| CONTRACT CARD ||=========================== //
|
// ===========================|| CONTRACT CARD ||=========================== //
|
||||||
|
|
||||||
const ItemCard = ({ isLoading, data, images, color, onClick }) => {
|
const ItemCard = ({ isLoading, data, images, onClick }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
|
@ -43,21 +43,35 @@ const ItemCard = ({ isLoading, data, images, color, onClick }) => {
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{color && (
|
{data.iconSrc && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: 35,
|
width: 35,
|
||||||
height: 35,
|
height: 35,
|
||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
background: color
|
background: `url(${data.iconSrc})`,
|
||||||
|
backgroundSize: 'contain',
|
||||||
|
backgroundRepeat: 'no-repeat',
|
||||||
|
backgroundPosition: 'center center'
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
)}
|
||||||
|
{!data.iconSrc && data.color && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: 35,
|
||||||
|
height: 35,
|
||||||
|
marginRight: 10,
|
||||||
|
borderRadius: '50%',
|
||||||
|
background: data.color
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
)}
|
)}
|
||||||
<Typography
|
<Typography
|
||||||
sx={{ fontSize: '1.5rem', fontWeight: 500, overflowWrap: 'break-word', whiteSpace: 'pre-line' }}
|
sx={{ fontSize: '1.5rem', fontWeight: 500, overflowWrap: 'break-word', whiteSpace: 'pre-line' }}
|
||||||
>
|
>
|
||||||
{data.name}
|
{data.templateName || data.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
{data.description && (
|
{data.description && (
|
||||||
|
|
@ -107,7 +121,6 @@ ItemCard.propTypes = {
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
data: PropTypes.object,
|
data: PropTypes.object,
|
||||||
images: PropTypes.array,
|
images: PropTypes.array,
|
||||||
color: PropTypes.string,
|
|
||||||
onClick: PropTypes.func
|
onClick: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { DataGrid } from '@mui/x-data-grid'
|
||||||
import { IconPlus } from '@tabler/icons'
|
import { IconPlus } from '@tabler/icons'
|
||||||
import { Button } from '@mui/material'
|
import { Button } from '@mui/material'
|
||||||
|
|
||||||
export const Grid = ({ columns, rows, style, onRowUpdate, addNewRow }) => {
|
export const Grid = ({ columns, rows, style, disabled = false, onRowUpdate, addNewRow }) => {
|
||||||
const handleProcessRowUpdate = (newRow) => {
|
const handleProcessRowUpdate = (newRow) => {
|
||||||
onRowUpdate(newRow)
|
onRowUpdate(newRow)
|
||||||
return newRow
|
return newRow
|
||||||
|
|
@ -11,13 +11,18 @@ export const Grid = ({ columns, rows, style, onRowUpdate, addNewRow }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{!disabled && (
|
||||||
<Button variant='outlined' onClick={addNewRow} startIcon={<IconPlus />}>
|
<Button variant='outlined' onClick={addNewRow} startIcon={<IconPlus />}>
|
||||||
Add Item
|
Add Item
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
{rows && columns && (
|
{rows && columns && (
|
||||||
<div style={{ marginTop: 10, height: 300, width: '100%', ...style }}>
|
<div style={{ marginTop: 10, height: 300, width: '100%', ...style }}>
|
||||||
<DataGrid
|
<DataGrid
|
||||||
processRowUpdate={handleProcessRowUpdate}
|
processRowUpdate={handleProcessRowUpdate}
|
||||||
|
isCellEditable={() => {
|
||||||
|
return !disabled
|
||||||
|
}}
|
||||||
onProcessRowUpdateError={(error) => console.error(error)}
|
onProcessRowUpdateError={(error) => console.error(error)}
|
||||||
rows={rows}
|
rows={rows}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
|
@ -32,6 +37,7 @@ Grid.propTypes = {
|
||||||
rows: PropTypes.array,
|
rows: PropTypes.array,
|
||||||
columns: PropTypes.array,
|
columns: PropTypes.array,
|
||||||
style: PropTypes.any,
|
style: PropTypes.any,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
addNewRow: PropTypes.func,
|
addNewRow: PropTypes.func,
|
||||||
onRowUpdate: PropTypes.func
|
onRowUpdate: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
// material-ui
|
// material-ui
|
||||||
import { Grid, Box, Stack } from '@mui/material'
|
import { Grid, Box, Stack, Tabs, Tab } from '@mui/material'
|
||||||
import { useTheme } from '@mui/material/styles'
|
import { useTheme } from '@mui/material/styles'
|
||||||
|
import { IconHierarchy, IconTool } from '@tabler/icons'
|
||||||
|
|
||||||
// project imports
|
// project imports
|
||||||
import MainCard from 'ui-component/cards/MainCard'
|
import MainCard from 'ui-component/cards/MainCard'
|
||||||
import ItemCard from 'ui-component/cards/ItemCard'
|
import ItemCard from 'ui-component/cards/ItemCard'
|
||||||
import { gridSpacing } from 'store/constant'
|
import { gridSpacing } from 'store/constant'
|
||||||
import WorkflowEmptySVG from 'assets/images/workflow_empty.svg'
|
import WorkflowEmptySVG from 'assets/images/workflow_empty.svg'
|
||||||
|
import ToolDialog from 'views/tools/ToolDialog'
|
||||||
|
|
||||||
// API
|
// API
|
||||||
import marketplacesApi from 'api/marketplaces'
|
import marketplacesApi from 'api/marketplaces'
|
||||||
|
|
@ -21,6 +24,27 @@ import useApi from 'hooks/useApi'
|
||||||
// const
|
// const
|
||||||
import { baseURL } from 'store/constant'
|
import { baseURL } from 'store/constant'
|
||||||
|
|
||||||
|
function TabPanel(props) {
|
||||||
|
const { children, value, index, ...other } = props
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role='tabpanel'
|
||||||
|
hidden={value !== index}
|
||||||
|
id={`attachment-tabpanel-${index}`}
|
||||||
|
aria-labelledby={`attachment-tab-${index}`}
|
||||||
|
{...other}
|
||||||
|
>
|
||||||
|
{value === index && <Box sx={{ p: 1 }}>{children}</Box>}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TabPanel.propTypes = {
|
||||||
|
children: PropTypes.node,
|
||||||
|
index: PropTypes.number.isRequired,
|
||||||
|
value: PropTypes.number.isRequired
|
||||||
|
}
|
||||||
|
|
||||||
// ==============================|| Marketplace ||============================== //
|
// ==============================|| Marketplace ||============================== //
|
||||||
|
|
||||||
const Marketplace = () => {
|
const Marketplace = () => {
|
||||||
|
|
@ -29,29 +53,66 @@ const Marketplace = () => {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const customization = useSelector((state) => state.customization)
|
const customization = useSelector((state) => state.customization)
|
||||||
|
|
||||||
const [isLoading, setLoading] = useState(true)
|
const [isChatflowsLoading, setChatflowsLoading] = useState(true)
|
||||||
|
const [isToolsLoading, setToolsLoading] = useState(true)
|
||||||
const [images, setImages] = useState({})
|
const [images, setImages] = useState({})
|
||||||
|
const tabItems = ['Chatflows', 'Tools']
|
||||||
|
const [value, setValue] = useState(0)
|
||||||
|
const [showToolDialog, setShowToolDialog] = useState(false)
|
||||||
|
const [toolDialogProps, setToolDialogProps] = useState({})
|
||||||
|
|
||||||
const getAllMarketplacesApi = useApi(marketplacesApi.getAllMarketplaces)
|
const getAllChatflowsMarketplacesApi = useApi(marketplacesApi.getAllChatflowsMarketplaces)
|
||||||
|
const getAllToolsMarketplacesApi = useApi(marketplacesApi.getAllToolsMarketplaces)
|
||||||
|
|
||||||
|
const onUseTemplate = (selectedTool) => {
|
||||||
|
const dialogProp = {
|
||||||
|
title: 'Add New Tool',
|
||||||
|
type: 'IMPORT',
|
||||||
|
cancelButtonName: 'Cancel',
|
||||||
|
confirmButtonName: 'Add',
|
||||||
|
data: selectedTool
|
||||||
|
}
|
||||||
|
setToolDialogProps(dialogProp)
|
||||||
|
setShowToolDialog(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToTool = (selectedTool) => {
|
||||||
|
const dialogProp = {
|
||||||
|
title: selectedTool.templateName,
|
||||||
|
type: 'TEMPLATE',
|
||||||
|
data: selectedTool
|
||||||
|
}
|
||||||
|
setToolDialogProps(dialogProp)
|
||||||
|
setShowToolDialog(true)
|
||||||
|
}
|
||||||
|
|
||||||
const goToCanvas = (selectedChatflow) => {
|
const goToCanvas = (selectedChatflow) => {
|
||||||
navigate(`/marketplace/${selectedChatflow.id}`, { state: selectedChatflow })
|
navigate(`/marketplace/${selectedChatflow.id}`, { state: selectedChatflow })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleChange = (event, newValue) => {
|
||||||
|
setValue(newValue)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getAllMarketplacesApi.request()
|
getAllChatflowsMarketplacesApi.request()
|
||||||
|
getAllToolsMarketplacesApi.request()
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(getAllMarketplacesApi.loading)
|
setChatflowsLoading(getAllChatflowsMarketplacesApi.loading)
|
||||||
}, [getAllMarketplacesApi.loading])
|
}, [getAllChatflowsMarketplacesApi.loading])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (getAllMarketplacesApi.data) {
|
setToolsLoading(getAllToolsMarketplacesApi.loading)
|
||||||
|
}, [getAllToolsMarketplacesApi.loading])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (getAllChatflowsMarketplacesApi.data) {
|
||||||
try {
|
try {
|
||||||
const chatflows = getAllMarketplacesApi.data
|
const chatflows = getAllChatflowsMarketplacesApi.data
|
||||||
const images = {}
|
const images = {}
|
||||||
for (let i = 0; i < chatflows.length; i += 1) {
|
for (let i = 0; i < chatflows.length; i += 1) {
|
||||||
const flowDataStr = chatflows[i].flowData
|
const flowDataStr = chatflows[i].flowData
|
||||||
|
|
@ -70,31 +131,83 @@ const Marketplace = () => {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [getAllMarketplacesApi.data])
|
}, [getAllChatflowsMarketplacesApi.data])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>
|
<MainCard sx={{ background: customization.isDarkMode ? theme.palette.common.black : '' }}>
|
||||||
<Stack flexDirection='row'>
|
<Stack flexDirection='row'>
|
||||||
<h1>Marketplace</h1>
|
<h1>Marketplace</h1>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<Tabs sx={{ mb: 2 }} variant='fullWidth' value={value} onChange={handleChange} aria-label='tabs'>
|
||||||
|
{tabItems.map((item, index) => (
|
||||||
|
<Tab
|
||||||
|
key={index}
|
||||||
|
icon={index === 0 ? <IconHierarchy /> : <IconTool />}
|
||||||
|
iconPosition='start'
|
||||||
|
label={<span style={{ fontSize: '1.1rem' }}>{item}</span>}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
{tabItems.map((item, index) => (
|
||||||
|
<TabPanel key={index} value={value} index={index}>
|
||||||
|
{item === 'Chatflows' && (
|
||||||
<Grid container spacing={gridSpacing}>
|
<Grid container spacing={gridSpacing}>
|
||||||
{!isLoading &&
|
{!isChatflowsLoading &&
|
||||||
getAllMarketplacesApi.data &&
|
getAllChatflowsMarketplacesApi.data &&
|
||||||
getAllMarketplacesApi.data.map((data, index) => (
|
getAllChatflowsMarketplacesApi.data.map((data, index) => (
|
||||||
<Grid key={index} item lg={3} md={4} sm={6} xs={12}>
|
<Grid key={index} item lg={3} md={4} sm={6} xs={12}>
|
||||||
<ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} />
|
<ItemCard onClick={() => goToCanvas(data)} data={data} images={images[data.id]} />
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
{!isLoading && (!getAllMarketplacesApi.data || getAllMarketplacesApi.data.length === 0) && (
|
)}
|
||||||
|
{item === 'Tools' && (
|
||||||
|
<Grid container spacing={gridSpacing}>
|
||||||
|
{!isToolsLoading &&
|
||||||
|
getAllToolsMarketplacesApi.data &&
|
||||||
|
getAllToolsMarketplacesApi.data.map((data, index) => (
|
||||||
|
<Grid key={index} item lg={3} md={4} sm={6} xs={12}>
|
||||||
|
<ItemCard data={data} onClick={() => goToTool(data)} />
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</TabPanel>
|
||||||
|
))}
|
||||||
|
{!isChatflowsLoading && (!getAllChatflowsMarketplacesApi.data || getAllChatflowsMarketplacesApi.data.length === 0) && (
|
||||||
<Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>
|
<Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>
|
||||||
<Box sx={{ p: 2, height: 'auto' }}>
|
<Box sx={{ p: 2, height: 'auto' }}>
|
||||||
<img style={{ objectFit: 'cover', height: '30vh', width: 'auto' }} src={WorkflowEmptySVG} alt='WorkflowEmptySVG' />
|
<img
|
||||||
|
style={{ objectFit: 'cover', height: '30vh', width: 'auto' }}
|
||||||
|
src={WorkflowEmptySVG}
|
||||||
|
alt='WorkflowEmptySVG'
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<div>No Marketplace Yet</div>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
{!isToolsLoading && (!getAllToolsMarketplacesApi.data || getAllToolsMarketplacesApi.data.length === 0) && (
|
||||||
|
<Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>
|
||||||
|
<Box sx={{ p: 2, height: 'auto' }}>
|
||||||
|
<img
|
||||||
|
style={{ objectFit: 'cover', height: '30vh', width: 'auto' }}
|
||||||
|
src={WorkflowEmptySVG}
|
||||||
|
alt='WorkflowEmptySVG'
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<div>No Marketplace Yet</div>
|
<div>No Marketplace Yet</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</MainCard>
|
</MainCard>
|
||||||
|
<ToolDialog
|
||||||
|
show={showToolDialog}
|
||||||
|
dialogProps={toolDialogProps}
|
||||||
|
onCancel={() => setShowToolDialog(false)}
|
||||||
|
onConfirm={() => setShowToolDialog(false)}
|
||||||
|
onUseTemplate={(tool) => onUseTemplate(tool)}
|
||||||
|
></ToolDialog>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ import { LightCodeEditor } from 'ui-component/editor/LightCodeEditor'
|
||||||
import { useTheme } from '@mui/material/styles'
|
import { useTheme } from '@mui/material/styles'
|
||||||
|
|
||||||
// Icons
|
// Icons
|
||||||
import { IconX } from '@tabler/icons'
|
import { IconX, IconFileExport } from '@tabler/icons'
|
||||||
|
|
||||||
// API
|
// API
|
||||||
import toolsApi from 'api/tools'
|
import toolsApi from 'api/tools'
|
||||||
|
|
@ -53,7 +53,7 @@ try {
|
||||||
return '';
|
return '';
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
const ToolDialog = ({ show, dialogProps, onUseTemplate, onCancel, onConfirm }) => {
|
||||||
const portalElement = document.getElementById('portal')
|
const portalElement = document.getElementById('portal')
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
|
||||||
|
|
@ -73,6 +73,7 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
const [toolId, setToolId] = useState('')
|
const [toolId, setToolId] = useState('')
|
||||||
const [toolName, setToolName] = useState('')
|
const [toolName, setToolName] = useState('')
|
||||||
const [toolDesc, setToolDesc] = useState('')
|
const [toolDesc, setToolDesc] = useState('')
|
||||||
|
const [toolIcon, setToolIcon] = useState('')
|
||||||
const [toolSchema, setToolSchema] = useState([])
|
const [toolSchema, setToolSchema] = useState([])
|
||||||
const [toolFunc, setToolFunc] = useState('')
|
const [toolFunc, setToolFunc] = useState('')
|
||||||
|
|
||||||
|
|
@ -167,18 +168,39 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dialogProps.type === 'EDIT' && dialogProps.data) {
|
if (dialogProps.type === 'EDIT' && dialogProps.data) {
|
||||||
|
// When tool dialog is opened from Tools dashboard
|
||||||
setToolId(dialogProps.data.id)
|
setToolId(dialogProps.data.id)
|
||||||
setToolName(dialogProps.data.name)
|
setToolName(dialogProps.data.name)
|
||||||
setToolDesc(dialogProps.data.description)
|
setToolDesc(dialogProps.data.description)
|
||||||
|
setToolIcon(dialogProps.data.iconSrc)
|
||||||
setToolSchema(formatSchema(dialogProps.data.schema))
|
setToolSchema(formatSchema(dialogProps.data.schema))
|
||||||
if (dialogProps.data.func) setToolFunc(dialogProps.data.func)
|
if (dialogProps.data.func) setToolFunc(dialogProps.data.func)
|
||||||
else setToolFunc('')
|
else setToolFunc('')
|
||||||
} else if (dialogProps.type === 'EDIT' && dialogProps.toolId) {
|
} else if (dialogProps.type === 'EDIT' && dialogProps.toolId) {
|
||||||
|
// When tool dialog is opened from CustomTool node in canvas
|
||||||
getSpecificToolApi.request(dialogProps.toolId)
|
getSpecificToolApi.request(dialogProps.toolId)
|
||||||
|
} else if (dialogProps.type === 'IMPORT' && dialogProps.data) {
|
||||||
|
// When tool dialog is to import existing tool
|
||||||
|
setToolName(dialogProps.data.name)
|
||||||
|
setToolDesc(dialogProps.data.description)
|
||||||
|
setToolIcon(dialogProps.data.iconSrc)
|
||||||
|
setToolSchema(formatSchema(dialogProps.data.schema))
|
||||||
|
if (dialogProps.data.func) setToolFunc(dialogProps.data.func)
|
||||||
|
else setToolFunc('')
|
||||||
|
} else if (dialogProps.type === 'TEMPLATE' && dialogProps.data) {
|
||||||
|
// When tool dialog is a template
|
||||||
|
setToolName(dialogProps.data.name)
|
||||||
|
setToolDesc(dialogProps.data.description)
|
||||||
|
setToolIcon(dialogProps.data.iconSrc)
|
||||||
|
setToolSchema(formatSchema(dialogProps.data.schema))
|
||||||
|
if (dialogProps.data.func) setToolFunc(dialogProps.data.func)
|
||||||
|
else setToolFunc('')
|
||||||
} else if (dialogProps.type === 'ADD') {
|
} else if (dialogProps.type === 'ADD') {
|
||||||
|
// When tool dialog is to add a new tool
|
||||||
setToolId('')
|
setToolId('')
|
||||||
setToolName('')
|
setToolName('')
|
||||||
setToolDesc('')
|
setToolDesc('')
|
||||||
|
setToolIcon('')
|
||||||
setToolSchema([])
|
setToolSchema([])
|
||||||
setToolFunc('')
|
setToolFunc('')
|
||||||
}
|
}
|
||||||
|
|
@ -186,6 +208,47 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [dialogProps])
|
}, [dialogProps])
|
||||||
|
|
||||||
|
const useToolTemplate = () => {
|
||||||
|
onUseTemplate(dialogProps.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportTool = async () => {
|
||||||
|
try {
|
||||||
|
const toolResp = await toolsApi.getSpecificTool(toolId)
|
||||||
|
if (toolResp.data) {
|
||||||
|
const toolData = toolResp.data
|
||||||
|
delete toolData.id
|
||||||
|
delete toolData.createdDate
|
||||||
|
delete toolData.updatedDate
|
||||||
|
let dataStr = JSON.stringify(toolData)
|
||||||
|
let dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr)
|
||||||
|
|
||||||
|
let exportFileDefaultName = `${toolName}-CustomTool.json`
|
||||||
|
|
||||||
|
let linkElement = document.createElement('a')
|
||||||
|
linkElement.setAttribute('href', dataUri)
|
||||||
|
linkElement.setAttribute('download', exportFileDefaultName)
|
||||||
|
linkElement.click()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
||||||
|
enqueueSnackbar({
|
||||||
|
message: `Failed to export Tool: ${errorData}`,
|
||||||
|
options: {
|
||||||
|
key: new Date().getTime() + Math.random(),
|
||||||
|
variant: 'error',
|
||||||
|
persist: true,
|
||||||
|
action: (key) => (
|
||||||
|
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||||
|
<IconX />
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onCancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const addNewTool = async () => {
|
const addNewTool = async () => {
|
||||||
try {
|
try {
|
||||||
const obj = {
|
const obj = {
|
||||||
|
|
@ -193,7 +256,8 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
description: toolDesc,
|
description: toolDesc,
|
||||||
color: generateRandomGradient(),
|
color: generateRandomGradient(),
|
||||||
schema: JSON.stringify(toolSchema),
|
schema: JSON.stringify(toolSchema),
|
||||||
func: toolFunc
|
func: toolFunc,
|
||||||
|
iconSrc: toolIcon
|
||||||
}
|
}
|
||||||
const createResp = await toolsApi.createNewTool(obj)
|
const createResp = await toolsApi.createNewTool(obj)
|
||||||
if (createResp.data) {
|
if (createResp.data) {
|
||||||
|
|
@ -236,7 +300,8 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
name: toolName,
|
name: toolName,
|
||||||
description: toolDesc,
|
description: toolDesc,
|
||||||
schema: JSON.stringify(toolSchema),
|
schema: JSON.stringify(toolSchema),
|
||||||
func: toolFunc
|
func: toolFunc,
|
||||||
|
iconSrc: toolIcon
|
||||||
})
|
})
|
||||||
if (saveResp.data) {
|
if (saveResp.data) {
|
||||||
enqueueSnackbar({
|
enqueueSnackbar({
|
||||||
|
|
@ -330,7 +395,15 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
aria-describedby='alert-dialog-description'
|
aria-describedby='alert-dialog-description'
|
||||||
>
|
>
|
||||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||||
{dialogProps.title}
|
{dialogProps.title}
|
||||||
|
<div style={{ flex: 1 }} />
|
||||||
|
{dialogProps.type === 'EDIT' && (
|
||||||
|
<Button variant='outlined' onClick={() => exportTool()} startIcon={<IconFileExport />}>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<Box sx={{ p: 2 }}>
|
<Box sx={{ p: 2 }}>
|
||||||
|
|
@ -338,12 +411,17 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
<Typography variant='overline'>
|
<Typography variant='overline'>
|
||||||
Tool Name
|
Tool Name
|
||||||
<span style={{ color: 'red' }}> *</span>
|
<span style={{ color: 'red' }}> *</span>
|
||||||
|
<TooltipWithParser
|
||||||
|
style={{ marginLeft: 10 }}
|
||||||
|
title={'Tool name must be small capital letter with underscore. Ex: my_tool'}
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<OutlinedInput
|
<OutlinedInput
|
||||||
id='toolName'
|
id='toolName'
|
||||||
type='string'
|
type='string'
|
||||||
fullWidth
|
fullWidth
|
||||||
|
disabled={dialogProps.type === 'TEMPLATE'}
|
||||||
placeholder='My New Tool'
|
placeholder='My New Tool'
|
||||||
value={toolName}
|
value={toolName}
|
||||||
name='toolName'
|
name='toolName'
|
||||||
|
|
@ -355,12 +433,17 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
<Typography variant='overline'>
|
<Typography variant='overline'>
|
||||||
Tool description
|
Tool description
|
||||||
<span style={{ color: 'red' }}> *</span>
|
<span style={{ color: 'red' }}> *</span>
|
||||||
|
<TooltipWithParser
|
||||||
|
style={{ marginLeft: 10 }}
|
||||||
|
title={'Description of what the tool does. This is for ChatGPT to determine when to use this tool.'}
|
||||||
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<OutlinedInput
|
<OutlinedInput
|
||||||
id='toolDesc'
|
id='toolDesc'
|
||||||
type='string'
|
type='string'
|
||||||
fullWidth
|
fullWidth
|
||||||
|
disabled={dialogProps.type === 'TEMPLATE'}
|
||||||
placeholder='Description of what the tool does. This is for ChatGPT to determine when to use this tool.'
|
placeholder='Description of what the tool does. This is for ChatGPT to determine when to use this tool.'
|
||||||
multiline={true}
|
multiline={true}
|
||||||
rows={3}
|
rows={3}
|
||||||
|
|
@ -369,6 +452,21 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
onChange={(e) => setToolDesc(e.target.value)}
|
onChange={(e) => setToolDesc(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box sx={{ p: 2 }}>
|
||||||
|
<Stack sx={{ position: 'relative' }} direction='row'>
|
||||||
|
<Typography variant='overline'>Tool Icon Src</Typography>
|
||||||
|
</Stack>
|
||||||
|
<OutlinedInput
|
||||||
|
id='toolIcon'
|
||||||
|
type='string'
|
||||||
|
fullWidth
|
||||||
|
disabled={dialogProps.type === 'TEMPLATE'}
|
||||||
|
placeholder='https://raw.githubusercontent.com/gilbarbara/logos/main/logos/airtable.svg'
|
||||||
|
value={toolIcon}
|
||||||
|
name='toolIcon'
|
||||||
|
onChange={(e) => setToolIcon(e.target.value)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
<Box sx={{ p: 2 }}>
|
<Box sx={{ p: 2 }}>
|
||||||
<Stack sx={{ position: 'relative' }} direction='row'>
|
<Stack sx={{ position: 'relative' }} direction='row'>
|
||||||
<Typography variant='overline'>
|
<Typography variant='overline'>
|
||||||
|
|
@ -376,7 +474,13 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
<TooltipWithParser style={{ marginLeft: 10 }} title={'What should be the output response in JSON format?'} />
|
<TooltipWithParser style={{ marginLeft: 10 }} title={'What should be the output response in JSON format?'} />
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Grid columns={columns} rows={toolSchema} addNewRow={addNewRow} onRowUpdate={onRowUpdate} />
|
<Grid
|
||||||
|
columns={columns}
|
||||||
|
rows={toolSchema}
|
||||||
|
disabled={dialogProps.type === 'TEMPLATE'}
|
||||||
|
addNewRow={addNewRow}
|
||||||
|
onRowUpdate={onRowUpdate}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ p: 2 }}>
|
<Box sx={{ p: 2 }}>
|
||||||
<Stack sx={{ position: 'relative' }} direction='row'>
|
<Stack sx={{ position: 'relative' }} direction='row'>
|
||||||
|
|
@ -388,12 +492,15 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
/>
|
/>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
{dialogProps.type !== 'TEMPLATE' && (
|
||||||
<Button style={{ marginBottom: 10 }} variant='outlined' onClick={() => setToolFunc(exampleAPIFunc)}>
|
<Button style={{ marginBottom: 10 }} variant='outlined' onClick={() => setToolFunc(exampleAPIFunc)}>
|
||||||
See Example
|
See Example
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
{customization.isDarkMode ? (
|
{customization.isDarkMode ? (
|
||||||
<DarkCodeEditor
|
<DarkCodeEditor
|
||||||
value={toolFunc}
|
value={toolFunc}
|
||||||
|
disabled={dialogProps.type === 'TEMPLATE'}
|
||||||
onValueChange={(code) => setToolFunc(code)}
|
onValueChange={(code) => setToolFunc(code)}
|
||||||
style={{
|
style={{
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
|
|
@ -405,6 +512,7 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
) : (
|
) : (
|
||||||
<LightCodeEditor
|
<LightCodeEditor
|
||||||
value={toolFunc}
|
value={toolFunc}
|
||||||
|
disabled={dialogProps.type === 'TEMPLATE'}
|
||||||
onValueChange={(code) => setToolFunc(code)}
|
onValueChange={(code) => setToolFunc(code)}
|
||||||
style={{
|
style={{
|
||||||
fontSize: '0.875rem',
|
fontSize: '0.875rem',
|
||||||
|
|
@ -423,13 +531,20 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
Delete
|
Delete
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
)}
|
)}
|
||||||
|
{dialogProps.type === 'TEMPLATE' && (
|
||||||
|
<StyledButton color='secondary' variant='contained' onClick={useToolTemplate}>
|
||||||
|
Use Template
|
||||||
|
</StyledButton>
|
||||||
|
)}
|
||||||
|
{dialogProps.type !== 'TEMPLATE' && (
|
||||||
<StyledButton
|
<StyledButton
|
||||||
disabled={!(toolName && toolDesc)}
|
disabled={!(toolName && toolDesc)}
|
||||||
variant='contained'
|
variant='contained'
|
||||||
onClick={() => (dialogProps.type === 'ADD' ? addNewTool() : saveTool())}
|
onClick={() => (dialogProps.type === 'ADD' || dialogProps.type === 'IMPORT' ? addNewTool() : saveTool())}
|
||||||
>
|
>
|
||||||
{dialogProps.confirmButtonName}
|
{dialogProps.confirmButtonName}
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
|
)}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
<ConfirmDialog />
|
<ConfirmDialog />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
@ -441,6 +556,7 @@ const ToolDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
|
||||||
ToolDialog.propTypes = {
|
ToolDialog.propTypes = {
|
||||||
show: PropTypes.bool,
|
show: PropTypes.bool,
|
||||||
dialogProps: PropTypes.object,
|
dialogProps: PropTypes.object,
|
||||||
|
onUseTemplate: PropTypes.func,
|
||||||
onCancel: PropTypes.func,
|
onCancel: PropTypes.func,
|
||||||
onConfirm: PropTypes.func
|
onConfirm: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState, useRef } from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
// material-ui
|
// material-ui
|
||||||
import { Grid, Box, Stack } from '@mui/material'
|
import { Grid, Box, Stack, Button } from '@mui/material'
|
||||||
import { useTheme } from '@mui/material/styles'
|
import { useTheme } from '@mui/material/styles'
|
||||||
|
|
||||||
// project imports
|
// project imports
|
||||||
|
|
@ -20,7 +20,7 @@ import toolsApi from 'api/tools'
|
||||||
import useApi from 'hooks/useApi'
|
import useApi from 'hooks/useApi'
|
||||||
|
|
||||||
// icons
|
// icons
|
||||||
import { IconPlus } from '@tabler/icons'
|
import { IconPlus, IconFileImport } from '@tabler/icons'
|
||||||
|
|
||||||
// ==============================|| CHATFLOWS ||============================== //
|
// ==============================|| CHATFLOWS ||============================== //
|
||||||
|
|
||||||
|
|
@ -33,6 +33,40 @@ const Tools = () => {
|
||||||
const [showDialog, setShowDialog] = useState(false)
|
const [showDialog, setShowDialog] = useState(false)
|
||||||
const [dialogProps, setDialogProps] = useState({})
|
const [dialogProps, setDialogProps] = useState({})
|
||||||
|
|
||||||
|
const inputRef = useRef(null)
|
||||||
|
|
||||||
|
const onUploadFile = (file) => {
|
||||||
|
try {
|
||||||
|
const dialogProp = {
|
||||||
|
title: 'Add New Tool',
|
||||||
|
type: 'IMPORT',
|
||||||
|
cancelButtonName: 'Cancel',
|
||||||
|
confirmButtonName: 'Save',
|
||||||
|
data: JSON.parse(file)
|
||||||
|
}
|
||||||
|
setDialogProps(dialogProp)
|
||||||
|
setShowDialog(true)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFileUpload = (e) => {
|
||||||
|
if (!e.target.files) return
|
||||||
|
|
||||||
|
const file = e.target.files[0]
|
||||||
|
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.onload = (evt) => {
|
||||||
|
if (!evt?.target?.result) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const { result } = evt.target
|
||||||
|
onUploadFile(result)
|
||||||
|
}
|
||||||
|
reader.readAsText(file)
|
||||||
|
}
|
||||||
|
|
||||||
const addNew = () => {
|
const addNew = () => {
|
||||||
const dialogProp = {
|
const dialogProp = {
|
||||||
title: 'Add New Tool',
|
title: 'Add New Tool',
|
||||||
|
|
@ -75,8 +109,17 @@ const Tools = () => {
|
||||||
<Grid sx={{ mb: 1.25 }} container direction='row'>
|
<Grid sx={{ mb: 1.25 }} container direction='row'>
|
||||||
<Box sx={{ flexGrow: 1 }} />
|
<Box sx={{ flexGrow: 1 }} />
|
||||||
<Grid item>
|
<Grid item>
|
||||||
|
<Button
|
||||||
|
variant='outlined'
|
||||||
|
sx={{ mr: 2 }}
|
||||||
|
onClick={() => inputRef.current.click()}
|
||||||
|
startIcon={<IconFileImport />}
|
||||||
|
>
|
||||||
|
Load
|
||||||
|
</Button>
|
||||||
|
<input ref={inputRef} type='file' hidden accept='.json' onChange={(e) => handleFileUpload(e)} />
|
||||||
<StyledButton variant='contained' sx={{ color: 'white' }} onClick={addNew} startIcon={<IconPlus />}>
|
<StyledButton variant='contained' sx={{ color: 'white' }} onClick={addNew} startIcon={<IconPlus />}>
|
||||||
Create New
|
Create
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
@ -86,7 +129,7 @@ const Tools = () => {
|
||||||
getAllToolsApi.data &&
|
getAllToolsApi.data &&
|
||||||
getAllToolsApi.data.map((data, index) => (
|
getAllToolsApi.data.map((data, index) => (
|
||||||
<Grid key={index} item lg={3} md={4} sm={6} xs={12}>
|
<Grid key={index} item lg={3} md={4} sm={6} xs={12}>
|
||||||
<ItemCard data={data} color={data.color} onClick={() => edit(data)} />
|
<ItemCard data={data} onClick={() => edit(data)} />
|
||||||
</Grid>
|
</Grid>
|
||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue