From 78674897270d58a7086c6c7ccefcc44a5fe9fbf6 Mon Sep 17 00:00:00 2001 From: Saqib Ahmed Date: Fri, 14 Mar 2025 20:24:17 +0500 Subject: [PATCH 01/43] Add curl in docker image for native healthchecks (#4170) * Add curl in docker image for native healthchecks Fixes this issue: https://github.com/FlowiseAI/Flowise/issues/4126 * fix: add container-native healthcheck - Added curl utility in dockerfile - Added healthcheck configuration in docker compose * fix: exclude ping endpoint from logging --- Dockerfile | 4 ++++ docker/Dockerfile | 2 +- docker/docker-compose.yml | 6 ++++++ packages/server/src/utils/logger.ts | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index dfbf58d1bd0..a824b7f8090 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,10 @@ RUN apk add --no-cache build-base cairo-dev pango-dev # Install Chromium RUN apk add --no-cache chromium +# Install curl for container-level health checks +# Fixes: https://github.com/FlowiseAI/Flowise/issues/4126 +RUN apk add --no-cache curl + #install PNPM globaly RUN npm install -g pnpm diff --git a/docker/Dockerfile b/docker/Dockerfile index 762e3d29641..82a55d6a2b4 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -13,7 +13,7 @@ RUN npm install -g flowise FROM node:20-alpine # Install runtime dependencies -RUN apk add --no-cache chromium git python3 py3-pip make g++ build-base cairo-dev pango-dev +RUN apk add --no-cache chromium git python3 py3-pip make g++ build-base cairo-dev pango-dev curl # Set the environment variable for Puppeteer to find Chromium ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 42b81bab29c..a9bda9e272f 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -49,6 +49,12 @@ services: - REDIS_CA=${REDIS_CA} ports: - '${PORT}:${PORT}' + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:${PORT}/api/v1/ping"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s volumes: - ~/.flowise:/root/.flowise entrypoint: /bin/sh -c "sleep 3; flowise start" diff --git a/packages/server/src/utils/logger.ts b/packages/server/src/utils/logger.ts index c49670aa05a..4d13e7892b5 100644 --- a/packages/server/src/utils/logger.ts +++ b/packages/server/src/utils/logger.ts @@ -138,7 +138,7 @@ const logger = createLogger({ }) export function expressRequestLogger(req: Request, res: Response, next: NextFunction): void { - const unwantedLogURLs = ['/api/v1/node-icon/', '/api/v1/components-credentials-icon/'] + const unwantedLogURLs = ['/api/v1/node-icon/', '/api/v1/components-credentials-icon/', '/api/v1/ping'] if (/\/api\/v1\//i.test(req.url) && !unwantedLogURLs.some((url) => new RegExp(url, 'i').test(req.url))) { const fileLogger = createLogger({ format: combine(timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), format.json(), errors({ stack: true })), From 4fa2672c9d50184379328b9894c5e67d2dc462ce Mon Sep 17 00:00:00 2001 From: Ilango Date: Mon, 24 Mar 2025 15:20:09 +0530 Subject: [PATCH 02/43] NVIDIA NIM fixes (#4215) * fix: udpate label to "NVIDIA NIM API Key" * test: update tag from ":latest" to ":1.8.0-rtx" * test: add image URL path "nvcr.io/nim/" * fix/nvidia-nim-2 (#4208) * fix: update nim-container-manager * feat: add "DeepSeek R1 Distill Llama 8B" * fix/nidia-nim-3 (#4209) * chore: add error message NVIDIA NIM is not installed. * chore: standardize NVIDIA NGC API Key * chore: capitalize Nvidia to NVIDIA * chore: generalize error message for chat models * fix/nvidia-nim-4-yau (#4212) * test: nimRelaxMemConstraints and hostPort * test: add logger for hostPort and nimRelaxMemConstraints * test: nim-container-manager version 1.0.9 * test: parseInt nimRelaxMemConstraints * test: update nim-container-manager version to 1.0.10 * chore: update nim-container-manager version to 1.0.11 * Update start container behaviour - show existing containers and give users the choice * Go back to previous step when clicking start new so user can change port number * Update condition for showing existing container dialog * Fix start new in different port not working * Update get container controller * Update again * fix: generalize error message for chat models * Update getContainer controller * Fix incorrect image check in getContainer controller * Update existing container dialog text * Fix styles in container exists dialog for nvidia nim --------- Co-authored-by: chungyau97 Co-authored-by: Ong Chung Yau <33013947+chungyau97@users.noreply.github.com> --- .../credentials/NvdiaNIMApi.credential.ts | 6 +- .../chatmodels/ChatNvdiaNIM/ChatNvdiaNIM.ts | 14 +- packages/server/package.json | 2 +- .../src/controllers/nvidia-nim/index.ts | 60 ++- .../server/src/middlewares/errors/index.ts | 2 + .../server/src/routes/nvidia-nim/index.ts | 2 + packages/server/src/utils/SSEStreamer.ts | 1 + .../ui-component/dialog/NvidiaNIMDialog.jsx | 349 +++++++++++++----- .../ui/src/views/canvas/NodeInputHandler.jsx | 52 +-- pnpm-lock.yaml | 222 +++++------ 10 files changed, 458 insertions(+), 252 deletions(-) diff --git a/packages/components/credentials/NvdiaNIMApi.credential.ts b/packages/components/credentials/NvdiaNIMApi.credential.ts index 3cae6961cfc..4910032df78 100644 --- a/packages/components/credentials/NvdiaNIMApi.credential.ts +++ b/packages/components/credentials/NvdiaNIMApi.credential.ts @@ -1,4 +1,4 @@ -import { INodeParams, INodeCredential } from '../src/Interface' +import { INodeCredential, INodeParams } from '../src/Interface' class NvidiaNIMApi implements INodeCredential { label: string @@ -8,12 +8,12 @@ class NvidiaNIMApi implements INodeCredential { inputs: INodeParams[] constructor() { - this.label = 'Nvdia NIM API Key' + this.label = 'NVIDIA NGC API Key' this.name = 'nvidiaNIMApi' this.version = 1.0 this.inputs = [ { - label: 'Nvidia NIM API Key', + label: 'NVIDIA NGC API Key', name: 'nvidiaNIMApiKey', type: 'password' } diff --git a/packages/components/nodes/chatmodels/ChatNvdiaNIM/ChatNvdiaNIM.ts b/packages/components/nodes/chatmodels/ChatNvdiaNIM/ChatNvdiaNIM.ts index 86fdebfbf31..b4636ad3d93 100644 --- a/packages/components/nodes/chatmodels/ChatNvdiaNIM/ChatNvdiaNIM.ts +++ b/packages/components/nodes/chatmodels/ChatNvdiaNIM/ChatNvdiaNIM.ts @@ -1,5 +1,5 @@ -import { ChatOpenAI, ChatOpenAIFields } from '@langchain/openai' import { BaseCache } from '@langchain/core/caches' +import { ChatOpenAI, ChatOpenAIFields } from '@langchain/openai' import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' @@ -16,13 +16,13 @@ class ChatNvdiaNIM_ChatModels implements INode { inputs: INodeParams[] constructor() { - this.label = 'Chat Nvidia NIM' - this.name = 'chatNvidiaNIM' - this.version = 1.0 - this.type = 'ChatNvidiaNIM' + this.label = 'Chat NVIDIA NIM' + this.name = 'Chat NVIDIA NIM' + this.version = 1.1 + this.type = 'Chat NVIDIA NIM' this.icon = 'nvdia.svg' this.category = 'Chat Models' - this.description = 'Wrapper around Nvdia NIM Inference API' + this.description = 'Wrapper around NVIDIA NIM Inference API' this.baseClasses = [this.type, ...getBaseClasses(ChatOpenAI)] this.credential = { label: 'Connect Credential', @@ -153,7 +153,7 @@ class ChatNvdiaNIM_ChatModels implements INode { try { parsedBaseOptions = typeof baseOptions === 'object' ? baseOptions : JSON.parse(baseOptions) } catch (exception) { - throw new Error("Invalid JSON in the ChatNvidiaNIM's baseOptions: " + exception) + throw new Error("Invalid JSON in the Chat NVIDIA NIM's baseOptions: " + exception) } } diff --git a/packages/server/package.json b/packages/server/package.json index 9e8308f8025..0bd7ff19053 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -97,7 +97,7 @@ "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "mysql2": "^3.11.3", - "nim-container-manager": "^1.0.5", + "flowise-nim-container-manager": "^1.0.11", "openai": "^4.82.0", "pg": "^8.11.1", "posthog-node": "^3.5.0", diff --git a/packages/server/src/controllers/nvidia-nim/index.ts b/packages/server/src/controllers/nvidia-nim/index.ts index ce99c14abc8..54bc4a3f6cd 100644 --- a/packages/server/src/controllers/nvidia-nim/index.ts +++ b/packages/server/src/controllers/nvidia-nim/index.ts @@ -1,7 +1,7 @@ import axios from 'axios' -import { Request, Response, NextFunction } from 'express' +import { NextFunction, Request, Response } from 'express' -const { NimContainerManager } = require('nim-container-manager') +const { NimContainerManager } = require('flowise-nim-container-manager') const getToken = async (req: Request, res: Response, next: NextFunction) => { try { @@ -55,7 +55,13 @@ const startContainer = async (req: Request, res: Response, next: NextFunction) = try { const imageTag = req.body.imageTag const apiKey = req.body.apiKey - await NimContainerManager.startContainer(imageTag, apiKey) + const hostPort = req.body.hostPort + const nimRelaxMemConstraints = parseInt(req.body.nimRelaxMemConstraints) + // Validate nimRelaxMemConstraints + if (isNaN(nimRelaxMemConstraints) || (nimRelaxMemConstraints !== 0 && nimRelaxMemConstraints !== 1)) { + return res.status(400).send('nimRelaxMemConstraints must be 0 or 1') + } + await NimContainerManager.startContainer(imageTag, apiKey, hostPort, nimRelaxMemConstraints) return res.send(`Starting container ${imageTag}`) } catch (error) { next(error) @@ -79,17 +85,51 @@ const getImage = async (req: Request, res: Response, next: NextFunction) => { const getContainer = async (req: Request, res: Response, next: NextFunction) => { try { const imageTag = req.body.imageTag + const port = req.body.port + + // First check if the image exists const images = await NimContainerManager.userImageLibrary() const image = images.find((img: any) => img.tag === imageTag) if (!image) { return res.status(404).send(`Image ${imageTag} not found`) } - if (!image.container) { - return res.status(404).send(`Container of ${imageTag} not found`) + + const containers = await NimContainerManager.listRunningContainers() + const portInUse = containers.find((cont: any) => cont.port === port) + if (portInUse) { + const isModelContainer = portInUse.image === image.tag + if (isModelContainer) { + portInUse.image = image.name + return res.json(portInUse) + } else { + return res.status(409).send({ + message: `Port ${port} is already in use by another container`, + container: portInUse + }) + } } - const container = image.container - container.image = image.name - return res.json(container) + + // If no container found with matching port, return 404 + return res.status(404).send(`Container of ${imageTag} with port ${port} not found`) + } catch (error) { + next(error) + } +} + +const listRunningContainers = async (req: Request, res: Response, next: NextFunction) => { + try { + const containers = await NimContainerManager.listRunningContainers() + return res.json(containers) + } catch (error) { + next(error) + } +} + +const stopContainer = async (req: Request, res: Response, next: NextFunction) => { + try { + const containerId = req.body.containerId + const containerInfo = await NimContainerManager.stopContainer(containerId) + return res.json(containerInfo) } catch (error) { next(error) } @@ -102,5 +142,7 @@ export default { pullImage, startContainer, getImage, - getContainer + getContainer, + listRunningContainers, + stopContainer } diff --git a/packages/server/src/middlewares/errors/index.ts b/packages/server/src/middlewares/errors/index.ts index 2f649843991..88b3dd80cb7 100644 --- a/packages/server/src/middlewares/errors/index.ts +++ b/packages/server/src/middlewares/errors/index.ts @@ -5,6 +5,8 @@ import { InternalFlowiseError } from '../../errors/internalFlowiseError' // we need eslint because we have to pass next arg for the error middleware // eslint-disable-next-line async function errorHandlerMiddleware(err: InternalFlowiseError, req: Request, res: Response, next: NextFunction) { + if (err.message.includes('401 Incorrect API key provided')) + err.message = '401 Invalid model key or Incorrect local model configuration.' let displayedError = { statusCode: err.statusCode || StatusCodes.INTERNAL_SERVER_ERROR, success: false, diff --git a/packages/server/src/routes/nvidia-nim/index.ts b/packages/server/src/routes/nvidia-nim/index.ts index 9f695db4c63..473b57156e9 100644 --- a/packages/server/src/routes/nvidia-nim/index.ts +++ b/packages/server/src/routes/nvidia-nim/index.ts @@ -6,8 +6,10 @@ const router = express.Router() router.get('/preload', nimController.preload) router.get('/get-token', nimController.getToken) router.get('/download-installer', nimController.downloadInstaller) +router.get('/list-running-containers', nimController.listRunningContainers) router.post('/pull-image', nimController.pullImage) router.post('/start-container', nimController.startContainer) +router.post('/stop-container', nimController.stopContainer) router.post('/get-image', nimController.getImage) router.post('/get-container', nimController.getContainer) diff --git a/packages/server/src/utils/SSEStreamer.ts b/packages/server/src/utils/SSEStreamer.ts index a5327bad1f0..6240d0a04c4 100644 --- a/packages/server/src/utils/SSEStreamer.ts +++ b/packages/server/src/utils/SSEStreamer.ts @@ -166,6 +166,7 @@ export class SSEStreamer implements IServerSideEventStreamer { } streamErrorEvent(chatId: string, msg: string) { + if (msg.includes('401 Incorrect API key provided')) msg = '401 Invalid model key or Incorrect local model configuration.' const client = this.clients[chatId] if (client) { const clientResponse = { diff --git a/packages/ui/src/ui-component/dialog/NvidiaNIMDialog.jsx b/packages/ui/src/ui-component/dialog/NvidiaNIMDialog.jsx index aca39c2bbb7..a0f7b9e1dd7 100644 --- a/packages/ui/src/ui-component/dialog/NvidiaNIMDialog.jsx +++ b/packages/ui/src/ui-component/dialog/NvidiaNIMDialog.jsx @@ -1,34 +1,39 @@ -import { useState, useEffect } from 'react' -import { createPortal } from 'react-dom' -import axios from 'axios' -import PropTypes from 'prop-types' import { - Dialog, - DialogTitle, - DialogContent, - DialogActions, Button, CircularProgress, - Stepper, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + FormControl, + InputLabel, + MenuItem, + Select, Step, StepLabel, - Select, - MenuItem, - FormControl, - InputLabel + Stepper, + TextField } from '@mui/material' +import axios from 'axios' +import PropTypes from 'prop-types' +import { useEffect, useState } from 'react' +import { createPortal } from 'react-dom' const NvidiaNIMDialog = ({ open, onClose, onComplete }) => { const portalElement = document.getElementById('portal') const modelOptions = { - 'nv-mistralai/mistral-nemo-12b-instruct:latest': { - label: 'Mistral Nemo 12B Instruct', - licenseUrl: 'https://catalog.ngc.nvidia.com/orgs/nvidia/teams/nv-mistralai/containers/mistral-nemo-12b-instruct' - }, - 'meta/llama-3.1-8b-instruct-rtx:latest': { + 'nvcr.io/nim/meta/llama-3.1-8b-instruct:1.8.0-RTX': { label: 'Llama 3.1 8B Instruct', licenseUrl: 'https://catalog.ngc.nvidia.com/orgs/nim/teams/meta/containers/llama-3.1-8b-instruct' + }, + 'nvcr.io/nim/deepseek-ai/deepseek-r1-distill-llama-8b:1.8.0-RTX': { + label: 'DeepSeek R1 Distill Llama 8B', + licenseUrl: 'https://catalog.ngc.nvidia.com/orgs/nim/teams/deepseek-ai/containers/deepseek-r1-distill-llama-8b' + }, + 'nvcr.io/nim/nv-mistralai/mistral-nemo-12b-instruct:1.8.0-rtx': { + label: 'Mistral Nemo 12B Instruct', + licenseUrl: 'https://catalog.ngc.nvidia.com/orgs/nim/teams/nv-mistralai/containers/mistral-nemo-12b-instruct' } } @@ -36,6 +41,10 @@ const NvidiaNIMDialog = ({ open, onClose, onComplete }) => { const [loading, setLoading] = useState(false) const [imageTag, setImageTag] = useState('') const [pollInterval, setPollInterval] = useState(null) + const [nimRelaxMemConstraints, setNimRelaxMemConstraints] = useState('0') + const [hostPort, setHostPort] = useState('8080') + const [showContainerConfirm, setShowContainerConfirm] = useState(false) + const [existingContainer, setExistingContainer] = useState(null) const steps = ['Download Installer', 'Pull Image', 'Start Container'] @@ -137,34 +146,63 @@ const NvidiaNIMDialog = ({ open, onClose, onComplete }) => { try { setLoading(true) try { - const containerResponse = await axios.post('/api/v1/nvidia-nim/get-container', { imageTag }) - if (containerResponse.data && containerResponse.data && containerResponse.data.status === 'running') { - // wait additional 10 seconds for container to be ready - await new Promise((resolve) => setTimeout(resolve, 10000)) + const containerResponse = await axios.post('/api/v1/nvidia-nim/get-container', { + imageTag, + port: parseInt(hostPort) + }) + if (containerResponse.data) { + setExistingContainer(containerResponse.data) + setShowContainerConfirm(true) setLoading(false) - onComplete(containerResponse.data) - onClose() return } } catch (err) { + // Handle port in use by non-model container + if (err.response?.status === 409) { + alert(`Port ${hostPort} is already in use by another container. Please choose a different port.`) + setLoading(false) + return + } // Continue if container not found if (err.response?.status !== 404) { throw err } } + // No container found with this port, proceed with starting new container + await startNewContainer() + } catch (err) { + let errorData = err.message + if (typeof err === 'string') { + errorData = err + } else if (err.response?.data) { + errorData = err.response.data.message + } + alert('Failed to check container status: ' + errorData) + setLoading(false) + } + } + + const startNewContainer = async () => { + try { + setLoading(true) const tokenResponse = await axios.get('/api/v1/nvidia-nim/get-token') const apiKey = tokenResponse.data.access_token await axios.post('/api/v1/nvidia-nim/start-container', { imageTag, - apiKey + apiKey, + nimRelaxMemConstraints: parseInt(nimRelaxMemConstraints), + hostPort: parseInt(hostPort) }) // Start polling for container status const interval = setInterval(async () => { try { - const containerResponse = await axios.post('/api/v1/nvidia-nim/get-container', { imageTag }) + const containerResponse = await axios.post('/api/v1/nvidia-nim/get-container', { + imageTag, + port: parseInt(hostPort) + }) if (containerResponse.data) { clearInterval(interval) setLoading(false) @@ -194,12 +232,59 @@ const NvidiaNIMDialog = ({ open, onClose, onComplete }) => { } } + const handleUseExistingContainer = async () => { + try { + setLoading(true) + // Start polling for container status + const interval = setInterval(async () => { + try { + const containerResponse = await axios.post('/api/v1/nvidia-nim/get-container', { + imageTag, + port: parseInt(hostPort) + }) + if (containerResponse.data) { + clearInterval(interval) + setLoading(false) + onComplete(containerResponse.data) + onClose() + } + } catch (err) { + // Continue polling if container not found + if (err.response?.status !== 404) { + clearInterval(interval) + alert('Failed to check container status: ' + err.message) + setLoading(false) + } + } + }, 5000) + + setPollInterval(interval) + } catch (err) { + let errorData = err.message + if (typeof err === 'string') { + errorData = err + } else if (err.response?.data) { + errorData = err.response.data.message + } + alert('Failed to check container status: ' + errorData) + setLoading(false) + } + } + const handleNext = () => { if (activeStep === 1 && !imageTag) { alert('Please enter an image tag') return } + if (activeStep === 2) { + const port = parseInt(hostPort) + if (isNaN(port) || port < 1 || port > 65535) { + alert('Please enter a valid port number between 1 and 65535') + return + } + } + switch (activeStep) { case 0: preload() @@ -234,86 +319,150 @@ const NvidiaNIMDialog = ({ open, onClose, onComplete }) => { }, [open]) const component = open ? ( - - NIM Setup - - - {steps.map((label) => ( - - {label} - - ))} - + <> + + NIM Setup + + + {steps.map((label) => ( + + {label} + + ))} + - {activeStep === 0 && ( -
-

- Would you like to download the NIM installer? Click Next if it has been installed -

- {loading && } -
- )} + {activeStep === 0 && ( +
+

+ Would you like to download the NIM installer? Click Next if it has been installed +

+ {loading && } +
+ )} - {activeStep === 1 && ( -
- - Model - - - {imageTag && ( - - )} - {loading && ( -
-
- -

Pulling image...

-
- )} -
- )} + {activeStep === 1 && ( +
+ + Model + + + {imageTag && ( + + )} + {loading && ( +
+
+ +

Pulling image...

+
+ )} +
+ )} - {activeStep === 2 && ( + {activeStep === 2 && ( +
+ {loading ? ( + <> +
+ +

Starting container...

+ + ) : ( + <> + + Relax Memory Constraints + + + setHostPort(e.target.value)} + inputProps={{ min: 1, max: 65535 }} + sx={{ mt: 2 }} + /> +

Click Next to start the container.

+ + )} +
+ )} + + + + {activeStep === 0 && ( + + )} + + +
+ setShowContainerConfirm(false)}> + Container Already Exists + +

A container for this image already exists:

- {loading ? ( - <> -
- -

Starting container...

- - ) : ( -

Image is ready! Click Next to start the container.

- )} +

+ Name: {existingContainer?.name || 'N/A'} +

+

+ Status: {existingContainer?.status || 'N/A'} +

- )} - - - - {activeStep === 0 && ( - + - )} - - -
+ +
+ ) : null return createPortal(component, portalElement) diff --git a/packages/ui/src/views/canvas/NodeInputHandler.jsx b/packages/ui/src/views/canvas/NodeInputHandler.jsx index b21592f0ef4..613666b4c82 100644 --- a/packages/ui/src/views/canvas/NodeInputHandler.jsx +++ b/packages/ui/src/views/canvas/NodeInputHandler.jsx @@ -1,46 +1,46 @@ import PropTypes from 'prop-types' -import { Handle, Position, useUpdateNodeInternals } from 'reactflow' -import { useEffect, useRef, useState, useContext } from 'react' +import { useContext, useEffect, useRef, useState } from 'react' import { useSelector } from 'react-redux' +import { Handle, Position, useUpdateNodeInternals } from 'reactflow' // material-ui -import { useTheme, styled } from '@mui/material/styles' -import { Popper, Box, Typography, Tooltip, IconButton, Button, TextField } from '@mui/material' -import { useGridApiContext } from '@mui/x-data-grid' -import IconAutoFixHigh from '@mui/icons-material/AutoFixHigh' -import { tooltipClasses } from '@mui/material/Tooltip' -import { IconArrowsMaximize, IconEdit, IconAlertTriangle, IconBulb, IconRefresh } from '@tabler/icons-react' import { Tabs } from '@mui/base/Tabs' +import IconAutoFixHigh from '@mui/icons-material/AutoFixHigh' +import { Box, Button, IconButton, Popper, TextField, Tooltip, Typography } from '@mui/material' import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete' +import { styled, useTheme } from '@mui/material/styles' +import { tooltipClasses } from '@mui/material/Tooltip' +import { useGridApiContext } from '@mui/x-data-grid' +import { IconAlertTriangle, IconArrowsMaximize, IconBulb, IconEdit, IconRefresh } from '@tabler/icons-react' // project import +import { flowContext } from '@/store/context/ReactFlowContext' +import ConditionDialog from '@/ui-component/dialog/ConditionDialog' +import ExpandTextDialog from '@/ui-component/dialog/ExpandTextDialog' +import FormatPromptValuesDialog from '@/ui-component/dialog/FormatPromptValuesDialog' +import InputHintDialog from '@/ui-component/dialog/InputHintDialog' +import ManageScrapedLinksDialog from '@/ui-component/dialog/ManageScrapedLinksDialog' +import NvidiaNIMDialog from '@/ui-component/dialog/NvidiaNIMDialog' +import PromptLangsmithHubDialog from '@/ui-component/dialog/PromptLangsmithHubDialog' +import { AsyncDropdown } from '@/ui-component/dropdown/AsyncDropdown' import { Dropdown } from '@/ui-component/dropdown/Dropdown' import { MultiDropdown } from '@/ui-component/dropdown/MultiDropdown' -import { AsyncDropdown } from '@/ui-component/dropdown/AsyncDropdown' -import { Input } from '@/ui-component/input/Input' -import { DataGrid } from '@/ui-component/grid/DataGrid' +import { CodeEditor } from '@/ui-component/editor/CodeEditor' import { File } from '@/ui-component/file/File' -import { SwitchInput } from '@/ui-component/switch/Switch' -import { flowContext } from '@/store/context/ReactFlowContext' +import { DataGrid } from '@/ui-component/grid/DataGrid' +import { Input } from '@/ui-component/input/Input' import { JsonEditorInput } from '@/ui-component/json/JsonEditor' -import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser' -import { CodeEditor } from '@/ui-component/editor/CodeEditor' +import { SwitchInput } from '@/ui-component/switch/Switch' +import { Tab } from '@/ui-component/tabs/Tab' import { TabPanel } from '@/ui-component/tabs/TabPanel' import { TabsList } from '@/ui-component/tabs/TabsList' -import { Tab } from '@/ui-component/tabs/Tab' -import ToolDialog from '@/views/tools/ToolDialog' +import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser' import AssistantDialog from '@/views/assistants/openai/AssistantDialog' -import FormatPromptValuesDialog from '@/ui-component/dialog/FormatPromptValuesDialog' -import ExpandTextDialog from '@/ui-component/dialog/ExpandTextDialog' -import ConditionDialog from '@/ui-component/dialog/ConditionDialog' -import PromptLangsmithHubDialog from '@/ui-component/dialog/PromptLangsmithHubDialog' -import ManageScrapedLinksDialog from '@/ui-component/dialog/ManageScrapedLinksDialog' +import ToolDialog from '@/views/tools/ToolDialog' import CredentialInputHandler from './CredentialInputHandler' -import InputHintDialog from '@/ui-component/dialog/InputHintDialog' -import NvidiaNIMDialog from '@/ui-component/dialog/NvidiaNIMDialog' // utils -import { getInputVariables, getCustomConditionOutputs, isValidConnection, getAvailableNodesForVariable } from '@/utils/genericHelper' +import { getAvailableNodesForVariable, getCustomConditionOutputs, getInputVariables, isValidConnection } from '@/utils/genericHelper' // const import { FLOWISE_CREDENTIAL_ID } from '@/store/constant' @@ -537,7 +537,7 @@ const NodeInputHandler = ({ > )} - {data.name === 'chatNvidiaNIM' && inputParam.name === 'modelName' && ( + {data.name === 'Chat NVIDIA NIM' && inputParam.name === 'modelName' && ( <>