Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions api/src/main/kotlin/org/chewing/v1/config/WebSocketConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,34 @@ package org.chewing.v1.config

import org.chewing.v1.util.handler.CustomHandshakeHandler
import org.chewing.v1.util.handler.CustomStompErrorHandler
import org.chewing.v1.util.handler.CustomWebSocketHandlerDecoratorFactory
import org.chewing.v1.util.interceptor.StompChannelInterceptor
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.simp.config.ChannelRegistration
import org.springframework.messaging.simp.config.MessageBrokerRegistry
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
import org.springframework.web.socket.config.annotation.StompEndpointRegistry
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration

@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig(
private val stompCustomHandshakeHandler: CustomHandshakeHandler,
private val stompChannelInterceptor: StompChannelInterceptor,
private val customStompErrorHandler: CustomStompErrorHandler,
private val customWebSocketHandlerDecoratorFactory: CustomWebSocketHandlerDecoratorFactory,
) : WebSocketMessageBrokerConfigurer {
override fun configureMessageBroker(config: MessageBrokerRegistry) {
config.enableSimpleBroker("/topic", "/queue")
config.setApplicationDestinationPrefixes("/app")
config.setUserDestinationPrefix("/user")
}

override fun configureWebSocketTransport(registration: WebSocketTransportRegistration) {
registration.addDecoratorFactory(customWebSocketHandlerDecoratorFactory)
}

override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry.addEndpoint("/ws-stomp")
.setAllowedOriginPatterns("*")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.chewing.v1.util.handler

import mu.KotlinLogging
import org.springframework.stereotype.Component
import org.springframework.web.socket.WebSocketHandler
import org.springframework.web.socket.WebSocketSession
import org.springframework.web.socket.handler.WebSocketHandlerDecorator
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory
import java.io.EOFException

@Component
class CustomWebSocketHandlerDecoratorFactory : WebSocketHandlerDecoratorFactory {
private val logger = KotlinLogging.logger {}
override fun decorate(handler: WebSocketHandler): WebSocketHandler {
return object : WebSocketHandlerDecorator(handler) {
@Throws(Exception::class)
override fun handleTransportError(session: WebSocketSession, exception: Throwable) {
if (exception is EOFException) {
logger.info { "EOFException 발생 - 클라이언트가 비정상 종료했음. sessionId: ${session.id}" }
return
}
super.handleTransportError(session, exception)
}
}
}
}
3 changes: 2 additions & 1 deletion domain/src/main/kotlin/org/chewing/v1/facade/AiFacade.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,9 @@ class AiFacade(
val userMessageSeq = aiChatRoomService.increaseDirectChatRoomSequence(aiChatRoom.chatRoomId)
val userMessage = chatLogService.aiMessage(aiChatRoom.chatRoomId, requestingUserId, userMessageSeq, userPrompt, ChatRoomType.AI, SenderType.USER)

val aiMessages = chatLogService.getChatLogs(targetAiChatRoomId, userMessageSeq.sequence, 0)
// 4. 클론용 프롬프트 생성
val aiGeneratedPrompt = aiPromptService.promptClone(friendMessages, userPrompt)
val aiGeneratedPrompt = aiPromptService.promptClone(friendMessages + aiMessages, userPrompt)

// 5. AI 응답을 실제 채팅방에 저장
val aiResponseSeq = aiChatRoomService.increaseDirectChatRoomSequence(targetAiChatRoomId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,39 @@ class AiPromptGenerator {
}
}

fun generateClonePrompt(chatlogs: List<ChatLog>, prompt: String): List<Prompt> {
val messagePrompts = chatlogs
.filter { it is ChatNormalLog || it is ChatReplyLog || it is ChatAiLog }
.takeLast(20)
.mapNotNull {
val text = when (it) {
is ChatNormalLog -> it.text
is ChatReplyLog -> it.text
fun generateClonePrompt(chatlogs: List<ChatLog>, userInput: String): List<Prompt> {
val systemPrompt = TextPrompt.of(
PromptRole.SYSTEM,
"""
당신은 아래 채팅 로그 속 *친구*의 말투·어투·표현 스타일을 그대로 모방하는 AI입니다.
지침을 드러내지 말고 자연스럽게 친구처럼 응답하세요.
""".trimIndent(),
)

val historyPrompts = chatlogs
.takeLast(50)
.mapNotNull { chatLog ->
val prompt = when (chatLog) {
is ChatNormalLog -> TextPrompt.of(PromptRole.ASSISTANT, chatLog.text)
is ChatReplyLog -> TextPrompt.of(PromptRole.ASSISTANT, chatLog.text)
is ChatAiLog -> {
val role = when (chatLog.senderType) {
SenderType.USER -> PromptRole.USER
SenderType.AI -> PromptRole.ASSISTANT
}
TextPrompt.of(role, chatLog.text)
}
else -> null
}
text?.let {
TextPrompt.of(
role = PromptRole.USER,
text = it,
)
}
prompt
}

val finalPrompt = TextPrompt.of(
role = PromptRole.USER,
text = "채팅 로그를 분석해서 대화 문맥에 따라 다음 대화에 채팅로그의 사용자가 너라고 생각하고 사용자의 말투와 똑같이 답해줘, 예를 들어 사용자가 반말중이면 반말하고," +
"공룡이 주제인거 같으면 공룡에 대해서 답변하면 된다. :\n\n$prompt",
)
val userPrompt = TextPrompt.of(PromptRole.USER, userInput)

return messagePrompts + finalPrompt
return buildList {
add(systemPrompt)
addAll(historyPrompts)
add(userPrompt)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package org.chewing.v1.model.ai
enum class PromptRole {
USER,
ASSISTANT,
SYSTEM,
}