@@ -4,8 +4,20 @@ import { useState, useEffect } from 'react'
4
4
import { useDispatch } from 'react-redux'
5
5
import PropTypes from 'prop-types'
6
6
7
- import { Tabs , Tab , Dialog , DialogContent , DialogTitle , Box } from '@mui/material'
7
+ import {
8
+ Tabs ,
9
+ Tab ,
10
+ Dialog ,
11
+ DialogContent ,
12
+ DialogTitle ,
13
+ Box ,
14
+ Accordion ,
15
+ AccordionSummary ,
16
+ AccordionDetails ,
17
+ Typography
18
+ } from '@mui/material'
8
19
import { CopyBlock , atomOneDark } from 'react-code-blocks'
20
+ import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
9
21
10
22
// Project import
11
23
import { Dropdown } from 'ui-component/dropdown/Dropdown'
@@ -33,6 +45,8 @@ import useApi from 'hooks/useApi'
33
45
import { CheckboxInput } from 'ui-component/checkbox/Checkbox'
34
46
import { TableViewOnly } from 'ui-component/table/Table'
35
47
48
+ import { IconBulb } from '@tabler/icons'
49
+
36
50
function TabPanel ( props ) {
37
51
const { children, value, index, ...other } = props
38
52
return (
@@ -82,7 +96,7 @@ const getConfigExamplesForJS = (configData, bodyType) => {
82
96
else if ( config . type === 'number' ) exampleVal = `1`
83
97
else if ( config . name === 'files' ) exampleVal = `input.files[0]`
84
98
finalStr += bodyType === 'json' ? `\n "${ config . name } ": ${ exampleVal } ,` : `formData.append("${ config . name } ", ${ exampleVal } )\n`
85
- if ( i === loop - 1 && bodyType !== 'json' ) `formData.append("question", "Hey, how are you?")\n`
99
+ if ( i === loop - 1 && bodyType !== 'json' ) finalStr += `formData.append("question", "Hey, how are you?")\n`
86
100
}
87
101
return finalStr
88
102
}
@@ -134,6 +148,8 @@ const APICodeDialog = ({ show, dialogProps, onCancel }) => {
134
148
const [ chatflowApiKeyId , setChatflowApiKeyId ] = useState ( '' )
135
149
const [ selectedApiKey , setSelectedApiKey ] = useState ( { } )
136
150
const [ checkboxVal , setCheckbox ] = useState ( false )
151
+ const [ nodeConfig , setNodeConfig ] = useState ( { } )
152
+ const [ nodeConfigExpanded , setNodeConfigExpanded ] = useState ( { } )
137
153
138
154
const getAllAPIKeysApi = useApi ( apiKeyApi . getAllAPIKeys )
139
155
const updateChatflowApi = useApi ( chatflowsApi . updateChatflow )
@@ -160,12 +176,36 @@ const APICodeDialog = ({ show, dialogProps, onCancel }) => {
160
176
updateChatflowApi . request ( dialogProps . chatflowid , updateBody )
161
177
}
162
178
179
+ const groupByNodeLabel = ( nodes , isFilter = false ) => {
180
+ const accordianNodes = { }
181
+ const result = nodes . reduce ( function ( r , a ) {
182
+ r [ a . node ] = r [ a . node ] || [ ]
183
+ r [ a . node ] . push ( a )
184
+ accordianNodes [ a . node ] = isFilter ? true : false
185
+ return r
186
+ } , Object . create ( null ) )
187
+ setNodeConfig ( result )
188
+ setNodeConfigExpanded ( accordianNodes )
189
+ }
190
+
191
+ const handleAccordionChange = ( nodeLabel ) => ( event , isExpanded ) => {
192
+ const accordianNodes = { ...nodeConfigExpanded }
193
+ accordianNodes [ nodeLabel ] = isExpanded
194
+ setNodeConfigExpanded ( accordianNodes )
195
+ }
196
+
163
197
useEffect ( ( ) => {
164
198
if ( updateChatflowApi . data ) {
165
199
dispatch ( { type : SET_CHATFLOW , chatflow : updateChatflowApi . data } )
166
200
}
167
201
} , [ updateChatflowApi . data , dispatch ] )
168
202
203
+ useEffect ( ( ) => {
204
+ if ( getConfigApi . data ) {
205
+ groupByNodeLabel ( getConfigApi . data )
206
+ }
207
+ } , [ getConfigApi . data ] )
208
+
169
209
const handleChange = ( event , newValue ) => {
170
210
setValue ( newValue )
171
211
}
@@ -493,6 +533,32 @@ query({
493
533
return ''
494
534
}
495
535
536
+ const getMultiConfigCodeWithFormData = ( codeLang ) => {
537
+ if ( codeLang === 'Python' ) {
538
+ return `body_data = {
539
+ "openAIApiKey[chatOpenAI_0]": "sk-my-openai-1st-key",
540
+ "openAIApiKey[openAIEmbeddings_0]": "sk-my-openai-2nd-key"
541
+ }`
542
+ } else if ( codeLang === 'JavaScript' ) {
543
+ return `formData.append("openAIApiKey[chatOpenAI_0]", "sk-my-openai-1st-key")
544
+ formData.append("openAIApiKey[openAIEmbeddings_0]", "sk-my-openai-2nd-key")`
545
+ } else if ( codeLang === 'cURL' ) {
546
+ return `-F "openAIApiKey[chatOpenAI_0]=sk-my-openai-1st-key" \\
547
+ -F "openAIApiKey[openAIEmbeddings_0]=sk-my-openai-2nd-key" \\`
548
+ }
549
+ }
550
+
551
+ const getMultiConfigCode = ( ) => {
552
+ return `{
553
+ "overrideConfig": {
554
+ "openAIApiKey": {
555
+ "chatOpenAI_0": "sk-my-openai-1st-key",
556
+ "openAIEmbeddings_0": "sk-my-openai-2nd-key"
557
+ }
558
+ }
559
+ }`
560
+ }
561
+
496
562
useEffect ( ( ) => {
497
563
if ( getAllAPIKeysApi . data ) {
498
564
const options = [
@@ -593,7 +659,49 @@ query({
593
659
< CheckboxInput label = 'Show Input Config' value = { checkboxVal } onChange = { onCheckBoxChanged } />
594
660
{ checkboxVal && getConfigApi . data && getConfigApi . data . length > 0 && (
595
661
< >
596
- < TableViewOnly rows = { getConfigApi . data } columns = { Object . keys ( getConfigApi . data [ 0 ] ) } />
662
+ { Object . keys ( nodeConfig )
663
+ . sort ( )
664
+ . map ( ( nodeLabel ) => (
665
+ < Accordion
666
+ expanded = { nodeConfigExpanded [ nodeLabel ] || false }
667
+ onChange = { handleAccordionChange ( nodeLabel ) }
668
+ key = { nodeLabel }
669
+ disableGutters
670
+ >
671
+ < AccordionSummary
672
+ expandIcon = { < ExpandMoreIcon /> }
673
+ aria-controls = { `nodes-accordian-${ nodeLabel } ` }
674
+ id = { `nodes-accordian-header-${ nodeLabel } ` }
675
+ >
676
+ < div style = { { display : 'flex' , flexDirection : 'row' , alignItems : 'center' } } >
677
+ < Typography variant = 'h5' > { nodeLabel } </ Typography >
678
+ < div
679
+ style = { {
680
+ display : 'flex' ,
681
+ flexDirection : 'row' ,
682
+ width : 'max-content' ,
683
+ borderRadius : 15 ,
684
+ background : 'rgb(254,252,191)' ,
685
+ padding : 5 ,
686
+ paddingLeft : 10 ,
687
+ paddingRight : 10 ,
688
+ marginLeft : 10
689
+ } }
690
+ >
691
+ < span style = { { color : 'rgb(116,66,16)' , fontSize : '0.825rem' } } >
692
+ { nodeConfig [ nodeLabel ] [ 0 ] . nodeId }
693
+ </ span >
694
+ </ div >
695
+ </ div >
696
+ </ AccordionSummary >
697
+ < AccordionDetails >
698
+ < TableViewOnly
699
+ rows = { nodeConfig [ nodeLabel ] }
700
+ columns = { Object . keys ( nodeConfig [ nodeLabel ] [ 0 ] ) . slice ( - 3 ) }
701
+ />
702
+ </ AccordionDetails >
703
+ </ Accordion >
704
+ ) ) }
597
705
< CopyBlock
598
706
theme = { atomOneDark }
599
707
text = {
@@ -609,6 +717,43 @@ query({
609
717
showLineNumbers = { false }
610
718
wrapLines
611
719
/>
720
+ < div
721
+ style = { {
722
+ display : 'flex' ,
723
+ flexDirection : 'column' ,
724
+ borderRadius : 10 ,
725
+ background : '#d8f3dc' ,
726
+ padding : 10 ,
727
+ marginTop : 10 ,
728
+ marginBottom : 10
729
+ } }
730
+ >
731
+ < div
732
+ style = { {
733
+ display : 'flex' ,
734
+ flexDirection : 'row' ,
735
+ alignItems : 'center'
736
+ } }
737
+ >
738
+ < IconBulb size = { 30 } color = '#2d6a4f' />
739
+ < span style = { { color : '#2d6a4f' , marginLeft : 10 , fontWeight : 500 } } >
740
+ You can also specify multiple values for a config parameter by specifying the node id
741
+ </ span >
742
+ </ div >
743
+ < div style = { { padding : 10 } } >
744
+ < CopyBlock
745
+ theme = { atomOneDark }
746
+ text = {
747
+ dialogProps . isFormDataRequired
748
+ ? getMultiConfigCodeWithFormData ( codeLang )
749
+ : getMultiConfigCode ( )
750
+ }
751
+ language = { getLang ( codeLang ) }
752
+ showLineNumbers = { false }
753
+ wrapLines
754
+ />
755
+ </ div >
756
+ </ div >
612
757
</ >
613
758
) }
614
759
{ getIsChatflowStreamingApi . data ?. isStreaming && (
0 commit comments