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 <chungyau97@gmail.com>
Co-authored-by: Ong Chung Yau <33013947+chungyau97@users.noreply.github.com>
This commit is contained in:
Ilango 2025-03-24 15:20:09 +05:30 committed by GitHub
parent 7867489727
commit 4fa2672c9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 461 additions and 255 deletions

View File

@ -1,4 +1,4 @@
import { INodeParams, INodeCredential } from '../src/Interface' import { INodeCredential, INodeParams } from '../src/Interface'
class NvidiaNIMApi implements INodeCredential { class NvidiaNIMApi implements INodeCredential {
label: string label: string
@ -8,12 +8,12 @@ class NvidiaNIMApi implements INodeCredential {
inputs: INodeParams[] inputs: INodeParams[]
constructor() { constructor() {
this.label = 'Nvdia NIM API Key' this.label = 'NVIDIA NGC API Key'
this.name = 'nvidiaNIMApi' this.name = 'nvidiaNIMApi'
this.version = 1.0 this.version = 1.0
this.inputs = [ this.inputs = [
{ {
label: 'Nvidia NIM API Key', label: 'NVIDIA NGC API Key',
name: 'nvidiaNIMApiKey', name: 'nvidiaNIMApiKey',
type: 'password' type: 'password'
} }

View File

@ -1,5 +1,5 @@
import { ChatOpenAI, ChatOpenAIFields } from '@langchain/openai'
import { BaseCache } from '@langchain/core/caches' import { BaseCache } from '@langchain/core/caches'
import { ChatOpenAI, ChatOpenAIFields } from '@langchain/openai'
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface' import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils' import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
@ -16,13 +16,13 @@ class ChatNvdiaNIM_ChatModels implements INode {
inputs: INodeParams[] inputs: INodeParams[]
constructor() { constructor() {
this.label = 'Chat Nvidia NIM' this.label = 'Chat NVIDIA NIM'
this.name = 'chatNvidiaNIM' this.name = 'Chat NVIDIA NIM'
this.version = 1.0 this.version = 1.1
this.type = 'ChatNvidiaNIM' this.type = 'Chat NVIDIA NIM'
this.icon = 'nvdia.svg' this.icon = 'nvdia.svg'
this.category = 'Chat Models' 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.baseClasses = [this.type, ...getBaseClasses(ChatOpenAI)]
this.credential = { this.credential = {
label: 'Connect Credential', label: 'Connect Credential',
@ -153,7 +153,7 @@ class ChatNvdiaNIM_ChatModels implements INode {
try { try {
parsedBaseOptions = typeof baseOptions === 'object' ? baseOptions : JSON.parse(baseOptions) parsedBaseOptions = typeof baseOptions === 'object' ? baseOptions : JSON.parse(baseOptions)
} catch (exception) { } 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)
} }
} }

View File

@ -97,7 +97,7 @@
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1", "multer-s3": "^3.0.1",
"mysql2": "^3.11.3", "mysql2": "^3.11.3",
"nim-container-manager": "^1.0.5", "flowise-nim-container-manager": "^1.0.11",
"openai": "^4.82.0", "openai": "^4.82.0",
"pg": "^8.11.1", "pg": "^8.11.1",
"posthog-node": "^3.5.0", "posthog-node": "^3.5.0",

View File

@ -1,7 +1,7 @@
import axios from 'axios' 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) => { const getToken = async (req: Request, res: Response, next: NextFunction) => {
try { try {
@ -55,7 +55,13 @@ const startContainer = async (req: Request, res: Response, next: NextFunction) =
try { try {
const imageTag = req.body.imageTag const imageTag = req.body.imageTag
const apiKey = req.body.apiKey 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}`) return res.send(`Starting container ${imageTag}`)
} catch (error) { } catch (error) {
next(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) => { const getContainer = async (req: Request, res: Response, next: NextFunction) => {
try { try {
const imageTag = req.body.imageTag const imageTag = req.body.imageTag
const port = req.body.port
// First check if the image exists
const images = await NimContainerManager.userImageLibrary() const images = await NimContainerManager.userImageLibrary()
const image = images.find((img: any) => img.tag === imageTag) const image = images.find((img: any) => img.tag === imageTag)
if (!image) { if (!image) {
return res.status(404).send(`Image ${imageTag} not found`) 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) { } catch (error) {
next(error) next(error)
} }
@ -102,5 +142,7 @@ export default {
pullImage, pullImage,
startContainer, startContainer,
getImage, getImage,
getContainer getContainer,
listRunningContainers,
stopContainer
} }

View File

@ -5,6 +5,8 @@ import { InternalFlowiseError } from '../../errors/internalFlowiseError'
// we need eslint because we have to pass next arg for the error middleware // we need eslint because we have to pass next arg for the error middleware
// eslint-disable-next-line // eslint-disable-next-line
async function errorHandlerMiddleware(err: InternalFlowiseError, req: Request, res: Response, next: NextFunction) { 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 = { let displayedError = {
statusCode: err.statusCode || StatusCodes.INTERNAL_SERVER_ERROR, statusCode: err.statusCode || StatusCodes.INTERNAL_SERVER_ERROR,
success: false, success: false,

View File

@ -6,8 +6,10 @@ const router = express.Router()
router.get('/preload', nimController.preload) router.get('/preload', nimController.preload)
router.get('/get-token', nimController.getToken) router.get('/get-token', nimController.getToken)
router.get('/download-installer', nimController.downloadInstaller) router.get('/download-installer', nimController.downloadInstaller)
router.get('/list-running-containers', nimController.listRunningContainers)
router.post('/pull-image', nimController.pullImage) router.post('/pull-image', nimController.pullImage)
router.post('/start-container', nimController.startContainer) router.post('/start-container', nimController.startContainer)
router.post('/stop-container', nimController.stopContainer)
router.post('/get-image', nimController.getImage) router.post('/get-image', nimController.getImage)
router.post('/get-container', nimController.getContainer) router.post('/get-container', nimController.getContainer)

View File

@ -166,6 +166,7 @@ export class SSEStreamer implements IServerSideEventStreamer {
} }
streamErrorEvent(chatId: string, msg: string) { 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] const client = this.clients[chatId]
if (client) { if (client) {
const clientResponse = { const clientResponse = {

View File

@ -1,34 +1,39 @@
import { useState, useEffect } from 'react'
import { createPortal } from 'react-dom'
import axios from 'axios'
import PropTypes from 'prop-types'
import { import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button, Button,
CircularProgress, CircularProgress,
Stepper, Dialog,
DialogActions,
DialogContent,
DialogTitle,
FormControl,
InputLabel,
MenuItem,
Select,
Step, Step,
StepLabel, StepLabel,
Select, Stepper,
MenuItem, TextField
FormControl,
InputLabel
} from '@mui/material' } 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 NvidiaNIMDialog = ({ open, onClose, onComplete }) => {
const portalElement = document.getElementById('portal') const portalElement = document.getElementById('portal')
const modelOptions = { const modelOptions = {
'nv-mistralai/mistral-nemo-12b-instruct:latest': { 'nvcr.io/nim/meta/llama-3.1-8b-instruct:1.8.0-RTX': {
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': {
label: 'Llama 3.1 8B Instruct', label: 'Llama 3.1 8B Instruct',
licenseUrl: 'https://catalog.ngc.nvidia.com/orgs/nim/teams/meta/containers/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 [loading, setLoading] = useState(false)
const [imageTag, setImageTag] = useState('') const [imageTag, setImageTag] = useState('')
const [pollInterval, setPollInterval] = useState(null) 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'] const steps = ['Download Installer', 'Pull Image', 'Start Container']
@ -137,34 +146,63 @@ const NvidiaNIMDialog = ({ open, onClose, onComplete }) => {
try { try {
setLoading(true) setLoading(true)
try { try {
const containerResponse = await axios.post('/api/v1/nvidia-nim/get-container', { imageTag }) const containerResponse = await axios.post('/api/v1/nvidia-nim/get-container', {
if (containerResponse.data && containerResponse.data && containerResponse.data.status === 'running') { imageTag,
// wait additional 10 seconds for container to be ready port: parseInt(hostPort)
await new Promise((resolve) => setTimeout(resolve, 10000)) })
if (containerResponse.data) {
setExistingContainer(containerResponse.data)
setShowContainerConfirm(true)
setLoading(false) setLoading(false)
onComplete(containerResponse.data)
onClose()
return return
} }
} catch (err) { } 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 // Continue if container not found
if (err.response?.status !== 404) { if (err.response?.status !== 404) {
throw err 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 tokenResponse = await axios.get('/api/v1/nvidia-nim/get-token')
const apiKey = tokenResponse.data.access_token const apiKey = tokenResponse.data.access_token
await axios.post('/api/v1/nvidia-nim/start-container', { await axios.post('/api/v1/nvidia-nim/start-container', {
imageTag, imageTag,
apiKey apiKey,
nimRelaxMemConstraints: parseInt(nimRelaxMemConstraints),
hostPort: parseInt(hostPort)
}) })
// Start polling for container status // Start polling for container status
const interval = setInterval(async () => { const interval = setInterval(async () => {
try { 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) { if (containerResponse.data) {
clearInterval(interval) clearInterval(interval)
setLoading(false) 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 = () => { const handleNext = () => {
if (activeStep === 1 && !imageTag) { if (activeStep === 1 && !imageTag) {
alert('Please enter an image tag') alert('Please enter an image tag')
return 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) { switch (activeStep) {
case 0: case 0:
preload() preload()
@ -234,6 +319,7 @@ const NvidiaNIMDialog = ({ open, onClose, onComplete }) => {
}, [open]) }, [open])
const component = open ? ( const component = open ? (
<>
<Dialog open={open}> <Dialog open={open}>
<DialogTitle>NIM Setup</DialogTitle> <DialogTitle>NIM Setup</DialogTitle>
<DialogContent> <DialogContent>
@ -295,7 +381,29 @@ const NvidiaNIMDialog = ({ open, onClose, onComplete }) => {
<p>Starting container...</p> <p>Starting container...</p>
</> </>
) : ( ) : (
<p>Image is ready! Click Next to start the container.</p> <>
<FormControl fullWidth sx={{ mt: 2 }}>
<InputLabel>Relax Memory Constraints</InputLabel>
<Select
label='Relax Memory Constraints'
value={nimRelaxMemConstraints}
onChange={(e) => setNimRelaxMemConstraints(e.target.value)}
>
<MenuItem value='1'>Yes</MenuItem>
<MenuItem value='0'>No</MenuItem>
</Select>
</FormControl>
<TextField
fullWidth
type='number'
label='Host Port'
value={hostPort}
onChange={(e) => setHostPort(e.target.value)}
inputProps={{ min: 1, max: 65535 }}
sx={{ mt: 2 }}
/>
<p style={{ marginTop: 20 }}>Click Next to start the container.</p>
</>
)} )}
</div> </div>
)} )}
@ -309,11 +417,52 @@ const NvidiaNIMDialog = ({ open, onClose, onComplete }) => {
Next Next
</Button> </Button>
)} )}
<Button onClick={activeStep === 0 ? handleDownloadInstaller : handleNext} disabled={loading}> <Button
onClick={activeStep === 0 ? handleDownloadInstaller : handleNext}
disabled={loading || (activeStep === 2 && (!nimRelaxMemConstraints || !hostPort))}
>
{activeStep === 0 ? 'Download' : 'Next'} {activeStep === 0 ? 'Download' : 'Next'}
</Button> </Button>
</DialogActions> </DialogActions>
</Dialog> </Dialog>
<Dialog open={showContainerConfirm} onClose={() => setShowContainerConfirm(false)}>
<DialogTitle>Container Already Exists</DialogTitle>
<DialogContent>
<p>A container for this image already exists:</p>
<div>
<p>
<strong>Name:</strong> {existingContainer?.name || 'N/A'}
</p>
<p>
<strong>Status:</strong> {existingContainer?.status || 'N/A'}
</p>
</div>
<p>You can:</p>
<ul>
<li>Use the existing container (recommended)</li>
<li>Change the port and try again</li>
</ul>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setShowContainerConfirm(false)
setExistingContainer(null)
}}
>
Cancel
</Button>
<Button
onClick={() => {
setShowContainerConfirm(false)
handleUseExistingContainer()
}}
>
Use Existing
</Button>
</DialogActions>
</Dialog>
</>
) : null ) : null
return createPortal(component, portalElement) return createPortal(component, portalElement)

View File

@ -1,46 +1,46 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { Handle, Position, useUpdateNodeInternals } from 'reactflow' import { useContext, useEffect, useRef, useState } from 'react'
import { useEffect, useRef, useState, useContext } from 'react'
import { useSelector } from 'react-redux' import { useSelector } from 'react-redux'
import { Handle, Position, useUpdateNodeInternals } from 'reactflow'
// material-ui // 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 { 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 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 // 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 { Dropdown } from '@/ui-component/dropdown/Dropdown'
import { MultiDropdown } from '@/ui-component/dropdown/MultiDropdown' 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 { File } from '@/ui-component/file/File'
import { SwitchInput } from '@/ui-component/switch/Switch'
import { flowContext } from '@/store/context/ReactFlowContext'
import { JsonEditorInput } from '@/ui-component/json/JsonEditor'
import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser'
import { CodeEditor } from '@/ui-component/editor/CodeEditor' import { CodeEditor } from '@/ui-component/editor/CodeEditor'
import { File } from '@/ui-component/file/File'
import { DataGrid } from '@/ui-component/grid/DataGrid'
import { Input } from '@/ui-component/input/Input'
import { JsonEditorInput } from '@/ui-component/json/JsonEditor'
import { SwitchInput } from '@/ui-component/switch/Switch'
import { Tab } from '@/ui-component/tabs/Tab'
import { TabPanel } from '@/ui-component/tabs/TabPanel' import { TabPanel } from '@/ui-component/tabs/TabPanel'
import { TabsList } from '@/ui-component/tabs/TabsList' import { TabsList } from '@/ui-component/tabs/TabsList'
import { Tab } from '@/ui-component/tabs/Tab' import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser'
import ToolDialog from '@/views/tools/ToolDialog'
import AssistantDialog from '@/views/assistants/openai/AssistantDialog' import AssistantDialog from '@/views/assistants/openai/AssistantDialog'
import FormatPromptValuesDialog from '@/ui-component/dialog/FormatPromptValuesDialog' import ToolDialog from '@/views/tools/ToolDialog'
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 CredentialInputHandler from './CredentialInputHandler' import CredentialInputHandler from './CredentialInputHandler'
import InputHintDialog from '@/ui-component/dialog/InputHintDialog'
import NvidiaNIMDialog from '@/ui-component/dialog/NvidiaNIMDialog'
// utils // utils
import { getInputVariables, getCustomConditionOutputs, isValidConnection, getAvailableNodesForVariable } from '@/utils/genericHelper' import { getAvailableNodesForVariable, getCustomConditionOutputs, getInputVariables, isValidConnection } from '@/utils/genericHelper'
// const // const
import { FLOWISE_CREDENTIAL_ID } from '@/store/constant' import { FLOWISE_CREDENTIAL_ID } from '@/store/constant'
@ -537,7 +537,7 @@ const NodeInputHandler = ({
></PromptLangsmithHubDialog> ></PromptLangsmithHubDialog>
</> </>
)} )}
{data.name === 'chatNvidiaNIM' && inputParam.name === 'modelName' && ( {data.name === 'Chat NVIDIA NIM' && inputParam.name === 'modelName' && (
<> <>
<Button <Button
style={{ style={{

File diff suppressed because one or more lines are too long