Feature/DocumentStore (#2106)

* datasource: initial commit

* datasource: datasource details and chunks

* datasource: Document Store Node

* more changes

* Document Store - Base functionality

* Document Store Loader Component

* Document Store Loader Component

* before merging the modularity PR

* after merging the modularity PR

* preview mode

* initial draft PR

* fixes

* minor updates and  fixes

* preview with loader and splitter

* preview with credential

* show stored chunks

* preview update...

* edit config

* save, preview and other changes

* save, preview and other changes

* save, process and other changes

* save, process and other changes

* alpha1 - for internal testing

* rerouting urls

* bug fix on new leader create

* pagination support for chunks

* delete document store

* Update pnpm-lock.yaml

* doc store card view

* Update store files to use updated storage functions, Document Store Table View and other changes

* ui changes

* add expanded chunk dialog, improve ui

* change throw Error to InternalError

* Bug Fixes and removal of subFolder, adding of view chunks for store

* lint fixes

* merge changes

* DocumentStoreStatus component

* ui changes for doc store

* add remove metadata key field, add custom document loader

* add chatflows used doc store chips

* add types/interfaces to DocumentStore Services

* document loader list dialog title bar color change

* update interfaces

* Whereused Chatflow Name and Added chunkNo to retain order of created chunks.

* use typeorm order chunkNo, ui changes

---------

Co-authored-by: Henry <hzj94@hotmail.com>
Co-authored-by: Henry Heng <henryheng@flowiseai.com>
This commit is contained in:
Vinod Kiran
2024-05-06 19:53:27 +05:30
committed by GitHub
parent af4e28aa91
commit 40e36d1b39
91 changed files with 38713 additions and 32791 deletions
@@ -0,0 +1,191 @@
import PropTypes from 'prop-types'
import { useSelector } from 'react-redux'
// material-ui
import { styled } from '@mui/material/styles'
import { Box, Grid, Typography, useTheme } from '@mui/material'
import { IconVectorBezier2, IconLanguage, IconScissors } from '@tabler/icons'
// project imports
import MainCard from '@/ui-component/cards/MainCard'
import DocumentStoreStatus from '@/views/docstore/DocumentStoreStatus'
import { kFormatter } from '@/utils/genericHelper'
const CardWrapper = styled(MainCard)(({ theme }) => ({
background: theme.palette.card.main,
color: theme.darkTextPrimary,
overflow: 'auto',
position: 'relative',
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 8%)',
cursor: 'pointer',
'&:hover': {
background: theme.palette.card.hover,
boxShadow: '0 2px 14px 0 rgb(32 40 45 / 20%)'
},
height: '100%',
minHeight: '160px',
maxHeight: '300px',
width: '100%',
overflowWrap: 'break-word',
whiteSpace: 'pre-line'
}))
// ===========================|| DOC STORE CARD ||=========================== //
const DocumentStoreCard = ({ data, images, onClick }) => {
const theme = useTheme()
const customization = useSelector((state) => state.customization)
return (
<CardWrapper content={false} onClick={onClick} sx={{ border: 1, borderColor: theme.palette.grey[900] + 25, borderRadius: 2 }}>
<Box sx={{ height: '100%', p: 2.25 }}>
<Grid container justifyContent='space-between' direction='column' sx={{ height: '100%' }} gap={2}>
<Box display='flex' flexDirection='column' sx={{ flex: 1, width: '100%' }}>
<div
style={{
width: '100%',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
overflow: 'hidden'
}}
>
<Typography
sx={{
display: '-webkit-box',
fontSize: '1.25rem',
fontWeight: 500,
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
textOverflow: 'ellipsis',
overflow: 'hidden',
flex: 1
}}
>
{data.name}
</Typography>
<DocumentStoreStatus status={data.status} />
</div>
<span
style={{
display: '-webkit-box',
marginTop: 10,
overflowWrap: 'break-word',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
textOverflow: 'ellipsis',
overflow: 'hidden'
}}
>
{data.description || ' '}
</span>
</Box>
<Grid container columnGap={2} rowGap={1}>
<div
style={{
paddingLeft: '7px',
paddingRight: '7px',
paddingTop: '3px',
paddingBottom: '3px',
fontSize: '11px',
width: 'max-content',
borderRadius: '25px',
boxShadow: customization.isDarkMode
? '0 2px 14px 0 rgb(255 255 255 / 20%)'
: '0 2px 14px 0 rgb(32 40 45 / 20%)',
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
}}
>
<IconVectorBezier2 style={{ marginRight: 5 }} size={15} />
{data.whereUsed?.length ?? 0} {data.whereUsed?.length <= 1 ? 'flow' : 'flows'}
</div>
<div
style={{
paddingLeft: '7px',
paddingRight: '7px',
paddingTop: '3px',
paddingBottom: '3px',
fontSize: '11px',
width: 'max-content',
borderRadius: '25px',
boxShadow: customization.isDarkMode
? '0 2px 14px 0 rgb(255 255 255 / 20%)'
: '0 2px 14px 0 rgb(32 40 45 / 20%)',
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
}}
>
<IconLanguage style={{ marginRight: 5 }} size={15} />
{kFormatter(data.totalChars ?? 0)} chars
</div>
<div
style={{
paddingLeft: '7px',
paddingRight: '7px',
paddingTop: '3px',
paddingBottom: '3px',
fontSize: '11px',
width: 'max-content',
borderRadius: '25px',
boxShadow: customization.isDarkMode
? '0 2px 14px 0 rgb(255 255 255 / 20%)'
: '0 2px 14px 0 rgb(32 40 45 / 20%)',
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
}}
>
<IconScissors style={{ marginRight: 5 }} size={15} />
{kFormatter(data.totalChunks ?? 0)} chunks
</div>
</Grid>
{images && images.length > 0 && (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'start',
gap: 1
}}
>
{images.slice(0, images.length > 3 ? 3 : images.length).map((img) => (
<Box
key={img}
sx={{
width: 30,
height: 30,
borderRadius: '50%',
backgroundColor: customization.isDarkMode
? theme.palette.common.white
: theme.palette.grey[300] + 75
}}
>
<img style={{ width: '100%', height: '100%', padding: 5, objectFit: 'contain' }} alt='' src={img} />
</Box>
))}
{images.length > 3 && (
<Typography sx={{ alignItems: 'center', display: 'flex', fontSize: '.9rem', fontWeight: 200 }}>
+ {images.length - 3} More
</Typography>
)}
</Box>
)}
</Grid>
</Box>
</CardWrapper>
)
}
DocumentStoreCard.propTypes = {
data: PropTypes.object,
images: PropTypes.array,
onClick: PropTypes.func
}
export default DocumentStoreCard
@@ -144,7 +144,6 @@ const ItemCard = ({ data, images, onClick }) => {
}
ItemCard.propTypes = {
isLoading: PropTypes.bool,
data: PropTypes.object,
images: PropTypes.array,
onClick: PropTypes.func
@@ -23,7 +23,7 @@ const StatsCard = ({ title, stat }) => {
StatsCard.propTypes = {
title: PropTypes.string,
stat: PropTypes.string
stat: PropTypes.string | PropTypes.number
}
export default StatsCard
@@ -113,7 +113,7 @@ const ExpandTextDialog = ({ show, dialogProps, onCancel, onConfirm }) => {
lang={languageType}
placeholder={inputParam.placeholder}
basicSetup={
languageType === 'json'
languageType !== 'js'
? { lineNumbers: false, foldGutter: false, autocompletion: false, highlightActiveLine: false }
: {}
}
@@ -161,7 +161,7 @@ const ManageScrapedLinksDialog = ({ show, dialogProps, onCancel, onSave }) => {
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1.5 }}>
<Typography sx={{ fontWeight: 500 }}>Scraped Links</Typography>
{selectedLinks.length > 0 ? (
<StyledButton
<Button
sx={{ height: 'max-content', width: 'max-content' }}
variant='outlined'
color='error'
@@ -170,7 +170,7 @@ const ManageScrapedLinksDialog = ({ show, dialogProps, onCancel, onSave }) => {
startIcon={<IconEraser />}
>
Clear All
</StyledButton>
</Button>
) : null}
</Box>
<>
@@ -22,7 +22,7 @@ export const MultiDropdown = ({ name, value, options, onSelect, formControlSx =
const customization = useSelector((state) => state.customization)
const findMatchingOptions = (options = [], internalValue) => {
let values = []
if (internalValue && typeof internalValue === 'string') values = JSON.parse(internalValue)
if ('choose an option' !== internalValue && internalValue && typeof internalValue === 'string') values = JSON.parse(internalValue)
else values = internalValue
return options.filter((option) => values.includes(option.name))
}
@@ -5,8 +5,21 @@ import { json } from '@codemirror/lang-json'
import { vscodeDark } from '@uiw/codemirror-theme-vscode'
import { sublime } from '@uiw/codemirror-theme-sublime'
import { EditorView } from '@codemirror/view'
import { useTheme } from '@mui/material/styles'
export const CodeEditor = ({
value,
height,
theme,
lang,
placeholder,
disabled = false,
autoFocus = false,
basicSetup = {},
onValueChange
}) => {
const colorTheme = useTheme()
export const CodeEditor = ({ value, height, theme, lang, placeholder, disabled = false, basicSetup = {}, onValueChange }) => {
const customStyle = EditorView.baseTheme({
'&': {
color: '#191b1f',
@@ -14,7 +27,18 @@ export const CodeEditor = ({ value, height, theme, lang, placeholder, disabled =
},
'.cm-placeholder': {
color: 'rgba(120, 120, 120, 0.5)'
}
},
'.cm-content':
lang !== 'js'
? {
fontFamily: 'Roboto, sans-serif',
fontSize: '0.95rem',
letterSpacing: '0em',
fontWeight: 400,
lineHeight: '1.5em',
color: colorTheme.darkTextPrimary
}
: {}
})
return (
@@ -31,6 +55,8 @@ export const CodeEditor = ({ value, height, theme, lang, placeholder, disabled =
onChange={onValueChange}
readOnly={disabled}
editable={!disabled}
// eslint-disable-next-line
autoFocus={autoFocus}
basicSetup={basicSetup}
/>
)
@@ -43,6 +69,7 @@ CodeEditor.propTypes = {
lang: PropTypes.string,
placeholder: PropTypes.string,
disabled: PropTypes.bool,
autoFocus: PropTypes.bool,
basicSetup: PropTypes.object,
onValueChange: PropTypes.func
}
@@ -0,0 +1,105 @@
import { styled } from '@mui/material/styles'
import Box from '@mui/material/Box'
import Slider from '@mui/material/Slider'
import { Grid, Input } from '@mui/material'
import PropTypes from 'prop-types'
const BoxShadow = '0 3px 1px rgba(0,0,0,0.1),0 4px 8px rgba(0,0,0,0.13),0 0 0 1px rgba(0,0,0,0.02)'
const CustomInputSlider = styled(Slider)(({ theme }) => ({
color: theme.palette.mode === 'dark' ? '#0a84ff' : '#007bff',
height: 5,
padding: '15px 0',
'& .MuiSlider-thumb': {
height: 20,
width: 20,
backgroundColor: '#333',
boxShadow: '0 0 2px 0px rgba(0, 0, 0, 0.1)',
'&:focus, &:hover, &.Mui-active': {
boxShadow: '0px 0px 3px 1px rgba(0, 0, 0, 0.1)',
// Reset on touch devices, it doesn't add specificity
'@media (hover: none)': {
boxShadow: BoxShadow
}
},
'&:before': {
boxShadow: '0px 0px 1px 0px rgba(0,0,0,0.2), 0px 0px 0px 0px rgba(0,0,0,0.14), 0px 0px 1px 0px rgba(0,0,0,0.12)'
}
},
'& .MuiSlider-valueLabel': {
fontSize: 12,
fontWeight: 'normal',
top: -1,
backgroundColor: 'unset',
color: theme.palette.text.primary,
'&::before': {
display: 'none'
},
'& *': {
background: 'transparent',
color: theme.palette.mode === 'dark' ? '#000' : '#000'
}
},
'& .MuiSlider-track': {
border: 'none',
height: 5
},
'& .MuiSlider-rail': {
opacity: 0.5,
boxShadow: 'inset 0px 0px 4px -2px #000',
backgroundColor: '#d0d0d0'
}
}))
export const InputSlider = ({ value, onChange }) => {
const handleSliderChange = (event, newValue) => onChange(newValue)
const handleInputChange = (event) => {
onChange(event.target.value === '' ? 0 : Number(event.target.value))
}
const handleBlur = () => {
if (value < 0) {
onChange(0)
}
}
return (
<Box sx={{ width: '100%' }}>
<Grid container spacing={2} sx={{ mt: 1 }} alignItems='center'>
<Grid item xs>
<CustomInputSlider
value={typeof value === 'number' ? value : 0}
onChange={handleSliderChange}
valueLabelDisplay='on'
aria-labelledby='input-slider'
step={10}
min={0}
max={5000}
/>
</Grid>
<Grid item>
<Input
sx={{ ml: 3, mr: 3 }}
value={value}
size='small'
onChange={handleInputChange}
onBlur={handleBlur}
inputProps={{
step: 10,
min: 0,
max: 10000,
type: 'number',
'aria-labelledby': 'input-slider'
}}
/>
</Grid>
</Grid>
</Box>
)
}
InputSlider.propTypes = {
value: PropTypes.number,
onChange: PropTypes.func
}