From cad0b3d8225f2a918d8f47a30742598baf17dc46 Mon Sep 17 00:00:00 2001 From: gamgyul163 Date: Tue, 26 Nov 2024 17:58:01 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81=20=EA=B2=B0=EA=B3=BC=EB=A5=BC=20?= =?UTF-8?q?=EB=B3=84=EB=8F=84=EC=9D=98=20=EC=9D=B4=EB=B2=A4=ED=8A=B8?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=EC=86=A1=EB=B0=9B=EC=9D=84=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apps/api/src/chats/chats.controller.ts | 2 +- Backend/apps/api/src/chats/chats.service.ts | 43 ++++++++++++++++--- Backend/apps/chats/src/chats.gateway.ts | 14 +++++- 3 files changed, 50 insertions(+), 9 deletions(-) diff --git a/Backend/apps/api/src/chats/chats.controller.ts b/Backend/apps/api/src/chats/chats.controller.ts index 8656e0e1..592d3c54 100644 --- a/Backend/apps/api/src/chats/chats.controller.ts +++ b/Backend/apps/api/src/chats/chats.controller.ts @@ -11,6 +11,6 @@ export class ChatsController { @Post() @UseGuards(JwtAuthGuard) async sendChat(@Body(ValidationPipe) sendChatDto: SendChatDto, @Req() req: Request & { user: UserEntity }) { - this.chatsService.publishChat({ ...sendChatDto, userId: req.user.id, nickname: req.user.nickname }); + this.chatsService.ingestChat({ ...sendChatDto, userId: req.user.id, nickname: req.user.nickname }); } } diff --git a/Backend/apps/api/src/chats/chats.service.ts b/Backend/apps/api/src/chats/chats.service.ts index 891e208c..c0f03115 100644 --- a/Backend/apps/api/src/chats/chats.service.ts +++ b/Backend/apps/api/src/chats/chats.service.ts @@ -8,13 +8,14 @@ import { firstValueFrom } from 'rxjs'; @Injectable() export class ChatsService { + private isFlushing = false; constructor( @InjectRedis() private readonly redisClient: Redis, private readonly httpService: HttpService, private readonly configService: ConfigService, ) {} - async publishChat({ + async ingestChat({ channelId, message, userId, @@ -25,9 +26,11 @@ export class ChatsService { userId: number; nickname: string; }) { - const filteringResult = await this.clovaFiltering(message); - const chat = JSON.stringify({ content: message, userId, nickname, timestamp: new Date(), filteringResult }); - await this.redisClient.multi().publish(`${channelId}:chat`, chat).rpush(`${channelId}:chats`, chat).exec(); + const chatId = crypto.randomUUID(); + const chat = { content: message, userId, nickname, timestamp: new Date(), channelId, chatId }; + const chatString = JSON.stringify(chat); + await this.redisClient.multi().publish(`${channelId}:chat`, chatString).lpush('chatQueue', chatId).exec(); + this.clovaFiltering(chat); } async readViewers(channelId: UUID) { @@ -38,7 +41,7 @@ export class ChatsService { this.redisClient.del(`${channelId}:chats`); } - async clovaFiltering(message: string) { + async clovaFiltering(chat) { const postData = { messages: [ { @@ -47,7 +50,7 @@ export class ChatsService { }, { role: 'user', - content: message, + content: chat.content, }, ], }; @@ -61,6 +64,32 @@ export class ChatsService { }), ); - return data?.result?.message?.content?.includes('true'); + chat.filteringResult = data?.result?.message?.content?.includes('false'); + await this.redisClient + .multi() + .publish( + `${chat.channelId}:filter`, + JSON.stringify({ chatId: chat.chatId, filteringResult: chat.filteringResult }), + ) + .hset('chatCache', chat.chatId, JSON.stringify(chat)) + .exec(); + this.flushChat(); + } + + async flushChat() { + if (!this.isFlushing) { + this.isFlushing = true; + while (true) { + const frontChatId = await this.redisClient.lindex('chatQueue', 0); + const chatString = await this.redisClient.hget('chatCache', frontChatId); + if (!chatString) { + break; + } else { + const chat = JSON.parse(chatString); + await this.redisClient.multi().rpush(`${chat.channelId}:chats`, chatString).lpop('chatQueue').exec(); + } + } + this.isFlushing = false; + } } } diff --git a/Backend/apps/chats/src/chats.gateway.ts b/Backend/apps/chats/src/chats.gateway.ts index ef557bdc..921ee0de 100644 --- a/Backend/apps/chats/src/chats.gateway.ts +++ b/Backend/apps/chats/src/chats.gateway.ts @@ -25,13 +25,21 @@ export class ChatsGateway implements OnGatewayDisconnect, OnGatewayConnection { constructor(@InjectRedis() private redisClient: Redis) { const subscriber = this.redisClient.duplicate(); subscriber.psubscribe('*:chat'); + subscriber.psubscribe('*:filter'); subscriber.on('pmessage', async (pattern, channel, message) => { if (pattern === '*:chat') { - const channelId = channel.slice(0, -5) as UUID; + const channelId = channel.split(':')[0]; const chat = plainToInstance(ChatDto, message); this.emitChat({ channelId, chat }); } }); + + subscriber.on('pmessage', async (pattern, channel, message) => { + if (pattern === '*:filter') { + const channelId = channel.split(':')[0]; + this.emitFilter({ channelId, filteringResult: JSON.parse(message) }); + } + }); } async handleConnection(socket: Socket) { @@ -67,4 +75,8 @@ export class ChatsGateway implements OnGatewayDisconnect, OnGatewayConnection { async emitChat({ channelId, chat }) { this.server.to(channelId).emit('chat', [chat]); } + + async emitFilter({ channelId, filteringResult }) { + this.server.to(channelId).emit('filter', [filteringResult]); + } } From 1754d975feed886926f29595d737388e60efe7ee Mon Sep 17 00:00:00 2001 From: gamgyul163 Date: Tue, 26 Nov 2024 18:04:39 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=EC=A0=84=EC=86=A1=20=EC=8B=9C=20=EA=B8=B0=EB=B3=B8=EC=A0=81?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=B6=9C=EB=A0=A5=20=EB=8C=80=EC=83=81?= =?UTF-8?q?=EC=97=90=20=ED=8F=AC=ED=95=A8=EB=90=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Backend/apps/api/src/chats/chats.service.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Backend/apps/api/src/chats/chats.service.ts b/Backend/apps/api/src/chats/chats.service.ts index c0f03115..e92791d9 100644 --- a/Backend/apps/api/src/chats/chats.service.ts +++ b/Backend/apps/api/src/chats/chats.service.ts @@ -27,7 +27,15 @@ export class ChatsService { nickname: string; }) { const chatId = crypto.randomUUID(); - const chat = { content: message, userId, nickname, timestamp: new Date(), channelId, chatId }; + const chat = { + content: message, + userId, + nickname, + timestamp: new Date(), + channelId, + chatId, + filteringResult: true, + }; const chatString = JSON.stringify(chat); await this.redisClient.multi().publish(`${channelId}:chat`, chatString).lpush('chatQueue', chatId).exec(); this.clovaFiltering(chat); @@ -64,7 +72,7 @@ export class ChatsService { }), ); - chat.filteringResult = data?.result?.message?.content?.includes('false'); + chat.filteringResult = data?.result?.message?.content?.includes('true'); await this.redisClient .multi() .publish( From ba75b7864253057f54f08448020d730eba4cf4d0 Mon Sep 17 00:00:00 2001 From: gamgyul163 Date: Tue, 26 Nov 2024 19:25:39 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=E2=9C=A8=20feat:=20flush=20=ED=95=A0=20?= =?UTF-8?q?=EB=95=8C=20redis=EB=A5=BC=20=EC=9D=B4=EC=9A=A9=ED=95=B4=20?= =?UTF-8?q?=EB=9D=BD=ED=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Backend/apps/api/src/chats/chats.service.ts | 32 ++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Backend/apps/api/src/chats/chats.service.ts b/Backend/apps/api/src/chats/chats.service.ts index e92791d9..1781116d 100644 --- a/Backend/apps/api/src/chats/chats.service.ts +++ b/Backend/apps/api/src/chats/chats.service.ts @@ -8,7 +8,6 @@ import { firstValueFrom } from 'rxjs'; @Injectable() export class ChatsService { - private isFlushing = false; constructor( @InjectRedis() private readonly redisClient: Redis, private readonly httpService: HttpService, @@ -37,7 +36,7 @@ export class ChatsService { filteringResult: true, }; const chatString = JSON.stringify(chat); - await this.redisClient.multi().publish(`${channelId}:chat`, chatString).lpush('chatQueue', chatId).exec(); + await this.redisClient.multi().publish(`${channelId}:chat`, chatString).rpush('chatQueue', chatId).exec(); this.clovaFiltering(chat); } @@ -85,19 +84,26 @@ export class ChatsService { } async flushChat() { - if (!this.isFlushing) { - this.isFlushing = true; - while (true) { - const frontChatId = await this.redisClient.lindex('chatQueue', 0); - const chatString = await this.redisClient.hget('chatCache', frontChatId); - if (!chatString) { - break; - } else { - const chat = JSON.parse(chatString); - await this.redisClient.multi().rpush(`${chat.channelId}:chats`, chatString).lpop('chatQueue').exec(); + const lockKey = 'chat:flush:lock'; + const lock = await this.redisClient.set(lockKey, 'lock', 'NX'); + + try { + if (lockKey) { + while (true) { + const frontChatId = await this.redisClient.lindex('chatQueue', 0); + const chatString = await this.redisClient.hget('chatCache', frontChatId); + if (!chatString) { + break; + } else { + const chat = JSON.parse(chatString); + await this.redisClient.multi().rpush(`${chat.channelId}:chats`, chatString).lpop('chatQueue').exec(); + } } } - this.isFlushing = false; + } catch (err) { + console.log(err); + } finally { + await this.redisClient.del(lockKey); } } } From a6b800a51463be5b739d3b5d4237ad2e64ed4d33 Mon Sep 17 00:00:00 2001 From: gamgyul163 Date: Tue, 26 Nov 2024 19:46:49 +0900 Subject: [PATCH 4/6] =?UTF-8?q?=E2=9C=A8=20feat:=20api=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=20=EB=8C=80=ED=99=94=EB=AC=B8=20=EC=95=9E=EC=97=90=20?= =?UTF-8?q?message=20=EB=AC=B8=EA=B5=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Backend/apps/api/src/chats/chats.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Backend/apps/api/src/chats/chats.service.ts b/Backend/apps/api/src/chats/chats.service.ts index 1781116d..8c619265 100644 --- a/Backend/apps/api/src/chats/chats.service.ts +++ b/Backend/apps/api/src/chats/chats.service.ts @@ -57,7 +57,7 @@ export class ChatsService { }, { role: 'user', - content: chat.content, + content: `message : ${chat.content}`, }, ], }; From a0d4818ad2e74ff895fcb89a0578c3f5c7beaecd Mon Sep 17 00:00:00 2001 From: gamgyul163 Date: Tue, 26 Nov 2024 19:51:38 +0900 Subject: [PATCH 5/6] =?UTF-8?q?=E2=9C=A8=20feat:=20api=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=20=EB=8C=80=ED=99=94=EB=AC=B8=20=EC=95=9E=EC=97=90=20?= =?UTF-8?q?=EC=B1=84=ED=8C=85=EB=82=B4=EC=9A=A9=20=EB=AC=B8=EA=B5=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Backend/apps/api/src/chats/chats.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Backend/apps/api/src/chats/chats.service.ts b/Backend/apps/api/src/chats/chats.service.ts index 8c619265..9b4c02af 100644 --- a/Backend/apps/api/src/chats/chats.service.ts +++ b/Backend/apps/api/src/chats/chats.service.ts @@ -57,7 +57,7 @@ export class ChatsService { }, { role: 'user', - content: `message : ${chat.content}`, + content: `채팅내용 : ${chat.content}`, }, ], }; From ad9c9b27fc20467aa2c8d4fb41dab344188dd9ba Mon Sep 17 00:00:00 2001 From: gamgyul163 Date: Tue, 26 Nov 2024 20:19:15 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=ED=95=84=ED=84=B0=EB=A7=81=20api=20=EC=9A=94=EC=B2=AD=20postda?= =?UTF-8?q?ta=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=9D=BD=ED=82=A4=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Backend/apps/api/src/chats/chats.service.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Backend/apps/api/src/chats/chats.service.ts b/Backend/apps/api/src/chats/chats.service.ts index 9b4c02af..20bf7f20 100644 --- a/Backend/apps/api/src/chats/chats.service.ts +++ b/Backend/apps/api/src/chats/chats.service.ts @@ -57,9 +57,16 @@ export class ChatsService { }, { role: 'user', - content: `채팅내용 : ${chat.content}`, + content: `채팅내용 : "${chat.content}"`, }, ], + maxTokens: this.configService.get('CLOVA_CHAT_FILTERING_MAX_TOKEN') || 10, + topP: 0.8, + topK: 1, + temperature: 0.1, + repeatPenalty: 1.0, + includeAiFilters: true, + seed: 0, }; const { data } = await firstValueFrom( this.httpService.post(this.configService.get('CLOVA_API_URL'), postData, { @@ -88,7 +95,7 @@ export class ChatsService { const lock = await this.redisClient.set(lockKey, 'lock', 'NX'); try { - if (lockKey) { + if (lock) { while (true) { const frontChatId = await this.redisClient.lindex('chatQueue', 0); const chatString = await this.redisClient.hget('chatCache', frontChatId);