From 86cf991ef18f929a7460e48717d58a94d4167ef0 Mon Sep 17 00:00:00 2001 From: Javi Pacheco Date: Thu, 20 Jul 2023 12:59:51 +0200 Subject: [PATCH 1/8] Bugs in conversations --- .../com/xebia/functional/xef/llm/Chat.kt | 4 ++-- .../xebia/functional/xef/auto/Conversation.kt | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt index d24b9d253..e68d440e5 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt @@ -242,7 +242,7 @@ interface Chat : LLM { Message(role = role, content = firstChoice.message?.content ?: "", name = role.name), timestamp = getTimeMillis() ) - context.addMemories(listOf(requestMemory, firstChoiceMemory)) + context.addMemories(listOf(firstChoiceMemory, requestMemory)) } } @@ -268,7 +268,7 @@ interface Chat : LLM { Message(role = role, content = firstChoice.message?.content ?: "", name = role.name), timestamp = getTimeMillis() ) - context.addMemories(listOf(requestMemory, firstChoiceMemory)) + context.addMemories(listOf(firstChoiceMemory, requestMemory)) } } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt new file mode 100644 index 000000000..9c7dd0134 --- /dev/null +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt @@ -0,0 +1,19 @@ +package com.xebia.functional.xef.auto + +import com.xebia.functional.xef.auto.llm.openai.getOrElse +import com.xebia.functional.xef.auto.llm.openai.promptMessage + +suspend fun main() { + ai { + + val email: String = + promptMessage("You are a Marketing Responsible and have the information about different products. You have to prepare an email template with the personal information") + println("First question:\n $email") + + val summarize: String = + promptMessage("You are a Marketing Responsible and have the information about the best rated products. Summarize the next information: Love this product and so does my husband! He tried it because his face gets chapped and red from working outside. It actually helped by about 60%! I love it cuz it’s lightweight and smells so yummy! After applying makeup, it doesn’t leave streaks like other moisturizers cause. i would definitely use this!\\n- Review 5 (rated 5): I’ve been using this for 10+yrs now. I don’t have any noticeable wrinkles at all. I use Estée Lauder’s micro essence then advance repair serum before I apply this lotion. A little goes a long way! It does feel greasier than most face lotions that I’ve tried previously but I don’t apply much. I enjoy cucumber like scent of the face lotion. I have combination skin and never broke out using this. This is my daily skincare product with or without makeup. And it has SPF but I also apply Kravebeauty SPF on top as well for extra protection") + println("Second question:\n $summarize") + + + }.getOrElse { println(it) } +} From 621f04ac09e17b32c5e927e4c0589c0fdeb76b4e Mon Sep 17 00:00:00 2001 From: Javi Pacheco Date: Thu, 20 Jul 2023 14:37:21 +0200 Subject: [PATCH 2/8] Using trim marging on Conversation example --- .../xebia/functional/xef/auto/Conversation.kt | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt index 9c7dd0134..7b0c201d8 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt @@ -7,11 +7,32 @@ suspend fun main() { ai { val email: String = - promptMessage("You are a Marketing Responsible and have the information about different products. You have to prepare an email template with the personal information") + promptMessage( + """ + |You are a Marketing Responsible and have the information about different products. You have to prepare + |an email template with the personal information + """.trimMargin() + ) println("First question:\n $email") val summarize: String = - promptMessage("You are a Marketing Responsible and have the information about the best rated products. Summarize the next information: Love this product and so does my husband! He tried it because his face gets chapped and red from working outside. It actually helped by about 60%! I love it cuz it’s lightweight and smells so yummy! After applying makeup, it doesn’t leave streaks like other moisturizers cause. i would definitely use this!\\n- Review 5 (rated 5): I’ve been using this for 10+yrs now. I don’t have any noticeable wrinkles at all. I use Estée Lauder’s micro essence then advance repair serum before I apply this lotion. A little goes a long way! It does feel greasier than most face lotions that I’ve tried previously but I don’t apply much. I enjoy cucumber like scent of the face lotion. I have combination skin and never broke out using this. This is my daily skincare product with or without makeup. And it has SPF but I also apply Kravebeauty SPF on top as well for extra protection") + promptMessage( + """ + |You are a Marketing Responsible and have the information about the best rated products. + |Summarize the next information: + |Love this product and so does my husband! He tried it because his face gets chapped and red from + |working outside. It actually helped by about 60%! I love it cuz it's lightweight and smells so yummy! + |After applying makeup, it doesn't leave streaks like other moisturizers cause. i would definitely use + |this! + | + |I've been using this for 10+yrs now. I don't have any noticeable + |wrinkles at all. I use Estée Lauder's micro essence then advance repair serum before I apply this + |lotion. A little goes a long way! It does feel greasier than most face lotions that I've tried + |previously but I don't apply much. I enjoy cucumber like scent of the face lotion. I have combination + |skin and never broke out using this. This is my daily skincare product with or without makeup. And it + |has SPF but I also apply Kravebeauty SPF on top as well for extra protection + """.trimMargin() + ) println("Second question:\n $summarize") From 9efef7224e09458e39819c87497ad89ca09216a5 Mon Sep 17 00:00:00 2001 From: Javi Pacheco Date: Fri, 21 Jul 2023 12:18:16 +0200 Subject: [PATCH 3/8] Bug fixed when storing messages on LocalVectorStore and tests --- .../com/xebia/functional/xef/llm/Chat.kt | 6 ++-- .../xef/vectorstores/LocalVectorStore.kt | 16 +++++++-- .../xef/vectorstores/FakeEmbeddings.kt | 14 ++++++++ .../xef/vectorstores/LocalVectorStoreSpec.kt | 34 +++++++++++++++++++ .../functional/xef/vectorstores/MemoryData.kt | 16 +++++++++ .../xebia/functional/xef/auto/Conversation.kt | 8 +++++ 6 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/FakeEmbeddings.kt create mode 100644 core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt create mode 100644 core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt index e68d440e5..537bf9571 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt @@ -242,7 +242,7 @@ interface Chat : LLM { Message(role = role, content = firstChoice.message?.content ?: "", name = role.name), timestamp = getTimeMillis() ) - context.addMemories(listOf(firstChoiceMemory, requestMemory)) + context.addMemories(listOf(requestMemory, firstChoiceMemory)) } } @@ -268,12 +268,12 @@ interface Chat : LLM { Message(role = role, content = firstChoice.message?.content ?: "", name = role.name), timestamp = getTimeMillis() ) - context.addMemories(listOf(firstChoiceMemory, requestMemory)) + context.addMemories(listOf(requestMemory, firstChoiceMemory)) } } private fun messages(memories: List, promptWithContext: String): List = - memories.reversed().map { it.content } + + memories.map { it.content } + listOf(Message(Role.USER, promptWithContext, Role.USER.name)) private suspend fun memories( diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt index 0affb3a5e..71d3332de 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt @@ -31,15 +31,27 @@ private constructor(private val embeddings: Embeddings, private val state: Atomi override suspend fun addMemories(memories: List) { state.update { prevState -> + val allNewMemories = memories.groupBy { it.conversationId } + + val prevConversationIdsInNewMemories = prevState.orderedMemories.map { it.key }.intersect(allNewMemories.keys) + + val prevMemoriesWithNewMessages = prevState.orderedMemories.map { (conversationId, memories) -> + conversationId to (memories + allNewMemories[conversationId].orEmpty()) + }.toMap() + + val allNewMemoriesWithoutPrevConversationId = allNewMemories.filterKeys { + !prevConversationIdsInNewMemories.contains(it) + } + prevState.copy( - orderedMemories = prevState.orderedMemories + memories.groupBy { it.conversationId } + orderedMemories = prevMemoriesWithNewMessages + allNewMemoriesWithoutPrevConversationId ) } } override suspend fun memories(conversationId: ConversationId, limit: Int): List { val memories = state.get().orderedMemories[conversationId] - return memories?.take(limit).orEmpty() + return memories?.takeLast(limit).orEmpty() } override suspend fun addTexts(texts: List) { diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/FakeEmbeddings.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/FakeEmbeddings.kt new file mode 100644 index 000000000..28ba5c42a --- /dev/null +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/FakeEmbeddings.kt @@ -0,0 +1,14 @@ +package com.xebia.functional.xef.vectorstores + +import com.xebia.functional.xef.embeddings.Embedding +import com.xebia.functional.xef.embeddings.Embeddings +import com.xebia.functional.xef.llm.models.embeddings.RequestConfig + +class FakeEmbeddings : Embeddings { + override suspend fun embedDocuments( + texts: List, chunkSize: Int?, requestConfig: RequestConfig + ): List = emptyList() + + + override suspend fun embedQuery(text: String, requestConfig: RequestConfig): List = emptyList() +} diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt new file mode 100644 index 000000000..d9e414714 --- /dev/null +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt @@ -0,0 +1,34 @@ +package com.xebia.functional.xef.vectorstores + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class LocalVectorStoreSpec : + StringSpec({ + "memories function should return all of messages in the right order when the limit is greater than the number of stored messages" { + val localVectorStore = LocalVectorStore(FakeEmbeddings()) + + localVectorStore.addMemories(messages1) + localVectorStore.addMemories(messages2) + + val messages = localVectorStore.memories(defaultConversationId, Int.MAX_VALUE) + + val messagesExpected = messages1 + messages2 + + messages shouldBe messagesExpected + } + + "memories function should return the last n messages in the right order" { + val localVectorStore = LocalVectorStore(FakeEmbeddings()) + + localVectorStore.addMemories(messages1) + localVectorStore.addMemories(messages2) + + val messages = localVectorStore.memories(defaultConversationId, 2) + + val messagesExpected = (messages1 + messages2).takeLast(2) + + messages shouldBe messagesExpected + } + + }) diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt new file mode 100644 index 000000000..5d89df637 --- /dev/null +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt @@ -0,0 +1,16 @@ +package com.xebia.functional.xef.vectorstores + +import com.xebia.functional.xef.llm.models.chat.Message +import com.xebia.functional.xef.llm.models.chat.Role + +val defaultConversationId = ConversationId("default-id") + +val messages1 = listOf( + Memory(defaultConversationId, Message(Role.USER, "Who is the best player in the world?", "USER"), 0), + Memory(defaultConversationId, Message(Role.ASSISTANT, "Magico Gonzalez", "ASSISTANT"), 0), +) + +val messages2 = listOf( + Memory(defaultConversationId, Message(Role.USER, "Which is the most beautiful city in the world?", "USER"), 0), + Memory(defaultConversationId, Message(Role.ASSISTANT, "More than a city better an area, La Bahia de Cadiz", "ASSISTANT"), 0), +) diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt index 7b0c201d8..4d28155ef 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt @@ -35,6 +35,14 @@ suspend fun main() { ) println("Second question:\n $summarize") + val meaning: String = + promptMessage( + """ + |What is the meaning of life? + """.trimMargin() + ) + println("Third question:\n $meaning") + }.getOrElse { println(it) } } From e662c6a9d9914f1a1b6b7eca8a08189bb3c16aa4 Mon Sep 17 00:00:00 2001 From: Javi Pacheco Date: Fri, 21 Jul 2023 14:51:05 +0200 Subject: [PATCH 4/8] victorcrrd comments addressed --- .../xef/vectorstores/LocalVectorStore.kt | 20 ++++------- .../xef/vectorstores/LocalVectorStoreSpec.kt | 35 +++++++++++++++++-- .../functional/xef/vectorstores/MemoryData.kt | 16 ++++----- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt index 71d3332de..d50cc6b02 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt @@ -31,20 +31,14 @@ private constructor(private val embeddings: Embeddings, private val state: Atomi override suspend fun addMemories(memories: List) { state.update { prevState -> - val allNewMemories = memories.groupBy { it.conversationId } - - val prevConversationIdsInNewMemories = prevState.orderedMemories.map { it.key }.intersect(allNewMemories.keys) - - val prevMemoriesWithNewMessages = prevState.orderedMemories.map { (conversationId, memories) -> - conversationId to (memories + allNewMemories[conversationId].orEmpty()) - }.toMap() - - val allNewMemoriesWithoutPrevConversationId = allNewMemories.filterKeys { - !prevConversationIdsInNewMemories.contains(it) - } - prevState.copy( - orderedMemories = prevMemoriesWithNewMessages + allNewMemoriesWithoutPrevConversationId + orderedMemories = memories.groupBy { it.conversationId }.let { memories -> + (prevState.orderedMemories.keys + memories.keys).associateWith { key -> + val l1 = prevState.orderedMemories[key] ?: emptyList() + val l2 = memories[key] ?: emptyList() + l1 + l2 + } + } ) } } diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt index d9e414714..e40c60411 100644 --- a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt @@ -8,6 +8,9 @@ class LocalVectorStoreSpec : "memories function should return all of messages in the right order when the limit is greater than the number of stored messages" { val localVectorStore = LocalVectorStore(FakeEmbeddings()) + val messages1 = generateRandomMessages(2) + val messages2 = generateRandomMessages(2) + localVectorStore.addMemories(messages1) localVectorStore.addMemories(messages2) @@ -21,14 +24,42 @@ class LocalVectorStoreSpec : "memories function should return the last n messages in the right order" { val localVectorStore = LocalVectorStore(FakeEmbeddings()) + val limit = 2 + + val messages1 = generateRandomMessages(2) + val messages2 = generateRandomMessages(2) + localVectorStore.addMemories(messages1) localVectorStore.addMemories(messages2) - val messages = localVectorStore.memories(defaultConversationId, 2) + val messages = localVectorStore.memories(defaultConversationId, limit) - val messagesExpected = (messages1 + messages2).takeLast(2) + val messagesExpected = (messages1 + messages2).takeLast(limit) messages shouldBe messagesExpected } + "memories function should return the last n messages in the right order for a specific conversation id" { + val localVectorStore = LocalVectorStore(FakeEmbeddings()) + + val limit = 2 + + val firstId = ConversationId("first-id") + val secondId = ConversationId("second-id") + + val messages1 = generateRandomMessages(4, firstId) + val messages2 = generateRandomMessages(3, secondId) + + localVectorStore.addMemories(messages1 + messages2) + + val messagesFirstId = localVectorStore.memories(firstId, limit) + val messagesFirstIdExpected = messages1.takeLast(limit) + + val messagesSecondId = localVectorStore.memories(secondId, limit) + val messagesSecondIdExpected = messages2.takeLast(limit) + + messagesFirstId shouldBe messagesFirstIdExpected + messagesSecondId shouldBe messagesSecondIdExpected + } + }) diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt index 5d89df637..db7623303 100644 --- a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt @@ -5,12 +5,10 @@ import com.xebia.functional.xef.llm.models.chat.Role val defaultConversationId = ConversationId("default-id") -val messages1 = listOf( - Memory(defaultConversationId, Message(Role.USER, "Who is the best player in the world?", "USER"), 0), - Memory(defaultConversationId, Message(Role.ASSISTANT, "Magico Gonzalez", "ASSISTANT"), 0), -) - -val messages2 = listOf( - Memory(defaultConversationId, Message(Role.USER, "Which is the most beautiful city in the world?", "USER"), 0), - Memory(defaultConversationId, Message(Role.ASSISTANT, "More than a city better an area, La Bahia de Cadiz", "ASSISTANT"), 0), -) +fun generateRandomMessages(n: Int, conversationId: ConversationId = defaultConversationId): List = + (0..n).flatMap { + listOf( + Memory(conversationId, Message(Role.USER, "Question $it", "USER"), 0), + Memory(conversationId, Message(Role.ASSISTANT, "Response $it", "ASSISTANT"), 0), + ) + } From 575fd4369cbaf708f803f95f638fe9ecd784a497 Mon Sep 17 00:00:00 2001 From: Javi Pacheco Date: Tue, 25 Jul 2023 10:12:19 +0200 Subject: [PATCH 5/8] CombinedVectorStore fixed and tests --- .../xef/vectorstores/CombinedVectorStore.kt | 4 +- .../vectorstores/CombinedVectorStoreSpec.kt | 65 +++++++++++++++++++ .../xef/vectorstores/LocalVectorStoreSpec.kt | 16 ++--- .../functional/xef/vectorstores/MemoryData.kt | 15 +++-- .../xebia/functional/xef/auto/Conversation.kt | 33 +++++----- 5 files changed, 104 insertions(+), 29 deletions(-) create mode 100644 core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStoreSpec.kt diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStore.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStore.kt index 563a7263c..e20a2cc9f 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStore.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStore.kt @@ -12,8 +12,8 @@ class CombinedVectorStore(private val top: VectorStore, private val bottom: Vect VectorStore by top { override suspend fun memories(conversationId: ConversationId, limit: Int): List { - val topResults = top.memories(conversationId, limit) - val bottomResults = bottom.memories(conversationId, limit - topResults.size) + val bottomResults = bottom.memories(conversationId, limit) + val topResults = top.memories(conversationId, limit - bottomResults.size) return topResults + bottomResults } diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStoreSpec.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStoreSpec.kt new file mode 100644 index 000000000..62670fe38 --- /dev/null +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStoreSpec.kt @@ -0,0 +1,65 @@ +package com.xebia.functional.xef.vectorstores + +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe + +class CombinedVectorStoreSpec : + StringSpec({ + "memories function should return all of messages combined in the right order" { + val topMessages = generateRandomMessages(4, append = "top") + val bottomMessages = generateRandomMessages(4, append = "bottom") + + val combinedVectorStore = topMessages.combine(bottomMessages) + + val messages = combinedVectorStore.memories(defaultConversationId, Int.MAX_VALUE) + + val messagesExpected = topMessages + bottomMessages + + messages shouldBe messagesExpected + } + + "memories function should return the last n combined messages in the right order" { + val topMessages = generateRandomMessages(4, append = "top") + val bottomMessages = generateRandomMessages(4, append = "bottom") + + val combinedVectorStore = topMessages.combine(bottomMessages) + + val messages = combinedVectorStore.memories(defaultConversationId, 6 * 2) + + val messagesExpected = topMessages.takeLast(2 * 2) + bottomMessages + + messages shouldBe messagesExpected + } + + "memories function should return the messages with common conversation id combined in the right order" { + + val topId = ConversationId("top-id") + val bottomId = ConversationId("bottom-id") + val commonId = ConversationId("common-id") + + val topMessages = generateRandomMessages(4, append = "top", conversationId = topId) + val commonTopMessages = generateRandomMessages(4, append = "common-top", conversationId = commonId) + + val bottomMessages = generateRandomMessages(4, append = "bottom", conversationId = bottomId) + val commonBottomMessages = generateRandomMessages(4, append = "common-bottom", conversationId = commonId) + + val combinedVectorStore = (topMessages + commonTopMessages).combine(bottomMessages + commonBottomMessages) + + val messages = combinedVectorStore.memories(commonId, Int.MAX_VALUE) + + val messagesExpected = commonTopMessages + commonBottomMessages + + messages shouldBe messagesExpected + } + + }) + +suspend fun List.combine(bottomMessages: List): CombinedVectorStore { + val top = LocalVectorStore(FakeEmbeddings()) + top.addMemories(this) + + val bottom = LocalVectorStore(FakeEmbeddings()) + bottom.addMemories(bottomMessages) + + return CombinedVectorStore(top, bottom) +} diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt index e40c60411..4cdb175e3 100644 --- a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt @@ -8,8 +8,8 @@ class LocalVectorStoreSpec : "memories function should return all of messages in the right order when the limit is greater than the number of stored messages" { val localVectorStore = LocalVectorStore(FakeEmbeddings()) - val messages1 = generateRandomMessages(2) - val messages2 = generateRandomMessages(2) + val messages1 = generateRandomMessages(4) + val messages2 = generateRandomMessages(3) localVectorStore.addMemories(messages1) localVectorStore.addMemories(messages2) @@ -24,10 +24,10 @@ class LocalVectorStoreSpec : "memories function should return the last n messages in the right order" { val localVectorStore = LocalVectorStore(FakeEmbeddings()) - val limit = 2 + val limit = 3 * 2 // 3 couples of messages - val messages1 = generateRandomMessages(2) - val messages2 = generateRandomMessages(2) + val messages1 = generateRandomMessages(4) + val messages2 = generateRandomMessages(3) localVectorStore.addMemories(messages1) localVectorStore.addMemories(messages2) @@ -42,13 +42,13 @@ class LocalVectorStoreSpec : "memories function should return the last n messages in the right order for a specific conversation id" { val localVectorStore = LocalVectorStore(FakeEmbeddings()) - val limit = 2 + val limit = 3 * 2 val firstId = ConversationId("first-id") val secondId = ConversationId("second-id") - val messages1 = generateRandomMessages(4, firstId) - val messages2 = generateRandomMessages(3, secondId) + val messages1 = generateRandomMessages(4, conversationId = firstId) + val messages2 = generateRandomMessages(3, conversationId = secondId) localVectorStore.addMemories(messages1 + messages2) diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt index db7623303..7cbb88a44 100644 --- a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt @@ -5,10 +5,17 @@ import com.xebia.functional.xef.llm.models.chat.Role val defaultConversationId = ConversationId("default-id") -fun generateRandomMessages(n: Int, conversationId: ConversationId = defaultConversationId): List = - (0..n).flatMap { +fun generateRandomMessages( + n: Int, + append: String? = null, + conversationId: ConversationId = defaultConversationId +): List = + (0 until n).flatMap { listOf( - Memory(conversationId, Message(Role.USER, "Question $it", "USER"), 0), - Memory(conversationId, Message(Role.ASSISTANT, "Response $it", "ASSISTANT"), 0), + Memory(conversationId, Message(Role.USER, "Question $it${append?.let { ": $it" } ?: ""}", "USER"), 0), + Memory( + conversationId, + Message(Role.ASSISTANT, "Response $it${append?.let { ": $it" } ?: ""}", "ASSISTANT"), + 0), ) } diff --git a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt index 4d28155ef..9f86ba34a 100644 --- a/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt +++ b/examples/kotlin/src/main/kotlin/com/xebia/functional/xef/auto/Conversation.kt @@ -6,18 +6,17 @@ import com.xebia.functional.xef.auto.llm.openai.promptMessage suspend fun main() { ai { - val email: String = - promptMessage( - """ + val emailMessage = """ |You are a Marketing Responsible and have the information about different products. You have to prepare |an email template with the personal information """.trimMargin() - ) - println("First question:\n $email") - val summarize: String = - promptMessage( - """ + val email: String = promptMessage(emailMessage) + + println("Prompt:\n $emailMessage") + println("Response:\n $email") + + val summarizePrompt = """ |You are a Marketing Responsible and have the information about the best rated products. |Summarize the next information: |Love this product and so does my husband! He tried it because his face gets chapped and red from @@ -32,16 +31,20 @@ suspend fun main() { |skin and never broke out using this. This is my daily skincare product with or without makeup. And it |has SPF but I also apply Kravebeauty SPF on top as well for extra protection """.trimMargin() - ) - println("Second question:\n $summarize") - val meaning: String = - promptMessage( - """ + val summarize: String = promptMessage(summarizePrompt) + + println("Prompt:\n $summarizePrompt}") + println("Response:\n $summarize") + + val meaningPrompt = """ |What is the meaning of life? """.trimMargin() - ) - println("Third question:\n $meaning") + + val meaning: String = promptMessage(meaningPrompt) + + println("Prompt:\n $meaningPrompt}") + println("Response:\n $meaning") }.getOrElse { println(it) } From 3a2f7e06bf1ce7d6aa07aff385420396a381f252 Mon Sep 17 00:00:00 2001 From: Victor Carrillo-Redondo Date: Tue, 25 Jul 2023 15:34:51 +0200 Subject: [PATCH 6/8] PGVectorStore tests (#272) * format * syntax error in createMemoryTable query * being able to connect to db * added missing test * added documentation to indicate that timestamp is measured in millis * modeling timestamp in postgres as a BIGINT instead of a TIMESTAMP to avoid problems * messages retrieved from postgres were assigned always the same name "role", now it matches the name stored in the database * getting postgres messages in the right order: from oldest to newest (in the form of a chat) * added simple test * specifying more the test * substituting localhost by IP 0.0.0.0 is fine for this test --- .../functional/xef/vectorstores/Memory.kt | 7 +++++ .../xef/vectorstores/PostgreSQLVectorStore.kt | 6 ++-- .../xef/vectorstores/postgresql/postgres.kt | 4 +-- .../src/test/kotlin/xef/PGVectorStoreSpec.kt | 30 ++++++++++++++++--- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/Memory.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/Memory.kt index e7b7395a4..4c619dc66 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/Memory.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/Memory.kt @@ -2,4 +2,11 @@ package com.xebia.functional.xef.vectorstores import com.xebia.functional.xef.llm.models.chat.Message +/** + * Representation of the memory of a message in a conversation. + * + * @property content message sent. + * @property conversationId uniquely identifies the conversation in which the message took place. + * @property timestamp in milliseconds. + */ data class Memory(val conversationId: ConversationId, val content: Message, val timestamp: Long) diff --git a/integrations/postgresql/src/main/kotlin/com/xebia/functional/xef/vectorstores/PostgreSQLVectorStore.kt b/integrations/postgresql/src/main/kotlin/com/xebia/functional/xef/vectorstores/PostgreSQLVectorStore.kt index f0986a970..0a01d9b63 100644 --- a/integrations/postgresql/src/main/kotlin/com/xebia/functional/xef/vectorstores/PostgreSQLVectorStore.kt +++ b/integrations/postgresql/src/main/kotlin/com/xebia/functional/xef/vectorstores/PostgreSQLVectorStore.kt @@ -51,7 +51,7 @@ class PGVectorStore( content = Message( role = Role.valueOf(role.uppercase()), content = content, - name = "role", + name = role, ), timestamp = timestamp, ) @@ -83,9 +83,9 @@ class PGVectorStore( fun createCollection(): Unit = dataSource.connection { - val xa = UUID.generateUUID() + val uuid = UUID.generateUUID() update(addNewCollection) { - bind(xa.toString()) + bind(uuid.toString()) bind(collectionName) } } diff --git a/integrations/postgresql/src/main/kotlin/com/xebia/functional/xef/vectorstores/postgresql/postgres.kt b/integrations/postgresql/src/main/kotlin/com/xebia/functional/xef/vectorstores/postgresql/postgres.kt index 48bcfdbfc..81de26743 100644 --- a/integrations/postgresql/src/main/kotlin/com/xebia/functional/xef/vectorstores/postgresql/postgres.kt +++ b/integrations/postgresql/src/main/kotlin/com/xebia/functional/xef/vectorstores/postgresql/postgres.kt @@ -23,7 +23,7 @@ val createMemoryTable: String = conversation_id TEXT NOT NULL, role TEXT NOT NULL, content TEXT UNIQUE NOT NULL, - timestamp TIMESTAMP NOT NULL, + timestamp BIGINT NOT NULL );""" .trimIndent() @@ -97,7 +97,7 @@ val getCollectionById: String = val getMemoriesByConversationId: String = """SELECT * FROM xef_memory WHERE conversation_id = ? - ORDER BY timestamp DESC LIMIT ?;""" + ORDER BY timestamp ASC LIMIT ?;""" .trimIndent() val addNewDocument: String = diff --git a/integrations/postgresql/src/test/kotlin/xef/PGVectorStoreSpec.kt b/integrations/postgresql/src/test/kotlin/xef/PGVectorStoreSpec.kt index e47870769..2b4dabdf5 100644 --- a/integrations/postgresql/src/test/kotlin/xef/PGVectorStoreSpec.kt +++ b/integrations/postgresql/src/test/kotlin/xef/PGVectorStoreSpec.kt @@ -2,16 +2,22 @@ package xef import com.xebia.functional.xef.embeddings.Embedding import com.xebia.functional.xef.embeddings.Embeddings +import com.xebia.functional.xef.llm.models.chat.Message +import com.xebia.functional.xef.llm.models.chat.Role import com.xebia.functional.xef.llm.models.embeddings.EmbeddingModel import com.xebia.functional.xef.llm.models.embeddings.RequestConfig +import com.xebia.functional.xef.vectorstores.ConversationId +import com.xebia.functional.xef.vectorstores.Memory import com.xebia.functional.xef.vectorstores.PGVectorStore import com.xebia.functional.xef.vectorstores.postgresql.PGDistanceStrategy import com.zaxxer.hikari.HikariConfig import com.zaxxer.hikari.HikariDataSource import io.kotest.core.extensions.install import io.kotest.core.spec.style.StringSpec -import io.kotest.extensions.testcontainers.SharedTestContainerExtension +import io.kotest.extensions.testcontainers.ContainerExtension import io.kotest.matchers.shouldBe +import kotlinx.uuid.UUID +import kotlinx.uuid.generateUUID import org.junit.jupiter.api.assertThrows import org.testcontainers.containers.PostgreSQLContainer import org.testcontainers.utility.DockerImageName @@ -23,12 +29,12 @@ val postgres: PostgreSQLContainer = class PGVectorStoreSpec : StringSpec({ - val container = install(SharedTestContainerExtension(postgres)) + val container = install(ContainerExtension(postgres)) val dataSource = autoClose( HikariDataSource( HikariConfig().apply { - jdbcUrl = container.jdbcUrl + jdbcUrl = container.jdbcUrl.replace("localhost", "0.0.0.0") username = container.username password = container.password driverClassName = "org.postgresql.Driver" @@ -63,6 +69,10 @@ class PGVectorStoreSpec : "createCollection should create collection" { pg.createCollection() } + "addTexts should not fail now that we created the collection" { + pg.addTexts(listOf("foo", "bar")) + } + "similaritySearchByVector should return both documents" { pg.similaritySearchByVector(Embedding(listOf(4.0f, 5.0f, 6.0f)), 2) shouldBe listOf("bar", "foo") @@ -78,6 +88,19 @@ class PGVectorStoreSpec : "similaritySearchByVector should return document" { pg.similaritySearchByVector(Embedding(listOf(1.0f, 2.0f, 3.0f)), 1) shouldBe listOf("foo") } + + "memories added in chronological order should be obtained in the same order" { + val messages = 10 + val conversationId = ConversationId(UUID.generateUUID().toString()) + val memories = (0 until messages).flatMap { + listOf( + Memory(conversationId, Message(Role.USER, "question $it", "user"), 2 * it.toLong()), + Memory(conversationId, Message(Role.ASSISTANT, "answer $it", "assistant"), 2 * it.toLong() + 1) + ) + } + pg.addMemories(memories) + memories shouldBe pg.memories(conversationId, memories.size) + } }) private fun Embeddings.Companion.mock( @@ -105,4 +128,3 @@ private fun Embeddings.Companion.mock( override suspend fun embedQuery(text: String, requestConfig: RequestConfig): List = embedQuery(text, requestConfig) } - From 64f1072b6ed2ba692fa5ca844850d3de8d0b1264 Mon Sep 17 00:00:00 2001 From: Javi Pacheco Date: Wed, 26 Jul 2023 15:28:14 +0200 Subject: [PATCH 7/8] Sorting messages by timestamp --- .../com/xebia/functional/xef/llm/Chat.kt | 3 +- .../xef/vectorstores/CombinedVectorStore.kt | 4 +- .../xef/vectorstores/LocalVectorStore.kt | 19 +- .../vectorstores/CombinedVectorStoreSpec.kt | 166 ++++++++++++------ .../xef/vectorstores/FakeEmbeddings.kt | 12 +- .../xef/vectorstores/LocalVectorStoreSpec.kt | 77 ++++---- .../functional/xef/vectorstores/MemoryData.kt | 30 ++-- .../functional/xef/reasoning/text/Text.kt | 12 ++ 8 files changed, 201 insertions(+), 122 deletions(-) diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt index 7ec9f4f37..4f265656e 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/llm/Chat.kt @@ -270,8 +270,7 @@ interface Chat : LLM { } private fun messages(memories: List, promptWithContext: String): List = - memories.map { it.content } + - listOf(Message(Role.USER, promptWithContext, Role.USER.name)) + memories.map { it.content } + listOf(Message(Role.USER, promptWithContext, Role.USER.name)) private suspend fun memories( conversationId: ConversationId?, diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStore.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStore.kt index e20a2cc9f..1f408ab76 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStore.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStore.kt @@ -13,8 +13,8 @@ class CombinedVectorStore(private val top: VectorStore, private val bottom: Vect override suspend fun memories(conversationId: ConversationId, limit: Int): List { val bottomResults = bottom.memories(conversationId, limit) - val topResults = top.memories(conversationId, limit - bottomResults.size) - return topResults + bottomResults + val topResults = top.memories(conversationId, limit) + return (topResults + bottomResults).sortedBy { it.timestamp }.takeLast(limit) } override suspend fun similaritySearch(query: String, limit: Int): List { diff --git a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt index d50cc6b02..580594720 100644 --- a/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt +++ b/core/src/commonMain/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStore.kt @@ -32,20 +32,23 @@ private constructor(private val embeddings: Embeddings, private val state: Atomi override suspend fun addMemories(memories: List) { state.update { prevState -> prevState.copy( - orderedMemories = memories.groupBy { it.conversationId }.let { memories -> - (prevState.orderedMemories.keys + memories.keys).associateWith { key -> - val l1 = prevState.orderedMemories[key] ?: emptyList() - val l2 = memories[key] ?: emptyList() - l1 + l2 - } - } + orderedMemories = + memories + .groupBy { it.conversationId } + .let { memories -> + (prevState.orderedMemories.keys + memories.keys).associateWith { key -> + val l1 = prevState.orderedMemories[key] ?: emptyList() + val l2 = memories[key] ?: emptyList() + l1 + l2 + } + } ) } } override suspend fun memories(conversationId: ConversationId, limit: Int): List { val memories = state.get().orderedMemories[conversationId] - return memories?.takeLast(limit).orEmpty() + return memories?.takeLast(limit).orEmpty().sortedBy { it.timestamp } } override suspend fun addTexts(texts: List) { diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStoreSpec.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStoreSpec.kt index 62670fe38..7ef5154d4 100644 --- a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStoreSpec.kt +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/CombinedVectorStoreSpec.kt @@ -4,62 +4,120 @@ import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe class CombinedVectorStoreSpec : - StringSpec({ - "memories function should return all of messages combined in the right order" { - val topMessages = generateRandomMessages(4, append = "top") - val bottomMessages = generateRandomMessages(4, append = "bottom") - - val combinedVectorStore = topMessages.combine(bottomMessages) - - val messages = combinedVectorStore.memories(defaultConversationId, Int.MAX_VALUE) - - val messagesExpected = topMessages + bottomMessages - - messages shouldBe messagesExpected - } - - "memories function should return the last n combined messages in the right order" { - val topMessages = generateRandomMessages(4, append = "top") - val bottomMessages = generateRandomMessages(4, append = "bottom") - - val combinedVectorStore = topMessages.combine(bottomMessages) - - val messages = combinedVectorStore.memories(defaultConversationId, 6 * 2) - - val messagesExpected = topMessages.takeLast(2 * 2) + bottomMessages - - messages shouldBe messagesExpected - } - - "memories function should return the messages with common conversation id combined in the right order" { - - val topId = ConversationId("top-id") - val bottomId = ConversationId("bottom-id") - val commonId = ConversationId("common-id") - - val topMessages = generateRandomMessages(4, append = "top", conversationId = topId) - val commonTopMessages = generateRandomMessages(4, append = "common-top", conversationId = commonId) - - val bottomMessages = generateRandomMessages(4, append = "bottom", conversationId = bottomId) - val commonBottomMessages = generateRandomMessages(4, append = "common-bottom", conversationId = commonId) - - val combinedVectorStore = (topMessages + commonTopMessages).combine(bottomMessages + commonBottomMessages) - - val messages = combinedVectorStore.memories(commonId, Int.MAX_VALUE) - - val messagesExpected = commonTopMessages + commonBottomMessages - - messages shouldBe messagesExpected - } - - }) + StringSpec({ + "memories function should return all of messages combined in the right order" { + val topMessages = generateRandomMessages(4, append = "top", startTimestamp = 1000) + val bottomMessages = generateRandomMessages(4, append = "bottom", startTimestamp = 2000) + + val combinedVectorStore = topMessages.combine(bottomMessages) + + val messages = combinedVectorStore.memories(defaultConversationId, Int.MAX_VALUE) + + val messagesExpected = topMessages + bottomMessages + + messages shouldBe messagesExpected + } + + "memories function should return the last n combined messages in the right order" { + val topMessages = generateRandomMessages(4, append = "top", startTimestamp = 1000) + val bottomMessages = generateRandomMessages(4, append = "bottom", startTimestamp = 2000) + + val combinedVectorStore = topMessages.combine(bottomMessages) + + val messages = combinedVectorStore.memories(defaultConversationId, 6 * 2) + + val messagesExpected = topMessages.takeLast(2 * 2) + bottomMessages + + messages shouldBe messagesExpected + } + + "memories function should return the messages with common conversation id combined in the right order" { + val topId = ConversationId("top-id") + val bottomId = ConversationId("bottom-id") + val commonId = ConversationId("common-id") + + val topMessages = + generateRandomMessages(4, append = "top", conversationId = topId, startTimestamp = 1000) + val commonTopMessages = + generateRandomMessages( + 4, + append = "common-top", + conversationId = commonId, + startTimestamp = 2000 + ) + + val bottomMessages = + generateRandomMessages( + 4, + append = "bottom", + conversationId = bottomId, + startTimestamp = 3000 + ) + val commonBottomMessages = + generateRandomMessages( + 4, + append = "common-bottom", + conversationId = commonId, + startTimestamp = 4000 + ) + + val combinedVectorStore = + (topMessages + commonTopMessages).combine(bottomMessages + commonBottomMessages) + + val messages = combinedVectorStore.memories(commonId, Int.MAX_VALUE) + + val messagesExpected = commonTopMessages + commonBottomMessages + + messages shouldBe messagesExpected + } + + "adding messages to a combined vector store" { + val topId = ConversationId("top-id") + val bottomId = ConversationId("bottom-id") + val commonId = ConversationId("common-id") + + val topMessages = + generateRandomMessages(4, append = "top", conversationId = topId, startTimestamp = 1000) + val commonTopMessages = + generateRandomMessages( + 4, + append = "common-top", + conversationId = commonId, + startTimestamp = 2000 + ) + + val bottomMessages = + generateRandomMessages( + 4, + append = "bottom", + conversationId = bottomId, + startTimestamp = 3000 + ) + val commonBottomMessages = + generateRandomMessages( + 4, + append = "common-bottom", + conversationId = commonId, + startTimestamp = 4000 + ) + + val combinedVectorStore = + (topMessages + commonTopMessages).combine(bottomMessages + commonBottomMessages) + + val newCommonMessages = + generateRandomMessages(4, append = "new", conversationId = commonId, startTimestamp = 5000) + combinedVectorStore.addMemories(newCommonMessages) + + combinedVectorStore.memories(commonId, 4 * 2) shouldBe newCommonMessages + } + }) suspend fun List.combine(bottomMessages: List): CombinedVectorStore { - val top = LocalVectorStore(FakeEmbeddings()) - top.addMemories(this) + val top = LocalVectorStore(FakeEmbeddings()) + top.addMemories(this) - val bottom = LocalVectorStore(FakeEmbeddings()) - bottom.addMemories(bottomMessages) + val bottom = LocalVectorStore(FakeEmbeddings()) + bottom.addMemories(bottomMessages) - return CombinedVectorStore(top, bottom) + return CombinedVectorStore(top, bottom) } diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/FakeEmbeddings.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/FakeEmbeddings.kt index 28ba5c42a..5dabfe93a 100644 --- a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/FakeEmbeddings.kt +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/FakeEmbeddings.kt @@ -5,10 +5,12 @@ import com.xebia.functional.xef.embeddings.Embeddings import com.xebia.functional.xef.llm.models.embeddings.RequestConfig class FakeEmbeddings : Embeddings { - override suspend fun embedDocuments( - texts: List, chunkSize: Int?, requestConfig: RequestConfig - ): List = emptyList() + override suspend fun embedDocuments( + texts: List, + chunkSize: Int?, + requestConfig: RequestConfig + ): List = emptyList() - - override suspend fun embedQuery(text: String, requestConfig: RequestConfig): List = emptyList() + override suspend fun embedQuery(text: String, requestConfig: RequestConfig): List = + emptyList() } diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt index 4cdb175e3..bef55a3db 100644 --- a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/LocalVectorStoreSpec.kt @@ -4,62 +4,61 @@ import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe class LocalVectorStoreSpec : - StringSpec({ - "memories function should return all of messages in the right order when the limit is greater than the number of stored messages" { - val localVectorStore = LocalVectorStore(FakeEmbeddings()) + StringSpec({ + "memories function should return all of messages in the right order when the limit is greater than the number of stored messages" { + val localVectorStore = LocalVectorStore(FakeEmbeddings()) - val messages1 = generateRandomMessages(4) - val messages2 = generateRandomMessages(3) + val messages1 = generateRandomMessages(4, startTimestamp = 1000) + val messages2 = generateRandomMessages(3, startTimestamp = 2000) - localVectorStore.addMemories(messages1) - localVectorStore.addMemories(messages2) + localVectorStore.addMemories(messages1) + localVectorStore.addMemories(messages2) - val messages = localVectorStore.memories(defaultConversationId, Int.MAX_VALUE) + val messages = localVectorStore.memories(defaultConversationId, Int.MAX_VALUE) - val messagesExpected = messages1 + messages2 + val messagesExpected = messages1 + messages2 - messages shouldBe messagesExpected - } + messages shouldBe messagesExpected + } - "memories function should return the last n messages in the right order" { - val localVectorStore = LocalVectorStore(FakeEmbeddings()) + "memories function should return the last n messages in the right order" { + val localVectorStore = LocalVectorStore(FakeEmbeddings()) - val limit = 3 * 2 // 3 couples of messages + val limit = 3 * 2 // 3 couples of messages - val messages1 = generateRandomMessages(4) - val messages2 = generateRandomMessages(3) + val messages1 = generateRandomMessages(4, startTimestamp = 1000) + val messages2 = generateRandomMessages(3, startTimestamp = 2000) - localVectorStore.addMemories(messages1) - localVectorStore.addMemories(messages2) + localVectorStore.addMemories(messages1) + localVectorStore.addMemories(messages2) - val messages = localVectorStore.memories(defaultConversationId, limit) + val messages = localVectorStore.memories(defaultConversationId, limit) - val messagesExpected = (messages1 + messages2).takeLast(limit) + val messagesExpected = (messages1 + messages2).takeLast(limit) - messages shouldBe messagesExpected - } + messages shouldBe messagesExpected + } - "memories function should return the last n messages in the right order for a specific conversation id" { - val localVectorStore = LocalVectorStore(FakeEmbeddings()) + "memories function should return the last n messages in the right order for a specific conversation id" { + val localVectorStore = LocalVectorStore(FakeEmbeddings()) - val limit = 3 * 2 + val limit = 3 * 2 - val firstId = ConversationId("first-id") - val secondId = ConversationId("second-id") + val firstId = ConversationId("first-id") + val secondId = ConversationId("second-id") - val messages1 = generateRandomMessages(4, conversationId = firstId) - val messages2 = generateRandomMessages(3, conversationId = secondId) + val messages1 = generateRandomMessages(4, conversationId = firstId, startTimestamp = 1000) + val messages2 = generateRandomMessages(3, conversationId = secondId, startTimestamp = 2000) - localVectorStore.addMemories(messages1 + messages2) + localVectorStore.addMemories(messages1 + messages2) - val messagesFirstId = localVectorStore.memories(firstId, limit) - val messagesFirstIdExpected = messages1.takeLast(limit) + val messagesFirstId = localVectorStore.memories(firstId, limit) + val messagesFirstIdExpected = messages1.takeLast(limit) - val messagesSecondId = localVectorStore.memories(secondId, limit) - val messagesSecondIdExpected = messages2.takeLast(limit) + val messagesSecondId = localVectorStore.memories(secondId, limit) + val messagesSecondIdExpected = messages2.takeLast(limit) - messagesFirstId shouldBe messagesFirstIdExpected - messagesSecondId shouldBe messagesSecondIdExpected - } - - }) + messagesFirstId shouldBe messagesFirstIdExpected + messagesSecondId shouldBe messagesSecondIdExpected + } + }) diff --git a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt index 7cbb88a44..556e7db68 100644 --- a/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt +++ b/core/src/commonTest/kotlin/com/xebia/functional/xef/vectorstores/MemoryData.kt @@ -6,16 +6,22 @@ import com.xebia.functional.xef.llm.models.chat.Role val defaultConversationId = ConversationId("default-id") fun generateRandomMessages( - n: Int, - append: String? = null, - conversationId: ConversationId = defaultConversationId + n: Int, + append: String? = null, + conversationId: ConversationId = defaultConversationId, + startTimestamp: Long = 0 ): List = - (0 until n).flatMap { - listOf( - Memory(conversationId, Message(Role.USER, "Question $it${append?.let { ": $it" } ?: ""}", "USER"), 0), - Memory( - conversationId, - Message(Role.ASSISTANT, "Response $it${append?.let { ": $it" } ?: ""}", "ASSISTANT"), - 0), - ) - } + (0 until n).flatMap { + listOf( + Memory( + conversationId, + Message(Role.USER, "Question $it${append?.let { ": $it" } ?: ""}", "USER"), + startTimestamp + (it * 10) + ), + Memory( + conversationId, + Message(Role.ASSISTANT, "Response $it${append?.let { ": $it" } ?: ""}", "ASSISTANT"), + startTimestamp + (it * 10) + 1 + ), + ) + } diff --git a/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/text/Text.kt b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/text/Text.kt index 16881a600..ad035b718 100644 --- a/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/text/Text.kt +++ b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/text/Text.kt @@ -36,6 +36,18 @@ class Text( scope = scope ), @JvmField + val categoriesSelection: LLMTool = + LLMTool.create( + name = "CategoriesSelection", + description = "Return a list of categories", + model = model, + scope = scope, + instructions = + listOf( + "Return a list of categories for the `text`", + ) + ), + @JvmField val coreferenceResolution: LLMTool = LLMTool.create( name = "CoreferenceResolution", From 4a96424fa4f0fc94ccd77f9dbe9bd4a603273312 Mon Sep 17 00:00:00 2001 From: Javi Pacheco Date: Wed, 26 Jul 2023 15:33:01 +0200 Subject: [PATCH 8/8] Removing categoriesSelection --- .../com/xebia/functional/xef/reasoning/text/Text.kt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/text/Text.kt b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/text/Text.kt index ad035b718..16881a600 100644 --- a/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/text/Text.kt +++ b/reasoning/src/commonMain/kotlin/com/xebia/functional/xef/reasoning/text/Text.kt @@ -36,18 +36,6 @@ class Text( scope = scope ), @JvmField - val categoriesSelection: LLMTool = - LLMTool.create( - name = "CategoriesSelection", - description = "Return a list of categories", - model = model, - scope = scope, - instructions = - listOf( - "Return a list of categories for the `text`", - ) - ), - @JvmField val coreferenceResolution: LLMTool = LLMTool.create( name = "CoreferenceResolution",