Skip to content

Commit 9252292

Browse files
feat(spa): displaying the bot's reply as a streaming message
1 parent 399a1d2 commit 9252292

File tree

5 files changed

+86
-18
lines changed

5 files changed

+86
-18
lines changed
Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,41 @@
11
import { Injectable } from '@angular/core';
22
import { ChatEvents } from './chat.model';
33
import io from 'socket.io-client';
4-
import { ChatCallDto } from '@boldare/openai-assistant';
4+
import {
5+
ChatCallDto,
6+
TextCreatedPayload, TextDeltaPayload, TextDonePayload
7+
} from '@boldare/openai-assistant';
58
import { Observable } from 'rxjs';
69
import { environment } from '../../../../environments/environment';
710

811
@Injectable({ providedIn: 'root' })
912
export class ChatGatewayService {
1013
private socket = io(environment.websocketUrl);
1114

12-
sendMessage(payload: ChatCallDto): void {
15+
watchEvent<T>(event: ChatEvents): Observable<T> {
16+
return new Observable<T>(observer => {
17+
this.socket.on(event, data => observer.next(data));
18+
return () => this.socket.disconnect();
19+
});
20+
}
21+
22+
callStart(payload: ChatCallDto): void {
1323
this.socket.emit(ChatEvents.CallStart, payload);
1424
}
1525

16-
getMessages(): Observable<ChatCallDto> {
17-
return new Observable<ChatCallDto>(observer => {
18-
this.socket.on(ChatEvents.CallDone, data => observer.next(data));
19-
return () => this.socket.disconnect();
20-
});
26+
callDone(): Observable<ChatCallDto> {
27+
return this.watchEvent(ChatEvents.CallDone);
28+
}
29+
30+
textCreated(): Observable<TextCreatedPayload> {
31+
return this.watchEvent(ChatEvents.TextCreated);
32+
}
33+
34+
textDelta(): Observable<TextDeltaPayload> {
35+
return this.watchEvent(ChatEvents.TextDelta);
36+
}
37+
38+
textDone(): Observable<TextDonePayload> {
39+
return this.watchEvent(ChatEvents.TextDone);
2140
}
2241
}

apps/spa/src/app/modules/+chat/shared/chat.model.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ export interface ChatMessage {
1717
export enum ChatEvents {
1818
CallStart = 'callStart',
1919
CallDone = 'callDone',
20-
TextDone = 'textCreated',
21-
TextCreated = 'textDelta',
22-
TextDelta = 'textDone',
2320
MessageCreated = 'messageCreated',
2421
MessageDelta = 'messageDelta',
2522
MessageDone = 'messageDone',
23+
TextCreated = 'textCreated',
24+
TextDelta = 'textDelta',
25+
TextDone = 'textDone',
2626
ImageFileDone = 'imageFileDone',
2727
ToolCallCreated = 'toolCallCreated',
2828
ToolCallDelta = 'toolCallDelta',

apps/spa/src/app/modules/+chat/shared/chat.service.ts

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,21 @@ export class ChatService {
3434
) {
3535
document.body.classList.add('ai-chat');
3636

37+
this.subscribeMessages();
3738
this.setInitialValues();
38-
this.watchMessages();
3939
this.watchVisibility();
4040
}
4141

42+
subscribeMessages(): void {
43+
if (!environment.isStreamingEnabled) {
44+
this.watchMessages();
45+
} else {
46+
this.watchTextCreated();
47+
this.watchTextDelta();
48+
this.watchTextDone();
49+
}
50+
}
51+
4252
isMessageInvisible(message: Message): boolean {
4353
const metadata = message.metadata as Record<string, unknown>;
4454
return metadata?.['status'] === ChatMessageStatus.Invisible;
@@ -86,11 +96,13 @@ export class ChatService {
8696

8797
refresh(): void {
8898
this.isLoading$.next(true);
99+
this.isTyping$.next(false);
89100
this.messages$.next([]);
90101
this.threadService.start().subscribe();
91102
}
92103

93104
clear(): void {
105+
this.isTyping$.next(false);
94106
this.threadService.clear();
95107
this.messages$.next([]);
96108
}
@@ -119,21 +131,56 @@ export class ChatService {
119131
const files = await this.chatFilesService.sendFiles();
120132
this.addFileMessage(files);
121133

122-
this.chatGatewayService.sendMessage({
134+
this.chatGatewayService.callStart({
123135
content,
124136
threadId: this.threadService.threadId$.value,
125137
file_ids: files.map(file => file.id) || [],
126138
});
127139
}
128140

141+
watchTextCreated(): Subscription {
142+
return this.chatGatewayService
143+
.textCreated()
144+
.subscribe((data) => {
145+
this.isTyping$.next(false)
146+
this.addMessage({ content: data.text.value, role: ChatRole.Assistant })
147+
});
148+
}
149+
150+
watchTextDelta(): Subscription {
151+
return this.chatGatewayService
152+
.textDelta()
153+
.subscribe((data) => {
154+
const length = this.messages$.value.length;
155+
this.messages$.value[length - 1].content = data.text.value;
156+
});
157+
}
158+
159+
watchTextDone(): Subscription {
160+
return this.chatGatewayService
161+
.textDone()
162+
.subscribe((data) => {
163+
this.isTyping$.next(false);
164+
this.messages$.next([
165+
...this.messages$.value.slice(0, -1),
166+
{
167+
content: data.text.value,
168+
role: ChatRole.Assistant,
169+
},
170+
]);
171+
});
172+
}
173+
129174
watchMessages(): Subscription {
130-
return this.chatGatewayService.getMessages().subscribe(data => {
131-
this.addMessage({
132-
content: data.content,
133-
role: ChatRole.Assistant,
175+
return this.chatGatewayService
176+
.callDone()
177+
.subscribe(data => {
178+
this.addMessage({
179+
content: data.content,
180+
role: ChatRole.Assistant,
181+
});
182+
this.isTyping$.next(false);
134183
});
135-
this.isTyping$.next(false);
136-
});
137184
}
138185

139186
sendAudio(file: Blob): void {

apps/spa/src/environments/environment.development.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export const environment = {
1010
isRefreshEnabled: true,
1111
isConfigEnabled: true,
1212
isAutoOpen: true,
13+
isStreamingEnabled: true,
1314
};

apps/spa/src/environments/environment.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ export const environment = {
1010
isRefreshEnabled: true,
1111
isConfigEnabled: true,
1212
isAutoOpen: true,
13+
isStreamingEnabled: true,
1314
};

0 commit comments

Comments
 (0)