mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 13:00:56 +03:00
Feature/Indexing (#1802)
* indexing * fix for multiple files upsert * fix default Postgres port * fix SQLite node description * add MySQLRecordManager node * fix MySQL unique index * add upsert history * update jsx ui * lint-fix * update dialog details * update llamaindex pinecone --------- Co-authored-by: chungyau97 <chungyau97@gmail.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import SaveChatflowDialog from '@/ui-component/dialog/SaveChatflowDialog'
|
||||
import APICodeDialog from '@/views/chatflows/APICodeDialog'
|
||||
import ViewMessagesDialog from '@/ui-component/dialog/ViewMessagesDialog'
|
||||
import ChatflowConfigurationDialog from '@/ui-component/dialog/ChatflowConfigurationDialog'
|
||||
import UpsertHistoryDialog from '@/views/vectorstore/UpsertHistoryDialog'
|
||||
|
||||
// API
|
||||
import chatflowsApi from '@/api/chatflows'
|
||||
@@ -45,6 +46,8 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
|
||||
const [apiDialogProps, setAPIDialogProps] = useState({})
|
||||
const [viewMessagesDialogOpen, setViewMessagesDialogOpen] = useState(false)
|
||||
const [viewMessagesDialogProps, setViewMessagesDialogProps] = useState({})
|
||||
const [upsertHistoryDialogOpen, setUpsertHistoryDialogOpen] = useState(false)
|
||||
const [upsertHistoryDialogProps, setUpsertHistoryDialogProps] = useState({})
|
||||
const [chatflowConfigurationDialogOpen, setChatflowConfigurationDialogOpen] = useState(false)
|
||||
const [chatflowConfigurationDialogProps, setChatflowConfigurationDialogProps] = useState({})
|
||||
|
||||
@@ -62,6 +65,12 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
|
||||
chatflow: chatflow
|
||||
})
|
||||
setViewMessagesDialogOpen(true)
|
||||
} else if (setting === 'viewUpsertHistory') {
|
||||
setUpsertHistoryDialogProps({
|
||||
title: 'View Upsert History',
|
||||
chatflow: chatflow
|
||||
})
|
||||
setUpsertHistoryDialogOpen(true)
|
||||
} else if (setting === 'chatflowConfiguration') {
|
||||
setChatflowConfigurationDialogProps({
|
||||
title: 'Chatflow Configuration',
|
||||
@@ -387,6 +396,11 @@ const CanvasHeader = ({ chatflow, handleSaveFlow, handleDeleteFlow, handleLoadFl
|
||||
dialogProps={viewMessagesDialogProps}
|
||||
onCancel={() => setViewMessagesDialogOpen(false)}
|
||||
/>
|
||||
<UpsertHistoryDialog
|
||||
show={upsertHistoryDialogOpen}
|
||||
dialogProps={upsertHistoryDialogProps}
|
||||
onCancel={() => setUpsertHistoryDialogOpen(false)}
|
||||
/>
|
||||
<ChatflowConfigurationDialog
|
||||
key='chatflowConfiguration'
|
||||
show={chatflowConfigurationDialogOpen}
|
||||
|
||||
@@ -406,6 +406,10 @@ export const ChatMessage = ({ open, chatflowid, isDialog, previews, setPreviews
|
||||
|
||||
if (response.data) {
|
||||
const data = response.data
|
||||
if (data.executionError) {
|
||||
handleError(data.msg)
|
||||
return
|
||||
}
|
||||
|
||||
setMessages((prevMessages) => {
|
||||
let allMessages = [...cloneDeep(prevMessages)]
|
||||
|
||||
@@ -0,0 +1,453 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useEffect, useState, forwardRef } from 'react'
|
||||
import DatePicker from 'react-datepicker'
|
||||
import moment from 'moment/moment'
|
||||
|
||||
// MUI
|
||||
import {
|
||||
Stack,
|
||||
Box,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
IconButton,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
ListItemButton,
|
||||
Collapse,
|
||||
Accordion,
|
||||
AccordionSummary,
|
||||
Typography,
|
||||
AccordionDetails,
|
||||
Checkbox
|
||||
} from '@mui/material'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
||||
import { IconChevronsUp, IconChevronsDown, IconTrash, IconX } from '@tabler/icons'
|
||||
|
||||
// Project imports
|
||||
import { TableViewOnly } from '@/ui-component/table/Table'
|
||||
import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser'
|
||||
import HistoryEmptySVG from '@/assets/images/upsert_history_empty.svg'
|
||||
|
||||
// Api
|
||||
import vectorstoreApi from '@/api/vectorstore'
|
||||
import useApi from '@/hooks/useApi'
|
||||
|
||||
// Store
|
||||
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
|
||||
import { baseURL } from '@/store/constant'
|
||||
import useNotifier from '@/utils/useNotifier'
|
||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from '@/store/actions'
|
||||
|
||||
const DatePickerCustomInput = forwardRef(function DatePickerCustomInput({ value, onClick }, ref) {
|
||||
return (
|
||||
<ListItemButton style={{ borderRadius: 15, border: '1px solid #e0e0e0' }} onClick={onClick} ref={ref}>
|
||||
{value}
|
||||
</ListItemButton>
|
||||
)
|
||||
})
|
||||
|
||||
DatePickerCustomInput.propTypes = {
|
||||
value: PropTypes.string,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
function UpsertHistoryRow(props) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [nodeConfigExpanded, setNodeConfigExpanded] = useState({})
|
||||
|
||||
const handleAccordionChange = (nodeLabel) => (event, isExpanded) => {
|
||||
const accordianNodes = { ...nodeConfigExpanded }
|
||||
accordianNodes[nodeLabel] = isExpanded
|
||||
setNodeConfigExpanded(accordianNodes)
|
||||
}
|
||||
|
||||
const isItemSelected = props.selected.indexOf(props.upsertHistory.id) !== -1
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableRow sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
|
||||
<TableCell padding='checkbox'>
|
||||
<Checkbox
|
||||
color='primary'
|
||||
checked={isItemSelected}
|
||||
onChange={(event) => props.handleSelect(event, props.upsertHistory.id)}
|
||||
inputProps={{
|
||||
'aria-labelledby': props.upsertHistory.id
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>{moment(props.upsertHistory.date).format('MMMM Do YYYY, h:mm:ss a')}</TableCell>
|
||||
<TableCell>{props.upsertHistory.result?.numAdded ?? '0'}</TableCell>
|
||||
<TableCell>{props.upsertHistory.result?.numUpdated ?? '0'}</TableCell>
|
||||
<TableCell>{props.upsertHistory.result?.numSkipped ?? '0'}</TableCell>
|
||||
<TableCell>{props.upsertHistory.result?.numDeleted ?? '0'}</TableCell>
|
||||
<TableCell>
|
||||
<IconButton aria-label='expand row' size='small' color='inherit' onClick={() => setOpen(!open)}>
|
||||
{open ? <IconChevronsUp /> : <IconChevronsDown />}
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{open && (
|
||||
<TableRow sx={{ '& td': { border: 0 } }}>
|
||||
<TableCell sx={{ pb: 0, pt: 0 }} colSpan={6}>
|
||||
<Collapse in={open} timeout='auto' unmountOnExit>
|
||||
<Box sx={{ mt: 1, mb: 2 }}>
|
||||
{(props.upsertHistory.flowData ?? []).map((node, index) => {
|
||||
return (
|
||||
<Accordion
|
||||
expanded={nodeConfigExpanded[node.id] || false}
|
||||
onChange={handleAccordionChange(node.id)}
|
||||
key={index}
|
||||
disableGutters
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls={`nodes-accordian-${node.name}`}
|
||||
id={`nodes-accordian-header-${node.name}`}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<div
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
marginRight: 10,
|
||||
borderRadius: '50%',
|
||||
backgroundColor: 'white'
|
||||
}}
|
||||
>
|
||||
<img
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: 7,
|
||||
borderRadius: '50%',
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
alt={node.name}
|
||||
src={`${baseURL}/api/v1/node-icon/${node.name}`}
|
||||
/>
|
||||
</div>
|
||||
<Typography variant='h5'>{node.label}</Typography>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
width: 'max-content',
|
||||
borderRadius: 15,
|
||||
background: 'rgb(254,252,191)',
|
||||
padding: 5,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
marginLeft: 10
|
||||
}}
|
||||
>
|
||||
<span style={{ color: 'rgb(116,66,16)', fontSize: '0.825rem' }}>{node.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<TableViewOnly
|
||||
sx={{ minWidth: 150 }}
|
||||
rows={node.paramValues}
|
||||
columns={Object.keys(node.paramValues[0])}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
UpsertHistoryRow.propTypes = {
|
||||
upsertHistory: PropTypes.object,
|
||||
theme: PropTypes.any,
|
||||
isDarkMode: PropTypes.bool,
|
||||
selected: PropTypes.array,
|
||||
handleSelect: PropTypes.func
|
||||
}
|
||||
|
||||
const UpsertHistoryDialog = ({ show, dialogProps, onCancel }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
const dispatch = useDispatch()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
const theme = useTheme()
|
||||
const getUpsertHistoryApi = useApi(vectorstoreApi.getUpsertHistory)
|
||||
|
||||
useNotifier()
|
||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
||||
|
||||
const [chatflowUpsertHistory, setChatflowUpsertHistory] = useState([])
|
||||
const [startDate, setStartDate] = useState(new Date().setMonth(new Date().getMonth() - 1))
|
||||
const [endDate, setEndDate] = useState(new Date())
|
||||
const [selected, setSelected] = useState([])
|
||||
|
||||
const onSelectAllClick = (event) => {
|
||||
if (event.target.checked) {
|
||||
const newSelected = chatflowUpsertHistory.map((n) => n.id)
|
||||
setSelected(newSelected)
|
||||
return
|
||||
}
|
||||
setSelected([])
|
||||
}
|
||||
|
||||
const onStartDateSelected = (date) => {
|
||||
setStartDate(date)
|
||||
getUpsertHistoryApi.request(dialogProps.chatflow.id, {
|
||||
startDate: date,
|
||||
endDate: endDate
|
||||
})
|
||||
}
|
||||
|
||||
const onEndDateSelected = (date) => {
|
||||
setEndDate(date)
|
||||
getUpsertHistoryApi.request(dialogProps.chatflow.id, {
|
||||
endDate: date,
|
||||
startDate: startDate
|
||||
})
|
||||
}
|
||||
|
||||
const handleSelect = (event, id) => {
|
||||
const selectedIndex = selected.indexOf(id)
|
||||
let newSelected = []
|
||||
|
||||
if (selectedIndex === -1) {
|
||||
newSelected = newSelected.concat(selected, id)
|
||||
} else if (selectedIndex === 0) {
|
||||
newSelected = newSelected.concat(selected.slice(1))
|
||||
} else if (selectedIndex === selected.length - 1) {
|
||||
newSelected = newSelected.concat(selected.slice(0, -1))
|
||||
} else if (selectedIndex > 0) {
|
||||
newSelected = newSelected.concat(selected.slice(0, selectedIndex), selected.slice(selectedIndex + 1))
|
||||
}
|
||||
setSelected(newSelected)
|
||||
}
|
||||
|
||||
const handleRemoveHistory = async () => {
|
||||
try {
|
||||
await vectorstoreApi.deleteUpsertHistory(selected)
|
||||
enqueueSnackbar({
|
||||
message: 'Succesfully deleted upsert history',
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'success',
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
setChatflowUpsertHistory(chatflowUpsertHistory.filter((hist) => !selected.includes(hist.id)))
|
||||
setSelected([])
|
||||
} catch (error) {
|
||||
enqueueSnackbar({
|
||||
message: 'Error deleting upsert history',
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'error',
|
||||
persist: true,
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (getUpsertHistoryApi.data) {
|
||||
setChatflowUpsertHistory(getUpsertHistoryApi.data)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getUpsertHistoryApi.data])
|
||||
|
||||
useEffect(() => {
|
||||
if (dialogProps.chatflow) {
|
||||
getUpsertHistoryApi.request(dialogProps.chatflow.id)
|
||||
}
|
||||
|
||||
return () => {
|
||||
setChatflowUpsertHistory([])
|
||||
setStartDate(new Date().setMonth(new Date().getMonth() - 1))
|
||||
setEndDate(new Date())
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dialogProps])
|
||||
|
||||
useEffect(() => {
|
||||
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
|
||||
else dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
}, [show, dispatch])
|
||||
|
||||
const component = show ? (
|
||||
<Dialog
|
||||
onClose={onCancel}
|
||||
open={show}
|
||||
fullWidth
|
||||
maxWidth='lg'
|
||||
aria-labelledby='upsert-history-dialog-title'
|
||||
aria-describedby='upsert-history-dialog-description'
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: '1rem' }} id='upsert-history-dialog-title'>
|
||||
{dialogProps.title}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', width: '100%', marginBottom: 10 }}>
|
||||
<div style={{ marginRight: 10 }}>
|
||||
<b style={{ marginRight: 10 }}>From Date</b>
|
||||
<DatePicker
|
||||
selected={startDate}
|
||||
onChange={(date) => onStartDateSelected(date)}
|
||||
selectsStart
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
customInput={<DatePickerCustomInput />}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ marginRight: 10 }}>
|
||||
<b style={{ marginRight: 10 }}>To Date</b>
|
||||
<DatePicker
|
||||
selected={endDate}
|
||||
onChange={(date) => onEndDateSelected(date)}
|
||||
selectsEnd
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
minDate={startDate}
|
||||
maxDate={new Date()}
|
||||
customInput={<DatePickerCustomInput />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{selected.length > 0 && (
|
||||
<Button
|
||||
sx={{ mt: 1, mb: 2 }}
|
||||
variant='outlined'
|
||||
onClick={handleRemoveHistory}
|
||||
color='error'
|
||||
startIcon={<IconTrash />}
|
||||
>
|
||||
Delete {selected.length} {selected.length === 1 ? 'row' : 'rows'}
|
||||
</Button>
|
||||
)}
|
||||
{chatflowUpsertHistory.length <= 0 && (
|
||||
<Stack sx={{ alignItems: 'center', justifyContent: 'center' }} flexDirection='column'>
|
||||
<Box sx={{ p: 7, height: 'auto' }}>
|
||||
<img
|
||||
style={{ objectFit: 'cover', height: '20vh', width: 'auto' }}
|
||||
src={HistoryEmptySVG}
|
||||
alt='HistoryEmptySVG'
|
||||
/>
|
||||
</Box>
|
||||
<div>No Upsert History Yet</div>
|
||||
</Stack>
|
||||
)}
|
||||
{chatflowUpsertHistory.length > 0 && (
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 650 }} aria-label='simple table'>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding='checkbox'>
|
||||
<Checkbox
|
||||
color='primary'
|
||||
checked={selected.length === chatflowUpsertHistory.length}
|
||||
onChange={onSelectAllClick}
|
||||
inputProps={{
|
||||
'aria-label': 'select all'
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>Date</TableCell>
|
||||
<TableCell>
|
||||
Added{' '}
|
||||
<TooltipWithParser
|
||||
style={{ marginBottom: 2, marginLeft: 10 }}
|
||||
title={'Number of vector embeddings added to Vector Store'}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
Updated{' '}
|
||||
<TooltipWithParser
|
||||
style={{ marginBottom: 2, marginLeft: 10 }}
|
||||
title={
|
||||
'Updated existing vector embeddings. Only works when a Record Manager is connected to the Vector Store'
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
Skipped{' '}
|
||||
<TooltipWithParser
|
||||
style={{ marginBottom: 2, marginLeft: 10 }}
|
||||
title={
|
||||
'Number of same vector embeddings that exists, and were skipped re-upserting again. Only works when a Record Manager is connected to the Vector Store'
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
Deleted{' '}
|
||||
<TooltipWithParser
|
||||
style={{ marginBottom: 2, marginLeft: 10 }}
|
||||
title={
|
||||
'Deleted vector embeddings. Only works when a Record Manager with a Cleanup method is connected to the Vector Store'
|
||||
}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>Details</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{chatflowUpsertHistory.map((upsertHistory, index) => (
|
||||
<UpsertHistoryRow
|
||||
key={index}
|
||||
upsertHistory={upsertHistory}
|
||||
theme={theme}
|
||||
isDarkMode={customization.isDarkMode}
|
||||
selected={selected}
|
||||
handleSelect={handleSelect}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)}
|
||||
</>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>Close</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
UpsertHistoryDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func
|
||||
}
|
||||
|
||||
export default UpsertHistoryDialog
|
||||
@@ -0,0 +1,94 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useDispatch, useSelector } from 'react-redux'
|
||||
import { useEffect } from 'react'
|
||||
import ReactJson from 'flowise-react-json-view'
|
||||
import { Typography, Card, CardContent, Button, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material'
|
||||
import StatsCard from '@/ui-component/cards/StatsCard'
|
||||
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '@/store/actions'
|
||||
|
||||
const UpsertResultDialog = ({ show, dialogProps, onCancel }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
const dispatch = useDispatch()
|
||||
const customization = useSelector((state) => state.customization)
|
||||
|
||||
useEffect(() => {
|
||||
if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
|
||||
else dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
return () => dispatch({ type: HIDE_CANVAS_DIALOG })
|
||||
}, [show, dispatch])
|
||||
|
||||
const component = show ? (
|
||||
<Dialog
|
||||
onClose={onCancel}
|
||||
open={show}
|
||||
fullWidth
|
||||
maxWidth='sm'
|
||||
aria-labelledby='upsert-result-dialog-title'
|
||||
aria-describedby='upsert-result-dialog-description'
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: '1rem' }} id='upsert-result-dialog-title'>
|
||||
Upsert Record
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(4, minmax(0, 1fr))',
|
||||
gap: 5
|
||||
}}
|
||||
>
|
||||
<StatsCard title='Added' stat={dialogProps.numAdded ?? 0} />
|
||||
<StatsCard title='Updated' stat={dialogProps.numUpdated ?? 0} />
|
||||
<StatsCard title='Skipped' stat={dialogProps.numSkipped ?? 0} />
|
||||
<StatsCard title='Deleted' stat={dialogProps.numDeleted ?? 0} />
|
||||
</div>
|
||||
{dialogProps.addedDocs && dialogProps.addedDocs.length > 0 && (
|
||||
<Typography sx={{ mt: 2, mb: 2, fontWeight: 500 }}>{dialogProps.numAdded} Added Documents</Typography>
|
||||
)}
|
||||
{dialogProps.addedDocs &&
|
||||
dialogProps.addedDocs.length > 0 &&
|
||||
dialogProps.addedDocs.map((docs, index) => {
|
||||
return (
|
||||
<Card
|
||||
key={index}
|
||||
sx={{ border: '1px solid #e0e0e0', borderRadius: `${customization.borderRadius}px`, mb: 1 }}
|
||||
>
|
||||
<CardContent>
|
||||
<Typography sx={{ fontSize: 14 }} color='text.primary' gutterBottom>
|
||||
{docs.pageContent}
|
||||
</Typography>
|
||||
<ReactJson
|
||||
theme={customization.isDarkMode ? 'ocean' : 'rjv-default'}
|
||||
style={{ padding: 10, borderRadius: 10 }}
|
||||
src={docs.metadata}
|
||||
name={null}
|
||||
quotesOnKeys={false}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
collapsed={true}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel}>Close</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
UpsertResultDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func,
|
||||
onSave: PropTypes.func
|
||||
}
|
||||
|
||||
export default UpsertResultDialog
|
||||
@@ -78,7 +78,7 @@ function a11yProps(index) {
|
||||
}
|
||||
}
|
||||
|
||||
const VectorStoreDialog = ({ show, dialogProps, onCancel }) => {
|
||||
const VectorStoreDialog = ({ show, dialogProps, onCancel, onIndexResult }) => {
|
||||
const portalElement = document.getElementById('portal')
|
||||
const { reactFlowInstance } = useContext(flowContext)
|
||||
const dispatch = useDispatch()
|
||||
@@ -276,7 +276,7 @@ query(formData).then((response) => {
|
||||
const onUpsertClicked = async (vectorStoreNode) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
await vectorstoreApi.upsertVectorStore(dialogProps.chatflowid, { stopNodeId: vectorStoreNode.data.id })
|
||||
const res = await vectorstoreApi.upsertVectorStore(dialogProps.chatflowid, { stopNodeId: vectorStoreNode.data.id })
|
||||
enqueueSnackbar({
|
||||
message: 'Succesfully upserted vector store. You can start chatting now!',
|
||||
options: {
|
||||
@@ -290,6 +290,7 @@ query(formData).then((response) => {
|
||||
}
|
||||
})
|
||||
setLoading(false)
|
||||
if (res && res.data && typeof res.data === 'object') onIndexResult(res.data)
|
||||
} catch (error) {
|
||||
enqueueSnackbar({
|
||||
message: error.response.data.message,
|
||||
@@ -549,7 +550,8 @@ query(formData).then((response) => {
|
||||
VectorStoreDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func
|
||||
onCancel: PropTypes.func,
|
||||
onIndexResult: PropTypes.func
|
||||
}
|
||||
|
||||
export default VectorStoreDialog
|
||||
|
||||
@@ -1,33 +1,19 @@
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { Button } from '@mui/material'
|
||||
import { IconDatabaseImport, IconX } from '@tabler/icons'
|
||||
|
||||
// project import
|
||||
import { StyledFab } from '@/ui-component/button/StyledFab'
|
||||
import VectorStoreDialog from './VectorStoreDialog'
|
||||
|
||||
// api
|
||||
import vectorstoreApi from '@/api/vectorstore'
|
||||
|
||||
// Hooks
|
||||
import useNotifier from '@/utils/useNotifier'
|
||||
|
||||
// Const
|
||||
import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction } from '@/store/actions'
|
||||
import UpsertResultDialog from './UpsertResultDialog'
|
||||
|
||||
export const VectorStorePopUp = ({ chatflowid }) => {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
useNotifier()
|
||||
const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args))
|
||||
const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args))
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const [showExpandDialog, setShowExpandDialog] = useState(false)
|
||||
const [expandDialogProps, setExpandDialogProps] = useState({})
|
||||
const [showUpsertResultDialog, setShowUpsertResultDialog] = useState(false)
|
||||
const [upsertResultDialogProps, setUpsertResultDialogProps] = useState({})
|
||||
|
||||
const anchorRef = useRef(null)
|
||||
const prevOpen = useRef(open)
|
||||
@@ -43,38 +29,6 @@ export const VectorStorePopUp = ({ chatflowid }) => {
|
||||
setShowExpandDialog(true)
|
||||
}
|
||||
|
||||
const onUpsert = async () => {
|
||||
try {
|
||||
await vectorstoreApi.upsertVectorStore(chatflowid, {})
|
||||
enqueueSnackbar({
|
||||
message: 'Succesfully upserted vector store',
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'success',
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
enqueueSnackbar({
|
||||
message: error.response.data.message,
|
||||
options: {
|
||||
key: new Date().getTime() + Math.random(),
|
||||
variant: 'error',
|
||||
persist: true,
|
||||
action: (key) => (
|
||||
<Button style={{ color: 'white' }} onClick={() => closeSnackbar(key)}>
|
||||
<IconX />
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (prevOpen.current === true && open === false) {
|
||||
anchorRef.current.focus()
|
||||
@@ -100,12 +54,24 @@ export const VectorStorePopUp = ({ chatflowid }) => {
|
||||
<VectorStoreDialog
|
||||
show={showExpandDialog}
|
||||
dialogProps={expandDialogProps}
|
||||
onUpsert={onUpsert}
|
||||
onCancel={() => {
|
||||
setShowExpandDialog(false)
|
||||
setOpen((prevopen) => !prevopen)
|
||||
}}
|
||||
onIndexResult={(indexRes) => {
|
||||
setShowExpandDialog(false)
|
||||
setShowUpsertResultDialog(true)
|
||||
setUpsertResultDialogProps({ ...indexRes })
|
||||
}}
|
||||
></VectorStoreDialog>
|
||||
<UpsertResultDialog
|
||||
show={showUpsertResultDialog}
|
||||
dialogProps={upsertResultDialogProps}
|
||||
onCancel={() => {
|
||||
setShowUpsertResultDialog(false)
|
||||
setOpen(false)
|
||||
}}
|
||||
></UpsertResultDialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user