11import { ChatUsecases } from '@/usecases/chat/chat.usecases' ;
2- import { ChatCompletionRole } from '@/domain/models/message.interface' ;
2+ import {
3+ ChatCompletionRole ,
4+ ContentType ,
5+ MessageStatus ,
6+ } from '@/domain/models/message.interface' ;
37import { exit , stdin , stdout } from 'node:process' ;
48import * as readline from 'node:readline/promises' ;
59import { ChatCompletionMessage } from '@/infrastructure/dtos/chat/chat-completion-message.dto' ;
610import { CreateChatCompletionDto } from '@/infrastructure/dtos/chat/create-chat-completion.dto' ;
711import { CortexUsecases } from '@/usecases/cortex/cortex.usecases' ;
812import { Injectable } from '@nestjs/common' ;
13+ import { ThreadsUsecases } from '@/usecases/threads/threads.usecases' ;
14+ import { Thread } from '@/domain/models/thread.interface' ;
15+ import { CreateThreadDto } from '@/infrastructure/dtos/threads/create-thread.dto' ;
16+ import { AssistantsUsecases } from '@/usecases/assistants/assistants.usecases' ;
17+ import { CreateThreadAssistantDto } from '@/infrastructure/dtos/threads/create-thread-assistant.dto' ;
18+ import { CreateThreadModelInfoDto } from '@/infrastructure/dtos/threads/create-thread-model-info.dto' ;
19+ import { ModelsUsecases } from '@/usecases/models/models.usecases' ;
20+ import stream from 'stream' ;
21+ import { CreateMessageDto } from '@/infrastructure/dtos/messages/create-message.dto' ;
22+ import { MessagesUsecases } from '@/usecases/messages/messages.usecases' ;
923
1024@Injectable ( )
1125export class ChatCliUsecases {
@@ -14,13 +28,59 @@ export class ChatCliUsecases {
1428 private exitMessage = 'Bye!' ;
1529
1630 constructor (
31+ private readonly assistantUsecases : AssistantsUsecases ,
32+ private readonly threadUsecases : ThreadsUsecases ,
1733 private readonly chatUsecases : ChatUsecases ,
1834 private readonly cortexUsecases : CortexUsecases ,
35+ private readonly modelsUsecases : ModelsUsecases ,
36+ private readonly messagesUsecases : MessagesUsecases ,
1937 ) { }
2038
21- async chat ( modelId : string ) : Promise < void > {
39+ private async getOrCreateNewThread (
40+ modelId : string ,
41+ threadId ?: string ,
42+ ) : Promise < Thread > {
43+ if ( threadId ) {
44+ const thread = await this . threadUsecases . findOne ( threadId ) ;
45+ if ( ! thread ) throw new Error ( `Cannot find thread with id: ${ threadId } ` ) ;
46+ return thread ;
47+ }
48+
49+ const model = await this . modelsUsecases . findOne ( modelId ) ;
50+ if ( ! model ) throw new Error ( `Cannot find model with id: ${ modelId } ` ) ;
51+
52+ const assistant = await this . assistantUsecases . findOne ( 'jan' ) ;
53+ if ( ! assistant ) throw new Error ( 'No assistant available' ) ;
54+
55+ const createThreadModel : CreateThreadModelInfoDto = {
56+ id : modelId ,
57+ settings : model . settings ,
58+ parameters : model . parameters ,
59+ } ;
60+
61+ const assistantDto : CreateThreadAssistantDto = {
62+ assistant_id : assistant . id ,
63+ assistant_name : assistant . name ,
64+ model : createThreadModel ,
65+ } ;
66+
67+ const createThreadDto : CreateThreadDto = {
68+ title : 'New Thread' ,
69+ assistants : [ assistantDto ] ,
70+ } ;
71+
72+ return this . threadUsecases . create ( createThreadDto ) ;
73+ }
74+
75+ async chat ( modelId : string , threadId ?: string ) : Promise < void > {
2276 console . log ( `Inorder to exit, type '${ this . exitClause } '.` ) ;
23- const messages : ChatCompletionMessage [ ] = [ ] ;
77+ const thread = await this . getOrCreateNewThread ( modelId , threadId ) ;
78+ const messages : ChatCompletionMessage [ ] = (
79+ await this . messagesUsecases . getLastMessagesByThread ( thread . id , 10 )
80+ ) . map ( ( message ) => ( {
81+ content : message . content [ 0 ] . text . value ,
82+ role : message . role ,
83+ } ) ) ;
2484
2585 const rl = readline . createInterface ( {
2686 input : stdin ,
@@ -36,6 +96,8 @@ export class ChatCliUsecases {
3696 } ) ;
3797 } ) ;
3898
99+ const decoder = new TextDecoder ( 'utf-8' ) ;
100+
39101 rl . on ( 'line' , ( userInput : string ) => {
40102 if ( userInput . trim ( ) === this . exitClause ) {
41103 rl . close ( ) ;
@@ -47,6 +109,22 @@ export class ChatCliUsecases {
47109 role : ChatCompletionRole . User ,
48110 } ) ;
49111
112+ const createMessageDto : CreateMessageDto = {
113+ thread_id : thread . id ,
114+ role : ChatCompletionRole . User ,
115+ content : [
116+ {
117+ type : ContentType . Text ,
118+ text : {
119+ value : userInput ,
120+ annotations : [ ] ,
121+ } ,
122+ } ,
123+ ] ,
124+ status : MessageStatus . Ready ,
125+ } ;
126+ this . messagesUsecases . create ( createMessageDto ) ;
127+
50128 const chatDto : CreateChatCompletionDto = {
51129 messages,
52130 model : modelId ,
@@ -59,44 +137,70 @@ export class ChatCliUsecases {
59137 top_p : 0.7 ,
60138 } ;
61139
62- const decoder = new TextDecoder ( 'utf-8' ) ;
63- this . chatUsecases . inference ( chatDto , { } ) . then ( ( response ) => {
64- response . on ( 'error' , ( error : any ) => {
65- console . error ( error ) ;
66- rl . prompt ( ) ;
67- } ) ;
140+ this . chatUsecases
141+ . inference ( chatDto , { } )
142+ . then ( ( response : stream . Readable ) => {
143+ let assistantResponse : string = '' ;
68144
69- response . on ( 'end ' , ( ) => {
70- console . log ( '\n' ) ;
71- rl . prompt ( ) ;
72- } ) ;
145+ response . on ( 'error ' , ( error : any ) => {
146+ console . error ( error ) ;
147+ rl . prompt ( ) ;
148+ } ) ;
73149
74- response . on ( 'data' , ( chunk : any ) => {
75- let content = '' ;
76- const text = decoder . decode ( chunk ) ;
77- const lines = text . trim ( ) . split ( '\n' ) ;
78- let cachedLines = '' ;
79- for ( const line of lines ) {
80- try {
81- const toParse = cachedLines + line ;
82- if ( ! line . includes ( 'data: [DONE]' ) ) {
83- const data = JSON . parse ( toParse . replace ( 'data: ' , '' ) ) ;
84- content += data . choices [ 0 ] ?. delta ?. content ?? '' ;
85-
86- if ( content . startsWith ( 'assistant: ' ) ) {
87- content = content . replace ( 'assistant: ' , '' ) ;
88- }
150+ response . on ( 'end' , ( ) => {
151+ messages . push ( {
152+ content : assistantResponse ,
153+ role : ChatCompletionRole . Assistant ,
154+ } ) ;
155+ const createMessageDto : CreateMessageDto = {
156+ thread_id : thread . id ,
157+ role : ChatCompletionRole . Assistant ,
158+ content : [
159+ {
160+ type : ContentType . Text ,
161+ text : {
162+ value : assistantResponse ,
163+ annotations : [ ] ,
164+ } ,
165+ } ,
166+ ] ,
167+ status : MessageStatus . Ready ,
168+ } ;
89169
90- if ( content . trim ( ) . length > 0 ) {
91- stdout . write ( content ) ;
170+ this . messagesUsecases . create ( createMessageDto ) . then ( ( ) => {
171+ assistantResponse = '' ;
172+ console . log ( '\n' ) ;
173+ rl . prompt ( ) ;
174+ } ) ;
175+ } ) ;
176+
177+ response . on ( 'data' , ( chunk : any ) => {
178+ let content = '' ;
179+ const text = decoder . decode ( chunk ) ;
180+ const lines = text . trim ( ) . split ( '\n' ) ;
181+ let cachedLines = '' ;
182+ for ( const line of lines ) {
183+ try {
184+ const toParse = cachedLines + line ;
185+ if ( ! line . includes ( 'data: [DONE]' ) ) {
186+ const data = JSON . parse ( toParse . replace ( 'data: ' , '' ) ) ;
187+ content += data . choices [ 0 ] ?. delta ?. content ?? '' ;
188+
189+ if ( content . startsWith ( 'assistant: ' ) ) {
190+ content = content . replace ( 'assistant: ' , '' ) ;
191+ }
192+
193+ if ( content . trim ( ) . length > 0 ) {
194+ assistantResponse += content ;
195+ stdout . write ( content ) ;
196+ }
92197 }
198+ } catch {
199+ cachedLines = line ;
93200 }
94- } catch {
95- cachedLines = line ;
96201 }
97- }
202+ } ) ;
98203 } ) ;
99- } ) ;
100204 } ) ;
101205 }
102206}
0 commit comments