From d932adc6510f2185740297e41d6f22eb9a714f5c Mon Sep 17 00:00:00 2001 From: Louis Date: Thu, 6 Jun 2024 16:25:47 +0700 Subject: [PATCH 1/3] refactor: add models list test cases --- cortex-js/constant.ts | 9 --- cortex-js/package.json | 12 ++-- .../src/file-manager/file-manager.service.ts | 2 +- .../commanders/serve.command.ts | 5 +- .../commanders/shortcuts/run.command.ts | 2 +- .../test/model-list.command.spec.ts | 61 +++++++++++++++++++ .../commanders/usecases/ps.cli.usecases.ts | 2 +- .../src/infrastructure/constants/cortex.ts | 9 ++- .../database/mysql-database.providers.ts | 2 +- .../database/sqlite-database.providers.ts | 2 +- .../dtos/cortex/start-cortex.dto.ts | 2 +- .../providers/cortex/cortex.provider.ts | 2 +- cortex-js/src/main.ts | 5 +- .../src/usecases/cortex/cortex.usecases.ts | 2 +- 14 files changed, 92 insertions(+), 25 deletions(-) delete mode 100644 cortex-js/constant.ts create mode 100644 cortex-js/src/infrastructure/commanders/test/model-list.command.spec.ts diff --git a/cortex-js/constant.ts b/cortex-js/constant.ts deleted file mode 100644 index 14bda4837..000000000 --- a/cortex-js/constant.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const databaseName = 'cortex'; - -export const databaseFile = `${databaseName}.db`; - -export const defaultCortexJsHost = 'localhost'; -export const defaultCortexJsPort = 1337; - -export const defaultCortexCppHost = '127.0.0.1'; -export const defaultCortexCppPort = 3928; diff --git a/cortex-js/package.json b/cortex-js/package.json index e795f942a..1e4b08149 100644 --- a/cortex-js/package.json +++ b/cortex-js/package.json @@ -55,12 +55,11 @@ "sqlite3": "^5.1.7", "typeorm": "^0.3.20", "ulid": "^2.3.0", - "yaml": "^2.4.2", + "update-notifier": "^5.0.0", "uuid": "^9.0.1", - "update-notifier": "^5.0.0" + "yaml": "^2.4.2" }, "devDependencies": { - "cpx": "^1.5.0", "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", "@nestjs/testing": "^10.0.0", @@ -76,18 +75,21 @@ "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "cpx": "^1.5.0", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", + "hanbi": "^1.0.3", "jest": "^29.5.0", + "nest-commander-testing": "^3.3.0", "prettier": "^3.0.0", + "run-script-os": "^1.1.6", "source-map-support": "^0.5.21", "supertest": "^6.3.3", "ts-jest": "^29.1.0", "ts-loader": "^9.4.3", "tsconfig-paths": "^4.2.0", - "typescript": "^5.1.3", - "run-script-os": "^1.1.6" + "typescript": "^5.1.3" }, "files": [ "dist" diff --git a/cortex-js/src/file-manager/file-manager.service.ts b/cortex-js/src/file-manager/file-manager.service.ts index 4d838db6f..78b29270c 100644 --- a/cortex-js/src/file-manager/file-manager.service.ts +++ b/cortex-js/src/file-manager/file-manager.service.ts @@ -43,7 +43,7 @@ export class FileManagerService { } } - private async writeConfigFile(config: Config): Promise { + async writeConfigFile(config: Config): Promise { const homeDir = os.homedir(); const configPath = join(homeDir, this.configFile); diff --git a/cortex-js/src/infrastructure/commanders/serve.command.ts b/cortex-js/src/infrastructure/commanders/serve.command.ts index 7e49ad590..f839b4a0b 100644 --- a/cortex-js/src/infrastructure/commanders/serve.command.ts +++ b/cortex-js/src/infrastructure/commanders/serve.command.ts @@ -1,5 +1,8 @@ import { spawn } from 'child_process'; -import { defaultCortexJsHost, defaultCortexJsPort } from 'constant'; +import { + defaultCortexJsHost, + defaultCortexJsPort, +} from '@/infrastructure/constants/cortex'; import { CommandRunner, SubCommand, Option } from 'nest-commander'; import { join } from 'path'; diff --git a/cortex-js/src/infrastructure/commanders/shortcuts/run.command.ts b/cortex-js/src/infrastructure/commanders/shortcuts/run.command.ts index 760b16ba3..118212590 100644 --- a/cortex-js/src/infrastructure/commanders/shortcuts/run.command.ts +++ b/cortex-js/src/infrastructure/commanders/shortcuts/run.command.ts @@ -7,7 +7,7 @@ import { } from 'nest-commander'; import { exit } from 'node:process'; import { ChatCliUsecases } from '../usecases/chat.cli.usecases'; -import { defaultCortexCppHost, defaultCortexCppPort } from 'constant'; +import { defaultCortexCppHost, defaultCortexCppPort } from '@/infrastructure/constants/cortex'; import { ModelsCliUsecases } from '../usecases/models.cli.usecases'; import { isLocalModel } from '../utils/normalize-model-id'; import { ModelNotFoundException } from '@/infrastructure/exception/model-not-found.exception'; diff --git a/cortex-js/src/infrastructure/commanders/test/model-list.command.spec.ts b/cortex-js/src/infrastructure/commanders/test/model-list.command.spec.ts new file mode 100644 index 000000000..4013a7bfe --- /dev/null +++ b/cortex-js/src/infrastructure/commanders/test/model-list.command.spec.ts @@ -0,0 +1,61 @@ +import { TestingModule } from '@nestjs/testing'; +import { stubMethod } from 'hanbi'; +import { CommandTestFactory } from 'nest-commander-testing'; +import { CommandModule } from '@/command.module'; +import { FileManagerService } from '@/file-manager/file-manager.service'; +import { join } from 'path'; +import { mkdirSync, rmSync, writeFileSync } from 'fs'; + +let commandInstance: TestingModule; + +beforeEach(async () => { + commandInstance = await CommandTestFactory.createTestingCommand({ + imports: [CommandModule], + }) + // .overrideProvider(LogService) + // .useValue({}) + .compile(); + const fileService = + commandInstance.resolve(FileManagerService); + + // Attempt to create test folder + (await fileService).writeConfigFile({ + dataFolderPath: join(__dirname, 'test_data'), + }); +}); + +afterEach(async () => { + // Attempt to clean test folder + try { + await rmSync(join(__dirname, 'test_data'), { + recursive: true, + force: true, + }); + } catch (e) {} +}); + +describe('models list returns array of models', () => { + test('empty model list', async () => { + const logMock = stubMethod(console, 'table'); + + await CommandTestFactory.run(commandInstance, ['models', 'list']); + expect(logMock.firstCall?.args[0]).toBeInstanceOf(Array); + expect(logMock.firstCall?.args[0].length).toBe(0); + }); + + test('many models in the list', async () => { + const logMock = stubMethod(console, 'table'); + + mkdirSync(join(__dirname, 'test_data', 'models'), { recursive: true }); + writeFileSync( + join(__dirname, 'test_data', 'models', 'test.yaml'), + 'model: test', + 'utf8', + ); + + await CommandTestFactory.run(commandInstance, ['models', 'list']); + expect(logMock.firstCall?.args[0]).toBeInstanceOf(Array); + expect(logMock.firstCall?.args[0].length).toBe(1); + expect(logMock.firstCall?.args[0][0].id).toBe('test'); + }); +}); diff --git a/cortex-js/src/infrastructure/commanders/usecases/ps.cli.usecases.ts b/cortex-js/src/infrastructure/commanders/usecases/ps.cli.usecases.ts index 51e097e3e..70d20a16b 100644 --- a/cortex-js/src/infrastructure/commanders/usecases/ps.cli.usecases.ts +++ b/cortex-js/src/infrastructure/commanders/usecases/ps.cli.usecases.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { defaultCortexCppHost, defaultCortexCppPort } from 'constant'; +import { defaultCortexCppHost, defaultCortexCppPort } from '@/infrastructure/constants/cortex'; export interface ModelStat { modelId: string; diff --git a/cortex-js/src/infrastructure/constants/cortex.ts b/cortex-js/src/infrastructure/constants/cortex.ts index 5fab81ecc..6e6b4c400 100644 --- a/cortex-js/src/infrastructure/constants/cortex.ts +++ b/cortex-js/src/infrastructure/constants/cortex.ts @@ -1,5 +1,12 @@ -import { defaultCortexCppHost, defaultCortexCppPort } from '@/../constant'; +export const databaseName = 'cortex'; +export const databaseFile = `${databaseName}.db`; + +export const defaultCortexJsHost = 'localhost'; +export const defaultCortexJsPort = 1337; + +export const defaultCortexCppHost = '127.0.0.1'; +export const defaultCortexCppPort = 3928; // CORTEX CPP export const CORTEX_CPP_EMBEDDINGS_URL = ( host: string = defaultCortexCppHost, diff --git a/cortex-js/src/infrastructure/database/mysql-database.providers.ts b/cortex-js/src/infrastructure/database/mysql-database.providers.ts index 006b726a3..347c82ef5 100644 --- a/cortex-js/src/infrastructure/database/mysql-database.providers.ts +++ b/cortex-js/src/infrastructure/database/mysql-database.providers.ts @@ -1,4 +1,4 @@ -import { databaseName } from 'constant'; +import { databaseName } from '@/infrastructure/constants/cortex'; import { DataSource } from 'typeorm'; export const mysqlDatabaseProviders = [ diff --git a/cortex-js/src/infrastructure/database/sqlite-database.providers.ts b/cortex-js/src/infrastructure/database/sqlite-database.providers.ts index bcedf7b0c..cd3bb54f8 100644 --- a/cortex-js/src/infrastructure/database/sqlite-database.providers.ts +++ b/cortex-js/src/infrastructure/database/sqlite-database.providers.ts @@ -1,5 +1,5 @@ import { FileManagerService } from '@/file-manager/file-manager.service'; -import { databaseFile } from '@/../constant'; +import { databaseFile } from '@/infrastructure/constants/cortex'; import { join } from 'path'; import { DataSource } from 'typeorm'; diff --git a/cortex-js/src/infrastructure/dtos/cortex/start-cortex.dto.ts b/cortex-js/src/infrastructure/dtos/cortex/start-cortex.dto.ts index 3cf6e6b11..6a8536bfc 100644 --- a/cortex-js/src/infrastructure/dtos/cortex/start-cortex.dto.ts +++ b/cortex-js/src/infrastructure/dtos/cortex/start-cortex.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsIP, IsNumber, IsString, Max, Min } from 'class-validator'; -import { defaultCortexCppHost, defaultCortexCppPort } from 'constant'; +import { defaultCortexCppHost, defaultCortexCppPort } from '@/infrastructure/constants/cortex'; export class StartCortexDto { @ApiProperty({ diff --git a/cortex-js/src/infrastructure/providers/cortex/cortex.provider.ts b/cortex-js/src/infrastructure/providers/cortex/cortex.provider.ts index 33aee645e..6d0b26e6b 100644 --- a/cortex-js/src/infrastructure/providers/cortex/cortex.provider.ts +++ b/cortex-js/src/infrastructure/providers/cortex/cortex.provider.ts @@ -4,7 +4,7 @@ import { PromptTemplate } from '@/domain/models/prompt-template.interface'; import { join } from 'path'; import { Model, ModelSettingParams } from '@/domain/models/model.interface'; import { HttpService } from '@nestjs/axios'; -import { defaultCortexCppHost, defaultCortexCppPort } from '@/../constant'; +import { defaultCortexCppHost, defaultCortexCppPort } from '@/infrastructure/constants/cortex'; import { readdirSync } from 'node:fs'; import { normalizeModelId } from '@/infrastructure/commanders/utils/normalize-model-id'; import { firstValueFrom } from 'rxjs'; diff --git a/cortex-js/src/main.ts b/cortex-js/src/main.ts index 9ec978be7..fa7564669 100644 --- a/cortex-js/src/main.ts +++ b/cortex-js/src/main.ts @@ -2,7 +2,10 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { INestApplication, ValidationPipe } from '@nestjs/common'; -import { defaultCortexJsHost, defaultCortexJsPort } from 'constant'; +import { + defaultCortexJsHost, + defaultCortexJsPort, +} from '@/infrastructure/constants/cortex'; import { SeedService } from './usecases/seed/seed.service'; import { FileManagerService } from './file-manager/file-manager.service'; diff --git a/cortex-js/src/usecases/cortex/cortex.usecases.ts b/cortex-js/src/usecases/cortex/cortex.usecases.ts index 6369be8cd..f867667f8 100644 --- a/cortex-js/src/usecases/cortex/cortex.usecases.ts +++ b/cortex-js/src/usecases/cortex/cortex.usecases.ts @@ -3,7 +3,7 @@ import { ChildProcess, spawn } from 'child_process'; import { join } from 'path'; import { CortexOperationSuccessfullyDto } from '@/infrastructure/dtos/cortex/cortex-operation-successfully.dto'; import { HttpService } from '@nestjs/axios'; -import { defaultCortexCppHost, defaultCortexCppPort } from '@/../constant'; +import { defaultCortexCppHost, defaultCortexCppPort } from '@/infrastructure/constants/cortex'; import { existsSync } from 'node:fs'; import { firstValueFrom } from 'rxjs'; import { FileManagerService } from '@/file-manager/file-manager.service'; From 79c400803e332b9ece754a4cdfbdbc28e5f43519 Mon Sep 17 00:00:00 2001 From: Louis Date: Thu, 6 Jun 2024 17:54:18 +0700 Subject: [PATCH 2/3] fix: async test preparation --- .../test/model-list.command.spec.ts | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/cortex-js/src/infrastructure/commanders/test/model-list.command.spec.ts b/cortex-js/src/infrastructure/commanders/test/model-list.command.spec.ts index 4013a7bfe..b90b26006 100644 --- a/cortex-js/src/infrastructure/commanders/test/model-list.command.spec.ts +++ b/cortex-js/src/infrastructure/commanders/test/model-list.command.spec.ts @@ -8,31 +8,37 @@ import { mkdirSync, rmSync, writeFileSync } from 'fs'; let commandInstance: TestingModule; -beforeEach(async () => { - commandInstance = await CommandTestFactory.createTestingCommand({ - imports: [CommandModule], - }) - // .overrideProvider(LogService) - // .useValue({}) - .compile(); - const fileService = - commandInstance.resolve(FileManagerService); - - // Attempt to create test folder - (await fileService).writeConfigFile({ - dataFolderPath: join(__dirname, 'test_data'), - }); -}); +beforeEach( + () => + new Promise(async (res) => { + commandInstance = await CommandTestFactory.createTestingCommand({ + imports: [CommandModule], + }) + // .overrideProvider(LogService) + // .useValue({}) + .compile(); + const fileService = + await commandInstance.resolve(FileManagerService); -afterEach(async () => { - // Attempt to clean test folder - try { - await rmSync(join(__dirname, 'test_data'), { - recursive: true, - force: true, - }); - } catch (e) {} -}); + // Attempt to create test folder + await fileService.writeConfigFile({ + dataFolderPath: join(__dirname, 'test_data'), + }); + res(); + }), +); + +afterEach( + () => + new Promise(async (res) => { + // Attempt to clean test folder + rmSync(join(__dirname, 'test_data'), { + recursive: true, + force: true, + }); + res(); + }), +); describe('models list returns array of models', () => { test('empty model list', async () => { From cd8275f68199e10cf1b0fdaa093c1182c0699bf1 Mon Sep 17 00:00:00 2001 From: Louis Date: Thu, 6 Jun 2024 19:01:03 +0700 Subject: [PATCH 3/3] fix: ignore preset --- .../infrastructure/commanders/usecases/models.cli.usecases.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cortex-js/src/infrastructure/commanders/usecases/models.cli.usecases.ts b/cortex-js/src/infrastructure/commanders/usecases/models.cli.usecases.ts index df60ad0c0..da07499a2 100644 --- a/cortex-js/src/infrastructure/commanders/usecases/models.cli.usecases.ts +++ b/cortex-js/src/infrastructure/commanders/usecases/models.cli.usecases.ts @@ -397,12 +397,14 @@ export class ModelsCliUsecases { private async parsePreset(preset?: string): Promise { const presetsFolder = await this.fileService.getPresetsPath(); + if (!existsSync(presetsFolder)) return {}; + const presetFile = readdirSync(presetsFolder).find( (file) => file.toLowerCase() === `${preset?.toLowerCase()}.yaml` || file.toLowerCase() === `${preset?.toLocaleLowerCase()}.yml`, ); - if (!presetFile) throw new Error(`Preset ${preset} not found`); + if (!presetFile) return {}; const presetPath = join(presetsFolder, presetFile); if (!preset || !existsSync(presetPath)) return {};