diff --git a/core/src/browser/extensions/engines/LocalOAIEngine.ts b/core/src/browser/extensions/engines/LocalOAIEngine.ts index ab5a2622c2..fb9e4962c4 100644 --- a/core/src/browser/extensions/engines/LocalOAIEngine.ts +++ b/core/src/browser/extensions/engines/LocalOAIEngine.ts @@ -53,11 +53,11 @@ export abstract class LocalOAIEngine extends OAIEngine { /** * Stops the model. */ - override async unloadModel(model?: Model): Promise { + override async unloadModel(model?: Model) { if (model?.engine && model.engine?.toString() !== this.provider) return Promise.resolve() this.loadedModel = undefined - return executeOnMain(this.nodeModule, this.unloadModelFunctionName).then(() => { + await executeOnMain(this.nodeModule, this.unloadModelFunctionName).then(() => { events.emit(ModelEvent.OnModelStopped, {}) }) } diff --git a/electron/managers/mainWindowConfig.ts b/electron/managers/mainWindowConfig.ts index 4f1715a94f..990e78b859 100644 --- a/electron/managers/mainWindowConfig.ts +++ b/electron/managers/mainWindowConfig.ts @@ -1,9 +1,10 @@ const DEFAULT_WIDTH = 1200 +const DEFAULT_MIN_WIDTH = 400 const DEFAULT_HEIGHT = 800 export const mainWindowConfig: Electron.BrowserWindowConstructorOptions = { width: DEFAULT_WIDTH, - minWidth: DEFAULT_WIDTH, + minWidth: DEFAULT_MIN_WIDTH, height: DEFAULT_HEIGHT, skipTaskbar: false, show: true, diff --git a/extensions/assistant-extension/package.json b/extensions/assistant-extension/package.json index a767632442..094f9820cb 100644 --- a/extensions/assistant-extension/package.json +++ b/extensions/assistant-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/assistant-extension", - "productName": "Jan Assistant Extension", + "productName": "Jan Assistant", "version": "1.0.1", "description": "This extension enables assistants, including Jan, a default assistant that can call all downloaded models", "main": "dist/index.js", diff --git a/extensions/conversational-extension/package.json b/extensions/conversational-extension/package.json index 712a9883ce..a29967da46 100644 --- a/extensions/conversational-extension/package.json +++ b/extensions/conversational-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/conversational-extension", - "productName": "Conversational Extension", + "productName": "Conversational", "version": "1.0.0", "description": "This extension enables conversations and state persistence via your filesystem", "main": "dist/index.js", diff --git a/extensions/huggingface-extension/package.json b/extensions/huggingface-extension/package.json index 234b806d84..c0c18c5ebc 100644 --- a/extensions/huggingface-extension/package.json +++ b/extensions/huggingface-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/huggingface-extension", - "productName": "HuggingFace Extension", + "productName": "HuggingFace", "version": "1.0.0", "description": "Hugging Face extension for converting HF models to GGUF", "main": "dist/index.js", diff --git a/extensions/inference-groq-extension/package.json b/extensions/inference-groq-extension/package.json index 8d70d1d9fe..faf1b4a98b 100644 --- a/extensions/inference-groq-extension/package.json +++ b/extensions/inference-groq-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/inference-groq-extension", - "productName": "Groq Inference Engine Extension", + "productName": "Groq Inference Engine", "version": "1.0.0", "description": "This extension enables fast Groq chat completion API calls", "main": "dist/index.js", diff --git a/extensions/inference-mistral-extension/package.json b/extensions/inference-mistral-extension/package.json index c1de1f959c..86fa8bc77d 100644 --- a/extensions/inference-mistral-extension/package.json +++ b/extensions/inference-mistral-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/inference-mistral-extension", - "productName": "Mistral AI Inference Engine Extension", + "productName": "MistralAI Inference Engine", "version": "1.0.0", "description": "This extension enables Mistral chat completion API calls", "main": "dist/index.js", diff --git a/extensions/inference-nitro-extension/bin/version.txt b/extensions/inference-nitro-extension/bin/version.txt index dfdc368868..6a5f415dff 100644 --- a/extensions/inference-nitro-extension/bin/version.txt +++ b/extensions/inference-nitro-extension/bin/version.txt @@ -1 +1 @@ -0.3.21 +0.3.16-hotfix diff --git a/extensions/inference-nitro-extension/package.json b/extensions/inference-nitro-extension/package.json index 78558b1c39..1916da4b6f 100644 --- a/extensions/inference-nitro-extension/package.json +++ b/extensions/inference-nitro-extension/package.json @@ -1,8 +1,8 @@ { "name": "@janhq/inference-nitro-extension", - "productName": "Nitro Inference Engine Extension", - "version": "1.0.0", - "description": "This extension embeds Nitro, a lightweight (3mb) inference engine written in C++. See https://nitro.jan.ai.\nUse this setting if you encounter errors related to **CUDA toolkit** during application execution.", + "productName": "Nitro Inference Engine", + "version": "1.0.1", + "description": "This extension embeds Nitro, a lightweight (3mb) inference engine written in C++. See https://nitro.jan.ai.\nAdditional dependencies could be installed to run without Cuda Toolkit installation.", "main": "dist/index.js", "node": "dist/node/index.cjs.js", "author": "Jan ", diff --git a/extensions/inference-nitro-extension/resources/models/command-r-34b/model.json b/extensions/inference-nitro-extension/resources/models/command-r-34b/model.json index acb66779cf..850429376f 100644 --- a/extensions/inference-nitro-extension/resources/models/command-r-34b/model.json +++ b/extensions/inference-nitro-extension/resources/models/command-r-34b/model.json @@ -8,7 +8,7 @@ "id": "command-r-34b", "object": "model", "name": "Command-R v01 34B Q4", - "version": "1.0", + "version": "1.1", "description": "C4AI Command-R developed by CohereAI is optimized for a variety of use cases including reasoning, summarization, and question answering.", "format": "gguf", "settings": { @@ -27,7 +27,7 @@ }, "metadata": { "author": "CohereAI", - "tags": ["34B", "Finetuned"], + "tags": ["34B", "Finetuned", "Coming Soon", "Unavailable"], "size": 21500000000 }, "engine": "nitro" diff --git a/extensions/inference-nitro-extension/resources/models/wizardcoder-13b/model.json b/extensions/inference-nitro-extension/resources/models/wizardcoder-13b/model.json index 051c739a09..cae96c26b9 100644 --- a/extensions/inference-nitro-extension/resources/models/wizardcoder-13b/model.json +++ b/extensions/inference-nitro-extension/resources/models/wizardcoder-13b/model.json @@ -1,20 +1,20 @@ { "sources": [ { - "filename": "wizardcoder-python-13b-v1.0.Q5_K_M.gguf", - "url": "https://huggingface.co/TheBloke/WizardCoder-Python-13B-V1.0-GGUF/resolve/main/wizardcoder-python-13b-v1.0.Q5_K_M.gguf" + "filename": "wizardcoder-python-13b-v1.0.Q4_K_M.gguf", + "url": "https://huggingface.co/TheBloke/WizardCoder-Python-13B-V1.0-GGUF/resolve/main/wizardcoder-python-13b-v1.0.Q4_K_M.gguf" } ], "id": "wizardcoder-13b", "object": "model", - "name": "Wizard Coder Python 13B Q5", - "version": "1.0", + "name": "Wizard Coder Python 13B Q4", + "version": "1.1", "description": "WizardCoder 13B is a Python coding model. This model demonstrate high proficiency in specific domains like coding and mathematics.", "format": "gguf", "settings": { "ctx_len": 4096, "prompt_template": "### Instruction:\n{prompt}\n### Response:", - "llama_model_path": "wizardcoder-python-13b-v1.0.Q5_K_M.gguf" + "llama_model_path": "wizardcoder-python-13b-v1.0.Q4_K_M.gguf" }, "parameters": { "temperature": 0.7, diff --git a/extensions/inference-nitro-extension/src/index.ts b/extensions/inference-nitro-extension/src/index.ts index 119d7762ff..1a8de783a2 100644 --- a/extensions/inference-nitro-extension/src/index.ts +++ b/extensions/inference-nitro-extension/src/index.ts @@ -102,7 +102,7 @@ export default class JanInferenceNitroExtension extends LocalOAIEngine { return super.loadModel(model) } - override async unloadModel(model?: Model) { + override async unloadModel(model?: Model): Promise { if (model?.engine && model.engine !== this.provider) return // stop the periocally health check diff --git a/extensions/inference-nitro-extension/src/node/index.ts b/extensions/inference-nitro-extension/src/node/index.ts index 3d742721e2..7d20ee8c75 100644 --- a/extensions/inference-nitro-extension/src/node/index.ts +++ b/extensions/inference-nitro-extension/src/node/index.ts @@ -323,14 +323,14 @@ async function killSubprocess(): Promise { return new Promise((resolve, reject) => { terminate(pid, function (err) { if (err) { - return killRequest() + killRequest().then(resolve).catch(reject) } else { - return tcpPortUsed + tcpPortUsed .waitUntilFree(PORT, NITRO_PORT_FREE_CHECK_INTERVAL, 5000) .then(() => resolve()) .then(() => log(`[NITRO]::Debug: Nitro process is terminated`)) .catch(() => { - killRequest() + killRequest().then(resolve).catch(reject) }) } }) diff --git a/extensions/inference-openai-extension/package.json b/extensions/inference-openai-extension/package.json index 3435a53462..2dd75d300e 100644 --- a/extensions/inference-openai-extension/package.json +++ b/extensions/inference-openai-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/inference-openai-extension", - "productName": "OpenAI Inference Engine Extension", + "productName": "OpenAI Inference Engine", "version": "1.0.0", "description": "This extension enables OpenAI chat completion API calls", "main": "dist/index.js", diff --git a/extensions/inference-triton-trtllm-extension/package.json b/extensions/inference-triton-trtllm-extension/package.json index 9ce8f11a91..06c4976e1a 100644 --- a/extensions/inference-triton-trtllm-extension/package.json +++ b/extensions/inference-triton-trtllm-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/inference-triton-trt-llm-extension", - "productName": "Triton-TRT-LLM Inference Engine Extension", + "productName": "Triton-TRT-LLM Inference Engine", "version": "1.0.0", "description": "This extension enables Nvidia's TensorRT-LLM as an inference engine option", "main": "dist/index.js", diff --git a/extensions/model-extension/package.json b/extensions/model-extension/package.json index c6b6593224..0967e16324 100644 --- a/extensions/model-extension/package.json +++ b/extensions/model-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/model-extension", - "productName": "Model Management Extension", + "productName": "Model Management", "version": "1.0.30", "description": "Model Management Extension provides model exploration and seamless downloads", "main": "dist/index.js", diff --git a/extensions/monitoring-extension/package.json b/extensions/monitoring-extension/package.json index c320db2ba2..e728b46291 100644 --- a/extensions/monitoring-extension/package.json +++ b/extensions/monitoring-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/monitoring-extension", - "productName": "System Monitoring Extension", + "productName": "System Monitoring", "version": "1.0.10", "description": "This extension provides system health and OS level data", "main": "dist/index.js", diff --git a/extensions/monitoring-extension/src/node/index.ts b/extensions/monitoring-extension/src/node/index.ts index bb0c4ac182..3f1be56098 100644 --- a/extensions/monitoring-extension/src/node/index.ts +++ b/extensions/monitoring-extension/src/node/index.ts @@ -49,7 +49,9 @@ const DEFAULT_SETTINGS: GpuSetting = { export const getGpuConfig = async (): Promise => { if (process.platform === 'darwin') return undefined - return JSON.parse(readFileSync(GPU_INFO_FILE, 'utf-8')) + if (existsSync(GPU_INFO_FILE)) + return JSON.parse(readFileSync(GPU_INFO_FILE, 'utf-8')) + return DEFAULT_SETTINGS } export const getResourcesInfo = async (): Promise => { diff --git a/extensions/tensorrt-llm-extension/package.json b/extensions/tensorrt-llm-extension/package.json index 02b0b4e8c6..c8eafb10d2 100644 --- a/extensions/tensorrt-llm-extension/package.json +++ b/extensions/tensorrt-llm-extension/package.json @@ -1,6 +1,6 @@ { "name": "@janhq/tensorrt-llm-extension", - "productName": "TensorRT-LLM Inference Engine Extension", + "productName": "TensorRT-LLM Inference Engine", "version": "0.0.3", "description": "This extension enables Nvidia's TensorRT-LLM for the fastest GPU acceleration. See the [setup guide](https://jan.ai/guides/providers/tensorrt-llm/) for next steps.", "main": "dist/index.js", diff --git a/web/containers/Checkbox/index.tsx b/web/containers/Checkbox/index.tsx index a545771b60..6e16eb02f8 100644 --- a/web/containers/Checkbox/index.tsx +++ b/web/containers/Checkbox/index.tsx @@ -14,7 +14,7 @@ import { InfoIcon } from 'lucide-react' type Props = { name: string title: string - enabled?: boolean + disabled?: boolean description: string checked: boolean onValueChanged?: (e: string | number | boolean) => void @@ -23,7 +23,7 @@ type Props = { const Checkbox: React.FC = ({ title, checked, - enabled = true, + disabled = false, description, onValueChanged, }) => { @@ -52,7 +52,7 @@ const Checkbox: React.FC = ({ ) diff --git a/web/containers/DropdownListSidebar/index.tsx b/web/containers/DropdownListSidebar/index.tsx index bf4d873cc9..75c139af37 100644 --- a/web/containers/DropdownListSidebar/index.tsx +++ b/web/containers/DropdownListSidebar/index.tsx @@ -276,7 +276,7 @@ const DropdownListSidebar = ({ {toGibibytes(x.metadata.size)} {x.metadata.size && ( - + )} diff --git a/web/containers/Layout/TopBar/index.tsx b/web/containers/Layout/TopBar/index.tsx index 7f108f15a0..cbb1517517 100644 --- a/web/containers/Layout/TopBar/index.tsx +++ b/web/containers/Layout/TopBar/index.tsx @@ -16,6 +16,8 @@ import CommandSearch from '@/containers/Layout/TopBar/CommandSearch' import { showLeftSideBarAtom } from '@/containers/Providers/KeyListener' +import { toaster } from '@/containers/Toast' + import { MainViewState } from '@/constants/screens' import { useClickOutside } from '@/hooks/useClickOutside' @@ -61,7 +63,11 @@ const TopBar = () => { const onCreateConversationClick = async () => { if (assistants.length === 0) { - alert('No assistant available') + toaster({ + title: 'No assistant available.', + description: `Could not create a new thread. Please add an assistant.`, + type: 'error', + }) } else { requestCreateNewThread(assistants[0]) } diff --git a/web/containers/ModelConfigInput/index.tsx b/web/containers/ModelConfigInput/index.tsx index 0c16c916cf..715ca5e484 100644 --- a/web/containers/ModelConfigInput/index.tsx +++ b/web/containers/ModelConfigInput/index.tsx @@ -11,7 +11,7 @@ import { InfoIcon } from 'lucide-react' type Props = { title: string - enabled?: boolean + disabled?: boolean name: string description: string placeholder: string @@ -21,7 +21,7 @@ type Props = { const ModelConfigInput: React.FC = ({ title, - enabled = true, + disabled = false, value, description, placeholder, @@ -48,7 +48,7 @@ const ModelConfigInput: React.FC = ({ placeholder={placeholder} onChange={(e) => onValueChanged?.(e.target.value)} value={value} - disabled={!enabled} + disabled={disabled} /> ) diff --git a/web/containers/ModelLabel/index.tsx b/web/containers/ModelLabel/index.tsx index 54f2838af5..5f32499ae4 100644 --- a/web/containers/ModelLabel/index.tsx +++ b/web/containers/ModelLabel/index.tsx @@ -1,5 +1,7 @@ import React from 'react' +import { ModelMetadata } from '@janhq/core' +import { Badge } from '@janhq/uikit' import { useAtomValue } from 'jotai' import { useActiveModel } from '@/hooks/useActiveModel' @@ -19,10 +21,17 @@ import { } from '@/helpers/atoms/SystemBar.atom' type Props = { - size: number + metadata: ModelMetadata +} +const UnsupportedModel = () => { + return ( + + Coming Soon + + ) } -const ModelLabel: React.FC = ({ size }) => { +const ModelLabel: React.FC = ({ metadata }) => { const { activeModel } = useActiveModel() const totalRam = useAtomValue(totalRamAtom) const usedRam = useAtomValue(usedRamAtom) @@ -52,7 +61,11 @@ const ModelLabel: React.FC = ({ size }) => { return null } - return getLabel(size) + return metadata.tags.includes('Coming Soon') ? ( + + ) : ( + getLabel(metadata.size ?? 0) + ) } export default React.memo(ModelLabel) diff --git a/web/containers/Providers/KeyListener.tsx b/web/containers/Providers/KeyListener.tsx index a4702783cc..1f888dd1c6 100644 --- a/web/containers/Providers/KeyListener.tsx +++ b/web/containers/Providers/KeyListener.tsx @@ -2,11 +2,14 @@ import { Fragment, ReactNode, useEffect } from 'react' -import { atom, useSetAtom } from 'jotai' +import { atom, useAtomValue, useSetAtom } from 'jotai' import { MainViewState } from '@/constants/screens' +import { useCreateNewThread } from '@/hooks/useCreateNewThread' + import { mainViewStateAtom } from '@/helpers/atoms/App.atom' +import { assistantsAtom } from '@/helpers/atoms/Assistant.atom' type Props = { children: ReactNode @@ -21,11 +24,19 @@ export default function KeyListener({ children }: Props) { const setShowSelectModelModal = useSetAtom(showSelectModelModalAtom) const setMainViewState = useSetAtom(mainViewStateAtom) const showCommandSearchModal = useSetAtom(showCommandSearchModalAtom) + const { requestCreateNewThread } = useCreateNewThread() + const assistants = useAtomValue(assistantsAtom) useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { const prefixKey = isMac ? e.metaKey : e.ctrlKey + if (e.key === 'n' && prefixKey) { + requestCreateNewThread(assistants[0]) + setMainViewState(MainViewState.Thread) + return + } + if (e.key === 'b' && prefixKey) { setShowLeftSideBar((showLeftSideBar) => !showLeftSideBar) return @@ -49,6 +60,8 @@ export default function KeyListener({ children }: Props) { document.addEventListener('keydown', onKeyDown) return () => document.removeEventListener('keydown', onKeyDown) }, [ + assistants, + requestCreateNewThread, setMainViewState, setShowLeftSideBar, setShowSelectModelModal, diff --git a/web/containers/ShortcutModal/index.tsx b/web/containers/ShortcutModal/index.tsx index 04d6dfc0f4..62eb9c0784 100644 --- a/web/containers/ShortcutModal/index.tsx +++ b/web/containers/ShortcutModal/index.tsx @@ -10,6 +10,11 @@ import { } from '@janhq/uikit' const availableShortcuts = [ + { + combination: 'N', + modifierKeys: [isMac ? '⌘' : 'Ctrl'], + description: 'Create a new thread', + }, { combination: 'E', modifierKeys: [isMac ? '⌘' : 'Ctrl'], diff --git a/web/containers/SliderRightPanel/index.tsx b/web/containers/SliderRightPanel/index.tsx index 7c017e70f0..3f7bef2c24 100644 --- a/web/containers/SliderRightPanel/index.tsx +++ b/web/containers/SliderRightPanel/index.tsx @@ -17,7 +17,7 @@ import { useClickOutside } from '@/hooks/useClickOutside' type Props = { name: string title: string - enabled: boolean + disabled: boolean description: string min: number max: number @@ -28,7 +28,7 @@ type Props = { const SliderRightPanel: React.FC = ({ title, - enabled, + disabled, min, max, step, @@ -65,7 +65,7 @@ const SliderRightPanel: React.FC = ({ min={min} max={max} step={step} - disabled={!enabled} + disabled={disabled} />

{min}

@@ -80,7 +80,7 @@ const SliderRightPanel: React.FC = ({ min={min} max={max} value={String(value)} - disabled={!enabled} + disabled={disabled} onBlur={(e) => { if (Number(e.target.value) > Number(max)) { onValueChanged?.(Number(max)) diff --git a/web/hooks/useActiveModel.ts b/web/hooks/useActiveModel.ts index 34ffd1af73..1e648f60ee 100644 --- a/web/hooks/useActiveModel.ts +++ b/web/hooks/useActiveModel.ts @@ -126,33 +126,27 @@ export function useActiveModel() { }) } - const stopModel = useCallback( - async (model?: Model) => { - const stoppingModel = activeModel || model - if ( - !stoppingModel || - (!model && stateModel.state === 'stop' && stateModel.loading) - ) - return - - setStateModel({ state: 'stop', loading: true, model: stoppingModel }) - const engine = EngineManager.instance().get(stoppingModel.engine) - return engine - ?.unloadModel(stoppingModel) - .catch() - .then(() => { - setActiveModel(undefined) - setStateModel({ state: 'start', loading: false, model: undefined }) - loadModelController?.abort() - }) - }, - [activeModel, setActiveModel, setStateModel, stateModel] - ) + const stopModel = useCallback(async () => { + const stoppingModel = activeModel || stateModel.model + if (!stoppingModel || (stateModel.state === 'stop' && stateModel.loading)) + return + + setStateModel({ state: 'stop', loading: true, model: stoppingModel }) + const engine = EngineManager.instance().get(stoppingModel.engine) + return engine + ?.unloadModel(stoppingModel) + .catch() + .then(() => { + setActiveModel(undefined) + setStateModel({ state: 'start', loading: false, model: undefined }) + loadModelController?.abort() + }) + }, [activeModel, setActiveModel, setStateModel, stateModel]) const stopInference = useCallback(async () => { // Loading model if (stateModel.loading) { - stopModel(stateModel.model) + stopModel() return } if (!activeModel) return diff --git a/web/hooks/useFactoryReset.ts b/web/hooks/useFactoryReset.ts index 878461ef12..8364ca10d9 100644 --- a/web/hooks/useFactoryReset.ts +++ b/web/hooks/useFactoryReset.ts @@ -19,7 +19,7 @@ export const factoryResetStateAtom = atom(FactoryResetState.Idle) export default function useFactoryReset() { const defaultJanDataFolder = useAtomValue(defaultJanDataFolderAtom) - const { activeModel, stopModel } = useActiveModel() + const { stopModel } = useActiveModel() const setFactoryResetState = useSetAtom(factoryResetStateAtom) const resetAll = useCallback( @@ -44,11 +44,9 @@ export default function useFactoryReset() { await window.core?.api?.updateAppConfiguration(configuration) } - if (activeModel) { - setFactoryResetState(FactoryResetState.StoppingModel) - await stopModel() - await new Promise((resolve) => setTimeout(resolve, 4000)) - } + setFactoryResetState(FactoryResetState.StoppingModel) + await stopModel() + await new Promise((resolve) => setTimeout(resolve, 4000)) setFactoryResetState(FactoryResetState.DeletingData) await fs.rm(janDataFolderPath) @@ -59,7 +57,7 @@ export default function useFactoryReset() { await window.core?.api?.relaunch() }, - [defaultJanDataFolder, activeModel, stopModel, setFactoryResetState] + [defaultJanDataFolder, stopModel, setFactoryResetState] ) return { diff --git a/web/screens/Chat/EngineSetting/index.tsx b/web/screens/Chat/EngineSetting/index.tsx index 9269bd1c7d..57b19a4842 100644 --- a/web/screens/Chat/EngineSetting/index.tsx +++ b/web/screens/Chat/EngineSetting/index.tsx @@ -1,54 +1,23 @@ -import { useCallback } from 'react' - import { SettingComponentProps } from '@janhq/core/.' -import { useAtomValue, useSetAtom } from 'jotai' - -import { useActiveModel } from '@/hooks/useActiveModel' -import useUpdateModelParameters from '@/hooks/useUpdateModelParameters' - import SettingComponentBuilder from '../../Chat/ModelSetting/SettingComponent' -import { serverEnabledAtom } from '@/helpers/atoms/LocalServer.atom' -import { - activeThreadAtom, - engineParamsUpdateAtom, -} from '@/helpers/atoms/Thread.atom' - type Props = { componentData: SettingComponentProps[] + onValueChanged: (key: string, value: string | number | boolean) => void + disabled?: boolean } -const EngineSetting: React.FC = ({ componentData }) => { - const isLocalServerRunning = useAtomValue(serverEnabledAtom) - const activeThread = useAtomValue(activeThreadAtom) - - const { stopModel } = useActiveModel() - const { updateModelParameter } = useUpdateModelParameters() - - const setEngineParamsUpdate = useSetAtom(engineParamsUpdateAtom) - - const onValueChanged = useCallback( - (key: string, value: string | number | boolean) => { - if (!activeThread) return - - setEngineParamsUpdate(true) - stopModel() - - updateModelParameter(activeThread, { - params: { [key]: value }, - }) - }, - [activeThread, setEngineParamsUpdate, stopModel, updateModelParameter] - ) - - return ( - - ) -} +const EngineSetting: React.FC = ({ + componentData, + onValueChanged, + disabled = false, +}) => ( + +) export default EngineSetting diff --git a/web/screens/Chat/ModelSetting/SettingComponent.tsx b/web/screens/Chat/ModelSetting/SettingComponent.tsx index 8dda7a1ada..43df16430d 100644 --- a/web/screens/Chat/ModelSetting/SettingComponent.tsx +++ b/web/screens/Chat/ModelSetting/SettingComponent.tsx @@ -11,13 +11,13 @@ import SliderRightPanel from '@/containers/SliderRightPanel' type Props = { componentProps: SettingComponentProps[] - enabled?: boolean + disabled?: boolean onValueUpdated: (key: string, value: string | number | boolean) => void } const SettingComponent: React.FC = ({ componentProps, - enabled = true, + disabled = false, onValueUpdated, }) => { const components = componentProps.map((data) => { @@ -35,7 +35,7 @@ const SettingComponent: React.FC = ({ step={step} value={value} name={data.key} - enabled={enabled} + disabled={disabled} onValueChanged={(value) => onValueUpdated(data.key, value)} /> ) @@ -47,7 +47,7 @@ const SettingComponent: React.FC = ({ return ( = ({ return ( void + disabled?: boolean } -const ModelSetting: React.FC = ({ componentProps }) => { - const isLocalServerRunning = useAtomValue(serverEnabledAtom) - const activeThread = useAtomValue(activeThreadAtom) - const { updateModelParameter } = useUpdateModelParameters() - - const onValueChanged = useCallback( - (key: string, value: string | number | boolean) => { - if (!activeThread) return - - if (key === 'stop' && typeof value === 'string') { - updateModelParameter(activeThread, { - params: { [key]: [value] }, - }) - } else { - updateModelParameter(activeThread, { - params: { [key]: value }, - }) - } - }, - [activeThread, updateModelParameter] - ) - - return ( - - ) -} +const ModelSetting: React.FC = ({ + componentProps, + onValueChanged, + disabled = false, +}) => ( + +) export default React.memo(ModelSetting) diff --git a/web/screens/Chat/Sidebar/index.tsx b/web/screens/Chat/Sidebar/index.tsx index 78249c2426..62bda54d0a 100644 --- a/web/screens/Chat/Sidebar/index.tsx +++ b/web/screens/Chat/Sidebar/index.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useMemo } from 'react' import { Input, Textarea } from '@janhq/uikit' -import { atom, useAtomValue } from 'jotai' +import { atom, useAtomValue, useSetAtom } from 'jotai' import { twMerge } from 'tailwind-merge' @@ -13,8 +13,11 @@ import DropdownListSidebar, { selectedModelAtom, } from '@/containers/DropdownListSidebar' +import { useActiveModel } from '@/hooks/useActiveModel' import { useCreateNewThread } from '@/hooks/useCreateNewThread' +import useUpdateModelParameters from '@/hooks/useUpdateModelParameters' + import { getConfigurationsData } from '@/utils/componentSettings' import { toRuntimeParams, toSettingParams } from '@/utils/modelParam' @@ -27,6 +30,7 @@ import PromptTemplateSetting from './PromptTemplateSetting' import { activeThreadAtom, + engineParamsUpdateAtom, getActiveThreadModelParamsAtom, } from '@/helpers/atoms/Thread.atom' @@ -39,6 +43,10 @@ const Sidebar: React.FC = () => { const selectedModel = useAtomValue(selectedModelAtom) const { updateThreadMetadata } = useCreateNewThread() + const setEngineParamsUpdate = useSetAtom(engineParamsUpdateAtom) + const { stopModel } = useActiveModel() + const { updateModelParameter } = useUpdateModelParameters() + const modelSettings = useMemo(() => { const modelRuntimeParams = toRuntimeParams(activeModelParams) @@ -96,6 +104,22 @@ const Sidebar: React.FC = () => { [activeThread, updateThreadMetadata] ) + const onValueChanged = useCallback( + (key: string, value: string | number | boolean) => { + if (!activeThread) { + return + } + + setEngineParamsUpdate(true) + stopModel() + + updateModelParameter(activeThread, { + params: { [key]: value }, + }) + }, + [activeThread, setEngineParamsUpdate, stopModel, updateModelParameter] + ) + return (
{ {modelSettings.length > 0 && (
- +
)} @@ -188,7 +215,10 @@ const Sidebar: React.FC = () => { {engineSettings.length > 0 && (
- +
)} diff --git a/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx b/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx index 86af5e3dd1..1a794c83f3 100644 --- a/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx +++ b/web/screens/ExploreModels/ExploreModelItemHeader/index.tsx @@ -19,6 +19,10 @@ import { twMerge } from 'tailwind-merge' import ModalCancelDownload from '@/containers/ModalCancelDownload' +import ModelLabel from '@/containers/ModelLabel' + +import { toaster } from '@/containers/Toast' + import { MainViewState } from '@/constants/screens' import { useCreateNewThread } from '@/hooks/useCreateNewThread' @@ -47,22 +51,6 @@ type Props = { open: string } -const getLabel = (size: number, ram: number, unit: string = 'RAM') => { - if (size * 1.25 >= ram) { - return ( - - Not enough {unit} - - ) - } else { - return ( - - Recommended - - ) - } -} - const ExploreModelItemHeader: React.FC = ({ model, onClick, open }) => { const { downloadModel } = useDownloadModel() const downloadingModels = useAtomValue(getDownloadingModelAtom) @@ -105,7 +93,11 @@ const ExploreModelItemHeader: React.FC = ({ model, onClick, open }) => { const onUseModelClick = useCallback(async () => { if (assistants.length === 0) { - alert('No assistant available') + toaster({ + title: 'No assistant available.', + description: `Could not use Model ${model.name} as no assistant is available.`, + type: 'error', + }) return } await requestCreateNewThread(assistants[0], model) @@ -163,11 +155,7 @@ const ExploreModelItemHeader: React.FC = ({ model, onClick, open }) => { {toGibibytes(model.metadata.size)} - {getLabel( - model.metadata.size, - ram, - settings?.run_mode === 'gpu' ? 'VRAM' : 'RAM' - )} + {} {downloadButton} { const { startModel, stateModel } = useActiveModel() const selectedModel = useAtomValue(selectedModelAtom) - const modelEngineParams = toSettingParams(selectedModel?.settings) const modelRuntimeParams = toRuntimeParams(selectedModel?.settings) - const componentDataEngineSetting = getConfigurationsData(modelEngineParams) + const [currentModelSettingParams, setCurrentModelSettingParams] = useState( + toSettingParams(selectedModel?.settings) + ) + + const componentDataEngineSetting = getConfigurationsData( + currentModelSettingParams + ) const componentDataRuntimeSetting = getConfigurationsData( modelRuntimeParams, selectedModel @@ -177,6 +182,16 @@ const LocalServerScreen = () => { } } + const onValueChanged = useCallback( + (key: string, value: string | number | boolean) => { + setCurrentModelSettingParams({ + ...currentModelSettingParams, + [key]: value, + }) + }, + [currentModelSettingParams] + ) + return (
{/* Left SideBar */} @@ -495,7 +510,11 @@ const LocalServerScreen = () => {
- +
@@ -505,7 +524,11 @@ const LocalServerScreen = () => {
- +
diff --git a/web/screens/Settings/Advanced/index.tsx b/web/screens/Settings/Advanced/index.tsx index 4376c2deb9..9f892bd704 100644 --- a/web/screens/Settings/Advanced/index.tsx +++ b/web/screens/Settings/Advanced/index.tsx @@ -26,8 +26,6 @@ import { import { useAtom, useAtomValue } from 'jotai' import { AlertTriangleIcon, AlertCircleIcon } from 'lucide-react' -import ShortcutModal from '@/containers/ShortcutModal' - import { snackbar, toaster } from '@/containers/Toast' import { useActiveModel } from '@/hooks/useActiveModel' @@ -177,22 +175,7 @@ const Advanced = () => { return ( -
- {/* Keyboard shortcut */} -
-
-
-
- Keyboard Shortcuts -
-
-

- Shortcuts that you might find useful in Jan app. -

-
- -
- +
{/* Experimental */}
diff --git a/web/screens/Settings/Appearance/index.tsx b/web/screens/Settings/Appearance/index.tsx index 51899ba40c..162c17d130 100644 --- a/web/screens/Settings/Appearance/index.tsx +++ b/web/screens/Settings/Appearance/index.tsx @@ -1,3 +1,5 @@ +import ShortcutModal from '@/containers/ShortcutModal' + import ToggleAccent from '@/screens/Settings/Appearance/TogglePrimary' import ToggleTheme from '@/screens/Settings/Appearance/ToggleTheme' @@ -24,6 +26,20 @@ export default function AppearanceOptions() {
+ {/* Keyboard shortcut */} +
+
+
+
+ Keyboard Shortcuts +
+
+

+ Shortcuts that you might find useful in Jan app. +

+
+ +
) } diff --git a/web/screens/Settings/CoreExtensions/ExtensionItem.tsx b/web/screens/Settings/CoreExtensions/ExtensionItem.tsx index 5d31cf20d4..72f8ea89b8 100644 --- a/web/screens/Settings/CoreExtensions/ExtensionItem.tsx +++ b/web/screens/Settings/CoreExtensions/ExtensionItem.tsx @@ -83,7 +83,7 @@ const ExtensionItem: React.FC = ({ item }) => { const description = marked.parse(item.description ?? '', { async: false }) return ( -
+
Additional Dependencies
diff --git a/web/screens/Settings/CoreExtensions/index.tsx b/web/screens/Settings/CoreExtensions/index.tsx index dd39d5a237..2704ac8ced 100644 --- a/web/screens/Settings/CoreExtensions/index.tsx +++ b/web/screens/Settings/CoreExtensions/index.tsx @@ -1,8 +1,8 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ - import React, { useState, useEffect, useRef } from 'react' import { Button, ScrollArea } from '@janhq/uikit' +import { Marked, Renderer } from 'marked' import Loader from '@/containers/Loader' @@ -88,9 +88,16 @@ const ExtensionCatalog = () => { {item.version}
-

- {item.description} -

+ { +
+ }
) @@ -130,4 +137,14 @@ const ExtensionCatalog = () => { ) } +const marked: Marked = new Marked({ + renderer: { + link: (href, title, text) => { + return Renderer.prototype.link + ?.apply(this, [href, title, text]) + .replace('