Merge pull request #666 from FlowiseAI/feature/MultiConfig

Feature/Multi Config
This commit is contained in:
Henry Heng 2023-07-31 23:41:54 +01:00 committed by GitHub
commit ef6125ec24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 166 additions and 6 deletions

View File

@ -157,6 +157,7 @@ export interface IActiveChatflows {
export interface IOverrideConfig {
node: string
nodeId: string
label: string
name: string
type: string

View File

@ -445,6 +445,14 @@ export const replaceInputsWithConfig = (flowNodeData: INodeData, overrideConfig:
const getParamValues = (paramsObj: ICommonObject) => {
for (const config in overrideConfig) {
// If overrideConfig[key] is object
if (overrideConfig[config] && typeof overrideConfig[config] === 'object') {
const nodeIds = Object.keys(overrideConfig[config])
if (!nodeIds.includes(flowNodeData.id)) continue
else paramsObj[config] = overrideConfig[config][flowNodeData.id]
continue
}
let paramValue = overrideConfig[config] ?? paramsObj[config]
// Check if boolean
if (paramValue === 'true') paramValue = true
@ -695,6 +703,7 @@ export const findAvailableConfigs = (reactFlowNodes: IReactFlowNode[], component
if (inputParam.type === 'file') {
obj = {
node: flowNode.data.label,
nodeId: flowNode.data.id,
label: inputParam.label,
name: 'files',
type: inputParam.fileType ?? inputParam.type
@ -702,6 +711,7 @@ export const findAvailableConfigs = (reactFlowNodes: IReactFlowNode[], component
} else if (inputParam.type === 'options') {
obj = {
node: flowNode.data.label,
nodeId: flowNode.data.id,
label: inputParam.label,
name: inputParam.name,
type: inputParam.options
@ -720,6 +730,7 @@ export const findAvailableConfigs = (reactFlowNodes: IReactFlowNode[], component
for (const input of inputs) {
obj = {
node: flowNode.data.label,
nodeId: flowNode.data.id,
label: input.label,
name: input.name,
type: input.type === 'password' ? 'string' : input.type
@ -732,6 +743,7 @@ export const findAvailableConfigs = (reactFlowNodes: IReactFlowNode[], component
} else {
obj = {
node: flowNode.data.label,
nodeId: flowNode.data.id,
label: inputParam.label,
name: inputParam.name,
type: inputParam.type === 'password' ? 'string' : inputParam.type

View File

@ -16,9 +16,11 @@ export const TableViewOnly = ({ columns, rows }) => {
<TableBody>
{rows.map((row, index) => (
<TableRow key={index} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
{Object.keys(row).map((key, index) => (
<TableCell key={index}>{row[key]}</TableCell>
))}
{Object.keys(row)
.slice(-3)
.map((key, index) => (
<TableCell key={index}>{row[key]}</TableCell>
))}
</TableRow>
))}
</TableBody>

View File

@ -4,8 +4,20 @@ import { useState, useEffect } from 'react'
import { useDispatch } from 'react-redux'
import PropTypes from 'prop-types'
import { Tabs, Tab, Dialog, DialogContent, DialogTitle, Box } from '@mui/material'
import {
Tabs,
Tab,
Dialog,
DialogContent,
DialogTitle,
Box,
Accordion,
AccordionSummary,
AccordionDetails,
Typography
} from '@mui/material'
import { CopyBlock, atomOneDark } from 'react-code-blocks'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
// Project import
import { Dropdown } from 'ui-component/dropdown/Dropdown'
@ -33,6 +45,8 @@ import useApi from 'hooks/useApi'
import { CheckboxInput } from 'ui-component/checkbox/Checkbox'
import { TableViewOnly } from 'ui-component/table/Table'
import { IconBulb } from '@tabler/icons'
function TabPanel(props) {
const { children, value, index, ...other } = props
return (
@ -82,7 +96,7 @@ const getConfigExamplesForJS = (configData, bodyType) => {
else if (config.type === 'number') exampleVal = `1`
else if (config.name === 'files') exampleVal = `input.files[0]`
finalStr += bodyType === 'json' ? `\n "${config.name}": ${exampleVal},` : `formData.append("${config.name}", ${exampleVal})\n`
if (i === loop - 1 && bodyType !== 'json') `formData.append("question", "Hey, how are you?")\n`
if (i === loop - 1 && bodyType !== 'json') finalStr += `formData.append("question", "Hey, how are you?")\n`
}
return finalStr
}
@ -134,6 +148,8 @@ const APICodeDialog = ({ show, dialogProps, onCancel }) => {
const [chatflowApiKeyId, setChatflowApiKeyId] = useState('')
const [selectedApiKey, setSelectedApiKey] = useState({})
const [checkboxVal, setCheckbox] = useState(false)
const [nodeConfig, setNodeConfig] = useState({})
const [nodeConfigExpanded, setNodeConfigExpanded] = useState({})
const getAllAPIKeysApi = useApi(apiKeyApi.getAllAPIKeys)
const updateChatflowApi = useApi(chatflowsApi.updateChatflow)
@ -160,12 +176,36 @@ const APICodeDialog = ({ show, dialogProps, onCancel }) => {
updateChatflowApi.request(dialogProps.chatflowid, updateBody)
}
const groupByNodeLabel = (nodes, isFilter = false) => {
const accordianNodes = {}
const result = nodes.reduce(function (r, a) {
r[a.node] = r[a.node] || []
r[a.node].push(a)
accordianNodes[a.node] = isFilter ? true : false
return r
}, Object.create(null))
setNodeConfig(result)
setNodeConfigExpanded(accordianNodes)
}
const handleAccordionChange = (nodeLabel) => (event, isExpanded) => {
const accordianNodes = { ...nodeConfigExpanded }
accordianNodes[nodeLabel] = isExpanded
setNodeConfigExpanded(accordianNodes)
}
useEffect(() => {
if (updateChatflowApi.data) {
dispatch({ type: SET_CHATFLOW, chatflow: updateChatflowApi.data })
}
}, [updateChatflowApi.data, dispatch])
useEffect(() => {
if (getConfigApi.data) {
groupByNodeLabel(getConfigApi.data)
}
}, [getConfigApi.data])
const handleChange = (event, newValue) => {
setValue(newValue)
}
@ -493,6 +533,32 @@ query({
return ''
}
const getMultiConfigCodeWithFormData = (codeLang) => {
if (codeLang === 'Python') {
return `body_data = {
"openAIApiKey[chatOpenAI_0]": "sk-my-openai-1st-key",
"openAIApiKey[openAIEmbeddings_0]": "sk-my-openai-2nd-key"
}`
} else if (codeLang === 'JavaScript') {
return `formData.append("openAIApiKey[chatOpenAI_0]", "sk-my-openai-1st-key")
formData.append("openAIApiKey[openAIEmbeddings_0]", "sk-my-openai-2nd-key")`
} else if (codeLang === 'cURL') {
return `-F "openAIApiKey[chatOpenAI_0]=sk-my-openai-1st-key" \\
-F "openAIApiKey[openAIEmbeddings_0]=sk-my-openai-2nd-key" \\`
}
}
const getMultiConfigCode = () => {
return `{
"overrideConfig": {
"openAIApiKey": {
"chatOpenAI_0": "sk-my-openai-1st-key",
"openAIEmbeddings_0": "sk-my-openai-2nd-key"
}
}
}`
}
useEffect(() => {
if (getAllAPIKeysApi.data) {
const options = [
@ -593,7 +659,49 @@ query({
<CheckboxInput label='Show Input Config' value={checkboxVal} onChange={onCheckBoxChanged} />
{checkboxVal && getConfigApi.data && getConfigApi.data.length > 0 && (
<>
<TableViewOnly rows={getConfigApi.data} columns={Object.keys(getConfigApi.data[0])} />
{Object.keys(nodeConfig)
.sort()
.map((nodeLabel) => (
<Accordion
expanded={nodeConfigExpanded[nodeLabel] || false}
onChange={handleAccordionChange(nodeLabel)}
key={nodeLabel}
disableGutters
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls={`nodes-accordian-${nodeLabel}`}
id={`nodes-accordian-header-${nodeLabel}`}
>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
<Typography variant='h5'>{nodeLabel}</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' }}>
{nodeConfig[nodeLabel][0].nodeId}
</span>
</div>
</div>
</AccordionSummary>
<AccordionDetails>
<TableViewOnly
rows={nodeConfig[nodeLabel]}
columns={Object.keys(nodeConfig[nodeLabel][0]).slice(-3)}
/>
</AccordionDetails>
</Accordion>
))}
<CopyBlock
theme={atomOneDark}
text={
@ -609,6 +717,43 @@ query({
showLineNumbers={false}
wrapLines
/>
<div
style={{
display: 'flex',
flexDirection: 'column',
borderRadius: 10,
background: '#d8f3dc',
padding: 10,
marginTop: 10,
marginBottom: 10
}}
>
<div
style={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center'
}}
>
<IconBulb size={30} color='#2d6a4f' />
<span style={{ color: '#2d6a4f', marginLeft: 10, fontWeight: 500 }}>
You can also specify multiple values for a config parameter by specifying the node id
</span>
</div>
<div style={{ padding: 10 }}>
<CopyBlock
theme={atomOneDark}
text={
dialogProps.isFormDataRequired
? getMultiConfigCodeWithFormData(codeLang)
: getMultiConfigCode()
}
language={getLang(codeLang)}
showLineNumbers={false}
wrapLines
/>
</div>
</div>
</>
)}
{getIsChatflowStreamingApi.data?.isStreaming && (