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

Commit 68f84fb

Browse files
authored
chore: cortex chat revamp with options (#620)
1 parent 488eadb commit 68f84fb

File tree

4 files changed

+149
-59
lines changed

4 files changed

+149
-59
lines changed

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

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,56 @@ import { ChatCliUsecases } from './usecases/chat.cli.usecases';
33
import { exit } from 'node:process';
44

55
type ChatOptions = {
6-
model?: string;
76
threadId?: string;
7+
message?: string;
8+
attach: boolean;
89
};
910

10-
@SubCommand({ name: 'chat', description: 'Start a chat with a model' })
11+
@SubCommand({ name: 'chat', description: 'Send a chat request to a model' })
1112
export class ChatCommand extends CommandRunner {
1213
constructor(private readonly chatCliUsecases: ChatCliUsecases) {
1314
super();
1415
}
1516

16-
async run(_input: string[], option: ChatOptions): Promise<void> {
17-
const modelId = option.model;
17+
async run(_input: string[], options: ChatOptions): Promise<void> {
18+
const modelId = _input[0];
1819
if (!modelId) {
1920
console.error('Model ID is required');
2021
exit(1);
2122
}
2223

23-
return this.chatCliUsecases.chat(modelId, option.threadId);
24+
return this.chatCliUsecases.chat(
25+
modelId,
26+
options.threadId,
27+
options.message,
28+
options.attach,
29+
);
2430
}
2531

2632
@Option({
27-
flags: '-m, --model <model_id>',
28-
description: 'Model Id to start chat with',
33+
flags: '-t, --thread <thread_id>',
34+
description: 'Thread Id. If not provided, will create new thread',
35+
})
36+
parseThreadId(value: string) {
37+
return value;
38+
}
39+
40+
@Option({
41+
flags: '-m, --message <message>',
42+
description: 'Message to send to the model',
2943
required: true,
3044
})
3145
parseModelId(value: string) {
3246
return value;
3347
}
3448

3549
@Option({
36-
flags: '-t, --thread <thread_id>',
37-
description: 'Thread Id. If not provided, will create new thread',
50+
flags: '-a, --attach',
51+
description: 'Attach to interactive chat session',
52+
defaultValue: false,
53+
name: 'attach',
3854
})
39-
parseThreadId(value: string) {
40-
return value;
55+
parseAttach() {
56+
return true;
4157
}
4258
}

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

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,13 @@ export class ChatCliUsecases {
7272
return this.threadUsecases.create(createThreadDto);
7373
}
7474

75-
async chat(modelId: string, threadId?: string): Promise<void> {
76-
console.log(`Inorder to exit, type '${this.exitClause}'.`);
75+
async chat(
76+
modelId: string,
77+
threadId?: string,
78+
message?: string,
79+
attach: boolean = true,
80+
): Promise<void> {
81+
if (attach) console.log(`Inorder to exit, type '${this.exitClause}'.`);
7782
const thread = await this.getOrCreateNewThread(modelId, threadId);
7883
const messages: ChatCompletionMessage[] = (
7984
await this.messagesUsecases.getLastMessagesByThread(thread.id, 10)
@@ -87,18 +92,19 @@ export class ChatCliUsecases {
8792
output: stdout,
8893
prompt: this.userIndicator,
8994
});
90-
rl.prompt();
95+
if (message) sendCompletionMessage.bind(this)(message);
96+
if (attach) rl.prompt();
9197

9298
rl.on('close', () => {
9399
this.cortexUsecases.stopCortex().then(() => {
94-
console.log(this.exitMessage);
100+
if (attach) console.log(this.exitMessage);
95101
exit(0);
96102
});
97103
});
98104

99-
const decoder = new TextDecoder('utf-8');
105+
rl.on('line', sendCompletionMessage.bind(this));
100106

101-
rl.on('line', (userInput: string) => {
107+
function sendCompletionMessage(userInput: string) {
102108
if (userInput.trim() === this.exitClause) {
103109
rl.close();
104110
return;
@@ -137,14 +143,17 @@ export class ChatCliUsecases {
137143
top_p: 0.7,
138144
};
139145

146+
const decoder = new TextDecoder('utf-8');
147+
140148
this.chatUsecases
141149
.inference(chatDto, {})
142150
.then((response: stream.Readable) => {
143151
let assistantResponse: string = '';
144152

145153
response.on('error', (error: any) => {
146154
console.error(error);
147-
rl.prompt();
155+
if (attach) rl.prompt();
156+
else rl.close();
148157
});
149158

150159
response.on('end', () => {
@@ -170,7 +179,8 @@ export class ChatCliUsecases {
170179
this.messagesUsecases.create(createMessageDto).then(() => {
171180
assistantResponse = '';
172181
console.log('\n');
173-
rl.prompt();
182+
if (attach) rl.prompt();
183+
else rl.close();
174184
});
175185
});
176186

@@ -201,6 +211,6 @@ export class ChatCliUsecases {
201211
}
202212
});
203213
});
204-
});
214+
}
205215
}
206216
}

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

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,7 @@ import { ModelDto } from '@/infrastructure/dtos/models/model-successfully-create
1616
import { ListModelsResponseDto } from '@/infrastructure/dtos/models/list-model-response.dto';
1717
import { DeleteModelResponseDto } from '@/infrastructure/dtos/models/delete-model.dto';
1818
import { DownloadModelResponseDto } from '@/infrastructure/dtos/models/download-model.dto';
19-
import {
20-
ApiCreatedResponse,
21-
ApiOkResponse,
22-
ApiOperation,
23-
ApiParam,
24-
ApiTags,
25-
ApiResponse
26-
} from '@nestjs/swagger';
19+
import { ApiOperation, ApiParam, ApiTags, ApiResponse } from '@nestjs/swagger';
2720
import { StartModelSuccessDto } from '@/infrastructure/dtos/models/start-model-success.dto';
2821
import { ModelSettingParamsDto } from '../dtos/models/model-setting-params.dto';
2922
import { TransformInterceptor } from '../interceptors/transform.interceptor';
@@ -40,7 +33,10 @@ export class ModelsController {
4033
description: 'The model has been successfully created.',
4134
type: StartModelSuccessDto,
4235
})
43-
@ApiOperation({ summary: 'Create model', description: "Creates a model `.json` instance file manually." })
36+
@ApiOperation({
37+
summary: 'Create model',
38+
description: 'Creates a model `.json` instance file manually.',
39+
})
4440
@Post()
4541
create(@Body() createModelDto: CreateModelDto) {
4642
return this.modelsUsecases.create(createModelDto);
@@ -52,8 +48,15 @@ export class ModelsController {
5248
description: 'The model has been successfully started.',
5349
type: StartModelSuccessDto,
5450
})
55-
@ApiOperation({ summary: 'Start model', description: "Starts a model operation defined by a model `id`." })
56-
@ApiParam({ name: 'modelId', required: true, description: "The unique identifier of the model." })
51+
@ApiOperation({
52+
summary: 'Start model',
53+
description: 'Starts a model operation defined by a model `id`.',
54+
})
55+
@ApiParam({
56+
name: 'modelId',
57+
required: true,
58+
description: 'The unique identifier of the model.',
59+
})
5760
@Post(':modelId/start')
5861
startModel(
5962
@Param('modelId') modelId: string,
@@ -68,8 +71,15 @@ export class ModelsController {
6871
description: 'The model has been successfully stopped.',
6972
type: StartModelSuccessDto,
7073
})
71-
@ApiOperation({ summary: 'Stop model', description: "Stops a model operation defined by a model `id`." })
72-
@ApiParam({ name: 'modelId', required: true, description: "The unique identifier of the model." })
74+
@ApiOperation({
75+
summary: 'Stop model',
76+
description: 'Stops a model operation defined by a model `id`.',
77+
})
78+
@ApiParam({
79+
name: 'modelId',
80+
required: true,
81+
description: 'The unique identifier of the model.',
82+
})
7383
@Post(':modelId/stop')
7484
stopModel(@Param('modelId') modelId: string) {
7585
return this.modelsUsecases.stopModel(modelId);
@@ -81,8 +91,15 @@ export class ModelsController {
8191
description: 'Ok',
8292
type: DownloadModelResponseDto,
8393
})
84-
@ApiOperation({ summary: 'Download model', description: "Downloads a specific model instance." })
85-
@ApiParam({ name: 'modelId', required: true, description: "The unique identifier of the model." })
94+
@ApiOperation({
95+
summary: 'Download model',
96+
description: 'Downloads a specific model instance.',
97+
})
98+
@ApiParam({
99+
name: 'modelId',
100+
required: true,
101+
description: 'The unique identifier of the model.',
102+
})
86103
@Get('download/:modelId')
87104
downloadModel(@Param('modelId') modelId: string) {
88105
return this.modelsUsecases.downloadModel(modelId);
@@ -94,7 +111,11 @@ export class ModelsController {
94111
description: 'Ok',
95112
type: ListModelsResponseDto,
96113
})
97-
@ApiOperation({ summary: 'List models', description: "Lists the currently available models, and provides basic information about each one such as the owner and availability. [Equivalent to OpenAI's list model](https://platform.openai.com/docs/api-reference/models/list)." })
114+
@ApiOperation({
115+
summary: 'List models',
116+
description:
117+
"Lists the currently available models, and provides basic information about each one such as the owner and availability. [Equivalent to OpenAI's list model](https://platform.openai.com/docs/api-reference/models/list).",
118+
})
98119
@Get()
99120
findAll() {
100121
return this.modelsUsecases.findAll();
@@ -106,8 +127,16 @@ export class ModelsController {
106127
description: 'Ok',
107128
type: ModelDto,
108129
})
109-
@ApiOperation({ summary: 'Get model', description: "Retrieves a model instance, providing basic information about the model such as the owner and permissions. [Equivalent to OpenAI's list model](https://platform.openai.com/docs/api-reference/models/retrieve)." })
110-
@ApiParam({ name: 'id', required: true, description: "The unique identifier of the model." })
130+
@ApiOperation({
131+
summary: 'Get model',
132+
description:
133+
"Retrieves a model instance, providing basic information about the model such as the owner and permissions. [Equivalent to OpenAI's list model](https://platform.openai.com/docs/api-reference/models/retrieve).",
134+
})
135+
@ApiParam({
136+
name: 'id',
137+
required: true,
138+
description: 'The unique identifier of the model.',
139+
})
111140
@Get(':id')
112141
findOne(@Param('id') id: string) {
113142
return this.modelsUsecases.findOne(id);
@@ -119,8 +148,15 @@ export class ModelsController {
119148
description: 'The model has been successfully updated.',
120149
type: UpdateModelDto,
121150
})
122-
@ApiOperation({ summary: 'Update model', description: "Updates a model instance defined by a model's `id`." })
123-
@ApiParam({ name: 'id', required: true, description: "The unique identifier of the model." })
151+
@ApiOperation({
152+
summary: 'Update model',
153+
description: "Updates a model instance defined by a model's `id`.",
154+
})
155+
@ApiParam({
156+
name: 'id',
157+
required: true,
158+
description: 'The unique identifier of the model.',
159+
})
124160
@Patch(':id')
125161
update(@Param('id') id: string, @Body() updateModelDto: UpdateModelDto) {
126162
return this.modelsUsecases.update(id, updateModelDto);
@@ -131,8 +167,16 @@ export class ModelsController {
131167
description: 'The model has been successfully deleted.',
132168
type: DeleteModelResponseDto,
133169
})
134-
@ApiOperation({ summary: 'Delete model', description: "Deletes a model. [Equivalent to OpenAI's delete model](https://platform.openai.com/docs/api-reference/models/delete)." })
135-
@ApiParam({ name: 'id', required: true, description: "The unique identifier of the model." })
170+
@ApiOperation({
171+
summary: 'Delete model',
172+
description:
173+
"Deletes a model. [Equivalent to OpenAI's delete model](https://platform.openai.com/docs/api-reference/models/delete).",
174+
})
175+
@ApiParam({
176+
name: 'id',
177+
required: true,
178+
description: 'The unique identifier of the model.',
179+
})
136180
@Delete(':id')
137181
remove(@Param('id') id: string) {
138182
return this.modelsUsecases.remove(id);

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

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,14 @@ import {
66
Patch,
77
Param,
88
Delete,
9-
HttpCode
109
UseInterceptors,
1110
} from '@nestjs/common';
1211
import { ThreadsUsecases } from '@/usecases/threads/threads.usecases';
1312
import { CreateThreadDto } from '@/infrastructure/dtos/threads/create-thread.dto';
1413
import { UpdateThreadDto } from '@/infrastructure/dtos/threads/update-thread.dto';
1514
import { DeleteThreadResponseDto } from '@/infrastructure/dtos/threads/delete-thread.dto';
1615
import { GetThreadResponseDto } from '@/infrastructure/dtos/threads/get-thread.dto';
17-
import {
18-
ApiCreatedResponse,
19-
ApiOkResponse,
20-
ApiOperation,
21-
ApiParam,
22-
ApiTags,
23-
ApiResponse
24-
} from '@nestjs/swagger';
16+
import { ApiOperation, ApiParam, ApiTags, ApiResponse } from '@nestjs/swagger';
2517
import { TransformInterceptor } from '../interceptors/transform.interceptor';
2618

2719
@ApiTags('Threads')
@@ -30,13 +22,20 @@ import { TransformInterceptor } from '../interceptors/transform.interceptor';
3022
export class ThreadsController {
3123
constructor(private readonly threadsService: ThreadsUsecases) {}
3224

33-
@ApiOperation({ summary: 'Create thread', description: "Creates a new thread." })
25+
@ApiOperation({
26+
summary: 'Create thread',
27+
description: 'Creates a new thread.',
28+
})
3429
@Post()
3530
create(@Body() createThreadDto: CreateThreadDto) {
3631
return this.threadsService.create(createThreadDto);
3732
}
3833

39-
@ApiOperation({ summary: 'List threads', description: "Lists all the available threads along with its configurations." })
34+
@ApiOperation({
35+
summary: 'List threads',
36+
description:
37+
'Lists all the available threads along with its configurations.',
38+
})
4039
@Get()
4140
findAll() {
4241
return this.threadsService.findAll();
@@ -47,8 +46,15 @@ export class ThreadsController {
4746
description: 'Ok',
4847
type: GetThreadResponseDto,
4948
})
50-
@ApiOperation({ summary: 'Get thread', description: "Retrieves a thread along with its configurations." })
51-
@ApiParam({ name: 'id', required: true, description: "The unique identifier of the thread." })
49+
@ApiOperation({
50+
summary: 'Get thread',
51+
description: 'Retrieves a thread along with its configurations.',
52+
})
53+
@ApiParam({
54+
name: 'id',
55+
required: true,
56+
description: 'The unique identifier of the thread.',
57+
})
5258
@Get(':id')
5359
findOne(@Param('id') id: string) {
5460
return this.threadsService.findOne(id);
@@ -59,8 +65,15 @@ export class ThreadsController {
5965
description: 'The thread has been successfully updated.',
6066
type: UpdateThreadDto,
6167
})
62-
@ApiOperation({ summary: 'Update thread', description: "Updates a thread's configurations." })
63-
@ApiParam({ name: 'id', required: true, description: "The unique identifier of the thread." })
68+
@ApiOperation({
69+
summary: 'Update thread',
70+
description: "Updates a thread's configurations.",
71+
})
72+
@ApiParam({
73+
name: 'id',
74+
required: true,
75+
description: 'The unique identifier of the thread.',
76+
})
6477
@Patch(':id')
6578
update(@Param('id') id: string, @Body() updateThreadDto: UpdateThreadDto) {
6679
return this.threadsService.update(id, updateThreadDto);
@@ -71,8 +84,15 @@ export class ThreadsController {
7184
description: 'The thread has been successfully deleted.',
7285
type: DeleteThreadResponseDto,
7386
})
74-
@ApiOperation({ summary: 'Delete thread', description: "Deletes a specific thread defined by a thread `id` ." })
75-
@ApiParam({ name: 'id', required: true, description: "The unique identifier of the thread." })
87+
@ApiOperation({
88+
summary: 'Delete thread',
89+
description: 'Deletes a specific thread defined by a thread `id` .',
90+
})
91+
@ApiParam({
92+
name: 'id',
93+
required: true,
94+
description: 'The unique identifier of the thread.',
95+
})
7696
@Delete(':id')
7797
remove(@Param('id') id: string) {
7898
return this.threadsService.remove(id);

0 commit comments

Comments
 (0)