847 lines
24 KiB
TypeScript
847 lines
24 KiB
TypeScript
/**
|
|
* @jest-environment jsdom
|
|
*/
|
|
const readDirSyncMock = jest.fn()
|
|
const existMock = jest.fn()
|
|
const readFileSyncMock = jest.fn()
|
|
const downloadMock = jest.fn()
|
|
const mkdirMock = jest.fn()
|
|
const writeFileSyncMock = jest.fn()
|
|
const copyFileMock = jest.fn()
|
|
const dirNameMock = jest.fn()
|
|
const executeMock = jest.fn()
|
|
|
|
jest.mock('@janhq/core', () => ({
|
|
...jest.requireActual('@janhq/core/node'),
|
|
events: {
|
|
emit: jest.fn(),
|
|
},
|
|
fs: {
|
|
existsSync: existMock,
|
|
readdirSync: readDirSyncMock,
|
|
readFileSync: readFileSyncMock,
|
|
writeFileSync: writeFileSyncMock,
|
|
mkdir: mkdirMock,
|
|
copyFile: copyFileMock,
|
|
fileStat: () => ({
|
|
isDirectory: false,
|
|
}),
|
|
},
|
|
dirName: dirNameMock,
|
|
joinPath: (paths) => paths.join('/'),
|
|
ModelExtension: jest.fn(),
|
|
downloadFile: downloadMock,
|
|
executeOnMain: executeMock,
|
|
}))
|
|
|
|
jest.mock('@huggingface/gguf')
|
|
|
|
global.fetch = jest.fn(() =>
|
|
Promise.resolve({
|
|
json: () => Promise.resolve({ test: 100 }),
|
|
arrayBuffer: jest.fn(),
|
|
})
|
|
) as jest.Mock
|
|
|
|
import JanModelExtension from '.'
|
|
import { fs, dirName } from '@janhq/core'
|
|
import { gguf } from '@huggingface/gguf'
|
|
|
|
describe('JanModelExtension', () => {
|
|
let sut: JanModelExtension
|
|
|
|
beforeAll(() => {
|
|
// @ts-ignore
|
|
sut = new JanModelExtension()
|
|
})
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks()
|
|
})
|
|
|
|
describe('getConfiguredModels', () => {
|
|
describe("when there's no models are pre-populated", () => {
|
|
it('should return empty array', async () => {
|
|
// Mock configured models data
|
|
const configuredModels = []
|
|
existMock.mockReturnValue(true)
|
|
readDirSyncMock.mockReturnValue([])
|
|
|
|
const result = await sut.getConfiguredModels()
|
|
expect(result).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe("when there's are pre-populated models - all flattened", () => {
|
|
it('returns configured models data - flatten folder - with correct file_path and model id', async () => {
|
|
// Mock configured models data
|
|
const configuredModels = [
|
|
{
|
|
id: '1',
|
|
name: 'Model 1',
|
|
version: '1.0.0',
|
|
description: 'Model 1 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model1',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
created: new Date(),
|
|
updated: new Date(),
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
{
|
|
id: '2',
|
|
name: 'Model 2',
|
|
version: '2.0.0',
|
|
description: 'Model 2 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model2',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
]
|
|
existMock.mockReturnValue(true)
|
|
|
|
readDirSyncMock.mockImplementation((path) => {
|
|
if (path === 'file://models') return ['model1', 'model2']
|
|
else return ['model.json']
|
|
})
|
|
|
|
readFileSyncMock.mockImplementation((path) => {
|
|
if (path.includes('model1'))
|
|
return JSON.stringify(configuredModels[0])
|
|
else return JSON.stringify(configuredModels[1])
|
|
})
|
|
|
|
const result = await sut.getConfiguredModels()
|
|
expect(result).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model1/model.json',
|
|
id: '1',
|
|
}),
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model2/model.json',
|
|
id: '2',
|
|
}),
|
|
])
|
|
)
|
|
})
|
|
})
|
|
|
|
describe("when there's are pre-populated models - there are nested folders", () => {
|
|
it('returns configured models data - flatten folder - with correct file_path and model id', async () => {
|
|
// Mock configured models data
|
|
const configuredModels = [
|
|
{
|
|
id: '1',
|
|
name: 'Model 1',
|
|
version: '1.0.0',
|
|
description: 'Model 1 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model1',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
created: new Date(),
|
|
updated: new Date(),
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
{
|
|
id: '2',
|
|
name: 'Model 2',
|
|
version: '2.0.0',
|
|
description: 'Model 2 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model2',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
]
|
|
existMock.mockReturnValue(true)
|
|
|
|
readDirSyncMock.mockImplementation((path) => {
|
|
if (path === 'file://models') return ['model1', 'model2/model2-1']
|
|
else return ['model.json']
|
|
})
|
|
|
|
readFileSyncMock.mockImplementation((path) => {
|
|
if (path.includes('model1'))
|
|
return JSON.stringify(configuredModels[0])
|
|
else if (path.includes('model2/model2-1'))
|
|
return JSON.stringify(configuredModels[1])
|
|
})
|
|
|
|
const result = await sut.getConfiguredModels()
|
|
expect(result).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model1/model.json',
|
|
id: '1',
|
|
}),
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model2/model2-1/model.json',
|
|
id: '2',
|
|
}),
|
|
])
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('getDownloadedModels', () => {
|
|
describe('no models downloaded', () => {
|
|
it('should return empty array', async () => {
|
|
// Mock downloaded models data
|
|
existMock.mockReturnValue(true)
|
|
readDirSyncMock.mockReturnValue([])
|
|
|
|
const result = await sut.getDownloadedModels()
|
|
expect(result).toEqual([])
|
|
})
|
|
})
|
|
describe('only one model is downloaded', () => {
|
|
describe('flatten folder', () => {
|
|
it('returns downloaded models - with correct file_path and model id', async () => {
|
|
// Mock configured models data
|
|
const configuredModels = [
|
|
{
|
|
id: '1',
|
|
name: 'Model 1',
|
|
version: '1.0.0',
|
|
description: 'Model 1 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model1',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
created: new Date(),
|
|
updated: new Date(),
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
{
|
|
id: '2',
|
|
name: 'Model 2',
|
|
version: '2.0.0',
|
|
description: 'Model 2 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model2',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
]
|
|
existMock.mockReturnValue(true)
|
|
|
|
readDirSyncMock.mockImplementation((path) => {
|
|
if (path === 'file://models') return ['model1', 'model2']
|
|
else if (path === 'file://models/model1')
|
|
return ['model.json', 'test.gguf']
|
|
else return ['model.json']
|
|
})
|
|
|
|
readFileSyncMock.mockImplementation((path) => {
|
|
if (path.includes('model1'))
|
|
return JSON.stringify(configuredModels[0])
|
|
else return JSON.stringify(configuredModels[1])
|
|
})
|
|
|
|
const result = await sut.getDownloadedModels()
|
|
expect(result).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model1/model.json',
|
|
id: '1',
|
|
}),
|
|
])
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('all models are downloaded', () => {
|
|
describe('nested folders', () => {
|
|
it('returns downloaded models - with correct file_path and model id', async () => {
|
|
// Mock configured models data
|
|
const configuredModels = [
|
|
{
|
|
id: '1',
|
|
name: 'Model 1',
|
|
version: '1.0.0',
|
|
description: 'Model 1 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model1',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
created: new Date(),
|
|
updated: new Date(),
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
{
|
|
id: '2',
|
|
name: 'Model 2',
|
|
version: '2.0.0',
|
|
description: 'Model 2 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model2',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
]
|
|
existMock.mockReturnValue(true)
|
|
|
|
readDirSyncMock.mockImplementation((path) => {
|
|
if (path === 'file://models') return ['model1', 'model2/model2-1']
|
|
else return ['model.json', 'test.gguf']
|
|
})
|
|
|
|
readFileSyncMock.mockImplementation((path) => {
|
|
if (path.includes('model1'))
|
|
return JSON.stringify(configuredModels[0])
|
|
else return JSON.stringify(configuredModels[1])
|
|
})
|
|
|
|
const result = await sut.getDownloadedModels()
|
|
expect(result).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model1/model.json',
|
|
id: '1',
|
|
}),
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model2/model2-1/model.json',
|
|
id: '2',
|
|
}),
|
|
])
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('all models are downloaded with uppercased GGUF files', () => {
|
|
it('returns downloaded models - with correct file_path and model id', async () => {
|
|
// Mock configured models data
|
|
const configuredModels = [
|
|
{
|
|
id: '1',
|
|
name: 'Model 1',
|
|
version: '1.0.0',
|
|
description: 'Model 1 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model1',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
created: new Date(),
|
|
updated: new Date(),
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
{
|
|
id: '2',
|
|
name: 'Model 2',
|
|
version: '2.0.0',
|
|
description: 'Model 2 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model2',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
]
|
|
existMock.mockReturnValue(true)
|
|
|
|
readDirSyncMock.mockImplementation((path) => {
|
|
if (path === 'file://models') return ['model1', 'model2/model2-1']
|
|
else if (path === 'file://models/model1')
|
|
return ['model.json', 'test.GGUF']
|
|
else return ['model.json', 'test.gguf']
|
|
})
|
|
|
|
readFileSyncMock.mockImplementation((path) => {
|
|
if (path.includes('model1'))
|
|
return JSON.stringify(configuredModels[0])
|
|
else return JSON.stringify(configuredModels[1])
|
|
})
|
|
|
|
const result = await sut.getDownloadedModels()
|
|
expect(result).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model1/model.json',
|
|
id: '1',
|
|
}),
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model2/model2-1/model.json',
|
|
id: '2',
|
|
}),
|
|
])
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('all models are downloaded - GGUF & Tensort RT', () => {
|
|
it('returns downloaded models - with correct file_path and model id', async () => {
|
|
// Mock configured models data
|
|
const configuredModels = [
|
|
{
|
|
id: '1',
|
|
name: 'Model 1',
|
|
version: '1.0.0',
|
|
description: 'Model 1 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model1',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
created: new Date(),
|
|
updated: new Date(),
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
{
|
|
id: '2',
|
|
name: 'Model 2',
|
|
version: '2.0.0',
|
|
description: 'Model 2 description',
|
|
object: {
|
|
type: 'model',
|
|
uri: 'http://localhost:5000/models/model2',
|
|
},
|
|
format: 'onnx',
|
|
sources: [],
|
|
parameters: {},
|
|
settings: {},
|
|
metadata: {},
|
|
engine: 'test',
|
|
} as any,
|
|
]
|
|
existMock.mockReturnValue(true)
|
|
|
|
readDirSyncMock.mockImplementation((path) => {
|
|
if (path === 'file://models') return ['model1', 'model2/model2-1']
|
|
else if (path === 'file://models/model1')
|
|
return ['model.json', 'test.gguf']
|
|
else return ['model.json', 'test.engine']
|
|
})
|
|
|
|
readFileSyncMock.mockImplementation((path) => {
|
|
if (path.includes('model1'))
|
|
return JSON.stringify(configuredModels[0])
|
|
else return JSON.stringify(configuredModels[1])
|
|
})
|
|
|
|
const result = await sut.getDownloadedModels()
|
|
expect(result).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model1/model.json',
|
|
id: '1',
|
|
}),
|
|
expect.objectContaining({
|
|
file_path: 'file://models/model2/model2-1/model.json',
|
|
id: '2',
|
|
}),
|
|
])
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('deleteModel', () => {
|
|
describe('model is a GGUF model', () => {
|
|
it('should delete the GGUF file', async () => {
|
|
fs.unlinkSync = jest.fn()
|
|
const dirMock = dirName as jest.Mock
|
|
dirMock.mockReturnValue('file://models/model1')
|
|
|
|
fs.readFileSync = jest.fn().mockReturnValue(JSON.stringify({}))
|
|
|
|
readDirSyncMock.mockImplementation((path) => {
|
|
return ['model.json', 'test.gguf']
|
|
})
|
|
|
|
existMock.mockReturnValue(true)
|
|
|
|
await sut.deleteModel({
|
|
file_path: 'file://models/model1/model.json',
|
|
} as any)
|
|
|
|
expect(fs.unlinkSync).toHaveBeenCalledWith(
|
|
'file://models/model1/test.gguf'
|
|
)
|
|
})
|
|
|
|
it('no gguf file presented', async () => {
|
|
fs.unlinkSync = jest.fn()
|
|
const dirMock = dirName as jest.Mock
|
|
dirMock.mockReturnValue('file://models/model1')
|
|
|
|
fs.readFileSync = jest.fn().mockReturnValue(JSON.stringify({}))
|
|
|
|
readDirSyncMock.mockReturnValue(['model.json'])
|
|
|
|
existMock.mockReturnValue(true)
|
|
|
|
await sut.deleteModel({
|
|
file_path: 'file://models/model1/model.json',
|
|
} as any)
|
|
|
|
expect(fs.unlinkSync).toHaveBeenCalledTimes(0)
|
|
})
|
|
|
|
it('delete an imported model', async () => {
|
|
fs.rm = jest.fn()
|
|
const dirMock = dirName as jest.Mock
|
|
dirMock.mockReturnValue('file://models/model1')
|
|
|
|
readDirSyncMock.mockReturnValue(['model.json', 'test.gguf'])
|
|
|
|
// MARK: This is a tricky logic implement?
|
|
// I will just add test for now but will align on the legacy implementation
|
|
fs.readFileSync = jest.fn().mockReturnValue(
|
|
JSON.stringify({
|
|
metadata: {
|
|
author: 'user',
|
|
},
|
|
})
|
|
)
|
|
|
|
existMock.mockReturnValue(true)
|
|
|
|
await sut.deleteModel({
|
|
file_path: 'file://models/model1/model.json',
|
|
} as any)
|
|
|
|
expect(fs.rm).toHaveBeenCalledWith('file://models/model1')
|
|
})
|
|
|
|
it('delete tensorrt-models', async () => {
|
|
fs.rm = jest.fn()
|
|
const dirMock = dirName as jest.Mock
|
|
dirMock.mockReturnValue('file://models/model1')
|
|
|
|
readDirSyncMock.mockReturnValue(['model.json', 'test.engine'])
|
|
|
|
fs.readFileSync = jest.fn().mockReturnValue(JSON.stringify({}))
|
|
|
|
existMock.mockReturnValue(true)
|
|
|
|
await sut.deleteModel({
|
|
file_path: 'file://models/model1/model.json',
|
|
} as any)
|
|
|
|
expect(fs.unlinkSync).toHaveBeenCalledWith(
|
|
'file://models/model1/test.engine'
|
|
)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('downloadModel', () => {
|
|
const model: any = {
|
|
id: 'model-id',
|
|
name: 'Test Model',
|
|
sources: [
|
|
{ url: 'http://example.com/model.gguf', filename: 'model.gguf' },
|
|
],
|
|
engine: 'test-engine',
|
|
}
|
|
|
|
const network = {
|
|
ignoreSSL: true,
|
|
proxy: 'http://proxy.example.com',
|
|
}
|
|
|
|
const gpuSettings: any = {
|
|
gpus: [{ name: 'nvidia-rtx-3080', arch: 'ampere' }],
|
|
}
|
|
|
|
it('should reject with invalid gguf metadata', async () => {
|
|
existMock.mockImplementation(() => false)
|
|
|
|
expect(
|
|
sut.downloadModel(model, gpuSettings, network)
|
|
).rejects.toBeTruthy()
|
|
})
|
|
|
|
it('should download corresponding ID', async () => {
|
|
existMock.mockImplementation(() => true)
|
|
dirNameMock.mockImplementation(() => 'file://models/model1')
|
|
downloadMock.mockImplementation(() => {
|
|
return Promise.resolve({})
|
|
})
|
|
|
|
expect(
|
|
await sut.downloadModel(
|
|
{ ...model, file_path: 'file://models/model1/model.json' },
|
|
gpuSettings,
|
|
network
|
|
)
|
|
).toBeUndefined()
|
|
|
|
expect(downloadMock).toHaveBeenCalledWith(
|
|
{
|
|
localPath: 'file://models/model1/model.gguf',
|
|
modelId: 'model-id',
|
|
url: 'http://example.com/model.gguf',
|
|
},
|
|
{ ignoreSSL: true, proxy: 'http://proxy.example.com' }
|
|
)
|
|
})
|
|
|
|
it('should handle invalid model file', async () => {
|
|
executeMock.mockResolvedValue({})
|
|
|
|
fs.readFileSync = jest.fn(() => {
|
|
return JSON.stringify({ metadata: { author: 'user' } })
|
|
})
|
|
|
|
expect(
|
|
sut.downloadModel(
|
|
{ ...model, file_path: 'file://models/model1/model.json' },
|
|
gpuSettings,
|
|
network
|
|
)
|
|
).resolves.not.toThrow()
|
|
|
|
expect(downloadMock).not.toHaveBeenCalled()
|
|
})
|
|
it('should handle model file with no sources', async () => {
|
|
executeMock.mockResolvedValue({})
|
|
const modelWithoutSources = { ...model, sources: [] }
|
|
|
|
expect(
|
|
sut.downloadModel(
|
|
{
|
|
...modelWithoutSources,
|
|
file_path: 'file://models/model1/model.json',
|
|
},
|
|
gpuSettings,
|
|
network
|
|
)
|
|
).resolves.toBe(undefined)
|
|
|
|
expect(downloadMock).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('should handle model file with multiple sources', async () => {
|
|
const modelWithMultipleSources = {
|
|
...model,
|
|
sources: [
|
|
{ url: 'http://example.com/model1.gguf', filename: 'model1.gguf' },
|
|
{ url: 'http://example.com/model2.gguf', filename: 'model2.gguf' },
|
|
],
|
|
}
|
|
|
|
executeMock.mockResolvedValue({
|
|
metadata: { 'tokenizer.ggml.eos_token_id': 0 },
|
|
})
|
|
;(gguf as jest.Mock).mockResolvedValue({
|
|
metadata: { 'tokenizer.ggml.eos_token_id': 0 },
|
|
})
|
|
// @ts-ignore
|
|
global.NODE = 'node'
|
|
// @ts-ignore
|
|
global.DEFAULT_MODEL = {
|
|
parameters: { stop: [] },
|
|
}
|
|
downloadMock.mockImplementation(() => {
|
|
return Promise.resolve({})
|
|
})
|
|
|
|
expect(
|
|
await sut.downloadModel(
|
|
{
|
|
...modelWithMultipleSources,
|
|
file_path: 'file://models/model1/model.json',
|
|
},
|
|
gpuSettings,
|
|
network
|
|
)
|
|
).toBeUndefined()
|
|
|
|
expect(downloadMock).toHaveBeenCalledWith(
|
|
{
|
|
localPath: 'file://models/model1/model1.gguf',
|
|
modelId: 'model-id',
|
|
url: 'http://example.com/model1.gguf',
|
|
},
|
|
{ ignoreSSL: true, proxy: 'http://proxy.example.com' }
|
|
)
|
|
|
|
expect(downloadMock).toHaveBeenCalledWith(
|
|
{
|
|
localPath: 'file://models/model1/model2.gguf',
|
|
modelId: 'model-id',
|
|
url: 'http://example.com/model2.gguf',
|
|
},
|
|
{ ignoreSSL: true, proxy: 'http://proxy.example.com' }
|
|
)
|
|
})
|
|
|
|
it('should handle model file with no file_path', async () => {
|
|
executeMock.mockResolvedValue({
|
|
metadata: { 'tokenizer.ggml.eos_token_id': 0 },
|
|
})
|
|
;(gguf as jest.Mock).mockResolvedValue({
|
|
metadata: { 'tokenizer.ggml.eos_token_id': 0 },
|
|
})
|
|
// @ts-ignore
|
|
global.NODE = 'node'
|
|
// @ts-ignore
|
|
global.DEFAULT_MODEL = {
|
|
parameters: { stop: [] },
|
|
}
|
|
const modelWithoutFilepath = { ...model, file_path: undefined }
|
|
|
|
await sut.downloadModel(modelWithoutFilepath, gpuSettings, network)
|
|
|
|
expect(downloadMock).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
localPath: 'file://models/model-id/model.gguf',
|
|
}),
|
|
expect.anything()
|
|
)
|
|
})
|
|
|
|
it('should handle model file with invalid file_path', async () => {
|
|
executeMock.mockResolvedValue({
|
|
metadata: { 'tokenizer.ggml.eos_token_id': 0 },
|
|
})
|
|
;(gguf as jest.Mock).mockResolvedValue({
|
|
metadata: { 'tokenizer.ggml.eos_token_id': 0 },
|
|
})
|
|
// @ts-ignore
|
|
global.NODE = 'node'
|
|
// @ts-ignore
|
|
global.DEFAULT_MODEL = {
|
|
parameters: { stop: [] },
|
|
}
|
|
const modelWithInvalidFilepath = {
|
|
...model,
|
|
file_path: 'file://models/invalid-model.json',
|
|
}
|
|
|
|
await sut.downloadModel(modelWithInvalidFilepath, gpuSettings, network)
|
|
|
|
expect(downloadMock).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
localPath: 'file://models/model1/model.gguf',
|
|
}),
|
|
expect.anything()
|
|
)
|
|
})
|
|
|
|
it('should handle model with valid chat_template', async () => {
|
|
executeMock.mockResolvedValue('{prompt}')
|
|
;(gguf as jest.Mock).mockResolvedValue({
|
|
metadata: {},
|
|
})
|
|
// @ts-ignore
|
|
global.NODE = 'node'
|
|
// @ts-ignore
|
|
global.DEFAULT_MODEL = {
|
|
parameters: { stop: [] },
|
|
settings: {
|
|
prompt_template: '<|im-start|>{prompt}<|im-end|>',
|
|
},
|
|
}
|
|
|
|
const result = await sut.retrieveGGUFMetadata({})
|
|
|
|
expect(result).toEqual({
|
|
parameters: {
|
|
stop: [],
|
|
},
|
|
settings: {
|
|
ctx_len: 4096,
|
|
ngl: 33,
|
|
prompt_template: '{prompt}',
|
|
},
|
|
})
|
|
})
|
|
|
|
it('should handle model without chat_template', async () => {
|
|
executeMock.mockRejectedValue({})
|
|
;(gguf as jest.Mock).mockResolvedValue({
|
|
metadata: {},
|
|
})
|
|
// @ts-ignore
|
|
global.NODE = 'node'
|
|
// @ts-ignore
|
|
global.DEFAULT_MODEL = {
|
|
parameters: { stop: [] },
|
|
settings: {
|
|
prompt_template: '<|im-start|>{prompt}<|im-end|>',
|
|
},
|
|
}
|
|
|
|
const result = await sut.retrieveGGUFMetadata({})
|
|
|
|
expect(result).toEqual({
|
|
parameters: {
|
|
stop: [],
|
|
},
|
|
settings: {
|
|
ctx_len: 4096,
|
|
ngl: 33,
|
|
prompt_template: '<|im-start|>{prompt}<|im-end|>',
|
|
},
|
|
})
|
|
})
|
|
})
|
|
})
|