mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 09:00:52 +03:00
Merge branch 'main' into feature/conversation-starters
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
import client from './client'
|
||||
|
||||
const getExportDatabase = () => client.get('/database/export')
|
||||
const createLoadDatabase = (body) => client.post('/database/load', body)
|
||||
|
||||
export default {
|
||||
getExportDatabase,
|
||||
createLoadDatabase
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import client from './client'
|
||||
|
||||
const getAvailablePrompts = (body) => client.post(`/prompts-list`, body)
|
||||
const getPrompt = (body) => client.post(`/load-prompt`, body)
|
||||
|
||||
export default {
|
||||
getAvailablePrompts,
|
||||
getPrompt
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 10 KiB |
@@ -1,7 +1,6 @@
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
// material-ui
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
@@ -26,16 +25,10 @@ import PerfectScrollbar from 'react-perfect-scrollbar'
|
||||
// project imports
|
||||
import MainCard from 'ui-component/cards/MainCard'
|
||||
import Transitions from 'ui-component/extended/Transitions'
|
||||
import { BackdropLoader } from 'ui-component/loading/BackdropLoader'
|
||||
import AboutDialog from 'ui-component/dialog/AboutDialog'
|
||||
|
||||
// assets
|
||||
import { IconLogout, IconSettings, IconFileExport, IconFileDownload, IconInfoCircle } from '@tabler/icons'
|
||||
|
||||
// API
|
||||
import databaseApi from 'api/database'
|
||||
|
||||
import { SET_MENU } from 'store/actions'
|
||||
import { IconLogout, IconSettings, IconInfoCircle } from '@tabler/icons'
|
||||
|
||||
import './index.css'
|
||||
|
||||
@@ -43,17 +36,13 @@ import './index.css'
|
||||
|
||||
const ProfileSection = ({ username, handleLogout }) => {
|
||||
const theme = useTheme()
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [aboutDialogOpen, setAboutDialogOpen] = useState(false)
|
||||
|
||||
const anchorRef = useRef(null)
|
||||
const uploadRef = useRef(null)
|
||||
|
||||
const handleClose = (event) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
||||
@@ -66,56 +55,6 @@ const ProfileSection = ({ username, handleLogout }) => {
|
||||
setOpen((prevOpen) => !prevOpen)
|
||||
}
|
||||
|
||||
const handleExportDB = async () => {
|
||||
setOpen(false)
|
||||
try {
|
||||
const response = await databaseApi.getExportDatabase()
|
||||
const exportItems = response.data
|
||||
let dataStr = JSON.stringify(exportItems, null, 2)
|
||||
let dataUri = 'data:application/json;charset=utf-8,' + encodeURIComponent(dataStr)
|
||||
|
||||
let exportFileDefaultName = `DB.json`
|
||||
|
||||
let linkElement = document.createElement('a')
|
||||
linkElement.setAttribute('href', dataUri)
|
||||
linkElement.setAttribute('download', exportFileDefaultName)
|
||||
linkElement.click()
|
||||
} 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 = async (evt) => {
|
||||
if (!evt?.target?.result) {
|
||||
return
|
||||
}
|
||||
const { result } = evt.target
|
||||
|
||||
if (result.includes(`"chatmessages":[`) && result.includes(`"chatflows":[`) && result.includes(`"apikeys":[`)) {
|
||||
dispatch({ type: SET_MENU, opened: false })
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
await databaseApi.createLoadDatabase(JSON.parse(result))
|
||||
setLoading(false)
|
||||
navigate('/', { replace: true })
|
||||
navigate(0)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
setLoading(false)
|
||||
}
|
||||
} else {
|
||||
alert('Incorrect Flowise Database Format')
|
||||
}
|
||||
}
|
||||
reader.readAsText(file)
|
||||
}
|
||||
|
||||
const prevOpen = useRef(open)
|
||||
useEffect(() => {
|
||||
if (prevOpen.current === true && open === false) {
|
||||
@@ -196,27 +135,6 @@ const ProfileSection = ({ username, handleLogout }) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ListItemButton
|
||||
sx={{ borderRadius: `${customization.borderRadius}px` }}
|
||||
onClick={() => {
|
||||
setOpen(false)
|
||||
uploadRef.current.click()
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<IconFileDownload stroke={1.5} size='1.3rem' />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={<Typography variant='body2'>Load Database</Typography>} />
|
||||
</ListItemButton>
|
||||
<ListItemButton
|
||||
sx={{ borderRadius: `${customization.borderRadius}px` }}
|
||||
onClick={handleExportDB}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<IconFileExport stroke={1.5} size='1.3rem' />
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={<Typography variant='body2'>Export Database</Typography>} />
|
||||
</ListItemButton>
|
||||
<ListItemButton
|
||||
sx={{ borderRadius: `${customization.borderRadius}px` }}
|
||||
onClick={() => {
|
||||
@@ -249,8 +167,6 @@ const ProfileSection = ({ username, handleLogout }) => {
|
||||
</Transitions>
|
||||
)}
|
||||
</Popper>
|
||||
<input ref={uploadRef} type='file' hidden accept='.json' onChange={(e) => handleFileUpload(e)} />
|
||||
<BackdropLoader open={loading} />
|
||||
<AboutDialog show={aboutDialogOpen} onCancel={() => setAboutDialogOpen(false)} />
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -23,7 +23,6 @@ import useConfirm from 'hooks/useConfirm'
|
||||
import { uiBaseURL } from '../../store/constant'
|
||||
import { closeSnackbar as closeSnackbarAction, enqueueSnackbar as enqueueSnackbarAction } from '../../store/actions'
|
||||
|
||||
import ConfirmDialog from '../dialog/ConfirmDialog'
|
||||
import SaveChatflowDialog from '../dialog/SaveChatflowDialog'
|
||||
import TagDialog from '../dialog/TagDialog'
|
||||
|
||||
@@ -286,7 +285,6 @@ export default function FlowListMenu({ chatflow, updateFlowsApi }) {
|
||||
Delete
|
||||
</MenuItem>
|
||||
</StyledMenu>
|
||||
<ConfirmDialog />
|
||||
<SaveChatflowDialog
|
||||
show={flowDialogOpen}
|
||||
dialogProps={{
|
||||
|
||||
@@ -0,0 +1,587 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import rehypeMathjax from 'rehype-mathjax'
|
||||
import rehypeRaw from 'rehype-raw'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import remarkMath from 'remark-math'
|
||||
|
||||
// MUI
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
CardContent,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Chip,
|
||||
Grid,
|
||||
InputLabel,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
OutlinedInput,
|
||||
Select,
|
||||
Typography,
|
||||
Stack,
|
||||
IconButton,
|
||||
FormControl,
|
||||
Checkbox,
|
||||
MenuItem
|
||||
} from '@mui/material'
|
||||
import MuiAccordion from '@mui/material/Accordion'
|
||||
import MuiAccordionSummary from '@mui/material/AccordionSummary'
|
||||
import MuiAccordionDetails from '@mui/material/AccordionDetails'
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
||||
import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp'
|
||||
import ClearIcon from '@mui/icons-material/Clear'
|
||||
import { styled } from '@mui/material/styles'
|
||||
|
||||
//Project Import
|
||||
import { StyledButton } from 'ui-component/button/StyledButton'
|
||||
import { MemoizedReactMarkdown } from 'ui-component/markdown/MemoizedReactMarkdown'
|
||||
import { CodeBlock } from 'ui-component/markdown/CodeBlock'
|
||||
import promptEmptySVG from 'assets/images/prompt_empty.svg'
|
||||
|
||||
import useApi from 'hooks/useApi'
|
||||
import promptApi from 'api/prompt'
|
||||
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
|
||||
|
||||
const NewLineToBr = ({ children = '' }) => {
|
||||
return children.split('\n').reduce(function (arr, line) {
|
||||
return arr.concat(line, <br />)
|
||||
}, [])
|
||||
}
|
||||
|
||||
const Accordion = styled((props) => <MuiAccordion disableGutters elevation={0} square {...props} />)(({ theme }) => ({
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
'&:not(:last-child)': {
|
||||
borderBottom: 0
|
||||
},
|
||||
'&:before': {
|
||||
display: 'none'
|
||||
}
|
||||
}))
|
||||
|
||||
const AccordionSummary = styled((props) => (
|
||||
<MuiAccordionSummary expandIcon={<ArrowForwardIosSharpIcon sx={{ fontSize: '0.9rem' }} />} {...props} />
|
||||
))(({ theme }) => ({
|
||||
backgroundColor: theme.palette.mode === 'dark' ? 'rgba(255, 255, 255, .05)' : 'rgba(0, 0, 0, .03)',
|
||||
flexDirection: 'row-reverse',
|
||||
'& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': {
|
||||
transform: 'rotate(180deg)'
|
||||
},
|
||||
'& .MuiAccordionSummary-content': {
|
||||
marginLeft: theme.spacing(1)
|
||||
}
|
||||
}))
|
||||
|
||||
const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
|
||||
padding: theme.spacing(2),
|
||||
borderTop: '1px solid rgba(0, 0, 0, .125)'
|
||||
}))
|
||||
|
||||
const PromptLangsmithHubDialog = ({ promptType, show, onCancel, onSubmit }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
const dispatch = useDispatch()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
const getAvailablePromptsApi = useApi(promptApi.getAvailablePrompts)
|
||||
|
||||
useEffect(() => {
|
||||
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
|
||||
else dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [show, dispatch])
|
||||
|
||||
useEffect(() => {
|
||||
if (promptType) {
|
||||
getAvailablePromptsApi.request({ tags: promptType === 'template' ? 'StringPromptTemplate&' : 'ChatPromptTemplate&' })
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [promptType])
|
||||
|
||||
useEffect(() => {
|
||||
if (getAvailablePromptsApi.data && getAvailablePromptsApi.data.repos) {
|
||||
setAvailablePrompNameList(getAvailablePromptsApi.data.repos)
|
||||
if (getAvailablePromptsApi.data.repos?.length) handleListItemClick(0, getAvailablePromptsApi.data.repos)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getAvailablePromptsApi.data])
|
||||
|
||||
const ITEM_HEIGHT = 48
|
||||
const ITEM_PADDING_TOP = 8
|
||||
const MenuProps = {
|
||||
PaperProps: {
|
||||
style: {
|
||||
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
|
||||
width: 250
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const models = [
|
||||
{ id: 101, name: 'anthropic:claude-instant-1' },
|
||||
{ id: 102, name: 'anthropic:claude-instant-1.2' },
|
||||
{ id: 103, name: 'anthropic:claude-2' },
|
||||
{ id: 104, name: 'google:palm-2-chat-bison' },
|
||||
{ id: 105, name: 'google:palm-2-codechat-bison' },
|
||||
{ id: 106, name: 'google:palm-2-text-bison' },
|
||||
{ id: 107, name: 'meta:llama-2-13b-chat' },
|
||||
{ id: 108, name: 'meta:llama-2-70b-chat' },
|
||||
{ id: 109, name: 'openai:gpt-3.5-turbo' },
|
||||
{ id: 110, name: 'openai:gpt-4' },
|
||||
{ id: 111, name: 'openai:text-davinci-003' }
|
||||
]
|
||||
const [modelName, setModelName] = useState([])
|
||||
|
||||
const usecases = [
|
||||
{ id: 201, name: 'Agents' },
|
||||
{ id: 202, name: 'Agent Stimulation' },
|
||||
{ id: 203, name: 'Autonomous agents' },
|
||||
{ id: 204, name: 'Classification' },
|
||||
{ id: 205, name: 'Chatbots' },
|
||||
{ id: 206, name: 'Code understanding' },
|
||||
{ id: 207, name: 'Code writing' },
|
||||
{ id: 208, name: 'Evaluation' },
|
||||
{ id: 209, name: 'Extraction' },
|
||||
{ id: 210, name: 'Interacting with APIs' },
|
||||
{ id: 211, name: 'Multi-modal' },
|
||||
{ id: 212, name: 'QA over documents' },
|
||||
{ id: 213, name: 'Self-checking' },
|
||||
{ id: 214, name: 'SQL' },
|
||||
{ id: 215, name: 'Summarization' },
|
||||
{ id: 216, name: 'Tagging' }
|
||||
]
|
||||
const [usecase, setUsecase] = useState([])
|
||||
|
||||
const languages = [
|
||||
{ id: 301, name: 'Chinese' },
|
||||
{ id: 302, name: 'English' },
|
||||
{ id: 303, name: 'French' },
|
||||
{ id: 304, name: 'German' },
|
||||
{ id: 305, name: 'Russian' },
|
||||
{ id: 306, name: 'Spanish' }
|
||||
]
|
||||
const [language, setLanguage] = useState([])
|
||||
const [availablePrompNameList, setAvailablePrompNameList] = useState([])
|
||||
const [selectedPrompt, setSelectedPrompt] = useState({})
|
||||
|
||||
const [accordionExpanded, setAccordionExpanded] = useState(['prompt'])
|
||||
|
||||
const handleAccordionChange = (accordionName) => (event, isExpanded) => {
|
||||
const accordians = [...accordionExpanded]
|
||||
if (!isExpanded) setAccordionExpanded(accordians.filter((accr) => accr !== accordionName))
|
||||
else {
|
||||
accordians.push(accordionName)
|
||||
setAccordionExpanded(accordians)
|
||||
}
|
||||
}
|
||||
|
||||
const handleListItemClick = async (index, overridePromptNameList = []) => {
|
||||
const prompt = overridePromptNameList.length ? overridePromptNameList[index] : availablePrompNameList[index]
|
||||
|
||||
if (!prompt.detailed) {
|
||||
const createResp = await promptApi.getPrompt({
|
||||
promptName: prompt.full_name
|
||||
})
|
||||
if (createResp.data) {
|
||||
prompt.detailed = createResp.data.templates
|
||||
}
|
||||
}
|
||||
setSelectedPrompt(prompt)
|
||||
}
|
||||
|
||||
const fetchPrompts = async () => {
|
||||
let tags = promptType === 'template' ? 'StringPromptTemplate&' : 'ChatPromptTemplate&'
|
||||
modelName.forEach((item) => {
|
||||
tags += `tags=${item.name}&`
|
||||
})
|
||||
usecase.forEach((item) => {
|
||||
tags += `tags=${item.name}&`
|
||||
})
|
||||
language.forEach((item) => {
|
||||
tags += `tags=${item.name}&`
|
||||
})
|
||||
getAvailablePromptsApi.request({ tags: tags })
|
||||
}
|
||||
|
||||
const removeDuplicates = (value) => {
|
||||
let duplicateRemoved = []
|
||||
|
||||
value.forEach((item) => {
|
||||
if (value.filter((o) => o.id === item.id).length === 1) {
|
||||
duplicateRemoved.push(item)
|
||||
}
|
||||
})
|
||||
return duplicateRemoved
|
||||
}
|
||||
|
||||
const handleModelChange = (event) => {
|
||||
const {
|
||||
target: { value }
|
||||
} = event
|
||||
|
||||
setModelName(removeDuplicates(value))
|
||||
}
|
||||
|
||||
const handleUsecaseChange = (event) => {
|
||||
const {
|
||||
target: { value }
|
||||
} = event
|
||||
|
||||
setUsecase(removeDuplicates(value))
|
||||
}
|
||||
const handleLanguageChange = (event) => {
|
||||
const {
|
||||
target: { value }
|
||||
} = event
|
||||
|
||||
setLanguage(removeDuplicates(value))
|
||||
}
|
||||
|
||||
const component = show ? (
|
||||
<Dialog
|
||||
onClose={onCancel}
|
||||
open={show}
|
||||
fullWidth
|
||||
maxWidth={'lg'}
|
||||
aria-labelledby='prompt-dialog-title'
|
||||
aria-describedby='prompt-dialog-description'
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: '1rem' }} id='prompt-dialog-title'>
|
||||
Langchain Hub ({promptType === 'template' ? 'PromptTemplate' : 'ChatPromptTemplate'})
|
||||
</DialogTitle>
|
||||
<DialogContent dividers sx={{ p: 1 }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', p: 2, pt: 1, alignItems: 'center' }}>
|
||||
<FormControl sx={{ mr: 1, width: '30%' }}>
|
||||
<InputLabel size='small' id='model-checkbox-label'>
|
||||
Model
|
||||
</InputLabel>
|
||||
<Select
|
||||
id='model-checkbox'
|
||||
labelId='model-checkbox-label'
|
||||
multiple
|
||||
size='small'
|
||||
value={modelName}
|
||||
onChange={handleModelChange}
|
||||
input={<OutlinedInput label='Model' />}
|
||||
renderValue={(selected) => selected.map((x) => x.name).join(', ')}
|
||||
endAdornment={
|
||||
modelName.length ? (
|
||||
<IconButton sx={{ mr: 2 }} onClick={() => setModelName([])}>
|
||||
<ClearIcon style={{ width: 20, height: 20 }} />
|
||||
</IconButton>
|
||||
) : (
|
||||
false
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
'.MuiSvgIcon-root ': {
|
||||
fill: customization.isDarkMode ? 'white !important' : ''
|
||||
}
|
||||
}}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{models.map((variant) => (
|
||||
<MenuItem key={variant.id} value={variant}>
|
||||
<Checkbox id={variant.id} checked={modelName.findIndex((item) => item.id === variant.id) >= 0} />
|
||||
<ListItemText primary={variant.name} />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl sx={{ mr: 1, width: '30%' }}>
|
||||
<InputLabel size='small' id='usecase-checkbox-label'>
|
||||
Usecase
|
||||
</InputLabel>
|
||||
<Select
|
||||
autoWidth={false}
|
||||
labelId='usecase-checkbox-label'
|
||||
id='usecase-checkbox'
|
||||
multiple
|
||||
size='small'
|
||||
value={usecase}
|
||||
onChange={handleUsecaseChange}
|
||||
input={<OutlinedInput label='Usecase' />}
|
||||
renderValue={(selected) => selected.map((x) => x.name).join(', ')}
|
||||
endAdornment={
|
||||
usecase.length ? (
|
||||
<IconButton sx={{ mr: 2 }} onClick={() => setUsecase([])}>
|
||||
<ClearIcon style={{ width: 20, height: 20 }} />
|
||||
</IconButton>
|
||||
) : (
|
||||
false
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
'.MuiSvgIcon-root ': {
|
||||
fill: customization.isDarkMode ? 'white !important' : ''
|
||||
}
|
||||
}}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{usecases.map((variant) => (
|
||||
<MenuItem key={variant.id} value={variant}>
|
||||
<Checkbox id={variant.id} checked={usecase.findIndex((item) => item.id === variant.id) >= 0} />
|
||||
<ListItemText primary={variant.name} />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl sx={{ mr: 1, width: '30%' }}>
|
||||
<InputLabel size='small' id='language-checkbox-label'>
|
||||
Language
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId='language-checkbox-label'
|
||||
id='language-checkbox'
|
||||
multiple
|
||||
size='small'
|
||||
value={language}
|
||||
onChange={handleLanguageChange}
|
||||
input={<OutlinedInput label='language' />}
|
||||
renderValue={(selected) => selected.map((x) => x.name).join(', ')}
|
||||
endAdornment={
|
||||
language.length ? (
|
||||
<IconButton sx={{ mr: 2 }} onClick={() => setLanguage([])}>
|
||||
<ClearIcon style={{ width: 20, height: 20 }} />
|
||||
</IconButton>
|
||||
) : (
|
||||
false
|
||||
)
|
||||
}
|
||||
sx={{
|
||||
'.MuiSvgIcon-root ': {
|
||||
fill: customization.isDarkMode ? 'white !important' : ''
|
||||
}
|
||||
}}
|
||||
MenuProps={MenuProps}
|
||||
>
|
||||
{languages.map((variant) => (
|
||||
<MenuItem key={variant.id} value={variant}>
|
||||
<Checkbox id={variant.id} checked={language.findIndex((item) => item.id === variant.id) >= 0} />
|
||||
<ListItemText primary={variant.name} />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl sx={{ width: '10%' }}>
|
||||
<Button disableElevation variant='outlined' onClick={fetchPrompts}>
|
||||
Search
|
||||
</Button>
|
||||
</FormControl>
|
||||
</Box>
|
||||
|
||||
{availablePrompNameList && availablePrompNameList.length == 0 && (
|
||||
<Stack sx={{ alignItems: 'center', justifyContent: 'center', width: '100%', pb: 3 }} flexDirection='column'>
|
||||
<Box sx={{ p: 5, height: 'auto' }}>
|
||||
<img style={{ objectFit: 'cover', height: '20vh', width: 'auto' }} src={promptEmptySVG} alt='promptEmptySVG' />
|
||||
</Box>
|
||||
<div>No Available Prompts</div>
|
||||
</Stack>
|
||||
)}
|
||||
{availablePrompNameList && availablePrompNameList.length > 0 && (
|
||||
<Stack sx={{ alignItems: 'center', justifyContent: 'center', width: '100%' }} flexDirection='column'>
|
||||
<Box sx={{ width: '100%', p: 2 }}>
|
||||
<Grid xs={12} container spacing={1} justifyContent='center' alignItems='center'>
|
||||
<Grid xs={4} item sx={{ textAlign: 'left' }}>
|
||||
<Box sx={{ width: '100%', maxWidth: 360 }}>
|
||||
<Card variant='outlined' sx={{ height: 470, overflow: 'auto', borderRadius: 0 }}>
|
||||
<CardContent sx={{ p: 1 }}>
|
||||
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
|
||||
Available Prompts
|
||||
</Typography>
|
||||
<List component='nav' aria-label='secondary mailbox folder'>
|
||||
{availablePrompNameList.map((item, index) => (
|
||||
<ListItemButton
|
||||
key={item.id}
|
||||
selected={item.id === selectedPrompt?.id}
|
||||
onClick={() => handleListItemClick(index)}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Typography sx={{ fontSize: 16, p: 1, fontWeight: 500 }}>
|
||||
{item.full_name}
|
||||
</Typography>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
marginTop: 5
|
||||
}}
|
||||
>
|
||||
{item.tags.map((tag, index) => (
|
||||
<Chip
|
||||
key={index}
|
||||
label={tag}
|
||||
style={{ marginRight: 5, marginBottom: 5 }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={8} item sx={{ textAlign: 'left' }}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<Card sx={{ height: 470, overflow: 'auto' }}>
|
||||
<CardContent sx={{ p: 0.5 }}>
|
||||
<Accordion
|
||||
expanded={accordionExpanded.includes('prompt')}
|
||||
onChange={handleAccordionChange('prompt')}
|
||||
>
|
||||
<AccordionSummary
|
||||
aria-controls='panel2d-content'
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
id='panel2d-header'
|
||||
>
|
||||
<Typography>Prompt</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography sx={{ wordWrap: 'true' }} color='text.primary'>
|
||||
{selectedPrompt?.detailed?.map((item) => (
|
||||
<>
|
||||
<Typography sx={{ fontSize: 12 }} color='text.secondary' gutterBottom>
|
||||
{item.typeDisplay.toUpperCase()}
|
||||
</Typography>
|
||||
<Typography>
|
||||
<p
|
||||
style={{
|
||||
whiteSpace: 'pre-wrap -moz-pre-wrap -pre-wrap -o-pre-wrap',
|
||||
wordWrap: 'break-word',
|
||||
fontFamily: 'inherit',
|
||||
wordSpacing: '0.1rem',
|
||||
lineHeight: '1.5rem'
|
||||
}}
|
||||
>
|
||||
<NewLineToBr>{item.template}</NewLineToBr>
|
||||
</p>
|
||||
</Typography>
|
||||
</>
|
||||
))}
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<Accordion
|
||||
expanded={accordionExpanded.includes('description')}
|
||||
onChange={handleAccordionChange('description')}
|
||||
>
|
||||
<AccordionSummary
|
||||
aria-controls='panel1d-content'
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
id='panel1d-header'
|
||||
>
|
||||
<Typography>Description</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography
|
||||
sx={{ wordWrap: 'true', wordSpacing: '0.1rem', lineHeight: '1.5rem' }}
|
||||
color='text.primary'
|
||||
>
|
||||
{selectedPrompt?.description}
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<Accordion
|
||||
expanded={accordionExpanded.includes('readme')}
|
||||
onChange={handleAccordionChange('readme')}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls='panel3d-content'
|
||||
id='panel3d-header'
|
||||
>
|
||||
<Typography>Readme</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<div
|
||||
style={{
|
||||
lineHeight: 1.75,
|
||||
'& a': {
|
||||
display: 'block',
|
||||
marginRight: '2.5rem',
|
||||
wordWrap: 'break-word',
|
||||
color: '#16bed7',
|
||||
fontWeight: 500
|
||||
},
|
||||
'& a:hover': { opacity: 0.8 },
|
||||
'& code': {
|
||||
color: '#0ab126',
|
||||
fontWeight: 500,
|
||||
whiteSpace: 'pre-wrap !important'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MemoizedReactMarkdown
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeMathjax, rehypeRaw]}
|
||||
components={{
|
||||
code({ inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
return !inline ? (
|
||||
<CodeBlock
|
||||
key={Math.random()}
|
||||
isDialog={true}
|
||||
language={(match && match[1]) || ''}
|
||||
value={String(children).replace(/\n$/, '')}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<code className={className} {...props}>
|
||||
{children}
|
||||
</code>
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{selectedPrompt?.readme}
|
||||
</MemoizedReactMarkdown>
|
||||
</div>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</DialogContent>
|
||||
{availablePrompNameList && availablePrompNameList.length > 0 && (
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>Cancel</Button>
|
||||
<StyledButton
|
||||
disabled={!selectedPrompt?.detailed}
|
||||
onClick={() => onSubmit(selectedPrompt.detailed)}
|
||||
variant='contained'
|
||||
>
|
||||
Load
|
||||
</StyledButton>
|
||||
</DialogActions>
|
||||
)}
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
PromptLangsmithHubDialog.propTypes = {
|
||||
promptType: PropTypes.string,
|
||||
show: PropTypes.bool,
|
||||
onCancel: PropTypes.func,
|
||||
onSubmit: PropTypes.func
|
||||
}
|
||||
|
||||
export default PromptLangsmithHubDialog
|
||||
@@ -69,7 +69,9 @@ export const FlowListTable = ({ data, images, filterFunction, updateFlowsApi })
|
||||
<Typography
|
||||
sx={{ fontSize: '1.2rem', fontWeight: 500, overflowWrap: 'break-word', whiteSpace: 'pre-line' }}
|
||||
>
|
||||
<Button onClick={() => goToCanvas(row)}>{row.templateName || row.name}</Button>
|
||||
<Button onClick={() => goToCanvas(row)} sx={{ textAlign: 'left' }}>
|
||||
{row.templateName || row.name}
|
||||
</Button>
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell key='1'>
|
||||
@@ -145,8 +147,8 @@ export const FlowListTable = ({ data, images, filterFunction, updateFlowsApi })
|
||||
}
|
||||
|
||||
FlowListTable.propTypes = {
|
||||
data: PropTypes.object,
|
||||
images: PropTypes.array,
|
||||
data: PropTypes.array,
|
||||
images: PropTypes.object,
|
||||
filterFunction: PropTypes.func,
|
||||
updateFlowsApi: PropTypes.object
|
||||
}
|
||||
|
||||
@@ -68,10 +68,14 @@ const AddNodes = ({ nodesData, node }) => {
|
||||
else newNodes.push(vsNode)
|
||||
}
|
||||
delete obj['Vector Stores']
|
||||
obj['Vector Stores;DEPRECATING'] = deprecatingNodes
|
||||
accordianCategories['Vector Stores;DEPRECATING'] = isFilter ? true : false
|
||||
obj['Vector Stores;NEW'] = newNodes
|
||||
accordianCategories['Vector Stores;NEW'] = isFilter ? true : false
|
||||
if (deprecatingNodes.length) {
|
||||
obj['Vector Stores;DEPRECATING'] = deprecatingNodes
|
||||
accordianCategories['Vector Stores;DEPRECATING'] = isFilter ? true : false
|
||||
}
|
||||
if (newNodes.length) {
|
||||
obj['Vector Stores;NEW'] = newNodes
|
||||
accordianCategories['Vector Stores;NEW'] = isFilter ? true : false
|
||||
}
|
||||
setNodes(obj)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useSelector } from 'react-redux'
|
||||
// material-ui
|
||||
import { useTheme, styled } from '@mui/material/styles'
|
||||
import { Box, Typography, Tooltip, IconButton, Button } from '@mui/material'
|
||||
import IconAutoFixHigh from '@mui/icons-material/AutoFixHigh'
|
||||
import { tooltipClasses } from '@mui/material/Tooltip'
|
||||
import { IconArrowsMaximize, IconEdit, IconAlertTriangle } from '@tabler/icons'
|
||||
|
||||
@@ -31,6 +32,7 @@ import { getInputVariables } from 'utils/genericHelper'
|
||||
|
||||
// const
|
||||
import { FLOWISE_CREDENTIAL_ID } from 'store/constant'
|
||||
import PromptLangsmithHubDialog from '../../ui-component/dialog/PromptLangsmithHubDialog'
|
||||
|
||||
const EDITABLE_OPTIONS = ['selectedTool', 'selectedAssistant']
|
||||
|
||||
@@ -56,6 +58,7 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false, isA
|
||||
const [reloadTimestamp, setReloadTimestamp] = useState(Date.now().toString())
|
||||
const [showFormatPromptValuesDialog, setShowFormatPromptValuesDialog] = useState(false)
|
||||
const [formatPromptValuesDialogProps, setFormatPromptValuesDialogProps] = useState({})
|
||||
const [showPromptHubDialog, setShowPromptHubDialog] = useState(false)
|
||||
|
||||
const onExpandDialogClicked = (value, inputParam) => {
|
||||
const dialogProp = {
|
||||
@@ -69,6 +72,17 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false, isA
|
||||
setShowExpandDialog(true)
|
||||
}
|
||||
|
||||
const onShowPromptHubButtonClicked = () => {
|
||||
setShowPromptHubDialog(true)
|
||||
}
|
||||
const onShowPromptHubButtonSubmit = (templates) => {
|
||||
setShowPromptHubDialog(false)
|
||||
for (const t of templates) {
|
||||
if (Object.prototype.hasOwnProperty.call(data.inputs, t.type)) {
|
||||
data.inputs[t.type] = t.template
|
||||
}
|
||||
}
|
||||
}
|
||||
const onFormatPromptValuesClicked = (value, inputParam) => {
|
||||
// Preset values if the field is format prompt values
|
||||
let inputValue = value
|
||||
@@ -209,6 +223,31 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false, isA
|
||||
</CustomWidthTooltip>
|
||||
)}
|
||||
<Box sx={{ p: 2 }}>
|
||||
{(data.name === 'promptTemplate' || data.name === 'chatPromptTemplate') &&
|
||||
(inputParam.name === 'template' || inputParam.name === 'systemMessagePrompt') && (
|
||||
<>
|
||||
<Button
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
width: '100%'
|
||||
}}
|
||||
disabled={disabled}
|
||||
sx={{ borderRadius: 25, width: '100%', mb: 2, mt: 0 }}
|
||||
variant='outlined'
|
||||
onClick={() => onShowPromptHubButtonClicked()}
|
||||
endIcon={<IconAutoFixHigh />}
|
||||
>
|
||||
Langchain Hub
|
||||
</Button>
|
||||
<PromptLangsmithHubDialog
|
||||
promptType={inputParam.name}
|
||||
show={showPromptHubDialog}
|
||||
onCancel={() => setShowPromptHubDialog(false)}
|
||||
onSubmit={onShowPromptHubButtonSubmit}
|
||||
></PromptLangsmithHubDialog>
|
||||
</>
|
||||
)}
|
||||
<div style={{ display: 'flex', flexDirection: 'row' }}>
|
||||
<Typography>
|
||||
{inputParam.label}
|
||||
@@ -260,6 +299,7 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false, isA
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{inputParam.type === 'file' && (
|
||||
<File
|
||||
disabled={disabled}
|
||||
|
||||
@@ -170,7 +170,7 @@ const Canvas = () => {
|
||||
try {
|
||||
await chatflowsApi.deleteChatflow(chatflow.id)
|
||||
localStorage.removeItem(`${chatflow.id}_INTERNAL`)
|
||||
navigate(-1)
|
||||
navigate('/')
|
||||
} catch (error) {
|
||||
const errorData = error.response.data || `${error.response.status}: ${error.response.statusText}`
|
||||
enqueueSnackbar({
|
||||
|
||||
@@ -12,6 +12,7 @@ import ItemCard from 'ui-component/cards/ItemCard'
|
||||
import { gridSpacing } from 'store/constant'
|
||||
import WorkflowEmptySVG from 'assets/images/workflow_empty.svg'
|
||||
import LoginDialog from 'ui-component/dialog/LoginDialog'
|
||||
import ConfirmDialog from 'ui-component/dialog/ConfirmDialog'
|
||||
|
||||
// API
|
||||
import chatflowsApi from 'api/chatflows'
|
||||
@@ -160,7 +161,6 @@ const Chatflows = () => {
|
||||
variant='contained'
|
||||
value='card'
|
||||
title='Card View'
|
||||
selectedColor='#00abc0'
|
||||
>
|
||||
<IconLayoutGrid />
|
||||
</ToggleButton>
|
||||
@@ -212,6 +212,7 @@ const Chatflows = () => {
|
||||
</Stack>
|
||||
)}
|
||||
<LoginDialog show={loginDialogOpen} dialogProps={loginDialogProps} onConfirm={onLoginClick} />
|
||||
<ConfirmDialog />
|
||||
</MainCard>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user