From 3b87b7ae3a4ff9e9857141025f1afbe44aaee348 Mon Sep 17 00:00:00 2001 From: Faisal Amir Date: Thu, 15 Aug 2024 21:21:10 +0700 Subject: [PATCH] chore: update model dropdown and my model with collapsible chevron --- core/src/types/model/modelEntity.ts | 4 + web/containers/ModelDropdown/index.tsx | 486 ++++++++++-------- web/containers/SetupRemoteModel/index.tsx | 21 +- web/helpers/atoms/Model.atom.ts | 6 +- web/public/images/ModelProvider/anthropic.svg | 10 +- web/public/images/ModelProvider/cohere.svg | 39 +- web/public/images/ModelProvider/cortex.svg | 14 + web/public/images/ModelProvider/dot.svg | 3 + web/public/images/ModelProvider/google.svg | 9 + web/public/images/ModelProvider/groq.svg | 9 + .../images/ModelProvider/hugging-face.svg | 8 + web/public/images/ModelProvider/martian.svg | 12 +- web/public/images/ModelProvider/meta.svg | 72 +++ web/public/images/ModelProvider/mistral.svg | 58 +-- web/public/images/ModelProvider/nitro.svg | 6 + web/public/images/ModelProvider/nvidia.svg | 10 + .../images/ModelProvider/openRouter.svg | 14 + web/public/images/ModelProvider/openai.svg | 33 +- web/public/images/ModelProvider/send.svg | 3 + .../Settings/MyModels/MyModelList/index.tsx | 27 +- web/screens/Settings/MyModels/index.tsx | 174 +++++-- web/utils/modelEngine.ts | 66 +++ 22 files changed, 716 insertions(+), 368 deletions(-) create mode 100644 web/public/images/ModelProvider/cortex.svg create mode 100644 web/public/images/ModelProvider/dot.svg create mode 100644 web/public/images/ModelProvider/google.svg create mode 100644 web/public/images/ModelProvider/groq.svg create mode 100644 web/public/images/ModelProvider/hugging-face.svg create mode 100644 web/public/images/ModelProvider/meta.svg create mode 100644 web/public/images/ModelProvider/nitro.svg create mode 100644 web/public/images/ModelProvider/nvidia.svg create mode 100644 web/public/images/ModelProvider/openRouter.svg create mode 100644 web/public/images/ModelProvider/send.svg create mode 100644 web/utils/modelEngine.ts diff --git a/core/src/types/model/modelEntity.ts b/core/src/types/model/modelEntity.ts index 426b30846..f3520a4e0 100644 --- a/core/src/types/model/modelEntity.ts +++ b/core/src/types/model/modelEntity.ts @@ -25,6 +25,10 @@ export enum InferenceEngine { triton_trtllm = 'triton_trtllm', nitro_tensorrt_llm = 'nitro-tensorrt-llm', cohere = 'cohere', + nvdia = 'nvidia', + cortex_llamacpp = 'cortex.llamacpp', + cortex_onnx = 'cortex.onnx', + cortex_tensorrtllm = 'cortex.tensorrtllm', } export type ModelArtifact = { diff --git a/web/containers/ModelDropdown/index.tsx b/web/containers/ModelDropdown/index.tsx index c19fb64bd..981ce3276 100644 --- a/web/containers/ModelDropdown/index.tsx +++ b/web/containers/ModelDropdown/index.tsx @@ -1,11 +1,25 @@ import { useState, useMemo, useEffect, useCallback, useRef } from 'react' +import Image from 'next/image' + import { InferenceEngine } from '@janhq/core' -import { Badge, Input, ScrollArea, Select, useClickOutside } from '@janhq/joi' +import { + Badge, + Button, + Input, + ScrollArea, + Select, + useClickOutside, +} from '@janhq/joi' import { useAtom, useAtomValue, useSetAtom } from 'jotai' -import { ChevronDownIcon, DownloadCloudIcon, XIcon } from 'lucide-react' +import { + ChevronDownIcon, + ChevronUpIcon, + DownloadCloudIcon, + XIcon, +} from 'lucide-react' import { twMerge } from 'tailwind-merge' import ProgressCircle from '@/containers/Loader/ProgressCircle' @@ -22,6 +36,13 @@ import useUpdateModelParameters from '@/hooks/useUpdateModelParameters' import { formatDownloadPercentage, toGibibytes } from '@/utils/converter' +import { + getLogoEngine, + getTitleByEngine, + localEngines, + priorityEngine, +} from '@/utils/modelEngine' + import { extensionManager } from '@/extension' import { inActiveEngineProviderAtom } from '@/helpers/atoms/Extension.atom' @@ -29,6 +50,7 @@ import { configuredModelsAtom, getDownloadingModelAtom, selectedModelAtom, + showEngineListModelAtom, } from '@/helpers/atoms/Model.atom' import { activeThreadAtom, @@ -41,14 +63,6 @@ type Props = { disabled?: boolean } -const engineHasLogo = [ - InferenceEngine.anthropic, - InferenceEngine.cohere, - InferenceEngine.martian, - InferenceEngine.mistral, - InferenceEngine.openai, -] - const ModelDropdown = ({ disabled, chatInputMode, @@ -81,6 +95,10 @@ const ModelDropdown = ({ toggle, ]) + const [showEngineListModel, setShowEngineListModel] = useAtom( + showEngineListModelAtom + ) + const filteredDownloadedModels = useMemo( () => configuredModels @@ -92,16 +110,10 @@ const ModelDropdown = ({ return e.engine } if (searchFilter === 'local') { - return ( - e.engine === InferenceEngine.nitro || - e.engine === InferenceEngine.nitro_tensorrt_llm - ) + return localEngines.includes(e.engine) } if (searchFilter === 'remote') { - return ( - e.engine !== InferenceEngine.nitro && - e.engine !== InferenceEngine.nitro_tensorrt_llm - ) + return !localEngines.includes(e.engine) } }) .sort((a, b) => a.name.localeCompare(b.name)) @@ -230,10 +242,37 @@ const ModelDropdown = ({ .filter((x) => !inActiveEngineProvider.includes(x.engine)) .map((x) => x.engine) - const groupByEngine = findByEngine.filter(function (item, index) { - if (findByEngine.indexOf(item) === index) - return item !== InferenceEngine.nitro - }) + const groupByEngine = findByEngine + .filter(function (item, index) { + if (findByEngine.indexOf(item) === index) return item + }) + .sort((a, b) => { + if (priorityEngine.includes(a) && priorityEngine.includes(b)) { + return priorityEngine.indexOf(a) - priorityEngine.indexOf(b) + } else if (priorityEngine.includes(a)) { + return -1 + } else if (priorityEngine.includes(b)) { + return 1 + } else { + return 0 // Leave the rest in their original order + } + }) + + const getEngineStatusReady: InferenceEngine[] = extensionHasSettings + ?.filter((e) => e.apiKey.length > 0) + .map((x) => x.provider as InferenceEngine) + + useEffect(() => { + setShowEngineListModel((prev) => [ + ...prev, + ...(getEngineStatusReady as InferenceEngine[]), + ]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [setShowEngineListModel, extensionHasSettings]) + + const isDownloadALocalModel = downloadedModels.some((x) => + localEngines.includes(x.engine) + ) if (strictedThread && !activeThread) { return null @@ -312,161 +351,22 @@ const ModelDropdown = ({ - {searchFilter !== 'remote' && ( -
-
-
- Cortex -
-
- {filteredDownloadedModels - .filter((x) => { - if (searchText.length === 0) { - return downloadedModels.find((c) => c.id === x.id) - } else { - return x - } - }) - .filter((x) => x.engine === InferenceEngine.nitro).length !== - 0 ? ( -
    - {filteredDownloadedModels - ? filteredDownloadedModels - .filter((x) => x.engine === InferenceEngine.nitro) - .filter((x) => { - if (searchText.length === 0) { - return downloadedModels.find((c) => c.id === x.id) - } else { - return x - } - }) - .map((model) => { - const isDownloading = downloadingModels.some( - (md) => md.id === model.id - ) - const isdDownloaded = downloadedModels.some( - (c) => c.id === model.id - ) - return ( -
  • { - if (isdDownloaded) { - onClickModelItem(model.id) - } - }} - > -
    -

    - {model.name} -

    - -
    -
    - {!isdDownloaded && ( - - {toGibibytes(model.metadata.size)} - - )} - {!isDownloading && !isdDownloaded ? ( - downloadModel(model)} - /> - ) : ( - Object.values(downloadStates) - .filter((x) => x.modelId === model.id) - .map((item) => ( - - )) - )} -
    -
  • - ) - }) - : null} -
- ) : ( -
    - {featuredModel.map((model) => { - const isDownloading = downloadingModels.some( - (md) => md.id === model.id - ) - return ( -
  • -
    -

    - {model.name} -

    - -
    -
    - - {toGibibytes(model.metadata.size)} - - {!isDownloading ? ( - downloadModel(model)} - /> - ) : ( - Object.values(downloadStates) - .filter((x) => x.modelId === model.id) - .map((item) => ( - - )) - )} -
    -
  • - ) - })} -
- )} -
- )} - {groupByEngine.map((engine, i) => { - const apiKey = - extensionHasSettings.filter((x) => x.provider === engine)[0] - ?.apiKey.length > 1 + const apiKey = !localEngines.includes(engine) + ? extensionHasSettings.filter((x) => x.provider === engine)[0] + ?.apiKey.length > 1 + : true + const engineLogo = getLogoEngine(engine as InferenceEngine) + const showModel = showEngineListModel.includes(engine) + const onClickChevron = () => { + if (showModel) { + setShowEngineListModel((prev) => + prev.filter((item) => item !== engine) + ) + } else { + setShowEngineListModel((prev) => [...prev, engine]) + } + } return (
-
- {engine} -
-
- +
+ {engineLogo && ( + logo + )} +
+ {getTitleByEngine(engine)} +
+
+
+ {!localEngines.includes(engine) && ( + + )} + {!showModel ? ( + + ) : ( + + )}
+ + {engine === InferenceEngine.nitro && + !isDownloadALocalModel && + showModel && ( + <> + {!searchText.length ? ( +
    + {featuredModel.map((model) => { + const isDownloading = downloadingModels.some( + (md) => md.id === model.id + ) + return ( +
  • +
    +

    + {model.name} +

    + +
    +
    + + {toGibibytes(model.metadata.size)} + + {!isDownloading ? ( + downloadModel(model)} + /> + ) : ( + Object.values(downloadStates) + .filter((x) => x.modelId === model.id) + .map((item) => ( + + )) + )} +
    +
  • + ) + })} +
+ ) : ( + <> + {filteredDownloadedModels + .filter( + (x) => x.engine === InferenceEngine.nitro + ) + .filter((x) => { + if (searchText.length === 0) { + return downloadedModels.find( + (c) => c.id === x.id + ) + } else { + return x + } + }) + .map((model) => { + const isDownloading = downloadingModels.some( + (md) => md.id === model.id + ) + const isdDownloaded = downloadedModels.some( + (c) => c.id === model.id + ) + return ( +
  • { + if (isdDownloaded) { + onClickModelItem(model.id) + } + }} + > +
    +

    + {model.name} +

    + +
    +
    + {!isdDownloaded && ( + + {toGibibytes(model.metadata.size)} + + )} + {!isDownloading && !isdDownloaded ? ( + downloadModel(model)} + /> + ) : ( + Object.values(downloadStates) + .filter( + (x) => x.modelId === model.id + ) + .map((item) => ( + + )) + )} +
    +
  • + ) + })} + + )} + + )} +
      {filteredDownloadedModels .filter((x) => x.engine === engine) + .filter((y) => { + if (localEngines.includes(y.engine)) { + return downloadedModels.find((c) => c.id === y.id) + } else { + return y + } + }) .map((model) => { + if (!showModel) return null return (
    • { - if ( - apiKey || - model.engine === - InferenceEngine.nitro_tensorrt_llm - ) { - onClickModelItem(model.id) - } + onClickModelItem(model.id) }} >
      - {engineHasLogo.map((x) => { - if (x === model.engine) { - return ( -
      - Model Provider -
      - ) - } - })} -

      +

      {model.name}

      diff --git a/web/containers/SetupRemoteModel/index.tsx b/web/containers/SetupRemoteModel/index.tsx index ab71240af..914f240de 100644 --- a/web/containers/SetupRemoteModel/index.tsx +++ b/web/containers/SetupRemoteModel/index.tsx @@ -4,10 +4,12 @@ import { InferenceEngine } from '@janhq/core' import { Button } from '@janhq/joi' import { useSetAtom } from 'jotai' -import { SettingsIcon } from 'lucide-react' +import { SettingsIcon, PlusIcon } from 'lucide-react' import { MainViewState } from '@/constants/screens' +import { localEngines } from '@/utils/modelEngine' + import { extensionManager } from '@/extension' import { mainViewStateAtom } from '@/helpers/atoms/App.atom' import { selectedSettingAtom } from '@/helpers/atoms/Setting.atom' @@ -72,6 +74,11 @@ const SetupRemoteModel = ({ engine }: Props) => { ) } + const apiKey = !localEngines.includes(engine) + ? extensionHasSettings.filter((x) => x.provider === engine)[0]?.apiKey + .length > 1 + : true + return ( ) } diff --git a/web/helpers/atoms/Model.atom.ts b/web/helpers/atoms/Model.atom.ts index 7ad65a15e..deb7b8622 100644 --- a/web/helpers/atoms/Model.atom.ts +++ b/web/helpers/atoms/Model.atom.ts @@ -1,6 +1,8 @@ -import { ImportingModel, Model } from '@janhq/core' +import { ImportingModel, Model, InferenceEngine } from '@janhq/core' import { atom } from 'jotai' +import { localEngines } from '@/utils/modelEngine' + export const stateModel = atom({ state: 'start', loading: false, model: '' }) export const activeAssistantModelAtom = atom(undefined) @@ -132,3 +134,5 @@ export const updateImportingModelAtom = atom( ) export const selectedModelAtom = atom(undefined) + +export const showEngineListModelAtom = atom(localEngines) diff --git a/web/public/images/ModelProvider/anthropic.svg b/web/public/images/ModelProvider/anthropic.svg index 7bb86df4a..1f3f18dcf 100644 --- a/web/public/images/ModelProvider/anthropic.svg +++ b/web/public/images/ModelProvider/anthropic.svg @@ -1,9 +1,9 @@ - - + + - - + + - + diff --git a/web/public/images/ModelProvider/cohere.svg b/web/public/images/ModelProvider/cohere.svg index 543bc2d6c..0ff4f0029 100644 --- a/web/public/images/ModelProvider/cohere.svg +++ b/web/public/images/ModelProvider/cohere.svg @@ -1,30 +1,9 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + diff --git a/web/public/images/ModelProvider/cortex.svg b/web/public/images/ModelProvider/cortex.svg new file mode 100644 index 000000000..c0ebd58bf --- /dev/null +++ b/web/public/images/ModelProvider/cortex.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/web/public/images/ModelProvider/dot.svg b/web/public/images/ModelProvider/dot.svg new file mode 100644 index 000000000..f667c20b1 --- /dev/null +++ b/web/public/images/ModelProvider/dot.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/public/images/ModelProvider/google.svg b/web/public/images/ModelProvider/google.svg new file mode 100644 index 000000000..1c44dd330 --- /dev/null +++ b/web/public/images/ModelProvider/google.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web/public/images/ModelProvider/groq.svg b/web/public/images/ModelProvider/groq.svg new file mode 100644 index 000000000..9c2e0a34a --- /dev/null +++ b/web/public/images/ModelProvider/groq.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web/public/images/ModelProvider/hugging-face.svg b/web/public/images/ModelProvider/hugging-face.svg new file mode 100644 index 000000000..9ac72080a --- /dev/null +++ b/web/public/images/ModelProvider/hugging-face.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/web/public/images/ModelProvider/martian.svg b/web/public/images/ModelProvider/martian.svg index f63ded55a..b5ceacdf8 100644 --- a/web/public/images/ModelProvider/martian.svg +++ b/web/public/images/ModelProvider/martian.svg @@ -1,11 +1,11 @@ - - - - + + + + - - + + diff --git a/web/public/images/ModelProvider/meta.svg b/web/public/images/ModelProvider/meta.svg new file mode 100644 index 000000000..91bdf9783 --- /dev/null +++ b/web/public/images/ModelProvider/meta.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/public/images/ModelProvider/mistral.svg b/web/public/images/ModelProvider/mistral.svg index 2bb14b9bc..22233c55c 100644 --- a/web/public/images/ModelProvider/mistral.svg +++ b/web/public/images/ModelProvider/mistral.svg @@ -1,32 +1,28 @@ - - - Mistral AI - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/public/images/ModelProvider/nitro.svg b/web/public/images/ModelProvider/nitro.svg new file mode 100644 index 000000000..775517a75 --- /dev/null +++ b/web/public/images/ModelProvider/nitro.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/web/public/images/ModelProvider/nvidia.svg b/web/public/images/ModelProvider/nvidia.svg new file mode 100644 index 000000000..09c2194ec --- /dev/null +++ b/web/public/images/ModelProvider/nvidia.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/web/public/images/ModelProvider/openRouter.svg b/web/public/images/ModelProvider/openRouter.svg new file mode 100644 index 000000000..62ff2b424 --- /dev/null +++ b/web/public/images/ModelProvider/openRouter.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/web/public/images/ModelProvider/openai.svg b/web/public/images/ModelProvider/openai.svg index 433ae3d45..8f0785415 100644 --- a/web/public/images/ModelProvider/openai.svg +++ b/web/public/images/ModelProvider/openai.svg @@ -1,24 +1,9 @@ - - - - - - - - - + + + + + + + + + diff --git a/web/public/images/ModelProvider/send.svg b/web/public/images/ModelProvider/send.svg new file mode 100644 index 000000000..28d30299f --- /dev/null +++ b/web/public/images/ModelProvider/send.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/screens/Settings/MyModels/MyModelList/index.tsx b/web/screens/Settings/MyModels/MyModelList/index.tsx index 045f454c0..ae9c344cb 100644 --- a/web/screens/Settings/MyModels/MyModelList/index.tsx +++ b/web/screens/Settings/MyModels/MyModelList/index.tsx @@ -16,6 +16,8 @@ import useDeleteModel from '@/hooks/useDeleteModel' import { toGibibytes } from '@/utils/converter' +import { localEngines } from '@/utils/modelEngine' + import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom' type Props = { @@ -44,33 +46,10 @@ const MyModelList = ({ model }: Props) => { } } - const engineHasLogo = [ - InferenceEngine.anthropic, - InferenceEngine.cohere, - InferenceEngine.martian, - InferenceEngine.mistral, - InferenceEngine.openai, - ] - return (
      - {engineHasLogo.map((x) => { - if (x === model.engine) { - return ( -
      - Model Provider -
      - ) - } - })}
      {
      - {model.engine === InferenceEngine.nitro && ( + {localEngines.includes(model.engine) && (
      {toGibibytes(model.metadata.size)} diff --git a/web/screens/Settings/MyModels/index.tsx b/web/screens/Settings/MyModels/index.tsx index d90081b6c..80402a548 100644 --- a/web/screens/Settings/MyModels/index.tsx +++ b/web/screens/Settings/MyModels/index.tsx @@ -1,13 +1,20 @@ -import { useCallback, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { useDropzone } from 'react-dropzone' +import Image from 'next/image' + import { InferenceEngine } from '@janhq/core' import { Button, ScrollArea } from '@janhq/joi' -import { useAtomValue, useSetAtom } from 'jotai' -import { UploadCloudIcon, UploadIcon } from 'lucide-react' +import { useAtom, useAtomValue, useSetAtom } from 'jotai' +import { + ChevronDownIcon, + ChevronUpIcon, + UploadCloudIcon, + UploadIcon, +} from 'lucide-react' import { twMerge } from 'tailwind-merge' @@ -18,15 +25,32 @@ import SetupRemoteModel from '@/containers/SetupRemoteModel' import useDropModelBinaries from '@/hooks/useDropModelBinaries' import { setImportModelStageAtom } from '@/hooks/useImportModel' +import { + getLogoEngine, + getTitleByEngine, + localEngines, + priorityEngine, +} from '@/utils/modelEngine' + import MyModelList from './MyModelList' -import { downloadedModelsAtom } from '@/helpers/atoms/Model.atom' +import { extensionManager } from '@/extension' +import { + downloadedModelsAtom, + showEngineListModelAtom, +} from '@/helpers/atoms/Model.atom' const MyModels = () => { const downloadedModels = useAtomValue(downloadedModelsAtom) const setImportModelStage = useSetAtom(setImportModelStageAtom) const { onDropModels } = useDropModelBinaries() const [searchText, setSearchText] = useState('') + const [showEngineListModel, setShowEngineListModel] = useAtom( + showEngineListModelAtom + ) + const [extensionHasSettings, setExtensionHasSettings] = useState< + { name?: string; setting: string; apiKey: string; provider: string }[] + >([]) const filteredDownloadedModels = useMemo( () => @@ -52,11 +76,73 @@ const MyModels = () => { setSearchText(input) }, []) + useEffect(() => { + const getAllSettings = async () => { + const extensionsMenu: { + name?: string + setting: string + apiKey: string + provider: string + }[] = [] + const extensions = extensionManager.getAll() + + for (const extension of extensions) { + if (typeof extension.getSettings === 'function') { + const settings = await extension.getSettings() + + if ( + (settings && settings.length > 0) || + (await extension.installationState()) !== 'NotRequired' + ) { + extensionsMenu.push({ + name: extension.productName, + setting: extension.name, + apiKey: + 'apiKey' in extension && typeof extension.apiKey === 'string' + ? extension.apiKey + : '', + provider: + 'provider' in extension && + typeof extension.provider === 'string' + ? extension.provider + : '', + }) + } + } + } + setExtensionHasSettings(extensionsMenu) + } + getAllSettings() + }, []) + const findByEngine = filteredDownloadedModels.map((x) => x.engine) - const groupByEngine = findByEngine.filter(function (item, index) { - if (findByEngine.indexOf(item) === index) - return item !== InferenceEngine.nitro - }) + const groupByEngine = findByEngine + .filter(function (item, index) { + if (findByEngine.indexOf(item) === index) return item + }) + .sort((a, b) => { + if (priorityEngine.includes(a) && priorityEngine.includes(b)) { + return priorityEngine.indexOf(a) - priorityEngine.indexOf(b) + } else if (priorityEngine.includes(a)) { + return -1 + } else if (priorityEngine.includes(b)) { + return 1 + } else { + return 0 // Leave the rest in their original order + } + }) + + const getEngineStatusReady: InferenceEngine[] = extensionHasSettings + ?.filter((e) => e.apiKey.length > 0) + .map((x) => x.provider as InferenceEngine) + + useEffect(() => { + setShowEngineListModel((prev) => [ + ...prev, + ...(getEngineStatusReady as InferenceEngine[]), + ]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [setShowEngineListModel, extensionHasSettings]) return (
      @@ -97,39 +183,65 @@ const MyModels = () => {
      - {filteredDownloadedModels.filter( - (x) => x.engine === InferenceEngine.nitro - ).length !== 0 && ( -
      -
      -
      Cortex
      -
      -
      - {filteredDownloadedModels - ? filteredDownloadedModels - .filter((x) => x.engine === InferenceEngine.nitro) - .map((model) => { - return - }) - : null} -
      -
      - )} - {groupByEngine.map((engine, i) => { + const engineLogo = getLogoEngine(engine as InferenceEngine) + const showModel = showEngineListModel.includes(engine) + const onClickChevron = () => { + if (showModel) { + setShowEngineListModel((prev) => + prev.filter((item) => item !== engine) + ) + } else { + setShowEngineListModel((prev) => [...prev, engine]) + } + } return (
      -
      - {engine} -
      - +
      + {engineLogo && ( + logo + )} +
      + {getTitleByEngine(engine)} +
      +
      +
      + {!localEngines.includes(engine) && ( + + )} + {!showModel ? ( + + ) : ( + + )} +
      {filteredDownloadedModels ? filteredDownloadedModels .filter((x) => x.engine === engine) .map((model) => { + if (!showModel) return null return }) : null} diff --git a/web/utils/modelEngine.ts b/web/utils/modelEngine.ts new file mode 100644 index 000000000..9e7ea0146 --- /dev/null +++ b/web/utils/modelEngine.ts @@ -0,0 +1,66 @@ +import { InferenceEngine } from '@janhq/core' + +export const getLogoEngine = (engine: InferenceEngine) => { + switch (engine) { + case 'anthropic': + return 'images/ModelProvider/anthropic.svg' + case 'nitro': + return 'images/ModelProvider/nitro.svg' + case 'cortex.llamacpp': + case 'cortex.onnx': + case 'cortex.tensorrtllm': + return 'images/ModelProvider/cortex.svg' + case 'mistral': + return 'images/ModelProvider/mistral.svg' + case 'martian': + return 'images/ModelProvider/martian.svg' + case 'openrouter': + return 'images/ModelProvider/openrouter.svg' + case 'openai': + return 'images/ModelProvider/openai.svg' + case 'groq': + return 'images/ModelProvider/groq.svg' + case 'triton_trtllm': + return 'images/ModelProvider/triton_trtllm.svg' + case 'cohere': + return 'images/ModelProvider/cohere.svg' + case 'nvidia': + return 'images/ModelProvider/nvidia.svg' + default: + return undefined + } +} + +export const localEngines = [ + InferenceEngine.nitro, + InferenceEngine.nitro_tensorrt_llm, + InferenceEngine.cortex_llamacpp, + InferenceEngine.cortex_onnx, + InferenceEngine.cortex_tensorrtllm, +] + +export const getTitleByEngine = (engine: InferenceEngine) => { + switch (engine) { + case 'nitro': + return 'Llama.cpp (Nitro)' + case 'cortex.llamacpp': + return 'Llama.cpp (Cortex)' + case 'cortex.onnx': + return 'Onnx (Cortex)' + case 'cortex.tensorrtllm': + return 'TensorRT-LLM (Cortex)' + case 'openai': + return 'OpenAI' + case 'openrouter': + return 'OpenRouter' + default: + return engine.charAt(0).toUpperCase() + engine.slice(1) + } +} + +export const priorityEngine = [ + InferenceEngine.cortex_llamacpp, + InferenceEngine.cortex_onnx, + InferenceEngine.cortex_tensorrtllm, + InferenceEngine.nitro, +]