diff --git a/src/main/kotlin/javabot/dao/ChatGPTDao.kt b/src/main/kotlin/javabot/dao/ChatGPTDao.kt index 0122fb61..3fa991fd 100644 --- a/src/main/kotlin/javabot/dao/ChatGPTDao.kt +++ b/src/main/kotlin/javabot/dao/ChatGPTDao.kt @@ -3,13 +3,15 @@ package javabot.dao import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper +import com.google.common.cache.CacheBuilder import com.google.inject.Inject import com.google.inject.Singleton import io.dropwizard.util.Duration -import javabot.Javabot import javabot.JavabotConfig import javabot.operations.throttle.BotRateLimiter import javabot.service.HttpService +import org.apache.commons.compress.harmony.unpack200.NewAttributeBands.Callable +import java.util.concurrent.TimeUnit data class GPTMessageContainer( val messages: List, @@ -52,22 +54,37 @@ constructor( private val javabotConfig: JavabotConfig, private val httpService: HttpService, ) { - private var limiter: BotRateLimiter = + private val mapper: ObjectMapper + + private val limiter: BotRateLimiter = BotRateLimiter(javabotConfig.chatGptLimit(), Duration.days(1).toMilliseconds()) + private val queryCache = CacheBuilder.newBuilder() + .maximumSize(100) + .expireAfterWrite(1, TimeUnit.DAYS) + .build(); + + init { + mapper = ObjectMapper() + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + } + + private fun getGPTResponse(key: String, prompt: String): GPTResponse { + val data = httpService.post( + "https://api.openai.com/v1/chat/completions", + emptyMap(), + mapOf("Authorization" to "Bearer ${javabotConfig.chatGptKey()}"), + emptyMap(), + GPTMessageContainer(listOf(GPTMessage(prompt))) + ) + val response = mapper.readValue(data, GPTResponse::class.java) + return response + } - fun sendPromptToChatGPT(prompt: String): String? { + fun sendPromptToChatGPT(key: String, prompt: String): String? { val mapper = ObjectMapper() mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) return if (javabotConfig.chatGptKey().isNotEmpty() && limiter.tryAcquire()) { - val data = - httpService.post( - "https://api.openai.com/v1/chat/completions", - emptyMap(), - mapOf("Authorization" to "Bearer ${javabotConfig.chatGptKey()}"), - emptyMap(), - GPTMessageContainer(listOf(GPTMessage(prompt))) - ) - val response = mapper.readValue(data, GPTResponse::class.java) + val response = queryCache.get(key) { getGPTResponse(key, prompt) } return response.choices.first().message.content } else { // no chatGPT key? No chatGPT attempt. diff --git a/src/main/kotlin/javabot/operations/ChatGPTOperation.kt b/src/main/kotlin/javabot/operations/ChatGPTOperation.kt index 5ee46a9a..956a672d 100644 --- a/src/main/kotlin/javabot/operations/ChatGPTOperation.kt +++ b/src/main/kotlin/javabot/operations/ChatGPTOperation.kt @@ -17,7 +17,7 @@ constructor( private var chatGPTDao: ChatGPTDao, private var getFactoidOperation: GetFactoidOperation -) : BotOperation(bot, adminDao) { +) : BotOperation(bot, adminDao) { override fun handleMessage(event: Message): List { val message = event.value val responses = mutableListOf() @@ -39,10 +39,11 @@ constructor( ) else -> { - val factoid=getFactoidOperation.handleMessage(Message(event.user,query)).firstOrNull()?.value - val seed=when { - factoid!=null -> + val factoid = getFactoidOperation.handleMessage(Message(event.user, query)).firstOrNull()?.value + val seed = when { + factoid != null -> "Please frame the response in the context of the query having a potential answer of '${factoid}'." + else -> "" } val uuid = UUID.randomUUID() @@ -50,16 +51,16 @@ constructor( """ Someone is asking '$query'. Restrict your answer to being applicable to the Java Virtual Machine, - and limit the response's length to under 510 characters, + and limit the response's length to under 500 characters, formatted as simple text, no markdown or other markup, but urls are acceptable. $seed If the answer does not contain constructive information for Java programmers, respond **ONLY** with "$uuid-not applicable" and no other text. """.trimIndent().trim() try { - val result = chatGPTDao.sendPromptToChatGPT(prompt) + val result = chatGPTDao.sendPromptToChatGPT(query, prompt) if (!result.isNullOrEmpty() && !result.lowercase().contains(uuid.toString())) { - val response=result.cleanForIRC() + val response = result.cleanForIRC() responses.add(Message(event, response)) } } catch (e: Throwable) { diff --git a/src/test/kotlin/javabot/operations/ChatGPTOperationTest.kt b/src/test/kotlin/javabot/operations/ChatGPTOperationTest.kt index 3843a030..daca9f09 100644 --- a/src/test/kotlin/javabot/operations/ChatGPTOperationTest.kt +++ b/src/test/kotlin/javabot/operations/ChatGPTOperationTest.kt @@ -34,7 +34,9 @@ class ChatGPTOperationTest : BaseTest() { arrayOf("speed of an african laden swallow", true, ""), arrayOf("what is the maven directory structure", false, "Maven directory structure"), arrayOf("suffering-oriented programming", false, "Suffering-oriented programming"), - arrayOf("list of DI frameworks", false, "Spring") + arrayOf("list of DI frameworks", false, "Spring"), + arrayOf("list of DI frameworks", false, "Spring"), + arrayOf("how do I declare a new variable in Javascript", true, "") ) @Test(dataProvider = "queries")