update UI

This commit is contained in:
Henry
2023-12-05 03:01:13 +00:00
parent acac9ee68d
commit 264a571abf
3 changed files with 368 additions and 225 deletions
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

@@ -1,6 +1,14 @@
import { createPortal } from 'react-dom' import { createPortal } from 'react-dom'
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import PropTypes from 'prop-types' 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 { import {
Box, Box,
Button, Button,
@@ -10,7 +18,7 @@ import {
DialogActions, DialogActions,
DialogContent, DialogContent,
DialogTitle, DialogTitle,
Divider, Chip,
Grid, Grid,
InputLabel, InputLabel,
List, List,
@@ -18,23 +26,30 @@ import {
ListItemText, ListItemText,
OutlinedInput, OutlinedInput,
Select, Select,
Typography Typography,
Stack,
IconButton,
FormControl,
Checkbox,
MenuItem
} from '@mui/material' } from '@mui/material'
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from '../../store/actions'
import { useDispatch } from 'react-redux'
import FormControl from '@mui/material/FormControl'
import Checkbox from '@mui/material/Checkbox'
import MenuItem from '@mui/material/MenuItem'
import ReactMarkdown from 'react-markdown'
import CredentialInputHandler from '../../views/canvas/CredentialInputHandler'
import promptApi from '../../api/prompt'
import { StyledButton } from '../button/StyledButton'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import { styled } from '@mui/material/styles'
import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp'
import MuiAccordion from '@mui/material/Accordion' import MuiAccordion from '@mui/material/Accordion'
import MuiAccordionSummary from '@mui/material/AccordionSummary' import MuiAccordionSummary from '@mui/material/AccordionSummary'
import MuiAccordionDetails from '@mui/material/AccordionDetails' 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 CredentialInputHandler from 'views/canvas/CredentialInputHandler'
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 promptApi from 'api/prompt'
import { HIDE_CANVAS_DIALOG, SHOW_CANVAS_DIALOG } from 'store/actions'
const NewLineToBr = ({ children = '' }) => { const NewLineToBr = ({ children = '' }) => {
return children.split('\n').reduce(function (arr, line) { return children.split('\n').reduce(function (arr, line) {
@@ -73,6 +88,7 @@ const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
const PromptLangsmithHubDialog = ({ promptType, show, onCancel, onSubmit }) => { const PromptLangsmithHubDialog = ({ promptType, show, onCancel, onSubmit }) => {
const portalElement = document.getElementById('portal') const portalElement = document.getElementById('portal')
const dispatch = useDispatch() const dispatch = useDispatch()
const customization = useSelector((state) => state.customization)
useEffect(() => { useEffect(() => {
if (show) dispatch({ type: SHOW_CANVAS_DIALOG }) if (show) dispatch({ type: SHOW_CANVAS_DIALOG })
@@ -141,19 +157,24 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel, onSubmit }) => {
const [selectedPrompt, setSelectedPrompt] = useState({}) const [selectedPrompt, setSelectedPrompt] = useState({})
const [credentialId, setCredentialId] = useState('') const [credentialId, setCredentialId] = useState('')
const [accordionExpanded, setAccordionExpanded] = useState('panel2') const [accordionExpanded, setAccordionExpanded] = useState(['prompt'])
const handleAccordionChange = (panel) => { const handleAccordionChange = (accordionName) => (event, isExpanded) => {
setAccordionExpanded(panel) const accordians = [...accordionExpanded]
if (!isExpanded) setAccordionExpanded(accordians.filter((accr) => accr !== accordionName))
else {
accordians.push(accordionName)
setAccordionExpanded(accordians)
}
} }
const handleListItemClick = async (event, index) => { const handleListItemClick = async (index, overridePromptNameList = []) => {
const prompt = availablePrompNameList[index] const prompt = overridePromptNameList.length ? overridePromptNameList[index] : availablePrompNameList[index]
if (!prompt.detailed) { if (!prompt.detailed) {
const createResp = await promptApi.getPrompt({ const createResp = await promptApi.getPrompt({
credential: credentialId, credential: credentialId,
promptName: selectedPrompt.full_name promptName: prompt.full_name
}) })
if (createResp.data) { if (createResp.data) {
prompt.detailed = createResp.data.templates prompt.detailed = createResp.data.templates
@@ -180,6 +201,7 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel, onSubmit }) => {
}) })
if (createResp.data) { if (createResp.data) {
setAvailablePrompNameList(createResp.data.repos) setAvailablePrompNameList(createResp.data.repos)
if (createResp.data.repos?.length) handleListItemClick(0, createResp.data.repos)
} }
} }
@@ -217,12 +239,21 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel, onSubmit }) => {
setLanguage(removeDuplicates(value)) setLanguage(removeDuplicates(value))
} }
const clear = () => {
setModelName([])
setUsecase([])
setLanguage([])
setSelectedPrompt({})
setAvailablePrompNameList([])
setAccordionExpanded(['prompt'])
}
const component = show ? ( const component = show ? (
<Dialog <Dialog
onClose={onCancel} onClose={onCancel}
open={show} open={show}
fullWidth fullWidth
maxWidth='lg' maxWidth={credentialId ? 'lg' : 'sm'}
aria-labelledby='prompt-dialog-title' aria-labelledby='prompt-dialog-title'
aria-describedby='prompt-dialog-description' aria-describedby='prompt-dialog-description'
> >
@@ -230,12 +261,12 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel, onSubmit }) => {
Load Prompts from Langsmith Hub ({promptType === 'template' ? 'PromptTemplate' : 'ChatPromptTemplate'}) Load Prompts from Langsmith Hub ({promptType === 'template' ? 'PromptTemplate' : 'ChatPromptTemplate'})
</DialogTitle> </DialogTitle>
<DialogContent dividers sx={{ p: 1 }}> <DialogContent dividers sx={{ p: 1 }}>
<Box sx={{ width: '100%' }} style={{ display: 'flex', flexDirection: 'row', p: 1, m: 0 }}> <Box sx={{ width: credentialId ? '40%' : '100%', display: 'flex', flexDirection: 'row', p: 2, alignItems: 'center' }}>
<Typography style={{ alignSelf: 'center' }} sx={{ mr: 5 }} variant='overline'> <Typography sx={{ mr: 2 }}>
Langsmith Credential Langsmith Credential &nbsp;
<span style={{ color: 'red' }}>*</span> <span style={{ color: 'red' }}>*</span>
</Typography> </Typography>
<FormControl style={{ width: '30%' }} xs={4} sx={{ m: 1 }}> <FormControl sx={{ flex: 1 }}>
<CredentialInputHandler <CredentialInputHandler
size='small' size='small'
sx={{ flexGrow: 1 }} sx={{ flexGrow: 1 }}
@@ -249,216 +280,327 @@ const PromptLangsmithHubDialog = ({ promptType, show, onCancel, onSubmit }) => {
}} }}
onSelect={(newValue) => { onSelect={(newValue) => {
setCredentialId(newValue) setCredentialId(newValue)
if (!newValue) clear()
}} }}
/> />
</FormControl> </FormControl>
</Box> </Box>
<Box sx={{ width: '100%' }} style={{ display: 'flex', flexDirection: 'row' }}> {credentialId && (
<FormControl <Box sx={{ display: 'flex', flexDirection: 'row', p: 2, pt: 1, alignItems: 'center' }}>
style={{ <FormControl sx={{ mr: 1, width: '30%' }}>
width: '30%' <InputLabel size='small' id='model-checkbox-label'>
}} Model
xs={4} </InputLabel>
sx={{ m: 1 }} <Select
> disabled={!credentialId}
<InputLabel size='small' id='model-checkbox-label'> id='model-checkbox'
Model labelId='model-checkbox-label'
</InputLabel> multiple
<Select size='small'
disabled={!credentialId} value={modelName}
id='model-checkbox' onChange={handleModelChange}
labelId='model-checkbox-label' input={<OutlinedInput label='Model' />}
multiple renderValue={(selected) => selected.map((x) => x.name).join(', ')}
size='small' endAdornment={
value={modelName} modelName.length ? (
onChange={handleModelChange} <IconButton sx={{ mr: 2 }} onClick={() => setModelName([])}>
input={<OutlinedInput label='Model' />} <ClearIcon style={{ width: 20, height: 20 }} />
renderValue={(selected) => selected.map((x) => x.name).join(', ')} </IconButton>
MenuProps={MenuProps} ) : (
> false
{models.map((variant) => ( )
<MenuItem key={variant.id} value={variant}> }
<Checkbox id={variant.id} checked={modelName.findIndex((item) => item.id === variant.id) >= 0} /> sx={{
<ListItemText primary={variant.name} /> '.MuiSvgIcon-root ': {
</MenuItem> fill: customization.isDarkMode ? 'white !important' : ''
))} }
</Select> }}
</FormControl> MenuProps={MenuProps}
<FormControl >
xs={4} {models.map((variant) => (
style={{ <MenuItem key={variant.id} value={variant}>
width: '30%' <Checkbox id={variant.id} checked={modelName.findIndex((item) => item.id === variant.id) >= 0} />
}} <ListItemText primary={variant.name} />
sx={{ m: 1 }} </MenuItem>
> ))}
<InputLabel size='small' id='usecase-checkbox-label'> </Select>
Usecase </FormControl>
</InputLabel> <FormControl sx={{ mr: 1, width: '30%' }}>
<Select <InputLabel size='small' id='usecase-checkbox-label'>
autoWidth={false} Usecase
disabled={!credentialId} </InputLabel>
labelId='usecase-checkbox-label' <Select
id='usecase-checkbox' autoWidth={false}
multiple disabled={!credentialId}
size='small' labelId='usecase-checkbox-label'
value={usecase} id='usecase-checkbox'
onChange={handleUsecaseChange} multiple
input={<OutlinedInput label='Usecase' />} size='small'
renderValue={(selected) => selected.map((x) => x.name).join(', ')} value={usecase}
MenuProps={MenuProps} onChange={handleUsecaseChange}
> input={<OutlinedInput label='Usecase' />}
{usecases.map((variant) => ( renderValue={(selected) => selected.map((x) => x.name).join(', ')}
<MenuItem key={variant.id} value={variant}> endAdornment={
<Checkbox id={variant.id} checked={usecase.findIndex((item) => item.id === variant.id) >= 0} /> usecase.length ? (
<ListItemText primary={variant.name} /> <IconButton sx={{ mr: 2 }} onClick={() => setUsecase([])}>
</MenuItem> <ClearIcon style={{ width: 20, height: 20 }} />
))} </IconButton>
</Select> ) : (
</FormControl> false
<FormControl )
style={{ }
width: '25%' sx={{
}} '.MuiSvgIcon-root ': {
xs={3} fill: customization.isDarkMode ? 'white !important' : ''
sx={{ m: 1 }} }
> }}
<InputLabel size='small' id='language-checkbox-label'> MenuProps={MenuProps}
Language >
</InputLabel> {usecases.map((variant) => (
<Select <MenuItem key={variant.id} value={variant}>
labelId='language-checkbox-label' <Checkbox id={variant.id} checked={usecase.findIndex((item) => item.id === variant.id) >= 0} />
id='language-checkbox' <ListItemText primary={variant.name} />
disabled={!credentialId} </MenuItem>
multiple ))}
size='small' </Select>
value={language} </FormControl>
onChange={handleLanguageChange} <FormControl sx={{ mr: 1, width: '30%' }}>
input={<OutlinedInput label='language' />} <InputLabel size='small' id='language-checkbox-label'>
renderValue={(selected) => selected.map((x) => x.name).join(', ')} Language
MenuProps={MenuProps} </InputLabel>
> <Select
{languages.map((variant) => ( labelId='language-checkbox-label'
<MenuItem key={variant.id} value={variant}> id='language-checkbox'
<Checkbox id={variant.id} checked={language.findIndex((item) => item.id === variant.id) >= 0} /> disabled={!credentialId}
<ListItemText primary={variant.name} /> multiple
</MenuItem> size='small'
))} value={language}
</Select> onChange={handleLanguageChange}
</FormControl> input={<OutlinedInput label='language' />}
<FormControl renderValue={(selected) => selected.map((x) => x.name).join(', ')}
style={{ endAdornment={
width: '5%' language.length ? (
}} <IconButton sx={{ mr: 2 }} onClick={() => setLanguage([])}>
xs={1} <ClearIcon style={{ width: 20, height: 20 }} />
sx={{ m: 1 }} </IconButton>
> ) : (
<Button disableElevation variant='outlined' onClick={fetchPrompts}> false
Fetch )
</Button> }
</FormControl> sx={{
</Box> '.MuiSvgIcon-root ': {
<Divider sx={{ mb: 2 }} /> fill: customization.isDarkMode ? 'white !important' : ''
<Grid xs={12} container spacing={1} justifyContent='center' alignItems='center'> }
<Grid xs={4} item style={{ textAlign: 'left' }}> }}
<Box sx={{ width: '100%', maxWidth: 360 }}> MenuProps={MenuProps}
<Card variant='outlined' style={{ height: 470, overflow: 'auto', borderRadius: 0 }}> >
<CardContent sx={{ p: 1 }}> {languages.map((variant) => (
<Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom> <MenuItem key={variant.id} value={variant}>
Available Prompts <Checkbox id={variant.id} checked={language.findIndex((item) => item.id === variant.id) >= 0} />
</Typography> <ListItemText primary={variant.name} />
<List component='nav' aria-label='secondary mailbox folder'> </MenuItem>
{availablePrompNameList.map((item, index) => ( ))}
<ListItemButton </Select>
key={item.id} </FormControl>
selected={item.id === selectedPrompt?.id} <FormControl sx={{ width: '10%' }}>
onClick={(event) => handleListItemClick(event, index)} <Button disableElevation variant='outlined' onClick={fetchPrompts}>
> Search
<ListItemText>{item.full_name}</ListItemText> </Button>
</ListItemButton> </FormControl>
))} </Box>
</List> )}
</CardContent> {availablePrompNameList && availablePrompNameList.length == 0 && (
</Card> <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> </Box>
</Grid> <div>No Available Prompts</div>
<Grid xs={8} item style={{ textAlign: 'left' }}> </Stack>
<Box sx={{ width: '100%' }} style={{ display: 'flex', flexDirection: 'column' }}> )}
<Card variant='outlined' style={{ height: 470, overflow: 'auto', borderRadius: 0 }}> {availablePrompNameList && availablePrompNameList.length > 0 && (
<CardContent sx={{ p: 0.5 }}> <Stack sx={{ alignItems: 'center', justifyContent: 'center', width: '100%' }} flexDirection='column'>
<Accordion expanded={accordionExpanded === 'panel1'} onChange={() => handleAccordionChange('panel1')}> <Box sx={{ width: '100%', p: 2 }}>
<AccordionSummary <Grid xs={12} container spacing={1} justifyContent='center' alignItems='center'>
aria-controls='panel1d-content' <Grid xs={4} item sx={{ textAlign: 'left' }}>
expandIcon={<ExpandMoreIcon />} <Box sx={{ width: '100%', maxWidth: 360 }}>
id='panel1d-header' <Card variant='outlined' sx={{ height: 470, overflow: 'auto', borderRadius: 0 }}>
> <CardContent sx={{ p: 1 }}>
<Typography>Description</Typography> <Typography sx={{ fontSize: 10 }} color='text.secondary' gutterBottom>
</AccordionSummary> Available Prompts
<AccordionDetails> </Typography>
<Typography sx={{ fontSize: 12, wordWrap: 'true' }} color='text.primary'> <List component='nav' aria-label='secondary mailbox folder'>
{selectedPrompt?.description} {availablePrompNameList.map((item, index) => (
</Typography> <ListItemButton
</AccordionDetails> key={item.id}
</Accordion> selected={item.id === selectedPrompt?.id}
<Accordion expanded={accordionExpanded === 'panel2'} onChange={() => handleAccordionChange('panel2')}> onClick={() => handleListItemClick(index)}
<AccordionSummary >
aria-controls='panel2d-content' <div style={{ display: 'flex', flexDirection: 'column' }}>
expandIcon={<ExpandMoreIcon />} <Typography sx={{ fontSize: 16, p: 1, fontWeight: 500 }}>
id='panel2d-header' {item.full_name}
> </Typography>
<Typography>Prompt</Typography> <div
</AccordionSummary> style={{
<AccordionDetails> display: 'flex',
<Typography sx={{ fontSize: 12, wordWrap: 'true' }} color='text.primary'> flexDirection: 'row',
{selectedPrompt?.detailed?.map((item) => ( flexWrap: 'wrap',
<> marginTop: 5
<Typography sx={{ fontSize: 12 }} color='text.secondary' gutterBottom> }}
{item.typeDisplay.toUpperCase()} >
{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> </Typography>
<Typography sx={{ fontSize: 12 }}> </AccordionDetails>
<p </Accordion>
style={{ <Accordion
whiteSpace: 'pre-wrap -moz-pre-wrap -pre-wrap -o-pre-wrap', 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', wordWrap: 'break-word',
fontFamily: 'inherit' 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>
)
}
}} }}
> >
<NewLineToBr>{item.template}</NewLineToBr> {selectedPrompt?.readme}
</p> </MemoizedReactMarkdown>
</Typography> </div>
</> </AccordionDetails>
))} </Accordion>
</Typography> </CardContent>
</AccordionDetails> </Card>
</Accordion> </Box>
<Accordion expanded={accordionExpanded === 'panel3'} onChange={() => handleAccordionChange('panel3')}> </Grid>
<AccordionSummary </Grid>
expandIcon={<ExpandMoreIcon />}
aria-controls='panel3d-content'
id='panel3d-header'
>
<Typography>Readme</Typography>
</AccordionSummary>
<AccordionDetails>
<ReactMarkdown
sx={{ fontSize: 11, wordWrap: 'true', width: '100%' }}
style={{ width: '100%', flexGrow: 1, resize: 'none' }}
>
{selectedPrompt?.readme}
</ReactMarkdown>
</AccordionDetails>
</Accordion>
</CardContent>
</Card>
</Box> </Box>
</Grid> </Stack>
</Grid> )}
</DialogContent> </DialogContent>
<DialogActions> {availablePrompNameList && availablePrompNameList.length > 0 && (
<Button onClick={onCancel}>Cancel</Button> <DialogActions>
<StyledButton disabled={!selectedPrompt?.detailed} onClick={() => onSubmit(selectedPrompt.detailed)} variant='contained'> <Button onClick={onCancel}>Cancel</Button>
Submit <StyledButton
</StyledButton> disabled={!selectedPrompt?.detailed}
</DialogActions> onClick={() => onSubmit(selectedPrompt.detailed)}
variant='contained'
>
Load
</StyledButton>
</DialogActions>
)}
</Dialog> </Dialog>
) : null ) : null
@@ -231,7 +231,7 @@ const NodeInputHandler = ({ inputAnchor, inputParam, data, disabled = false, isA
flexDirection: 'row', flexDirection: 'row',
width: '100%' width: '100%'
}} }}
sx={{ borderRadius: 25, width: '100%', mb: 2, mt: 2 }} sx={{ borderRadius: 25, width: '100%', mb: 2, mt: 0 }}
variant='outlined' variant='outlined'
onClick={() => onShowPromptHubButtonClicked()} onClick={() => onShowPromptHubButtonClicked()}
endIcon={<IconAutoFixHigh />} endIcon={<IconAutoFixHigh />}