mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 11:00:55 +03:00
Feature/update upsert API (#3836)
* update upsert API * add fix for override files in upsert vector
This commit is contained in:
@@ -6,6 +6,7 @@ const getSpecificDocumentStore = (id) => client.get(`/document-store/store/${id}
|
||||
const createDocumentStore = (body) => client.post(`/document-store/store`, body)
|
||||
const updateDocumentStore = (id, body) => client.put(`/document-store/store/${id}`, body)
|
||||
const deleteDocumentStore = (id) => client.delete(`/document-store/store/${id}`)
|
||||
const getDocumentStoreConfig = (storeId, loaderId) => client.get(`/document-store/store-configs/${storeId}/${loaderId}`)
|
||||
|
||||
const deleteLoaderFromStore = (id, fileId) => client.delete(`/document-store/loader/${id}/${fileId}`)
|
||||
const deleteChunkFromStore = (storeId, loaderId, chunkId) => client.delete(`/document-store/chunks/${storeId}/${loaderId}/${chunkId}`)
|
||||
@@ -52,5 +53,6 @@ export default {
|
||||
updateVectorStoreConfig,
|
||||
saveProcessingLoader,
|
||||
refreshLoader,
|
||||
generateDocStoreToolDesc
|
||||
generateDocStoreToolDesc,
|
||||
getDocumentStoreConfig
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import cURLSVG from '@/assets/images/cURL.svg'
|
||||
import EmbedSVG from '@/assets/images/embed.svg'
|
||||
import ShareChatbotSVG from '@/assets/images/sharing.png'
|
||||
import settingsSVG from '@/assets/images/settings.svg'
|
||||
import { IconBulb, IconBox, IconVariable } from '@tabler/icons-react'
|
||||
import { IconBulb, IconBox, IconVariable, IconExclamationCircle } from '@tabler/icons-react'
|
||||
|
||||
// API
|
||||
import apiKeyApi from '@/api/apikey'
|
||||
@@ -726,9 +726,44 @@ formData.append("openAIApiKey[openAIEmbeddings_0]", "sk-my-openai-2nd-key")`
|
||||
<CheckboxInput label='Show Override Config' value={checkboxVal} onChange={onCheckBoxChanged} />
|
||||
{checkboxVal && getConfigApi.data && getConfigApi.data.length > 0 && (
|
||||
<>
|
||||
<Typography sx={{ mt: 2, mb: 3 }}>
|
||||
<Typography sx={{ mt: 2 }}>
|
||||
You can override existing input configuration of the chatflow with overrideConfig property.
|
||||
</Typography>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRadius: 10,
|
||||
background: 'rgb(254,252,191)',
|
||||
padding: 10,
|
||||
marginTop: 10,
|
||||
marginBottom: 10
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<IconExclamationCircle size={30} color='rgb(116,66,16)' />
|
||||
<span style={{ color: 'rgb(116,66,16)', marginLeft: 10, fontWeight: 500 }}>
|
||||
{
|
||||
'For security reason, override config is disabled by default. You can change this by going into Chatflow Configuration -> Security tab, and enable the property you want to override.'
|
||||
}
|
||||
Refer{' '}
|
||||
<a
|
||||
rel='noreferrer'
|
||||
target='_blank'
|
||||
href='https://docs.flowiseai.com/using-flowise/api#override-config'
|
||||
>
|
||||
here
|
||||
</a>{' '}
|
||||
for more details
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Stack direction='column' spacing={2} sx={{ width: '100%', my: 2 }}>
|
||||
<Card sx={{ borderColor: theme.palette.primary[200] + 75, p: 2 }} variant='outlined'>
|
||||
<Stack sx={{ mt: 1, mb: 2, ml: 1, alignItems: 'center' }} direction='row' spacing={2}>
|
||||
|
||||
@@ -0,0 +1,396 @@
|
||||
import { createPortal } from 'react-dom'
|
||||
import { useState, useEffect } from 'react'
|
||||
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'
|
||||
import { MemoizedReactMarkdown } from '@/ui-component/markdown/MemoizedReactMarkdown'
|
||||
import { CodeBlock } from '@/ui-component/markdown/CodeBlock'
|
||||
import { Typography, Stack, Card, Accordion, AccordionSummary, AccordionDetails, Dialog, DialogContent, DialogTitle } from '@mui/material'
|
||||
import { TableViewOnly } from '@/ui-component/table/Table'
|
||||
import documentstoreApi from '@/api/documentstore'
|
||||
import useApi from '@/hooks/useApi'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
||||
|
||||
const DocStoreAPIDialog = ({ show, dialogProps, onCancel }) => {
|
||||
const [nodeConfig, setNodeConfig] = useState({})
|
||||
const [values, setValues] = useState('')
|
||||
const theme = useTheme()
|
||||
const [nodeConfigExpanded, setNodeConfigExpanded] = useState({})
|
||||
|
||||
const getConfigApi = useApi(documentstoreApi.getDocumentStoreConfig)
|
||||
|
||||
const formDataRequest = () => {
|
||||
return `With the Upsert API, you can choose an existing document and reuse the same configuration for upserting.
|
||||
|
||||
\`\`\`python
|
||||
import requests
|
||||
import json
|
||||
|
||||
API_URL = "http://localhost:3000/api/v1/document-store/upsert/${dialogProps.storeId}"
|
||||
API_KEY = "your_api_key_here"
|
||||
|
||||
# use form data to upload files
|
||||
form_data = {
|
||||
"files": ('my-another-file.pdf', open('my-another-file.pdf', 'rb'))
|
||||
}
|
||||
|
||||
body_data = {
|
||||
"docId": "${dialogProps.loaderId}",
|
||||
"metadata": {}, # Add additional metadata to the document chunks
|
||||
"replaceExisting": True, # Replace existing document with the new upserted chunks
|
||||
"splitter": json.dumps({"config":{"chunkSize":20000}}) # Override existing configuration
|
||||
# "loader": "",
|
||||
# "vectorStore": "",
|
||||
# "embedding": "",
|
||||
# "recordManager": "",
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {BEARER_TOKEN}"
|
||||
}
|
||||
|
||||
def query(form_data):
|
||||
response = requests.post(API_URL, files=form_data, data=body_data, headers=headers)
|
||||
print(response)
|
||||
return response.json()
|
||||
|
||||
output = query(form_data)
|
||||
print(output)
|
||||
\`\`\`
|
||||
|
||||
\`\`\`javascript
|
||||
// use FormData to upload files
|
||||
let formData = new FormData();
|
||||
formData.append("files", input.files[0]);
|
||||
formData.append("docId", "${dialogProps.loaderId}");
|
||||
formData.append("splitter", JSON.stringify({"config":{"chunkSize":20000}}));
|
||||
// Add additional metadata to the document chunks
|
||||
formData.append("metadata", "{}");
|
||||
// Replace existing document with the new upserted chunks
|
||||
formData.append("replaceExisting", "true");
|
||||
// Override existing configuration
|
||||
// formData.append("loader", "");
|
||||
// formData.append("embedding", "");
|
||||
// formData.append("vectorStore", "");
|
||||
// formData.append("recordManager", "");
|
||||
|
||||
async function query(formData) {
|
||||
const response = await fetch(
|
||||
"http://localhost:3000/api/v1/document-store/upsert/${dialogProps.storeId}",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": "Bearer <your_api_key_here>"
|
||||
},
|
||||
body: formData
|
||||
}
|
||||
);
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
query(formData).then((response) => {
|
||||
console.log(response);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
\`\`\`bash
|
||||
curl -X POST http://localhost:3000/api/v1/document-store/upsert/${dialogProps.storeId} \\
|
||||
-H "Authorization: Bearer <your_api_key_here>" \\
|
||||
-F "files=@<file-path>" \\
|
||||
-F "docId=${dialogProps.loaderId}" \\
|
||||
-F "splitter={"config":{"chunkSize":20000}}" \\
|
||||
-F "metadata={}" \\
|
||||
-F "replaceExisting=true" \\
|
||||
# Override existing configuration:
|
||||
# -F "loader=" \\
|
||||
# -F "embedding=" \\
|
||||
# -F "vectorStore=" \\
|
||||
# -F "recordManager="
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
|
||||
const jsonDataRequest = () => {
|
||||
return `With the Upsert API, you can choose an existing document and reuse the same configuration for upserting.
|
||||
|
||||
\`\`\`python
|
||||
import requests
|
||||
|
||||
API_URL = "http://localhost:3000/api/v1/document-store/upsert/${dialogProps.storeId}"
|
||||
API_KEY = "your_api_key_here"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {BEARER_TOKEN}"
|
||||
}
|
||||
|
||||
def query(payload):
|
||||
response = requests.post(API_URL, json=payload, headers=headers)
|
||||
return response.json()
|
||||
|
||||
output = query({
|
||||
"docId": "${dialogProps.loaderId}",
|
||||
"metadata": "{}", # Add additional metadata to the document chunks
|
||||
"replaceExisting": True, # Replace existing document with the new upserted chunks
|
||||
# Override existing configuration
|
||||
"loader": {
|
||||
"config": {
|
||||
"text": "This is a new text"
|
||||
}
|
||||
},
|
||||
"splitter": {
|
||||
"config": {
|
||||
"chunkSize": 20000
|
||||
}
|
||||
},
|
||||
# embedding: {},
|
||||
# vectorStore: {},
|
||||
# recordManager: {}
|
||||
})
|
||||
print(output)
|
||||
\`\`\`
|
||||
|
||||
\`\`\`javascript
|
||||
async function query(data) {
|
||||
const response = await fetch(
|
||||
"http://localhost:3000/api/v1/document-store/upsert/${dialogProps.storeId}",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": "Bearer <your_api_key_here>"
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
}
|
||||
);
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
query({
|
||||
"docId": "${dialogProps.loaderId},
|
||||
"metadata": "{}", // Add additional metadata to the document chunks
|
||||
"replaceExisting": true, // Replace existing document with the new upserted chunks
|
||||
// Override existing configuration
|
||||
"loader": {
|
||||
"config": {
|
||||
"text": "This is a new text"
|
||||
}
|
||||
},
|
||||
"splitter": {
|
||||
"config": {
|
||||
"chunkSize": 20000
|
||||
}
|
||||
},
|
||||
// embedding: {},
|
||||
// vectorStore: {},
|
||||
// recordManager: {}
|
||||
}).then((response) => {
|
||||
console.log(response);
|
||||
});
|
||||
\`\`\`
|
||||
|
||||
\`\`\`bash
|
||||
curl -X POST http://localhost:3000/api/v1/document-store/upsert/${dialogProps.storeId} \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "Authorization: Bearer <your_api_key_here>" \\
|
||||
-d '{
|
||||
"docId": "${dialogProps.loaderId}",
|
||||
"metadata": "{}",
|
||||
"replaceExisting": true,
|
||||
"loader": {
|
||||
"config": {
|
||||
"text": "This is a new text"
|
||||
}
|
||||
},
|
||||
"splitter": {
|
||||
"config": {
|
||||
"chunkSize": 20000
|
||||
}
|
||||
}
|
||||
// Override existing configuration
|
||||
// "embedding": {},
|
||||
// "vectorStore": {},
|
||||
// "recordManager": {}
|
||||
}'
|
||||
|
||||
\`\`\`
|
||||
`
|
||||
}
|
||||
|
||||
const groupByNodeLabel = (nodes) => {
|
||||
const result = {}
|
||||
const seenNodes = new Set()
|
||||
let isFormDataBody = false
|
||||
|
||||
nodes.forEach((item) => {
|
||||
const { node, nodeId, label, name, type } = item
|
||||
if (name === 'files') isFormDataBody = true
|
||||
seenNodes.add(node)
|
||||
|
||||
if (!result[node]) {
|
||||
result[node] = {
|
||||
nodeIds: [],
|
||||
params: []
|
||||
}
|
||||
}
|
||||
|
||||
if (!result[node].nodeIds.includes(nodeId)) result[node].nodeIds.push(nodeId)
|
||||
|
||||
const param = { label, name, type }
|
||||
|
||||
if (!result[node].params.some((existingParam) => JSON.stringify(existingParam) === JSON.stringify(param))) {
|
||||
result[node].params.push(param)
|
||||
}
|
||||
})
|
||||
|
||||
// Sort the nodeIds array
|
||||
for (const node in result) {
|
||||
result[node].nodeIds.sort()
|
||||
}
|
||||
setNodeConfig(result)
|
||||
|
||||
if (isFormDataBody) {
|
||||
setValues(formDataRequest())
|
||||
} else {
|
||||
setValues(jsonDataRequest())
|
||||
}
|
||||
}
|
||||
|
||||
const handleAccordionChange = (nodeLabel) => (event, isExpanded) => {
|
||||
const accordianNodes = { ...nodeConfigExpanded }
|
||||
accordianNodes[nodeLabel] = isExpanded
|
||||
setNodeConfigExpanded(accordianNodes)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (getConfigApi.data) {
|
||||
groupByNodeLabel(getConfigApi.data)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getConfigApi.data])
|
||||
|
||||
useEffect(() => {
|
||||
if (show && dialogProps) {
|
||||
getConfigApi.request(dialogProps.storeId, dialogProps.loaderId)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [show, dialogProps])
|
||||
|
||||
const portalElement = document.getElementById('portal')
|
||||
|
||||
const component = show ? (
|
||||
<Dialog
|
||||
onClose={onCancel}
|
||||
open={show}
|
||||
fullWidth
|
||||
maxWidth='lg'
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'
|
||||
>
|
||||
<DialogTitle sx={{ fontSize: '1rem' }} id='alert-dialog-title'>
|
||||
{dialogProps.title}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<MemoizedReactMarkdown
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeMathjax, rehypeRaw]}
|
||||
components={{
|
||||
code({ inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || '')
|
||||
return !inline ? (
|
||||
<CodeBlock
|
||||
isDialog={true}
|
||||
language={(match && match[1]) || ''}
|
||||
value={String(children).replace(/\n$/, '')}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<code className={className} {...props}>
|
||||
{children}
|
||||
</code>
|
||||
)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{values}
|
||||
</MemoizedReactMarkdown>
|
||||
|
||||
<Typography sx={{ mt: 3, mb: 1 }}>You can override existing configurations:</Typography>
|
||||
|
||||
<Stack direction='column' spacing={2} sx={{ width: '100%', my: 2 }}>
|
||||
<Card sx={{ borderColor: theme.palette.primary[200] + 75, p: 2 }} variant='outlined'>
|
||||
{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}`}
|
||||
>
|
||||
<Stack flexDirection='row' sx={{ gap: 2, alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<Typography variant='h5'>{nodeLabel}</Typography>
|
||||
{nodeConfig[nodeLabel].nodeIds.length > 0 &&
|
||||
nodeConfig[nodeLabel].nodeIds.map((nodeId, index) => (
|
||||
<div
|
||||
key={index}
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
width: 'max-content',
|
||||
borderRadius: 15,
|
||||
background: 'rgb(254,252,191)',
|
||||
padding: 5,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
color: 'rgb(116,66,16)',
|
||||
fontSize: '0.825rem'
|
||||
}}
|
||||
>
|
||||
{nodeId}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</Stack>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<TableViewOnly
|
||||
rows={nodeConfig[nodeLabel].params.map((obj) => {
|
||||
// eslint-disable-next-line
|
||||
const { node, nodeId, ...rest } = obj
|
||||
return rest
|
||||
})}
|
||||
columns={Object.keys(nodeConfig[nodeLabel].params[0]).slice(-3)}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
))}
|
||||
</Card>
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
) : null
|
||||
|
||||
return createPortal(component, portalElement)
|
||||
}
|
||||
|
||||
DocStoreAPIDialog.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
dialogProps: PropTypes.object,
|
||||
onCancel: PropTypes.func
|
||||
}
|
||||
|
||||
export default DocStoreAPIDialog
|
||||
@@ -37,6 +37,7 @@ import ViewHeader from '@/layout/MainLayout/ViewHeader'
|
||||
import DeleteDocStoreDialog from './DeleteDocStoreDialog'
|
||||
import DocumentStoreStatus from '@/views/docstore/DocumentStoreStatus'
|
||||
import ConfirmDialog from '@/ui-component/dialog/ConfirmDialog'
|
||||
import DocStoreAPIDialog from './DocStoreAPIDialog'
|
||||
|
||||
// API
|
||||
import documentsApi from '@/api/documentstore'
|
||||
@@ -56,6 +57,7 @@ import FileChunksIcon from '@mui/icons-material/AppRegistration'
|
||||
import NoteAddIcon from '@mui/icons-material/NoteAdd'
|
||||
import SearchIcon from '@mui/icons-material/Search'
|
||||
import RefreshIcon from '@mui/icons-material/Refresh'
|
||||
import CodeIcon from '@mui/icons-material/Code'
|
||||
import doc_store_details_emptySVG from '@/assets/images/doc_store_details_empty.svg'
|
||||
|
||||
// store
|
||||
@@ -142,6 +144,8 @@ const DocumentStoreDetails = () => {
|
||||
const [documentLoaderListDialogProps, setDocumentLoaderListDialogProps] = useState({})
|
||||
const [showDeleteDocStoreDialog, setShowDeleteDocStoreDialog] = useState(false)
|
||||
const [deleteDocStoreDialogProps, setDeleteDocStoreDialogProps] = useState({})
|
||||
const [showDocStoreAPIDialog, setShowDocStoreAPIDialog] = useState(false)
|
||||
const [docStoreAPIDialogProps, setDocStoreAPIDialogProps] = useState({})
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState(null)
|
||||
const open = Boolean(anchorEl)
|
||||
@@ -374,6 +378,16 @@ const DocumentStoreDetails = () => {
|
||||
setAnchorEl(event.currentTarget)
|
||||
}
|
||||
|
||||
const onViewUpsertAPI = (storeId, loaderId) => {
|
||||
const props = {
|
||||
title: `Upsert API`,
|
||||
storeId,
|
||||
loaderId
|
||||
}
|
||||
setDocStoreAPIDialogProps(props)
|
||||
setShowDocStoreAPIDialog(true)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null)
|
||||
}
|
||||
@@ -650,6 +664,7 @@ const DocumentStoreDetails = () => {
|
||||
onChunkUpsert={() =>
|
||||
navigate(`/document-stores/vector/${documentStore.id}/${loader.id}`)
|
||||
}
|
||||
onViewUpsertAPI={() => onViewUpsertAPI(documentStore.id, loader.id)}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
@@ -695,6 +710,13 @@ const DocumentStoreDetails = () => {
|
||||
onDelete={onDocStoreDelete}
|
||||
/>
|
||||
)}
|
||||
{showDocStoreAPIDialog && (
|
||||
<DocStoreAPIDialog
|
||||
show={showDocStoreAPIDialog}
|
||||
dialogProps={docStoreAPIDialogProps}
|
||||
onCancel={() => setShowDocStoreAPIDialog(false)}
|
||||
/>
|
||||
)}
|
||||
{isBackdropLoading && <BackdropLoader open={isBackdropLoading} />}
|
||||
<ConfirmDialog />
|
||||
</>
|
||||
@@ -784,6 +806,10 @@ function LoaderRow(props) {
|
||||
<NoteAddIcon />
|
||||
Upsert Chunks
|
||||
</MenuItem>
|
||||
<MenuItem onClick={props.onViewUpsertAPI} disableRipple>
|
||||
<CodeIcon />
|
||||
View API
|
||||
</MenuItem>
|
||||
<Divider sx={{ my: 0.5 }} />
|
||||
<MenuItem onClick={props.onDeleteClick} disableRipple>
|
||||
<FileDeleteIcon />
|
||||
@@ -805,6 +831,7 @@ LoaderRow.propTypes = {
|
||||
onViewChunksClick: PropTypes.func,
|
||||
onEditClick: PropTypes.func,
|
||||
onDeleteClick: PropTypes.func,
|
||||
onChunkUpsert: PropTypes.func
|
||||
onChunkUpsert: PropTypes.func,
|
||||
onViewUpsertAPI: PropTypes.func
|
||||
}
|
||||
export default DocumentStoreDetails
|
||||
|
||||
@@ -23,7 +23,7 @@ import { CheckboxInput } from '@/ui-component/checkbox/Checkbox'
|
||||
import { BackdropLoader } from '@/ui-component/loading/BackdropLoader'
|
||||
import { TableViewOnly } from '@/ui-component/table/Table'
|
||||
|
||||
import { IconX, IconBulb } from '@tabler/icons-react'
|
||||
import { IconX, IconBulb, IconExclamationCircle } from '@tabler/icons-react'
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
||||
import pythonSVG from '@/assets/images/python.svg'
|
||||
import javascriptSVG from '@/assets/images/javascript.svg'
|
||||
@@ -545,6 +545,47 @@ formData.append("openAIApiKey[openAIEmbeddings_0]", "sk-my-openai-2nd-key")`
|
||||
showLineNumbers={false}
|
||||
wrapLines
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRadius: 10,
|
||||
background: 'rgb(254,252,191)',
|
||||
padding: 10,
|
||||
marginTop: 20,
|
||||
marginBottom: 20
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<IconExclamationCircle size={30} color='rgb(116,66,16)' />
|
||||
<span
|
||||
style={{
|
||||
color: 'rgb(116,66,16)',
|
||||
marginLeft: 10,
|
||||
fontWeight: 500
|
||||
}}
|
||||
>
|
||||
{
|
||||
'For security reason, override config is disabled by default. You can change this by going into Chatflow Configuration -> Security tab, and enable the property you want to override.'
|
||||
}
|
||||
Refer{' '}
|
||||
<a
|
||||
rel='noreferrer'
|
||||
target='_blank'
|
||||
href='https://docs.flowiseai.com/using-flowise/api#override-config'
|
||||
>
|
||||
here
|
||||
</a>{' '}
|
||||
for more details
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
|
||||
Reference in New Issue
Block a user