Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.

Commit 1f136eb

Browse files
authored
feat: Model Presets (#651)
1 parent b879f66 commit 1f136eb

36 files changed

+630
-338
lines changed

cortex-js/src/command.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { MessagesModule } from './usecases/messages/messages.module';
2626
import { FileManagerModule } from './file-manager/file-manager.module';
2727
import { PSCommand } from './infrastructure/commanders/ps.command';
2828
import { KillCommand } from './infrastructure/commanders/kill.command';
29+
import { PresetCommand } from './infrastructure/commanders/presets.command';
2930

3031
@Module({
3132
imports: [
@@ -52,6 +53,7 @@ import { KillCommand } from './infrastructure/commanders/kill.command';
5253
InitCommand,
5354
PSCommand,
5455
KillCommand,
56+
PresetCommand,
5557

5658
// Questions
5759
InitRunModeQuestions,

cortex-js/src/infrastructure/commanders/cortex-command.commander.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ModelPullCommand } from './models/model-pull.command';
88
import { PSCommand } from './ps.command';
99
import { KillCommand } from './kill.command';
1010
import pkg from '@/../package.json';
11+
import { PresetCommand } from './presets.command';
1112

1213
interface CortexCommandOptions {
1314
version: boolean;
@@ -22,6 +23,7 @@ interface CortexCommandOptions {
2223
ModelPullCommand,
2324
PSCommand,
2425
KillCommand,
26+
PresetCommand,
2527
],
2628
description: 'Cortex CLI',
2729
})

cortex-js/src/infrastructure/commanders/models/model-list.command.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,21 @@ export class ModelListCommand extends CommandRunner {
1212

1313
async run(_input: string[], option: ModelListOptions): Promise<void> {
1414
const models = await this.modelsCliUsecases.listAllModels();
15-
option.format === 'table' ? console.table(models) : console.log(models);
15+
option.format === 'table'
16+
? console.table(
17+
models.map((e) => ({
18+
id: e.id,
19+
engine: e.engine,
20+
format: e.format,
21+
created: e.created,
22+
})),
23+
)
24+
: console.log(models);
1625
}
1726

1827
@Option({
1928
flags: '-f, --format <format>',
20-
defaultValue: 'json',
29+
defaultValue: 'table',
2130
description: 'Print models list in table or json format',
2231
})
2332
parseModelId(value: string) {

cortex-js/src/infrastructure/commanders/models/model-start.command.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { CortexUsecases } from '@/usecases/cortex/cortex.usecases';
1010

1111
type ModelStartOptions = {
1212
attach: boolean;
13+
preset?: string;
1314
};
1415
@SubCommand({ name: 'start', description: 'Start a model by ID.' })
1516
export class ModelStartCommand extends CommandRunner {
@@ -34,7 +35,7 @@ export class ModelStartCommand extends CommandRunner {
3435

3536
await this.cortexUsecases
3637
.startCortex(options.attach)
37-
.then(() => this.modelsCliUsecases.startModel(modelId))
38+
.then(() => this.modelsCliUsecases.startModel(modelId, options.preset))
3839
.then(console.log)
3940
.then(() => !options.attach && process.exit(0));
4041
}
@@ -63,4 +64,12 @@ export class ModelStartCommand extends CommandRunner {
6364
parseAttach() {
6465
return true;
6566
}
67+
68+
@Option({
69+
flags: '-p, --preset <preset>',
70+
description: 'Apply a chat preset to the chat session',
71+
})
72+
parseTemplate(value: string) {
73+
return value;
74+
}
6675
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { FileManagerService } from '@/file-manager/file-manager.service';
2+
import { readdirSync } from 'fs';
3+
import { CommandRunner, SubCommand } from 'nest-commander';
4+
import { join } from 'path';
5+
6+
@SubCommand({
7+
name: 'presets',
8+
description: 'Show all available presets',
9+
})
10+
export class PresetCommand extends CommandRunner {
11+
constructor(private readonly fileService: FileManagerService) {
12+
super();
13+
}
14+
async run(): Promise<void> {
15+
return console.table(
16+
readdirSync(
17+
join(await this.fileService.getDataFolderPath(), `presets`),
18+
).map((e) => ({
19+
preset: e.replace('.yaml', ''),
20+
})),
21+
);
22+
}
23+
}

cortex-js/src/infrastructure/commanders/shortcuts/run.command.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { ModelsCliUsecases } from '../usecases/models.cli.usecases';
1212

1313
type RunOptions = {
1414
threadId?: string;
15+
preset?: string;
1516
};
1617

1718
@SubCommand({
@@ -28,7 +29,7 @@ export class RunCommand extends CommandRunner {
2829
super();
2930
}
3031

31-
async run(input: string[], option?: RunOptions): Promise<void> {
32+
async run(input: string[], options: RunOptions): Promise<void> {
3233
let modelId = input[0];
3334
if (!modelId) {
3435
try {
@@ -41,8 +42,8 @@ export class RunCommand extends CommandRunner {
4142

4243
return this.cortexUsecases
4344
.startCortex(false, defaultCortexCppHost, defaultCortexCppPort)
44-
.then(() => this.modelsCliUsecases.startModel(modelId))
45-
.then(() => this.chatCliUsecases.chat(modelId, option?.threadId));
45+
.then(() => this.modelsCliUsecases.startModel(modelId, options.preset))
46+
.then(() => this.chatCliUsecases.chat(modelId, options.threadId));
4647
}
4748

4849
@Option({
@@ -53,6 +54,14 @@ export class RunCommand extends CommandRunner {
5354
return value;
5455
}
5556

57+
@Option({
58+
flags: '-p, --preset <preset>',
59+
description: 'Apply a chat preset to the chat session',
60+
})
61+
parseTemplate(value: string) {
62+
return value;
63+
}
64+
5665
modelInquiry = async () => {
5766
const models = await this.modelsCliUsecases.listAllModels();
5867
if (!models.length) throw 'No models found';

cortex-js/src/infrastructure/commanders/usecases/models.cli.usecases.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ import { ModelTokenizer } from '../types/model-tokenizer.interface';
2525
import { HttpService } from '@nestjs/axios';
2626
import { firstValueFrom } from 'rxjs';
2727
import { StartModelSuccessDto } from '@/infrastructure/dtos/models/start-model-success.dto';
28+
import { FileManagerService } from '@/file-manager/file-manager.service';
29+
import { join } from 'path';
30+
import { load } from 'js-yaml';
31+
import { existsSync, readFileSync } from 'node:fs';
2832

2933
const AllQuantizations = [
3034
'Q3_K_S',
@@ -56,15 +60,24 @@ export class ModelsCliUsecases {
5660
@Inject(InquirerService)
5761
private readonly inquirerService: InquirerService,
5862
private readonly httpService: HttpService,
63+
private readonly fileManagerService: FileManagerService,
5964
) {}
6065

6166
/**
6267
* Start a model by ID
6368
* @param modelId
6469
*/
65-
async startModel(modelId: string): Promise<StartModelSuccessDto> {
70+
async startModel(
71+
modelId: string,
72+
preset?: string,
73+
): Promise<StartModelSuccessDto> {
74+
const parsedPreset = await this.parsePreset(preset);
6675
return this.getModelOrStop(modelId)
67-
.then(() => this.modelsUsecases.startModel(modelId))
76+
.then((model) => ({
77+
...model.settings,
78+
...parsedPreset,
79+
}))
80+
.then((settings) => this.modelsUsecases.startModel(modelId, settings))
6881
.catch(() => {
6982
return {
7083
modelId: modelId,
@@ -164,7 +177,7 @@ export class ModelsCliUsecases {
164177
],
165178
id: modelId,
166179
name: modelId,
167-
version: '',
180+
version: '1.0.0',
168181
format: ModelFormat.GGUF,
169182
description: '',
170183
settings: {
@@ -440,4 +453,16 @@ export class ModelsCliUsecases {
440453
private getRepoModelsUrl(repoId: string, tree?: string): string {
441454
return `https://huggingface.co/api/models/${repoId}${tree ? `/tree/${tree}` : ''}`;
442455
}
456+
457+
private async parsePreset(preset?: string): Promise<object> {
458+
const presetPath = join(
459+
await this.fileManagerService.getDataFolderPath(),
460+
'presets',
461+
`${preset}.yaml`,
462+
);
463+
if (!preset || !existsSync(presetPath)) return {};
464+
return preset
465+
? (load(readFileSync(join(presetPath), 'utf-8')) as object)
466+
: {};
467+
}
443468
}

cortex-js/src/infrastructure/controllers/chat.controller.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,7 @@ import { Body, Controller, Post, Headers, Res, HttpCode } from '@nestjs/common';
22
import { CreateChatCompletionDto } from '@/infrastructure/dtos/chat/create-chat-completion.dto';
33
import { ChatUsecases } from '@/usecases/chat/chat.usecases';
44
import { Response } from 'express';
5-
import {
6-
ApiCreatedResponse,
7-
ApiExtraModels,
8-
ApiOperation,
9-
ApiTags,
10-
getSchemaPath,
11-
ApiResponse
12-
} from '@nestjs/swagger';
5+
import { ApiOperation, ApiTags, ApiResponse } from '@nestjs/swagger';
136
import { ChatCompletionResponseDto } from '../dtos/chat/chat-completion-response.dto';
147

158
@ApiTags('Inference')
@@ -19,7 +12,7 @@ export class ChatController {
1912

2013
@ApiOperation({
2114
summary: 'Create chat completion',
22-
description: "Creates a model response for the given conversation.",
15+
description: 'Creates a model response for the given conversation.',
2316
})
2417
@HttpCode(200)
2518
@ApiResponse({

cortex-js/src/infrastructure/dtos/assistants/create-assistant.dto.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
AssistantMetadata,
66
} from '@/domain/models/assistant.interface';
77
import { AssistantToolDto } from './assistant-tool.dto';
8-
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
8+
import { ApiProperty } from '@nestjs/swagger';
99

1010
export class CreateAssistantDto implements Partial<Assistant> {
1111
@ApiProperty({
Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import { ApiProperty } from '@nestjs/swagger';
22

33
export class DeleteAssistantResponseDto {
4-
@ApiProperty({
5-
example: 'assistant_123',
6-
description: 'The identifier of the assistant that was deleted.'
7-
})
8-
id: string;
4+
@ApiProperty({
5+
example: 'assistant_123',
6+
description: 'The identifier of the assistant that was deleted.',
7+
})
8+
id: string;
99

10-
@ApiProperty({
11-
example: 'assistant',
12-
description: 'Type of the object, indicating it\'s a assistant.',
13-
default: 'assistant'
14-
})
15-
object: string;
10+
@ApiProperty({
11+
example: 'assistant',
12+
description: "Type of the object, indicating it's a assistant.",
13+
default: 'assistant',
14+
})
15+
object: string;
1616

17-
@ApiProperty({
18-
example: true,
19-
description: 'Indicates whether the assistant was successfully deleted.'
20-
})
21-
deleted: boolean;
17+
@ApiProperty({
18+
example: true,
19+
description: 'Indicates whether the assistant was successfully deleted.',
20+
})
21+
deleted: boolean;
2222
}

0 commit comments

Comments
 (0)