diff --git a/extensions/engine-management-extension/src/index.test.ts b/extensions/engine-management-extension/src/index.test.ts index 78d71ab34..854ad4737 100644 --- a/extensions/engine-management-extension/src/index.test.ts +++ b/extensions/engine-management-extension/src/index.test.ts @@ -58,3 +58,126 @@ describe('migrate engine settings', () => { expect(mockUpdateEngines).not.toBeCalled() }) }) + +describe('getEngines', () => { + let extension: JanEngineManagementExtension + + beforeEach(() => { + // @ts-ignore + extension = new JanEngineManagementExtension() + }) + + it('should return a list of engines', async () => { + const mockKyGet = vi.spyOn(extension, 'getEngines') + mockKyGet.mockResolvedValue(mockEngines) + + const engines = await extension.getEngines() + + expect(engines).toEqual(mockEngines) + }) +}) + +describe('updateDefaultEngine', () => { + let extension: JanEngineManagementExtension + + beforeEach(() => { + // @ts-ignore + extension = new JanEngineManagementExtension() + }) + + it('should set default engine variant if not installed', async () => { + vi.stubGlobal('PLATFORM', 'win32') + vi.stubGlobal('CORTEX_ENGINE_VERSION', '1.0.0') + + const mockGetDefaultEngineVariant = vi.spyOn( + extension, + 'getDefaultEngineVariant' + ) + mockGetDefaultEngineVariant.mockResolvedValue({ + variant: 'variant1', + version: '1.0.0', + }) + + const mockGetInstalledEngines = vi.spyOn(extension, 'getInstalledEngines') + mockGetInstalledEngines.mockResolvedValue([]) + + const mockSetDefaultEngineVariant = vi.spyOn( + extension, + 'setDefaultEngineVariant' + ) + mockSetDefaultEngineVariant.mockResolvedValue({ messages: 'OK' }) + + vi.mock('@janhq/core', async (importOriginal) => { + const actual = (await importOriginal()) as any + return { + ...actual, + systemInformation: vi.fn().mockResolvedValue({ gpuSetting: 'high' }), + } + }) + + vi.mock('./utils', async (importOriginal) => { + const actual = (await importOriginal()) as any + return { + ...actual, + engineVariant: vi.fn().mockResolvedValue('windows-amd64-noavx'), + } + }) + + await extension.updateDefaultEngine() + + expect(mockSetDefaultEngineVariant).toHaveBeenCalledWith('llama-cpp', { + variant: 'windows-amd64-noavx', + version: '1.0.0', + }) + }) + + it('should not reset default engine variant if not installed', async () => { + vi.stubGlobal('PLATFORM', 'win32') + vi.stubGlobal('CORTEX_ENGINE_VERSION', '1.0.0') + + const mockGetDefaultEngineVariant = vi.spyOn( + extension, + 'getDefaultEngineVariant' + ) + mockGetDefaultEngineVariant.mockResolvedValue({ + variant: 'windows-amd64-noavx', + version: '1.0.0', + }) + + const mockGetInstalledEngines = vi.spyOn(extension, 'getInstalledEngines') + mockGetInstalledEngines.mockResolvedValue([ + { + name: 'windows-amd64-noavx', + version: '1.0.0', + type: 'local', + engine: InferenceEngine.cortex_llamacpp, + }, + ]) + + const mockSetDefaultEngineVariant = vi.spyOn( + extension, + 'setDefaultEngineVariant' + ) + mockSetDefaultEngineVariant.mockResolvedValue({ messages: 'OK' }) + + vi.mock('@janhq/core', async (importOriginal) => { + const actual = (await importOriginal()) as any + return { + ...actual, + systemInformation: vi.fn().mockResolvedValue({ gpuSetting: 'high' }), + } + }) + + vi.mock('./utils', async (importOriginal) => { + const actual = (await importOriginal()) as any + return { + ...actual, + engineVariant: vi.fn().mockResolvedValue('windows-amd64-noavx'), + } + }) + + await extension.updateDefaultEngine() + + expect(mockSetDefaultEngineVariant).not.toBeCalled() + }) +}) diff --git a/extensions/engine-management-extension/src/utils.test.ts b/extensions/engine-management-extension/src/utils.test.ts new file mode 100644 index 000000000..f48c6ad44 --- /dev/null +++ b/extensions/engine-management-extension/src/utils.test.ts @@ -0,0 +1,72 @@ +import { describe, it, expect, vi } from 'vitest' +import { engineVariant } from './utils' + +vi.mock('@janhq/core', () => { + return { + log: () => {}, + } +}) + +describe('engineVariant', () => { + it('should return mac-arm64 when platform is darwin and arch is arm64', async () => { + vi.stubGlobal('PLATFORM', 'darwin') + const result = await engineVariant({ + cpu: { arch: 'arm64', instructions: '' }, + gpus: [], + vulkan: false, + }) + expect(result).toBe('mac-arm64') + }) + + it('should return mac-amd64 when platform is darwin and arch is not arm64', async () => { + vi.stubGlobal('PLATFORM', 'darwin') + const result = await engineVariant({ + cpu: { arch: 'x64', instructions: '' }, + gpus: [], + vulkan: false, + }) + expect(result).toBe('mac-amd64') + }) + + it('should return windows-amd64-noavx-cuda-12-0 when platform is win32, cuda is enabled, and cuda version is 12', async () => { + vi.stubGlobal('PLATFORM', 'win32') + const result = await engineVariant({ + cpu: { arch: 'x64', instructions: 'avx2' }, + gpus: [ + { + activated: true, + version: '12', + additional_information: { driver_version: '1.0' }, + }, + ], + vulkan: false, + }) + expect(result).toBe('windows-amd64-avx2-cuda-12-0') + }) + + it('should return linux-amd64-noavx-cuda-11-7 when platform is linux, cuda is enabled, and cuda version is 11', async () => { + vi.stubGlobal('PLATFORM', 'linux') + const result = await engineVariant({ + cpu: { arch: 'x64', instructions: 'avx2' }, + gpus: [ + { + activated: true, + version: '11', + additional_information: { driver_version: '1.0' }, + }, + ], + vulkan: false, + }) + expect(result).toBe('linux-amd64-avx2-cuda-11-7') + }) + + it('should return windows-amd64-vulkan when platform is win32 and vulkan is enabled', async () => { + vi.stubGlobal('PLATFORM', 'win32') + const result = await engineVariant({ + cpu: { arch: 'x64', instructions: '' }, + gpus: [{ activated: true, version: '12' }], + vulkan: true, + }) + expect(result).toBe('windows-amd64-vulkan') + }) +}) diff --git a/extensions/engine-management-extension/src/utils.ts b/extensions/engine-management-extension/src/utils.ts index 30d482313..a80518d65 100644 --- a/extensions/engine-management-extension/src/utils.ts +++ b/extensions/engine-management-extension/src/utils.ts @@ -65,10 +65,12 @@ export const engineVariant = async ( // There is no need to append the variant extension for mac if (platform.startsWith('mac')) return platform + // Only Nvidia GPUs have addition_information set and activated by default let engineVariant = - gpuSetting?.vulkan || gpuSetting.gpus.some((e) => !e.additional_information) - ? [platform, 'vulkan'] - : [ + !gpuSetting?.vulkan || + !gpuSetting.gpus?.length || + gpuSetting.gpus.some((e) => e.additional_information && e.activated) + ? [ platform, gpuRunMode(gpuSetting) === 'cuda' && (gpuSetting.cpu.instructions.includes('avx2') || @@ -78,6 +80,7 @@ export const engineVariant = async ( gpuRunMode(gpuSetting), cudaVersion(gpuSetting), ].filter(Boolean) // Remove any falsy values + : [platform, 'vulkan'] let engineVariantString = engineVariant.join('-') diff --git a/extensions/model-extension/src/legacy/model-json.ts b/extensions/model-extension/src/legacy/model-json.ts index 03560cde2..e9f0d093b 100644 --- a/extensions/model-extension/src/legacy/model-json.ts +++ b/extensions/model-extension/src/legacy/model-json.ts @@ -112,7 +112,7 @@ export const scanModelsFolder = async (): Promise< } return undefined }) - .filter((e) => !!e) + .filter(Boolean) return modelData } catch (err) { diff --git a/web/screens/Thread/ThreadCenterPanel/TextMessage/MarkdownTextMessage.tsx b/web/screens/Thread/ThreadCenterPanel/TextMessage/MarkdownTextMessage.tsx index 581159eff..6dae7a6b6 100644 --- a/web/screens/Thread/ThreadCenterPanel/TextMessage/MarkdownTextMessage.tsx +++ b/web/screens/Thread/ThreadCenterPanel/TextMessage/MarkdownTextMessage.tsx @@ -226,7 +226,7 @@ export const MarkdownTextMessage = memo( renderKatex ? [rehypeKatex, { throwOnError: false }] : undefined, [rehypeHighlightCodeLines, { showLineNumbers: true }], wrapCodeBlocksWithoutVisit, - ].filter((e) => !!e) as PluggableList + ].filter(Boolean) as PluggableList } components={markdownComponents} >